ios - Creating a decoupled Router and ViewFactory
问题描述
I am experimenting with routing in an iOS app.
My current approach is hit a road block however, I am creating a ViewControllerFactory
and a NavigationControllerRouter
.
The challenge I have is that the ViewControllerFactory
needs to instruct the router on which route is required next and the NavigationControllerRouter
requires the factory to pass a view controller to the navigation controller.
How can these 2 components communicate without creating a strong coupling?
enum Route {
case login
case home
}
protocol ViewControllerFactory {
func create(for route: Route) -> UIViewController
}
class VCFactory: ViewControllerFactory {
func create(for route: Route) -> UIViewController {
switch route {
case .login:
let viewController = UIViewController()
viewController.view.backgroundColor = .purple
return viewController
case .home:
let viewController = UIViewController()
viewController.view.backgroundColor = .red
return viewController
}
}
}
class NavigationControllerRouter {
private let navigationController: UINavigationController
private let factory: ViewControllerFactory
init(_ navigationController: UINavigationController, factory: ViewControllerFactory) {
self.navigationController = navigationController
self.factory = factory
}
func route(to route: Route) {
let viewController = factory.create(for: route)
navigationController.pushViewController(viewController, animated: true)
}
}
I set this up as follows
@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
private lazy var navController = UINavigationController()
private lazy var router: NavigationControllerRouter = {
let viewControllerFactory = VCFactory()
return NavigationControllerRouter(navController, factory: viewControllerFactory)
}()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: scene)
configure(window)
}
func configure(_ window: UIWindow) {
self.window = window
self.window?.makeKeyAndVisible()
self.window?.rootViewController = navController
router.route(to: .login)
}
}
I had considered passing a weak reference for the router into the view controller factory, then injecting that into any rendered views, however this then couples the 2, especially if I decide to use the view factory for something like child views, which shouldn't know about the router at all.
解决方案
我认为传递对 your 的引用没有任何问题VCFactory
,只要它是一个弱引用以确保没有保留周期。
这与 MVP 方法中使用的模式非常相似,其中视图具有对演示者的引用,而演示者具有对视图的引用。
您可以通过使用协议来扩展它,以确保您ViewControllerFactory
不包含对路由器的未使用引用。
protocol Routable {
var router: NavigationControllerRouter? { get }
}
...
class VCFactory: ViewControllerFactory, Routable {
weak var router: NavigationControllerRouter?
....
}
然后,您应该能够在创建时传递参考
private lazy var router: NavigationControllerRouter = {
let viewControllerFactory = VCFactory()
let router = NavigationControllerRouter(navController, factory: viewControllerFactory)
viewControllerFactory.router = router
return router
}()
推荐阅读
- javascript - 如何简化我的双循环 javascript
- kotlin - 使用kotlin中的索引映射Arraylist的ArrayList
- log4j2 - Log4j2 Regexfilter 似乎忽略了过滤器配置
- unity3d - 是否有可能让 RactiveProperty 在 UniRx 中公开获取私有集
- javascript - 如何在javascript中检查对象数组和另一个对象数组之间的空数组
- python - python str Text等于dict的名称
- java - Java项目,Spring/显示学生问题/错误的数据库实现,SQL
- tensorflow.js - tfjs-models handpose 可以返回多个预测吗?
- git - 撤消推送并取消 PR 请求
- php - 什么是“掩盖”渐进式 ID 的好方法?