代码之家  ›  专栏  ›  技术社区  ›  Skwiggs

Swift:确保不同协议与相关类型的等价性

  •  0
  • Skwiggs  · 技术社区  · 6 年前

    我在一个项目中使用干净的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

    这对我来说似乎很奇怪,因为延伸到哪里 设置() 声明的应确保所有这些关联类型都是等效的。

    我知道这篇文章很长;复杂的一个,但我在这项工作中到底错过了什么?

    0 回复  |  直到 6 年前