Использование себя в функции в протоколе - PullRequest
0 голосов
/ 22 ноября 2018

У меня есть следующие протоколы:

Один для создания экземпляра ViewController из раскадровки:

protocol Storyboarded {
    static func instantiate() -> Self
}

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {

        // this pulls out "MyApp.MyViewController"
        let fullName = NSStringFromClass(self)

        // this splits by the dot and uses everything after, giving "MyViewController"
        let className = fullName.components(separatedBy: ".")[1]

        // load our storyboard
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

        // instantiate a view controller with that identifier, and force cast as the type that was requested
        return storyboard.instantiateViewController(withIdentifier: className) as! Self
    }
}

Один для добавления зависимостей в Viewcontrollers:

protocol DependencyInjection where Self: UIViewController {
    associatedtype myType: DependencyVC
    func injectDependencys(dependency: myType)
}

Теперь яхочу добавить еще один, чтобы я мог создать ViewController из самой зависимости:

protocol DependencyVC {
    associatedtype myType: DependencyInjectionVC & Storyboarded
    func createVC() -> myType
}


extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self {
        let viewController = T.instantiate()
        viewController.injectDependencys(dependency: self)
        return viewController
    }

}

Но я получаю эту ошибку для себя:

Невозможно вызвать 'injectDependencys' со списком аргументов типа '(dependency: Self)'

Это DependencyClass У меня есть:

class TopFlopDependency: DependencyVC {
    typealias myType = TopFlopVC


    var topFlopState: TopFlopState

    lazy var topFlopConfig: TopFlopConfig = {
        let SIBM = StatIntervalBaseModel(stat: "ppc", interval: "24h", base: "usd")
        return TopFlopConfig(group: Groups.large, base: "usd", valueOne: SIBM)
    }()

    init(state: TopFlopState) {
        self.topFlopState = state
    }

    func createVC() -> TopFlopVC {
        let topflopVC = TopFlopVC.instantiate()
        topflopVC.injectDependencys(dependency: self)

        let viewController: TopFlopVC = makeVC()

        return topflopVC
    }
}

Я получаю эту ошибку при использовании makeVC:

«TopFlopDependency» требует, чтобы типы «TopFlopDependency.myType» и «TopFlopDependency.myType» (также известный как «TopFlopVC») были эквивалентны использованию «makeVC»

другихРешение:

protocol DependencyVC {   
}
extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self {
        let viewController = T.instantiate()
        viewController.injectDependencys(dependency: self)
        return viewController
    }
}

При попытке использовать:

let viewController: TopFlopVC = makeVC()

Я получаю сообщение об ошибке, что T не может быть выведен.

Почему я не могу этого сделать?У вас есть решение, как мне заставить его работать?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Вам нужно добавить еще одно ограничение.Ваш протокол DependencyInjection требует очень определенного типа DependencyVC (myType).Но ваше расширение DependencyVC работает с любым DependencyVC.Таким образом, вам нужно ограничить myType T тем же типом с предложением where: func createVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self

Так что полный пример будет выглядеть следующим образом:

protocol Storyboarded {
    static func instantiate() -> Self
}

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        ...
    }
}

protocol DependencyVC {
}

protocol DependencyInjection where Self: UIViewController {
    associatedtype myType: DependencyVC
    func injectDependencys(dependency: myType)
}

extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>(type _: T.Type? = nil) -> T where T.myType == Self {
        let viewController = T.instantiate()
        viewController.injectDependencys(dependency: self)
        return viewController
    }
}

struct MyDependency: DependencyVC {}

class MyVC: UIViewController, Storyboarded, DependencyInjection {
    func injectDependencys(dependency: MyDependency) {
        print(dependency)
    }
}
0 голосов
/ 22 ноября 2018

Когда вы звоните viewController.injectDependencys(dependency: self), self, как известно, имеет некоторый подтип DependencyVC.Однако DependencyInjection associatedtype myType: DependencyVC просто говорит, что тип, соответствующий DependencyInjection, будет использовать некоторый тип для myType (что соответствует DependencyVC).Таким образом, нет никакой гарантии, что его фактический тип будет подтипом myType.

associatedtype s не совсем работает так же, как параметры универсального типа в том, что associatedtype s задаются при "определении«тип, в то время как параметры универсального типа задаются при« использовании »типа.

Все сводится к тому, что вы , вероятно, не хотите иметь associatedtype myType,вместо этого мы принимаем DependencyVC напрямую.

Обновление

В свете предоставленной вами дополнительной информации, я считаю, что это будет лучшим решением:

protocol DependencyInjection where Self: UIViewController {
    func injectDependency(_ dependency: DependencyVC)
}

protocol DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T
}

extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T {
        let viewController = T.instantiate()
        viewController.injectDependency(self)
        return viewController
    }
}

Как вы можете заметить, я позволил себе переименовать injectDependencys(dependency: DependencyVC) в injectDependency(_ dependency: DependencyVC), потому что вы вводите только одну зависимость, а метка dependency: на самом деле ничего не добавляет на сайт вызова.

В любом случае, это позволяет вам создавать экземпляры контроллеров представления, используя вашу зависимость.Скажем, у вас есть зависимость, хранящаяся в переменной с именем dependency, затем вы можете создать контроллер представления из нее, набрав let topFlopVC: TopFlopVC = dependency.makeVC()

...