protocols - inside - swift protocol and delegate example
Cómo pasar un protocolo como parámetro en Swift (5)
En Objective-C, sé cómo pasar un protocol
como parámetro:
- (void)MyMethod:(Protocol *)myparameter
Pero en Swift no hay más tipo de Protocol
.
¿Cómo puedo pasar un protocolo como parámetro sin saber cuál es?
Diseñó una manera hoy (Xcode 6.1):
En primer lugar, el protocolo debe estar marcado como @objc para que cualquier comprobación funcione. Luego use un molde "if let" para verificar la conformidad.
@objc protocol MyProtocol {
var protocolValue: Int { get set }
}
if let conformingObject = someObject as? MyProtocol {
// conformingObject is now someObject cast to MyProtocol
conformingObject.protocolValue = 3
}
En Swift 2.0, lo uso así antes:
classA.conformsToProtocol(XXXProtocol.self as! Protocol)
No funciona bien ...
Mira la definición de Protocol:
// All methods of class Protocol are unavailable.
// Use the functions in objc/runtime.h instead.
@available(iOS 2.0, *)
public class Protocol {
}
Todos no están disponibles ... y no sé cuál usar en objc / runtime.h
Así que tengo que usar este método:
if ClassA is protocol<XXXProtocol> {
// do something
}
Actualmente funciona ...
En uno de tus comentarios dices:
"Quiero crear un método que devuelva una matriz de tipo de clase que implemente un protocolo deseado".
¿Has probado algo como lo siguiente?
//notice the use of @objc here
@objc protocol AlertProtocol
{
func getMyName()->String
}
class Class1 : AlertProtocol
{
let name = "Object 1"
func getMyName() -> String
{
return name
}
}
class Class2 : AlertProtocol
{
let name = "Object 2"
func getMyName() -> String
{
return name
}
}
//borrowing from and refactoring siLo''s answer
func classesConformingToProtocol(proto:Protocol) -> [AnyClass]
{
let availableClasses : [AnyClass] = [ Class1.self, Class2.self ]
var conformingClasses = Array<AnyClass>()
for myClass : AnyClass in availableClasses
{
if myClass.conforms(to: proto)
{
conformingClasses.append(myClass)
}
}
return conformingClasses
}
Luego usa la estructura anterior como esta:
let classes = classesConformingToProtocol(AlertProtocol.self)
La parte difícil que hace el trabajo es el "@objc" que expone el protocolo al tiempo de ejecución objetivo y nos permite pasar cualquier "Tipo de protocolo" como parámetro.
Probablemente en algún momento en el futuro podamos hacer esto de una manera "pura" y rápida.
Esto es lo que he intentado:
@objc protocol Walker
{
func walk()
}
@objc protocol Runner
{
func run()
}
@objc class Zombie : Walker
{
func walk () { println("Brains...") }
}
@objc class Survivor : Runner
{
func run() { println("Aaaah, zombies!") }
}
func classesConformingToProtocol(proto:Protocol) -> AnyClass[]
{
let availableClasses : AnyClass[] = [ Zombie.self, Survivor.self ]
var conformingClasses = Array<AnyClass>()
for myClass : AnyClass in availableClasses
{
if myClass.conformsToProtocol(proto)
{
conformingClasses.append(myClass)
}
}
return conformingClasses
}
// This does not work
let walkers = classesConformingToProtocol(Walker.self)
let runners = classesConformingToProtocol(Runner.self)
No he podido convertir la información Metatype
de Swift en un objeto de Protocol
.
Si no permite el uso de @objc
(porque sus protocolos tienen propiedades, por ejemplo), la única solución que encontré es con el cierre. Luego, necesita usar un cierre para usar un protocolo y devolver un valor.
protocol Proto { }
protocol Proto2 { }
class Foo: Proto { }
class Bar: Proto, Proto2 { }
class Baz: Proto2 { }
class Qux { }
func printConforms(classList: [AnyClass], protoCond: (AnyClass) -> Any?) {
for i in classList {
print(i, terminator: " -> ")
if protoCond(i) != nil {
print("is subscriber")
} else {
print("NOT IS subscriber")
}
}
}
let myClasses: [AnyClass] = [Foo.self, Bar.self, Baz.self, Qux.self]
printConforms(classList: myClasses, protoCond: { $0 as? Proto.Type })
Ejemplo más completo: https://gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9
Editar
Otra solución mejor es usar genéricos, por ejemplo:
protocol Proto { }
class Foo: Proto { }
class Bar: Proto { }
class Baz { }
func filter<T>(classes: [AnyClass], byConformanceTo: T.Type) -> [AnyClass] {
return classes.filter { $0 is T }
}
filter(classes: [Foo.self, Bar.self, Baz.self], byConformanceTo: Proto.Type.self)
// return [Foo.self, Bar.self]