Skip to content

Commit f4115d5

Browse files
authored
[NL-42] AppRouter 초안 (#25)
* [NONE] - mege conflict * [NL-42] : LibTestCode 작성 * [NL-42] : AppRouter refactor - OnboardingRouter recactor * [NL-42] : AppRouter refactor - OnboardingRouter recactor * [NL-42] : mainactor 수정
1 parent 3354a56 commit f4115d5

22 files changed

+766
-111
lines changed

App/Sources/AppDelegate.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import DIInjector
2+
import UIKit
23
import Lib
34
import Onboarding
45
import Setting
@@ -11,6 +12,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1112
_ application: UIApplication,
1213
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
1314
) -> Bool {
15+
16+
registRouter()
1417

1518
return true
1619
}
@@ -42,6 +45,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
4245
}
4346

4447
private func registRouter() {
45-
48+
let appRouter = AppRouter.shared
49+
50+
appRouter.register(route: .onboarding(onboardingRoute: nil), factory: { OnboardingRouter() })
4651
}
4752
}

App/Sources/SceneDelegate.swift

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,45 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
55

66
var window: UIWindow?
77

8-
func scene(
8+
func scene(
99
_ scene: UIScene, willConnectTo session: UISceneSession,
1010
options connectionOptions: UIScene.ConnectionOptions
11-
) {
12-
guard let windowScene = (scene as? UIWindowScene) else { return }
13-
window = UIWindow(windowScene: windowScene)
14-
window?.windowScene = windowScene
11+
) {
12+
guard let windowScene = (scene as? UIWindowScene) else { return }
13+
window = UIWindow(windowScene: windowScene)
14+
window?.windowScene = windowScene
1515

16-
// TODO: Splash로 이동 or Home으로 이동 선택 로직 필요
17-
let viewController = LaunchScreenViewController()
16+
let viewController = LaunchScreenViewController()
1817

19-
window?.rootViewController = viewController
20-
window?.makeKeyAndVisible()
21-
}
18+
window?.rootViewController = viewController
19+
window?.makeKeyAndVisible()
20+
}
2221

23-
func sceneDidDisconnect(_ scene: UIScene) {
22+
func sceneDidDisconnect(_ scene: UIScene) {
2423
// Called as the scene is being released by the system.
2524
// This occurs shortly after the scene enters the background, or when its session is discarded.
2625
// Release any resources associated with this scene that can be re-created the next time the scene connects.
2726
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
28-
}
27+
}
2928

30-
func sceneDidBecomeActive(_ scene: UIScene) {
29+
func sceneDidBecomeActive(_ scene: UIScene) {
3130
// Called when the scene has moved from an inactive state to an active state.
3231
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
33-
}
32+
}
3433

35-
func sceneWillResignActive(_ scene: UIScene) {
34+
func sceneWillResignActive(_ scene: UIScene) {
3635
// Called when the scene will move from an active state to an inactive state.
3736
// This may occur due to temporary interruptions (ex. an incoming phone call).
38-
}
37+
}
3938

40-
func sceneWillEnterForeground(_ scene: UIScene) {
39+
func sceneWillEnterForeground(_ scene: UIScene) {
4140
// Called as the scene transitions from the background to the foreground.
4241
// Use this method to undo the changes made on entering the background.
43-
}
42+
}
4443

45-
func sceneDidEnterBackground(_ scene: UIScene) {
44+
func sceneDidEnterBackground(_ scene: UIScene) {
4645
// Called as the scene transitions from the foreground to the background.
4746
// Use this method to save data, release shared resources, and store enough scene-specific state information
4847
// to restore the scene back to its current state.
49-
}
50-
48+
}
5149
}

App/Sources/ViewController.swift

Lines changed: 0 additions & 11 deletions
This file was deleted.

Common/Lib/Sources/Router/AppRouter.swift

Lines changed: 117 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,56 +8,124 @@
88
import Foundation
99
import UIKit
1010

