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
:
- Invocar API de
MyProtocol
yUIViewController
-
UIViewController
métodoUIViewController
initialize()
para poderviewWillAppear:
Estas 2 capacidades parecen incompatibles (a partir de Swift 3) porque:
- No podemos extender clases con condiciones (es decir,
extension UIViewController where Self: MyProtocol
) - 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 podemospublic 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 soloMyProtocol
aquellos que se ajustan al protocoloMyProtocol
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
}
}