Skip to content

Commit

Permalink
✨ :: Moordinator와 Router의 역할 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
baekteun committed May 2, 2023
1 parent 1da1489 commit fc4a7c9
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
46522CA42A00A7B800064D9A /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46522CA32A00A7B800064D9A /* AppRouter.swift */; };
4662905E295D38ED00B5148C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4662905D295D38ED00B5148C /* AppDelegate.swift */; };
46629060295D38ED00B5148C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4662905F295D38ED00B5148C /* SceneDelegate.swift */; };
46629062295D38ED00B5148C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46629061295D38ED00B5148C /* ViewController.swift */; };
Expand All @@ -26,6 +27,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
46522CA32A00A7B800064D9A /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
4662905A295D38ED00B5148C /* MoordinatorExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MoordinatorExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
4662905D295D38ED00B5148C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
4662905F295D38ED00B5148C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -84,6 +86,7 @@
46AD55ED295D3BE3006858AC /* Root */,
4662905D295D38ED00B5148C /* AppDelegate.swift */,
4662905F295D38ED00B5148C /* SceneDelegate.swift */,
46522CA32A00A7B800064D9A /* AppRouter.swift */,
46629061295D38ED00B5148C /* ViewController.swift */,
46629066295D38EE00B5148C /* Assets.xcassets */,
46629068295D38EE00B5148C /* LaunchScreen.storyboard */,
Expand Down Expand Up @@ -223,6 +226,7 @@
46AD5604295D4695006858AC /* MainViewController.swift in Sources */,
46AD55FB295D3CEC006858AC /* MainRouter.swift in Sources */,
46AD5606295D46D8006858AC /* SubViewController.swift in Sources */,
46522CA42A00A7B800064D9A /* AppRouter.swift in Sources */,
46AD560B295D486B006858AC /* SubDetailViewController.swift in Sources */,
4662905E295D38ED00B5148C /* AppDelegate.swift in Sources */,
46AD5602295D4602006858AC /* SubMoordinator.swift in Sources */,
Expand Down
9 changes: 9 additions & 0 deletions MoordinatorExample/MoordinatorExample/AppRouter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Combine
import Moordinator

