我在一个项目中使用干净的Swift模式。该模式旨在解决MVC模式经常出现的大量视图控制器问题。
一般的想法是,ViewController将处理用户交互和一般业务逻辑的所有逻辑都推迟到交互器。该交互器执行其任务,然后将结果数据输出发送给演示者。演示者将数据包装到“视图模型”中,最后将其传递回ViewController。
这将使我能够轻松编写许多视图控制器需要的非常通用的函数(比如在加载时显示微调器、在safari视图中打开url等等)。
protocol CleanViewController: class {
associatedType Interactor: CleanInteractor
associatedType Router: CleanRouter
init()
init(interactor: Interactor, router: Router)
var interactor: Interactor! { get set }
var router: Router! { get set }
}
protocol CleanInteractor: class {
associatedType Presenter: CleanPresenter
init()
init(presenter: Presenter)
var presenter: Presenter! { get set }
}
protocol CleanPresenter: class {
associatedType ViewController: CleanViewController
init()
init(viewController: ViewController)
var viewController: ViewController? { get set }
}
protocol CleanRouter: class {
associatedType DataStore: CleanDataStore
associatedType ViewController: CleanViewController
init()
init(dataStore: DataStore, viewController: ViewController)
var dataStore: DataStore! {Â get set }
var viewController: ViewController? { get set }
}
所有这些都是相互参照的。这意味着,当我想要实现一个新的ViewController时,我需要以某种方式将它们链接在一起。现在这是通过定义
setup()
函数,从UIViewController的所有初始化器调用。
然后,每当我需要编写一个新的ViewController时,例如Login,它的实现方式如下:
protocol LoginDisplayLogic: CleanViewController {
func display(_ some: ViewModel)
}
class LoginViewController<Interactor: LoginBusinessLogic, Router: LoginRoutingLogic>: LoginDisplayLogic, UIViewController {
var interactor: Interactor!
func display(_ some: ViewModel) { // display something }
@IBAction func userDidTapLoginButton(_ sender: Any) { interactor.login() }
}
protocol LoginBusinessLogic: CleanInteractor {
func login()
}
protocol LoginDataStore: CleanDataStore {
var email: String {Â get set }
var password: String { get set }
}
class LoginInteractor<Presenter: LoginPresentationLogic>: LoginBusinessLogic, LoginDataStore {
var presenter: Presenter!
var email: String
var password: String
func login() {
// do something with email & password
}
}
protocol LoginPresentationLogic: CleanPresenter {
func display(_ loginResult: Result)
}
class LoginPresenter<ViewController: LoginDisplayLogic>: LoginPresentationLogic {
var viewController: ViewController?
func display(_ loginResult: Result) {
// wrap the result to a viewModel and call the viewController
}
}
protocol LoginRoutingLogic: CleanRouter {
func route(to some: Where)
}
class LoginRouter<DataStore: LoginDataStore, ViewController: LoginDisplayLogic>: LoginRoutingLogic {
func route(to some: Where) { // ... }
}
现在,我要做这个
设置()
函数泛型。由于每个协议都定义了自己的关联类型,所有这些类型都需要一个初始化器,所以我只需要编写一次。
所以我写了以下内容:
extension CleanViewController where
Interactor: CleanDataStore,
Interactor.Presenter.ViewController == Self,
Router.ViewController == Self,
Router.DataStore == Interactor {
func setup() {
let presenter = Interactor.Presenter(viewController: self)
let interactor = Interactor(presenter: presenter)
let router = Router(viewController: self, dataStore: interactor)
self.interactor = interactor
self.router = router
}
}
-
Referencing instance method 'setup()' on 'CleanViewController' requires the types 'LoginViewController<Interactor, Router>' and 'Router.ViewController' be equivalent
-
Referencing instance method 'setup()' on 'CleanViewController' requires the types 'Interactor' and 'Router.DataStore' be equivalent
-
Referencing instance method 'setup()' on 'CleanViewController' requires the types 'Router.ViewController' and 'Interactor.Presenter.ViewController' be equivalent
-
Referencing instance method 'setup()' on 'CleanViewController' requires the types 'Router' and 'Interactor.Presenter.ViewController.Router.ViewController.Router' be equivalent
这对我来说似乎很奇怪,因为延伸到哪里
设置()
声明的应确保所有这些关联类型都是等效的。
我知道这篇文章很长;复杂的一个,但我在这项工作中到底错过了什么?