Como faço para declarar uma variável que possui um tipo e implementa um protocolo?

Meu aplicativo tem um protocolo para controladores de exibição de detalhes, afirmando que eles devem ter uma propriedade viewModel :

 protocol DetailViewController: class { var viewModel: ViewModel? {get set} } 

Eu também tenho algumas classs diferentes que implementam o protocolo:

 class FormViewController: UITableViewController, DetailViewController { // ... } class MapViewController: UIViewController, DetailViewController { // ... } 

Meu controlador de visualização mestre precisa de uma propriedade que possa ser definida para qualquer subclass de UIViewController que implemente o protocolo DetailViewController .

Infelizmente não consigo encontrar nenhuma documentação sobre como fazer isso. Em Objective-C, seria trivial:

 @property (strong, nonatomic) UIViewController; 

Parece que não há syntax disponível no Swift para fazer isso. O mais próximo que cheguei é declarar um genérico na minha definição de class:

 class MasterViewController: UITableViewController { var detailViewController: T? // ... } 

Mas então eu recebo um erro dizendo que “Classe ‘MasterViewController’ não implementa os membros requeridos da sua superclass”

Isso parece que deve ser tão fácil de fazer no Swift quanto no Objective-C, mas não consigo encontrar nada em lugar algum que sugira como eu poderia fazer isso.

A partir do Swift 4, você pode fazer isso agora.

O Swift 4 implementou o SE-0156 (existenciais de class e subtipo).

O equivalente desta syntax do Objective-C:

 @property (strong, nonatomic) UIViewController * detailViewController; 

Agora se parece com isso no Swift 4:

 var detailViewController: UIViewController & DetailViewController 

Essencialmente, você pode definir uma class em que a variável está em conformidade e o número N de protocolos que ela implementa. Veja o documento vinculado para informações mais detalhadas.

Eu acho que você pode chegar lá adicionando uma extensão (vazia) ao UIViewController e, em seguida, especificando seu atributo detailViewController usando um protocolo composto da extensão vazia e seu DetailViewController . Como isso:

 protocol UIViewControllerInject {} extension UIViewController : UIViewControllerInject {} 

Agora todas as subclasss do UIViewController satisfazem o protocolo UIViewControllerInject . Então com isso, simplesmente:

 typealias DetailViewControllerComposed = protocol class MasterViewController : UITableViewController { var detailViewController : DetailViewControllerComposed? // ... } 

Mas isso não é particularmente “natural”.

=== Editar, adição ===

Na verdade, você poderia torná-lo um pouco melhor se você definir o seu DetailViewController usando o meu UIViewControllerInject sugerido. Como tal:

 protocol UIViewControllerInject {} extension UIViewController : UIViewControllerInject {} protocol DetailViewController : UIViewControllerInject { /* ... */ } 

e agora você não precisa compor explicitamente algo (meu DetailViewControllerComposed ) e pode usar o DetailViewController? como o tipo de detailViewController .

Outra maneira seria introduzir classs base intermediárias para os controladores de visualização apropriados do UIKit que implementam o protocolo:

 class MyUIViewControler : UIViewController, DetailViewController ... class MyUITableViewController : UITableViewController, DetailViewController ... 

Então você herda seus controladores de visualização desses controladores de visualização, não dos controladores UIKit.

Isso também não é natural, mas não força todos os seus UIViewControllers a satisfazer o protocolo UIViewControllerInject , como o GoZoner sugeriu.