struct AppRouter: Router {
var route: PassthroughSubject<any RoutePath, Never> = .init()
var initialPath: any RoutePath {
ExRoutePath.main
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ import UIKit

final class MainMoordinator: Moordinator {
private let rootVC = UINavigationController()
let router: any Router = MainRouter()

var root: Presentable {
rootVC
}

func route(to path: RoutePath) -> MoordinatorContributors {
func route(to path: any RoutePath) -> MoordinatorContributors {
guard let path = path as? ExRoutePath else { return .none }
switch path {
case .main:
let vc = MainViewController(router: router)
let vc = MainViewController()
rootVC.setViewControllers([vc], animated: true)
return .one(.contribute(withNextPresentable: vc, withNextRouter: vc))

case .sub:
return .one(.forwardToParent(with: ExRoutePath.sub))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import UIKit
import Combine
import Moordinator

final class MainViewController: UIViewController {
final class MainViewController: UIViewController, Router {
var route: PassthroughSubject<any RoutePath, Never> = .init()
private let button = UIButton()
private let router: any Router
var bag = Set<AnyCancellable>()

public init(router: any Router) {
self.router = router
public init() {
super.init(nibName: nil, bundle: nil)
}

Expand All @@ -29,6 +28,6 @@ final class MainViewController: UIViewController {
}

@objc func buttonDidTap(_ sender: UIButton) {
self.router.route.send(ExRoutePath.sub)
self.route.send(ExRoutePath.sub)
}
}
13 changes: 9 additions & 4 deletions MoordinatorExample/MoordinatorExample/Root/RootMoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ final class RootMoordinator: Moordinator {
let launchScreen = launchScreenStoryboard.instantiateViewController(withIdentifier: "LaunchScreen")
return launchScreen
}()
let router: any Router = RootRouter()

var root: Presentable {
rootVC
Expand All @@ -21,7 +20,7 @@ final class RootMoordinator: Moordinator {
window.makeKeyAndVisible()
}

func route(to path: RoutePath) -> MoordinatorContributors {
func route(to path: any RoutePath) -> MoordinatorContributors {
guard let path = path as? ExRoutePath else { return .none }
switch path {
case .main:
Expand All @@ -36,7 +35,10 @@ final class RootMoordinator: Moordinator {
completion: nil
)
}
return .one(.contribute(mainMoordinator))
return .one(.contribute(
withNextPresentable: mainMoordinator,
withNextRouter: DisposableRouter(singlePath: ExRoutePath.main)
))

case .sub:
let subMoordinator = SubMoordinator()
Expand All @@ -50,7 +52,10 @@ final class RootMoordinator: Moordinator {
completion: nil
)
}
return .one(.contribute(subMoordinator))
return .one(.contribute(
withNextPresentable: subMoordinator,
withNextRouter: DisposableRouter(singlePath: ExRoutePath.sub)
))

default:
return .none
Expand Down
3 changes: 2 additions & 1 deletion MoordinatorExample/MoordinatorExample/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?
private let moordinatorWorker = MoordinatorWorker()
private let appRouter = AppRouter()

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
Expand All @@ -13,7 +14,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
guard let scene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: scene)
let rootMoordinator = RootMoordinator(window: window)
moordinatorWorker.coordinate(moordinator: rootMoordinator)
moordinatorWorker.coordinate(moordinator: rootMoordinator, with: appRouter)
self.window = window
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import Combine
import UIKit
import Moordinator

final class SubDetailViewController: UIViewController {
private let router: any Router

public init(router: any Router) {
self.router = router
final class SubDetailViewController: UIViewController, Router {
var route: PassthroughSubject<any RoutePath, Never> = .init()
public init() {
super.init(nibName: nil, bundle: nil)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ import UIKit

final class SubMoordinator: Moordinator {
private let rootVC = UINavigationController()
let router: any Router = SubRouter()

var root: Presentable {
rootVC
}

func route(to path: RoutePath) -> MoordinatorContributors {
func route(to path: any RoutePath) -> MoordinatorContributors {
guard let path = path as? ExRoutePath else { return .none }
switch path {
case .sub:
let vc = SubViewController(router: router)
let vc = SubViewController()
rootVC.setViewControllers([vc], animated: true)
return .one(.contribute(withNextPresentable: vc, withNextRouter: vc))

case .subDetail:
let vc = SubDetailViewController(router: router)
let vc = SubDetailViewController()
rootVC.pushViewController(vc, animated: true)
return .one(.contribute(withNextPresentable: vc, withNextRouter: vc))

case .main:
return .one(.forwardToParent(with: ExRoutePath.main))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import UIKit
import Combine
import Moordinator

final class SubViewController: UIViewController {
final class SubViewController: UIViewController, Router {
var route: PassthroughSubject<any RoutePath, Never> = .init()
private let mainButton = UIButton()
private let detailButton = UIButton()
private let router: any Router
var bag = Set<AnyCancellable>()

public init(router: any Router) {
self.router = router
public init() {
super.init(nibName: nil, bundle: nil)
}

Expand All @@ -36,9 +35,9 @@ final class SubViewController: UIViewController {
}

@objc func mainButtonDidTap(_ sender: UIButton) {
self.router.route.send(ExRoutePath.main)
self.route.send(ExRoutePath.main)
}
@objc func detailButtonDidTap(_ sender: UIButton) {
self.router.route.send(ExRoutePath.subDetail)
self.route.send(ExRoutePath.subDetail)
}
}
7 changes: 7 additions & 0 deletions Sources/Moordinator/DefaultRouter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Combine

public final class DefaultRouter: Router {
public var route: PassthroughSubject<any RoutePath, Never> = .init()

public init() {}
}
2 changes: 1 addition & 1 deletion Sources/Moordinator/DisposableRouter.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Combine

public final class DisposableRouter: Router {
public var route: PassthroughSubject<RoutePath, Never> = .init()
public var route: PassthroughSubject<any RoutePath, Never> = .init()

public var initialPath: any RoutePath {
singlePath
Expand Down
1 change: 0 additions & 1 deletion Sources/Moordinator/Moordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import UIKit

public protocol Moordinator: Presentable {
var root: Presentable { get }
var router: any Router { get }

func route(to path: any RoutePath) -> MoordinatorContributors
}
5 changes: 4 additions & 1 deletion Sources/Moordinator/MoordinatorContributor.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import UIKit

public enum MoordinatorContributor {
case contribute(any Presentable)
case contribute(
withNextPresentable: any Presentable,
withNextRouter: any Router = DefaultRouter()
)
case forward(with: any RoutePath)
case forwardToParent(with: any RoutePath)
}
49 changes: 41 additions & 8 deletions Sources/Moordinator/MoordinatorWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ public final class MoordinatorWorker {
private var bag = Set<AnyCancellable>()
private var childMoordinatorWorker: [String: MoordinatorWorker] = [:]
private weak var parentMoordinatorWorker: MoordinatorWorker?
private let route = PassthroughSubject<RoutePath, Never>()
private let route = PassthroughSubject<any RoutePath, Never>()

public init() {}

public func coordinate(moordinator: any Moordinator) {
public func coordinate(
moordinator: any Moordinator,
with router: any Router = DefaultRouter()
) {
self.route
.append(Publishers.Merge(moordinator.router.route, route))
.append(Publishers.Merge(router.route, route))
.receive(on: RunLoop.main)
.map { moordinator.route(to: $0) }
.sink { [weak self] contributors in
.handleEvents(receiveOutput: { [weak self] contributors in
switch contributors {
case .none:
return
Expand All @@ -33,16 +36,22 @@ public final class MoordinatorWorker {
.childMoordinatorWorker
.removeValue(forKey: self?.id ?? "")
}
})
.map { [weak self] in self?.nextRouters(from: $0) ?? [] }
.flatMap { $0.publisher.eraseToAnyPublisher() }
.flatMap { [weak self] in self?.steps(from: $0) ?? Empty().eraseToAnyPublisher() }
.sink { [weak self] path in
self?.route.send(path)
}
.store(in: &bag)

moordinator.router.route
router.route
.sink { [weak self] route in
self?.route.send(route)
}
.store(in: &bag)

Just(moordinator.router.initialPath)
Just(router.initialPath)
.sink { [weak self] route in
self?.route.send(route)
}
Expand All @@ -51,12 +60,12 @@ public final class MoordinatorWorker {

private func performSideEffect(contributor: MoordinatorContributor) {
switch contributor {
case let .contribute(presentable):
case let .contribute(presentable, router):
if let childMoordinator = presentable as? Moordinator {
let worker = MoordinatorWorker()
worker.parentMoordinatorWorker = self
self.childMoordinatorWorker[worker.id] = worker
worker.coordinate(moordinator: childMoordinator)
worker.coordinate(moordinator: childMoordinator, with: router)
}

case let .forward(path):
Expand All @@ -66,4 +75,28 @@ public final class MoordinatorWorker {
self.parentMoordinatorWorker?.route.send(path)
}
}

private func nextRouters(from contributors: MoordinatorContributors) -> [any Router] {
switch contributors {
case let .one(.contribute(_, router)):
return [router]

case let .multiple(moordinatorContributors):
return moordinatorContributors.compactMap {
if case let .contribute(_, router) = $0 {
return router
}
return nil
}

default:
return []
}
}

private func steps(from router: any Router) -> AnyPublisher<any RoutePath, Never> {
router.route
.filter { !($0 is NoneRoutePath) }
.eraseToAnyPublisher()
}
}

0 comments on commit fc4a7c9

Please sign in to comment.