10
votes

I'm trying to create a default implementation of an MKMapViewDelegate by using the conditional extension as follows:

extension MKMapViewDelegate where Self: NSObject {
        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            ...
        }

        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            ...
        }
    }

However when I compile the code i get the warning

Non-'@objc' method 'mapView(_:viewFor:)' does not satisfy optional requirement of '@objc' protocol 'MKMapViewDelegate'

I expected the conformance on 'Self' to NSObject would mean that the warning would not occur. On top of the warning the delegate methods are not called even though the delegate instance is a UIViewController and hence conforms to NSObject.

Am I misunderstanding how 'where' works in extensions?

1

1 Answers

0
votes

NSObject will no longer infer @objc as of Swift 4 due to the acceptance of Doug Gregor's proposal: SE-0160.

I admit I have not put in the time to understand the reason behind your error though. I have merely posted this answer in hopes that anybody else reading your question will see the following advice.

It is bad practice, or 'code smell', to provide a default implementation to a protocol for your entire module. I would recommend an alternative approach wherein you create a custom type such as MapViewDelegate with some default behaviour that can be shared amongst types that explicitly conform to that protocol.

For example:

import MapKit
protocol MapViewDelegate: MKMapViewDelegate {}
extension MapViewDelegate where Self: NSObject {
    func annotationView(for annotation: MKAnnotation, in mapView: MKMapView) -> MKAnnotationView? {
        …
    }

    func renderer(for overlay: MKOverlay, in mapView: MKMapView) -> MKOverlayRenderer {
        …
    }
}
final class MyMapDelegate: NSObject, MapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        return annotationView(for: annotation, in: mapView)
    }
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        return renderer(for: overlay, in: mapView)
    }
}