Swift: обеспечить эквивалентность из разных протоколов со связанными типами - PullRequest
1 голос
/ 21 июня 2019

Я использую шаблон Clean Swift для проекта.Этот шаблон направлен на решение проблемы Massive View Controller, которая часто поставляется с шаблоном MVC.

Общая идея гласит, что ViewController откладывает всю логику, которая обрабатывает взаимодействие с пользователем и общую бизнес-логику, на Interactor.Этот Interactor делает свое дело, а затем отправляет полученные данные в Presenter.Этот Presenter оборачивает эти данные в «модель представления» и, наконец, передает их обратно ViewController.

Чтобы эффективно реализовать это и учесть насмешки, я решил написать протоколы, которые мои классы должны будут принять.

Это позволит мне легко написать очень общие функции, которые нужны многим контроллерам представлений (например, отображение счетчика во время загрузки, открытие URL-адресов в представлении safari и т. Д.).

По сути, мойпротоколы определены так:

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(), должно гарантировать, что все эти связанные типы эквивалентны.

Я знаю, что этот пост длинныйи сложный, но что именно мне здесь не хватает для этой работы?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...