Reputation: 152
The following code is just an example:
I need to implement MapProtocol
in my ViewController class. The ViewController itself has a variable of type MKMapView
. However in my case the MKMapViewDelegate
needs to be implemented by an extension of the protocol and cannot be implemented by the ViewController class, but this does not work. The delegate function won't be called at all (only if implemented by the ViewController)
Am I missing a swift restriction for adding default implementations for protocols within another protocol? If yes, is there are proper workaround?
Real case scenario: I have two ViewControllers which share some redundant code (MKMapViewDelegate, etc). So I wanted to outsource this code. I can’t use a superclass because both of the viewcontrollers are already subclasses of two different Types. My first approach was to use an extended protocol.
import UIKit
import MapKit
class ViewController: UIViewController, MapProtocol {
var mapView: MKMapView = MKMapView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupMapView()
}
private func setupMapView() {
self.view.addSubview(mapView)
mapView.mapType = MKMapType.standard
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
mapView.delegate = self
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
mapView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
mapView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
mapView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
}
}
/** Works if uncommented */
//extension ViewController: MKMapViewDelegate {
// func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
// NSLog("test")
// }
//}
protocol MapProtocol: MKMapViewDelegate {
var mapView: MKMapView { set get }
}
extension MapProtocol {
func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
NSLog("test")
}
}
Upvotes: 2
Views: 381
Reputation: 2073
If i understood the question correctly, you want to avoid repeating "map related code" in different viewcontroller.
In such a case, why not having a "data source" approach, create an additional class ViewControllerMapInjector
or whichever name, and in the initialization pass both the vc and execute whatever method you prefer
class ViewControllerMapInjector: MKMapViewDelegate {
private let vc: UIViewController
init(vc: UIViewController) {
self.vc = vc
}
func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
NSLog("test") // or use the vc as you want.
}
}
then in the viewController setup()
just
self.mapView.delegate = ViewControllerMapInjector(vc: self)
Eventually you may need to apply additional patterns to the view controller to let additional methods be visible to the provider if needed (in the end the simplest solution is to go full OO and subclass the ViewController). It's a bit convoluted, but the alternative i can think of are not much simpler.
Upvotes: 1
Reputation: 2252
if you are thinking that func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool)
is a MKMapViewDelegate protocol method then it is wrong. it is just method which is created by you not a MKMapViewDelegate method.
extension MapProtocol {
func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
NSLog("test")
}
}
same is reason that your delegate method is not calling. Callback is received by viewController because mapView.delegate = self
. Here it is no role of MapProtocol.
Upvotes: 0
Reputation: 1092
That's a bit of a strange case but I don't think the dispatch lookup for the delegate is going to "see" the delegate method when added by the protocol. Protocol extensions are statically bound and not part of the dynamic lookup scheme.
Upvotes: 1