Swift:确保具有相关类型的不同协议的等效性

时间:2019-06-21 12:34:59

标签: swift generics generic-programming clean-architecture associated-types

我正在为项目使用Clean Swift模式。该模式旨在解决MVC模式通常伴随的Massive View Controller问题。

一般想法指出,ViewController将所有处理用户交互的逻辑和一般业务逻辑推迟到一个Interactor。该Interactor会执行其操作,然后将结果数据输出发送到Presenter。 Presenter将该数据包装到“视图模型”中,最后将其传递回ViewController。

为了有效地实现此功能并允许进行模拟,我决定编写类需要采用的协议。

这将使我能够轻松编写许多视图控制器所需的非常通用的功能(例如在加载时显示微调器,在野生动物园视图中打开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) { // ... }
}

现在,我想使此setup()函数通用。由于每种协议都定义了自己的关联类型,而所有类型都需要初始化程序,因此我只需要编写一次即可。

所以我要做的是写以下内容:

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
    }
}

但是如果随后从我的LoginViewController调用此函数,则XCode会引发以下编译错误:

  • 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

在我看来这很奇怪,因为在声明setup()的扩展名中应确保所有这些关联的类型都是等效的。

我知道这篇文章是一篇冗长而复杂的文章,但是我在此工作中到底缺少什么呢?

0 个答案:

没有答案