11-
public protocol Routable {
12-
func navigate(to route: AppRoute, from: Routable?, with data: [String: Any])
11+
public enum AppRoute : Hashable {
12+
case fortune
13+
case history
14+
case home
15+
case onboarding(onboardingRoute : OnboardingRoute?)
16+
case setting
17+
18+
public func hash(into hasher: inout Hasher) {
19+
switch self {
20+
case .fortune:
21+
hasher.combine(0)
22+
case .history:
23+
hasher.combine(1)
24+
case .home:
25+
hasher.combine(2)
26+
case .onboarding(onboardingRoute: let onboardingRoute):
27+
hasher.combine(3)
28+
case .setting:
29+
hasher.combine(4)
30+
}
31+
}
32+
33+
public static func == (lhs: AppRoute, rhs: AppRoute) -> Bool {
34+
switch (lhs, rhs) {
35+
case (.fortune, .fortune), (.history, .history), (.home, .home), (.setting, .setting):
36+
return true
37+
case (.onboarding, .onboarding):
38+
return true
39+
default:
40+
return false
41+
}
42+
}
43+
}
44+
45+
@MainActor
46+
public protocol Routable : AnyObject {
47+
func navigate(to route: Any, how : NavigateType, with data: [String: Any])
1348
}
1449

1550
public final class AppRouter: Routable {
16-
public static let shared = AppRouter()
17-
18-
private var factories: [AppRoute: () -> Routable] = [:]
19-
20-
private init() {}
21-
22-
public func register(route: AppRoute, factory: @escaping () -> Routable) {
23-
factories[route] = factory
24-
}
25-
26-
public func navigate(to route: AppRoute, from: Routable?, with data: [String: Any]) {
27-
guard let router = factories[route]?() else { return }
28-
guard let navigateType = data["navigateType"] as? NavigateType else { return }
29-
30-
// TODO: topview 찾고 -> UIWindow의 rootviewcon을 찾아서 -> Navigation이 될꺼고 -> 띄우는걸로
31-
// TODO: 모듈마다 모듈 내부 view 이동 router -> 해당 router는 approtuer 에서 관리
32-
// 여기서는 그냥 각 모듈을 present, push 하는 정도 역할만
33-
// 만약 스택을 비워야 한다면 해당 모듈에서 스택을 비우고 가는 걸로
34-
35-
// switch navigateType {
36-
// case .push:
37-
// if let navigationController = from?.navigationController {
38-
// navigationController.pushViewController(viewController, animated: true)
39-
// }
40-
// case .present:
41-
// from?.present(viewController, animated: true)
42-
// case .fullscreen:
43-
// viewController.modalPresentationStyle = .fullScreen
44-
// from?.present(viewController, animated: true)
45-
// case .currentContext:
46-
// viewController.modalPresentationStyle = .currentContext
47-
// from?.present(viewController, animated: true)
48-
// case .overFullScreen:
49-
// viewController.modalPresentationStyle = .overFullScreen
50-
// from?.present(viewController, animated: true)
51-
// case .overCurrentContext:
52-
// viewController.modalPresentationStyle = .overCurrentContext
53-
// from?.present(viewController, animated: true)
54-
// case .custom:
55-
// if let transitioningDelegate = data["transitioningDelegate"] as? UIViewControllerTransitioningDelegate {
56-
// viewController.modalPresentationStyle = .custom
57-
// viewController.transitioningDelegate = transitioningDelegate
58-
// }
59-
// from?.present(viewController, animated: true)
60-
// }
61-
62-
}
51+
public static let shared = AppRouter()
52+
53+
private var factories: [AppRoute: () -> Routable] = [:]
54+
55+
private init() {}
56+
57+
public func register(route: AppRoute, factory: @escaping () -> Routable) {
58+
factories[route] = factory
59+
}
60+
61+
public func navigate(to route: Any, how : NavigateType, with data: [String: Any]) {
62+
guard let appRoute = route as? AppRoute else { return }
63+
guard let factory = factories[appRoute] else { return }
64+
let subRouter = factory()
65+
66+
switch appRoute {
67+
case .fortune:
68+
break
69+
case .history:
70+
break
71+
case .home:
72+
break
73+
case .onboarding(let onboardingRoute):
74+
guard let onboardingRoute = onboardingRoute else { return }
75+
subRouter.navigate(to: onboardingRoute, how: how, with: data)
76+
case .setting:
77+
break
78+
}
79+
}
80+
}
81+
82+
extension Routable {
83+
public func topViewController( from base: UIViewController? = UIApplication.shared.connectedScenes
84+
.compactMap { ($0 as? UIWindowScene)?.keyWindow }
85+
.first?.rootViewController
86+
) -> UIViewController? {
87+
if let nav = base as? UINavigationController {
88+
return topViewController(from: nav.visibleViewController)
89+
}
90+
if let tab = base as? UITabBarController {
91+
return topViewController(from: tab.selectedViewController)
92+
}
93+
if let presented = base?.presentedViewController {
94+
return topViewController(from: presented)
95+
}
96+
return base
97+
}
98+
99+
public func manageViewController(_ viewController: UIViewController, how: NavigateType) {
100+
switch how {
101+
case .push:
102+
if let navigationController = topViewController()?.navigationController {
103+
navigationController.pushViewController(viewController, animated: true)
104+
}
105+
case .present:
106+
topViewController()?.present(viewController, animated: true, completion: nil)
107+
case .fullscreen:
108+
viewController.modalPresentationStyle = .fullScreen
109+
topViewController()?.present(viewController, animated: true, completion: nil)
110+
case .currentContext:
111+
viewController.modalPresentationStyle = .currentContext
112+
topViewController()?.present(viewController, animated: true, completion: nil)
113+
case .overFullScreen:
114+
viewController.modalPresentationStyle = .overFullScreen
115+
topViewController()?.present(viewController, animated: true, completion: nil)
116+
case .overCurrentContext:
117+
viewController.modalPresentationStyle = .overCurrentContext
118+
topViewController()?.present(viewController, animated: true, completion: nil)
119+
case .custom:
120+
viewController.modalPresentationStyle = .custom
121+
topViewController()?.present(viewController, animated: true, completion: nil)
122+
case .clear:
123+
if let navigationController = topViewController()?.navigationController {
124+
navigationController.viewControllers.removeAll()
125+
navigationController.pushViewController(viewController, animated: true)
126+
} else {
127+
topViewController()?.dismiss(animated: true, completion: nil)
128+
}
129+
}
130+
}
63131
}

Common/Lib/Sources/Router/Concreate/AppRoute.swift

Lines changed: 0 additions & 15 deletions
This file was deleted.

Common/Lib/Sources/Router/Concreate/NavigateType.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
//
77

88
public enum NavigateType {
9-
case push // NavigationController.pushViewController
10-
case present // 기본 present (모달)
11-
case fullscreen // modalPresentationStyle = .fullScreen
12-
case currentContext // modalPresentationStyle = .currentContext
13-
case overFullScreen // modalPresentationStyle = .overFullScreen
14-
case overCurrentContext // modalPresentationStyle = .overCurrentContext
15-
case custom // custom 전환 (transitioningDelegate 필요)
9+
case push // NavigationController.pushViewController
10+
case present // 기본 present (모달)
11+
case fullscreen // modalPresentationStyle = .fullScreen
12+
case currentContext // modalPresentationStyle = .currentContext
13+
case overFullScreen // modalPresentationStyle = .overFullScreen
14+
case overCurrentContext // modalPresentationStyle = .overCurrentContext
15+
case custom // custom 전환 (transitioningDelegate 필요)
16+
case clear //해당 router 관련 view 전부 지움
1617
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// OnbaordingRouter.swift
3+
// CommonLayer
4+
//
5+
// Created by 최재혁 on 8/2/25.
6+
//
7+
8+
import Foundation
9+
10+
public enum OnboardingRoute {
11+
case splash
12+
case onboarding
13+
case agreement
14+
case timePicker
15+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// AppDelegate.swift
3+
// CommonLayer
4+
//
5+
// Created by 최재혁 on 8/3/25.
6+
//
7+
8+
import UIKit
9+
10+
@main
11+
class AppDelegate: UIResponder, UIApplicationDelegate {
12+
13+
func application(
14+
_ application: UIApplication,
15+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
16+
) -> Bool {
17+
// Override point for customization after application launch.
18+
registRouter()
19+
return true
20+
}
21+
22+
// MARK: UISceneSession Lifecycle
23+
24+
func application(
25+
_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession,
26+
options: UIScene.ConnectionOptions
27+
) -> UISceneConfiguration {
28+
// Called when a new scene session is being created.
29+
// Use this method to select a configuration to create the new scene with.
30+
return UISceneConfiguration(
31+
name: "Default Configuration", sessionRole: connectingSceneSession.role)
32+
}
33+
34+
func application(
35+
_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>
36+
) {
37+
// Called when the user discards a scene session.
38+
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
39+
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
40+
}
41+
42+
private func registRouter() {
43+
let appRouter = TestAppRouter.shared
44+
45+
var libFactories: [LibRoute: () -> UIViewController] = [:]
46+
libFactories[.lib] = { LibViewController() }
47+
let libRouteFactory = { LibRouter(factories: libFactories) }
48+
49+
var subFactories: [SubRoute: () -> UIViewController] = [:]
50+
subFactories[.sub] = { SubViewController() }
51+
let subRouteFactory = { SubRouter(factories: subFactories)}
52+
53+
appRouter.register(route: .lib, factory: libRouteFactory)
54+
appRouter.register(route: .subRoute, factory: subRouteFactory)
55+
}
56+
}

0 commit comments

Comments
 (0)