Protocol extensions cannot comply with CLLocationManagerDelegate?

I am trying to implement the CLLocationManagerDelegate protocol CLLocationManagerDelegate through a protocol extension, but the location manager does not see it in the protocol extension and fails. However, it works with the same code when moving to a class.

That's what I'm doing:

 class ViewController: UIViewController, MyLocationProtocol { let locationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers locationManager.distanceFilter = 1000.0 locationManager.delegate = self // Below crashes when implementation in protocol extension locationManager.requestLocation() } } protocol MyLocationProtocol: CLLocationManagerDelegate { func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) func locationManager(manager: CLLocationManager, didFailWithError error: NSError) } extension MyLocationProtocol /*where Self: UIViewControll*/ { // Tried using where clause but still no go :( // Not being triggered by CLLocationManagerDelegate! :( // Move to ViewController class and error goes away func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("MyLocationProtocol: locationManager: didUpdateLocations") } func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { print("MyLocationProtocol: locationManager: didFailWithError") } } 

Note that in extension MyLocationProtocol I put didUpdateLocations and didFailWithError . They never get a trigger and actually crash say: 'Delegate must respond to locationManager:didUpdateLocations:' . If I moved the same didUpdateLocations and didFailWithError to the ViewController , everything would work as expected.

Is there something I'm missing, why doesn't this work through protocol extensions? The class is recognized as CLLocationManagerDelegate , otherwise it will fail when locationManager.delegate = self . Any ideas on how to make this work, or is there a mistake somewhere?

+6
source share
3 answers
Protocol extension

is the pure Swift staff. Submission rules for protocol extensions:

IF the derived variable type is a protocol

  • And the method is defined in the source protocol. THEN calls the implementation of the execution types , regardless of whether a default implementation exists in the extension .
  • And the method is NOT defined in the original protocol. THEN the default implementation is called.

IF the selected variable type is a type. Then the type implementation is called.

Given all this ...

 import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true import Foundation import MapKit class Lm: NSObject, CLLocationManagerDelegate { func locationManager(manager: CLLocationManager, didUpdateLocations locations: [AnyObject]) { print("\(locations)") } } class C:Lm { let lm = CLLocationManager() override init() { super.init() lm.delegate = self lm.startUpdatingLocation() } } let c = C() /* 2016-02-22 08:41:56.506 Untitled Page 10[32000:11547708] ### Failed to load Addressbook class CNContactNameFormatter [<+48.71408491,+21.20868516> +/- 65.00m (speed -1.00 mps / course -1.00) @ 22/02/16 08 h 41 min 57 s Central European Standard Time] [<+48.71408491,+21.20868516> +/- 65.00m (speed -1.00 mps / course -1.00) @ 22/02/16 08 h 41 min 57 s Central European Standard Time] [<+48.71415732,+21.20859246> +/- 65.00m (speed -1.00 mps / course -1.00) @ 22/02/16 08 h 41 min 57 s Central European Standard Time] .... */ 

another option is to do something like ...

 import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true import Foundation import MapKit class MyLocationManager: CLLocationManager, CLLocationManagerDelegate { override init() { super.init() delegate = self } } extension MyLocationManager { func locationManager(manager: CLLocationManager, didUpdateLocations locations: [AnyObject]) { print("\(locations)") } } class C { let lm = MyLocationManager() init() { lm.startUpdatingLocation() } } 

if all types are known at compile time and there is no "optional" method in the protocol, it works "as expected"

 protocol P { func foo() } extension P { func foo() { print("p") } } protocol P1: P {} extension P1 { func foo() { print("p1") } } class C: P {} class C1: P1 {} let c = C() let c1 = C1() let p:P = C() let p1:P = C1() c.foo() // p c1.foo() // p1 p.foo() // p p1.foo() // p1 

for further reading see this and this

+1
source

The protocol extension is one of the new things from Swift 2.0. This allows you to determine the behavior of the protocol itself. If you performed the method definition in both the confirming class and in the extension of your protocol, the implementation of the method in the confirming class will be called at run time. But the optional protocol is actually a by-product of goal c. This means that if you want to define an optional protocol in swift, you need to insert the @objc attribute before declaring the protocol. When I experimented with protocol extension, as you explained, it works well when the protocol is not optional. But when the protocol is optional, the application broke up. CLLocationManagerDelegate delegate methods are declared optional. I think this may be the reason.

view controller 1

 class ViewController: UIViewController,MyViewControllerProtocol { var myViewController:NewViewController? override func viewDidLoad() { super.viewDidLoad() let sb = UIStoryboard(name: "Main", bundle: nil) myViewController = sb.instantiateViewControllerWithIdentifier("newViewController") as? NewViewController myViewController?.delegate = self myViewController?.view.frame = self.view.bounds self.view.addSubview((myViewController?.view)!) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func newViewControllerLoaded(){ print("protocol definition called inside class") } } protocol MyViewControllerProtocol: NewViewControllerProtocol { func newViewControllerLoaded() } extension MyViewControllerProtocol{ func newViewControllerLoaded(){ print("protocol definition called inside extension") } } 

View Controller 2

 protocol NewViewControllerProtocol { func newViewControllerLoaded() } class NewViewController: UIViewController { var delegate: NewViewControllerProtocol? override func viewDidLoad() { delegate?.newViewControllerLoaded() } } 

with protocol implementation in ViewController

 protocol definition called inside class 

after removing the protocol implementation from ViewController

 protocol definition called inside extension 

One way to fix your problem is to apply the extension to your class, then the protocol implementation will be inside your class. But this is not an extension of the protocol.

 extension ViewController /*where Self: UIViewControll*/ { // Tried using where clause but still no go :( // Not being triggered by CLLocationManagerDelegate! :( // Move to ViewController class and error goes away func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("MyLocationProtocol: locationManager: didUpdateLocations") } func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { print("MyLocationProtocol: locationManager: didFailWithError") } } 
0
source

I find this behavior a little strange. However, for your current problem you can use this solution.

Basically, the idea is to introduce a proxy object in which you implement your logic to handle the location in the protocol and write the class corresponding to that protocol. Then in your ViewController you create an instance of the class (proxy), and also implement the CLLocationManagerDelegate methods and simply pass the callback methods to the proxy object. This will help you separate the location processing logic from the ViewController. You can still use MyLocationManagerDelegateClass yourself in the Watch app.

Hope this helps.

 class ViewController: UIViewController, CLLocationManagerDelegate { let locationManager = CLLocationManager() let proxyLocationManagerDelegate = MyLocationManagerDelegateClass() override func viewDidLoad() { super.viewDidLoad() locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers locationManager.distanceFilter = 1000.0 // Below crashes when implementation in protocol extension locationManager.delegate = self locationManager.requestLocation() } func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("ViewController: locationManager: didUpdateLocations") proxyLocationManagerDelegate.locationManager(manager, didUpdateLocations: locations) } func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { print("ViewController: locationManager: didFailWithError") proxyLocationManagerDelegate.locationManager(manager, didFailWithError: error) } } class MyLocationManagerDelegateClass : NSObject, MyCLLocationManagerDelegate { } protocol MyCLLocationManagerDelegate : CLLocationManagerDelegate { func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) func locationManager(manager: CLLocationManager, didFailWithError error: NSError) } extension MyCLLocationManagerDelegate /*where Self: UIViewControll*/ { // Tried using where clause but still no go :( // Not being triggered by CLLocationManagerDelegate! :( // Move to ViewController class and error goes away func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("MyLocationProtocol: locationManager: didUpdateLocations") } func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { print("MyLocationProtocol: locationManager: didFailWithError") } } 
0
source

All Articles