Я использую шаблон 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()
, должно гарантировать, что все эти связанные типы эквивалентны.
Я знаю, что этот пост длинныйи сложный, но что именно мне здесь не хватает для этой работы?