que protocolos protocolo programacion orientada ios swift uiviewcontroller swift-protocols swift-extensions

ios - programacion - protocolos swift 4



Swift extensión de una clase SOLAMENTE cuando se ajusta a un protocolo específico (2)

Hola :) Me enfrenté a un problema de diseño en el que necesito (esencialmente) hacer lo siguiente:

Quiero inyectar un poco de código en viewWillAppear: de cualquier subclase UIViewController que se ajuste a un protocolo MyProtocol . Explicado en el código:

protocol MyProtocol { func protocolFunction() { //do cool stuff... } } extension UIViewController where Self: MyProtocol //<-----compilation error { public override class func initialize() { //swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool) } // MARK: - Swizzling func xxx_viewWillAppear(animated: Bool) { self.xxx_viewWillAppear(animated) //invoke APIs from self.protocolFunction() // MyProtocol APIs let viewLoaded = self.isViewLoaded // UIViewController APIs } }

El problema principal aquí es que necesito 2 dos cosas en la extensión UIVIewController :

  1. Invocar API de MyProtocol y UIViewController
  2. UIViewController método UIViewController initialize() para poder viewWillAppear:

Estas 2 capacidades parecen incompatibles (a partir de Swift 3) porque:

  1. No podemos extender clases con condiciones (es decir, extension UIViewController where Self: MyProtocol )
  2. si en cambio ampliamos el protocolo , PODEMOS agregar extension MyProtocol where Self: UIViewController pero NO PODEMOS anular los métodos de una clase en una extensión de protocolo , lo que significa que no podemos public override class func initialize() que se necesita para swizzling.

Entonces, me preguntaba si hay alguien por ahí que pueda ofrecer una solución Swifty a este problema que estoy enfrentando. =)

¡¡Gracias por adelantado!!


Bueno, hasta ahora no he encontrado una manera verdaderamente satisfactoria de hacerlo, pero decidí publicar lo que terminé haciendo por este problema en particular. En pocas palabras, la solución es la siguiente (usando el código de ejemplo original):

protocol MyProtocol { func protocolFunction() { //do cool stuff... } } extension UIViewController //------->simple extension on UIViewController directly { public override class func initialize() { //swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool) } // MARK: - Swizzling func xxx_viewWillAppear(animated: Bool) { self.xxx_viewWillAppear(animated) //------->only run when self conforms to MyProtocol if let protocolConformingSelf = self as? MyProtocol { //invoke APIs from protocolConformingSelf.protocolFunction() // MyProtocol APIs let viewLoaded = protocolConformingSelf.isViewLoaded // UIViewController APIs } } }

Inconvenientes:

  • No es "la manera rápida" de hacer las cosas
  • El método de swizzling se invocará y tendrá efecto en TODOS los UIViewControllers , aunque solo MyProtocol aquellos que se ajustan al protocolo MyProtocol para ejecutar las líneas de código confidenciales.

Espero sinceramente que ayude a alguien más que se encuentre en una situación similar =)


Estabas cerca de la solución. Solo necesito hacerlo de otra forma. Extienda el protocolo solo si forma parte de UIViewController.

protocol MyProtocol { func protocolFunction() { //do cool stuff... } } extension MyProtocol where Self: UIViewController { public override class func initialize() { //swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool) } // MARK: - Swizzling func xxx_viewWillAppear(animated: Bool) { self.xxx_viewWillAppear(animated) //invoke APIs from self.protocolFunction() // MyProtocol APIs let viewLoaded = self.isViewLoaded // UIViewController APIs } }