Когда вы используете общие функции, тип универсального параметра выводится из типа переменной, которой вы назначаете результат (или, в случае функции, возвращающей Void
, из конкретного типа параметра, который вы передаете аргументу тип T
).
В вашем примере вы не сообщаете компилятору, какой тип переменной vc
, чтобы вы получили сообщение об ошибке. Вы можете исправить это легко, как так:
class MyViewController: UIViewController, Routable {
public static func provideInstance(id: String?) -> Self? {
return self.init()
}
}
// give `vc` a concrete type so `T` can be inferred:
let vc: MyViewController? = Router().getDestinationViewController(url: URL(string: "www.domain.com/users/1")!)
EDIT:
Поскольку ваш вопрос, кажется, больше вдоль линий «Как реплицировать Objective-C понятие по UIViewController *<Routable>
», который вы не можете сделайте в Swift, я упомянул, что вам, возможно, стоит рассмотреть ваш дизайн. Когда я перешел из Objective-C в Swift, я думал, что не использовать что-то вроде UIViewController *<Routable>
было бы трудно обойти (и даже подал отчет об ошибке с Apple об этом), но на практике это не было проблемой.
Ваш Router
класс необходим информация на вашем контроллере, чтобы иметь возможность правильно маршрутизировать. В вашем примере вы ищете контроллер представления, связанный с определенным URL-адресом. Смысл в том, что ваш UIViewController
имеет свойство, который содержит этот URL, так, а не возвращать протокол Routable
, правильный подход к подклассу UIViewController как так:
class RoutableViewController: UIViewController {
let url: URL = URL(string: "...")
}
Теперь маршрутизатор выглядит следующим образом:
class Router {
var routables = [RoutableViewController]()
func viewControllerWithURL(_ url: URL) -> RoutableViewController? {
return routables.first
}
}
Теперь вам не нужно делать литье под давлением, а безопасность типа (очевидно) поддерживается.
EDIT 2:
Вот решение, которое делает каждый контроллер вид соответствует Routable
(я не думаю, что вы получите много против решения я предложил в моем предыдущем редактировать, но здесь это в любом случае) :
protocol Routable {
var url: URL? { get }
}
// now every UIViewController conforms to Routable
extension UIViewController: Routable {
var url: URL? { return nil }
}
class MyViewController: UIViewController {
private let myURL: URL
override var url: URL? { return myURL }
init(url: URL) {
myURL = url
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class Router {
var viewControllers: [UIViewController] = [
MyViewController(url: URL(string: "foo")!),
UIViewController()
]
func viewController(for url: URL) -> UIViewController? {
for viewController in viewControllers {
if viewController.url == url { return viewController }
}
return nil
}
}
действительно ли невозможно вернуть UIViewController, который также является маршрутизируемым? Это возможно в objc '- (UIViewController *) gimeeSomething' –
aryaxt
Это невозможно выразить в Swift. Вам нужно будет добавить любые специальные методы, которые вы хотите от UIViewController, к протоколу «Routable». Это известное ограничение и, вероятно, будет улучшено в будущих версиях Swift, но в настоящее время это невозможно. –