When you use generic functions the type of the generic parameter is inferred from the type of the variable you assign the result to (or, in the case of a function that returns Void
, from the concrete type of a parameter you pass to an argument of type T
).
In your example, you do not tell the compiler what type your vc
variable is so you get an error. You can fix this easily like so:
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:
Since your question seems to be more along the lines of "How do I replicate Objective-C's notion of UIViewController *<Routable>
", which you can't do in Swift, I'll mention that you might find it worthwhile to review your design. When I moved from Objective-C to Swift I thought not being able to use something like UIViewController *<Routable>
would be hard to get around (and even filed a bug report with Apple about it) but in practice it hasn't been an issue.
Your Router
class needs some information in your view controller to be able to route properly. In your example you're looking for a view controller that is associated with a particular URL. The implication is that your UIViewController
has a property that contains this URL, so rather than return a Routable
protocol, the correct approach is to subclass UIViewController like so:
class RoutableViewController: UIViewController {
let url: URL = URL(string: "...")
}
Now your router looks like:
class Router {
var routables = [RoutableViewController]()
func viewControllerWithURL(_ url: URL) -> RoutableViewController? {
return routables.first
}
}
Now you don't need to do any type-casting, and type-safety is (obviously) maintained.
EDIT 2:
Here's a solution that makes every view controller conform to Routable
(I don't think you gain much vs. the solution I proposed in my previous edit, but here it is anyway):
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
}
}