Instancetype de retorno em Swift

Estou tentando fazer essa extensão:

extension UIViewController { class func initialize(storyboardName: String, storyboardId: String) -> Self { let storyboad = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! Self return controller } } 

Mas eu recebo erro de compilation:

erro: não é possível converter a expressão de retorno do tipo ‘UIViewController’ para retornar o tipo ‘Self’

É possível? Também quero torná-lo como init(storyboardName: String, storyboardId: String)

Semelhante ao uso de ‘self’ em funções de extensão de class no Swift , você pode definir um método auxiliar genérico que infere o tipo de self do contexto de chamada:

 extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { return instantiateFromStoryboardHelper(storyboardName, storyboardId: storyboardId) } private class func instantiateFromStoryboardHelper(storyboardName: String, storyboardId: String) -> T { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewControllerWithIdentifier(storyboardId) as! T return controller } } 

Então

 let vc = MyViewController.instantiateFromStoryboard("name", storyboardId: "id") 

compila e o tipo é inferido como MyViewController .


Atualização para o Swift 3:

 extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { return instantiateFromStoryboardHelper(storyboardName: storyboardName, storyboardId: storyboardId) } private class func instantiateFromStoryboardHelper(storyboardName: String, storyboardId: String) -> T { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) as! T return controller } } 

Outra solução possível, usando unsafeDowncast :

 extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) return unsafeDowncast(controller, to: self) } } 

Self é determinado em tempo de compilation, não em tempo de execução. Em seu código, Self é exatamente equivalente a UIViewController , não “a subclass que está chamando isso”. Isso vai retornar UIViewController e o chamador terá que entrar na subclass direita. Eu suponho que é isso que você estava tentando evitar (embora seja a maneira “normal do Cocoa” de fazê-lo, então apenas retornar o UIViewController é provavelmente a melhor solução).

Nota: Você não deve nomear a function initialize em nenhum caso. Essa é uma function de class existente do NSObject e causaria confusão na melhor das hipóteses, erros na pior das hipóteses.

Mas se você quiser evitar o chamador, a subclass não é geralmente a ferramenta para adicionar funcionalidade no Swift. Em vez disso, você geralmente quer genéricos e protocolos. Neste caso, os genéricos são tudo que você precisa.

 func instantiateViewController(storyboardName: String, storyboardId: String) -> VC { let storyboad = UIStoryboard(name name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! VC return controller } 

Este não é um método de class. É apenas uma function. Não há necessidade de uma aula aqui.

 let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId) 

Outra maneira é usar um protocolo, que também permite retornar Self .

 protocol StoryboardGeneratable { } extension UIViewController: StoryboardGeneratable { } extension StoryboardGeneratable where Self: UIViewController { static func initialize(storyboardName: String, storyboardId: String) -> Self { let storyboad = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewController(withIdentifier: storyboardId) as! Self return controller } }