Coordinatorパターンを採用する際に便利なライブラリがあると言うことなので使おうとしましたが、和訳記事が見当たらなかったのでREADMEをできる限り和訳してみました。
Google翻訳をベースにしているので、もしかしたら意味が通ってない部分があるかと思いますがご容赦いただきたいです。
XCoordinatorを使用する理由
- ナビゲーションコードはすでに記述されている。
- 関心事の分離
- Coordinator:一連のルート
- Route:ナビゲーション先
- Transition:遷移の種類とアニメーション
- Coordinator、Router、Transitionをさまざまな組み合わせで再利用できる。
- CustomTransition/animationの完全サポート。
- 子View/コンテナビューの埋め込みのサポート。
- BasicCoordinatorが多くの使用ケースに適したジェネリッククラスであるため、独自のCoordinatorを作成する必要が少なくなる。
導入
アプリの起動からXCoordinatorを使用する
-
Info.plist
のStoryboard file base name
からMain
を削除 -
下のコードを
AppDelegate.swift
に追加
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let window: UIWindow! = UIWindow()
let router = AppCoordinator().strongRouter
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
router.setRoot(for: window)
return true
}
}
XCoordinatorを使用する
接続したいViewを含む列挙型を作成します。
enum UserListRoute: Route {
case home
case users
case user(String)
case registerUsersPeek(from: Container)
case logout
}
Route
に書かれている経路は、どのRoute
にtrigger
できるかを表します。
Coordinator
はtrigger
されたRoute
に基づいて、遷移の準備を担当します。
class UserListCoordinator: NavigationCoordinator<UserListRoute> {
init() {
super.init(initialRoute: .home)
}
override func prepareTransition(for route: UserListRoute) -> NavigationTransition {
switch route {
case .home:
let viewController = HomeViewController.instantiateFromNib()
let viewModel = HomeViewModelImpl(router: unownedRouter)
viewController.bind(to: viewModel)
return .push(viewController)
case .users:
let viewController = UsersViewController.instantiateFromNib()
let viewModel = UsersViewModelImpl(router: unownedRouter)
viewController.bind(to: viewModel)
return .push(viewController, animation: .interactiveFade)
case .user(let username):
let coordinator = UserCoordinator(user: username)
return .present(coordinator, animation: .default)
case .registerUsersPeek(let source):
return registerPeek(for: source, route: .users)
case .logout:
return .dismiss()
}
}
}
UserListRoute
は、フローのtrigger
を定義する列挙型を作成します。
例えば.home
がtrigger
されるとHomeViewController
が表示されます。
Route
は、CoordinatorまたはViewModel内からtrigger
されます。
下記のコードはViewModel内からRouteをtriggerする方法です。
class HomeViewModel {
let router: UnownedRouter<HomeRoute>
init(router: UnownedRouter<HomeRoute>) {
self.router = router
}
/* ... */
func usersButtonPressed() {
router.trigger(.users)
}
}
コンポーネント
Route
関連するナビゲーションパス
Coordinator/ルーター
Viewを読み込み、triggerされたRouteに基づいてviewModelsを作成するオブジェクト。
StrongRouter
元のCoordinatorへの強い参照を保持しています。子Coordinatorを保持したり、AppDelegateで特定のルーターを指定したりできます。
WeakRouter
元のCoordinatorへの弱い参照を保持しています。viewControllerまたはviewModelにCoordinatorを保持できます。また、兄弟または親のCoordinatorへの参照を保持するために使用することもできます。
UnownedRouter
元のCoordinatorへの所有されていない参照を保持します。viewControllerまたはviewModelにCoordinatorを保持できます。また、兄弟または親のCoordinatorへの参照を保持するために使用することもできます。
Transition
遷移方法を指定できる。使用中のrootViewControllerのタイプに基づいて利用できます。
例:ViewTransitionはすべてのrootViewControllerがサポートする基本的なTransitionのみをサポートしますが、NavigationTransitionはNavigationController固有のTransitionを追加します。
使用可能な遷移タイプは次のとおりです。
present
View階層の最上位にViewControllerを提示します
(rootViewControllerからする場合は、presentOnRootを使用します)
embed
ViewControllerをコンテナビューに埋め込みます
dismiss
最も上位に表示されているViewControllerを却下します
(dismissToRootを使用してrootViewControllerでdismissを呼び出します)
none
何もしません。Routeを無視したり、テスト目的で使用したりできます。
push
ViewControllerをNavigationStackにpushします
(NavigationTransition内のみ)
pop
NavigationStackからトップビューコントローラーをpopします
(NavigationTransition内のみ)
popToRoot
rootViewControllerを除くNavigationStack上のすべてのViewControllerをpopします
(NavigationTransition内のみ)
Coordinatorパターン参考記事
間違い等あればご指摘いただけると幸いです。