Hi there =) I was just faced with a design problem where I need to (essentially) do the following:
I want to inject a bit of code on viewWillAppear:
of any UIViewController
subclass that conforms to a protocol MyProtocol
. Explained in code:
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
}
}
The main issue here is that I need to 2 two things in the UIVIewController
extension:
MyProtocol
and UIViewController
API'sUIViewController
method initialize()
in order to be able to swizzle viewWillAppear:
These 2 capabilities seem incompatible (as of Swift 3) because:
extension UIViewController where Self: MyProtocol
)extension MyProtocol where Self: UIViewController
but we CAN'T override methods from a class in a protocol extension, meaning we can't public override class func initialize()
which is needed for swizzling.So I was wondering if there's somebody out there who can offer a Swifty solution to this problem I'm facing? =)
Thanks in advance!!
Well, thus far I've found no truly satisfactory way of doing it, but I decided to post what I ended up doing for this particular problem. In a nutshell, the solution goes like this (using the original example code):
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
}
}
}
Drawbacks:
UIViewControllers
, even though we validate for only those that conform to the MyProtocol
protocol to run the sensitive lines of code.I very much hope it helps anyone else out there facing a similar situation =)