How to call protocol extensions initializers in designated initializers?

792 Views Asked by At

I'm trying to inject a protocol extension initializer into an existing class's designated initializer. I don't think there's a way around it without overriding the designated initializer from the class, then call the protocol extension initializer within.

Below is what I'm trying, specifically with the UIViewController class:

class FirstViewController: UIViewController, MyProtocol {

    var locationManager: CLLocationManager?
    var lastRendered: NSDate?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        // TODO: How to call MyProtocol initializer?
        // (self as MyProtocol).init(aDecoder) // Didn't compile
    }

}

protocol MyProtocol: CLLocationManagerDelegate {

    var locationManager: CLLocationManager? { get set }
    var lastRendered: NSDate? { get set }

    init?(coder aDecoder: NSCoder)
}

extension MyProtocol where Self: UIViewController {

    // Possible to inject this into initialization process?
    init?(coder aDecoder: NSCoder) {
        self.init(coder: aDecoder)
        setupLocationManager()
    }

    func setupLocationManager() {
        locationManager = CLLocationManager()
        locationManager?.delegate = self
        locationManager?.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        locationManager?.distanceFilter = 1000.0
        locationManager?.startUpdatingLocation()
    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // TODO
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        // TODO
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        // TODO
    }
}

Is there a way to make use of protocol extensions initializers so it gets called automatically during the framework's existing initialization process?

1

There are 1 best solutions below

0
On BEST ANSWER

You don't need to call a different initializer; you're already initializing. Moreover, you don't need to cast self to MyProtocol; you've already declared that it adopt MyProtocol. Plus, you've already injected MyProtocol's setupLocationManager into FirstViewController, because your FirstViewController already adopts MyProtocol and the extension on MyProtocol is aimed at UIViewController, the superclass of FirstViewController.

So, the method is already injected; now just go right ahead and call the injected method right there in the initializer you're already running. The following pared-down version of your code compiles perfectly well:

class FirstViewController: UIViewController, MyProtocol {
    var locationManager: CLLocationManager?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupLocationManager() // no problem!
    }
}

protocol MyProtocol: CLLocationManagerDelegate {
    // this next line is necessary so that
    // ...setupLocationManager can refer to `self.locationManager`
    var locationManager: CLLocationManager? { get set }
}

extension MyProtocol where Self: UIViewController {
    func setupLocationManager() {
        locationManager = CLLocationManager()
        // ... etc.
    }
    // ... etc.
}