3
votes

I want to get 2 or more different object types on a map easily. Swift 2.0, I wanted to use Protocols.

I created a protocol that these object need to conform too. I assume that now any item the conforms to PinProtocol as essentially the same as being an MKAnnotation... just more!

protocol PinProtocol: MKAnnotation {
    // stuff
}

I have 2 Classes, Staff and Clients

Both conform to PinProtocol (which then also needs to conform to MKAnnotation)

I know this is working as if I set up my class as such,

class Client: NSObject, PinProtocol {
     //var coordinate:CLLocationCoordinate2D // Leave out get doesn't conform to protocol warning as expected
}

So, this tells me that that PinProtocol is working as expected, as items that need to adhere to the PinProtocol are also conforming to the MKAnnotation protocol as well. Because coordinate:CLLocationCoordinate2D, is required by MKAnnotation.

So why do I get this issue with

let staffAndClients = [PinProtocol]()

mapView.addAnnotations(staffAndClients) // not allowed!
//mapView.addAnnotations(pins as! [MKAnnotation]) // also not allowed

The error is, cannot convert value of type [PinProtocol] to to expected argument [MKAnnotation]

Isn't PinProtocol conforming to MKAnnotation so should work.

But doing this works fine

let staff = [Staff]()
mapView.addAnnotations(staff) // no problem
let clients = [Client]()
mapView.addAnnotations(clients) // no problem

I can get around the issue using AnyObject, but why cannot I use PinProtocol - which to me seems cleaner and the whole idea of protocol extensions.

Thanks for any help.

Addit...

The way I am getting around it for those who are facing a similar issue is

var pins = [AnyObject]()
mapView.addAnnotations(pins as! [MKAnnotation])
2

2 Answers

3
votes

Consider protocol as a template, you can use that template to make something useful like school presentation, but you cannot use the template itself for any presentation.

That's because protocol lacks the implementation of the methods or properties. To really get an object of that particular protocol, these methods needs to be implemented. Java in that case lets you anonymously sub-class a protocol, and forces you to implement the required methods. (Java calls it interface, don't confuse it Objective-C interface)

Unlike java, Swift doesn't support creating anonymous sub-classes, so any class that itself is a protocol cannot create objects in Objective-C and Swift. The only way of instantiating them is getting another class (interface in Objective C) conform that protocol and create instance of that particular class, Staff and Client in your case. However you can have a variable with the type of protocol as follow:

let staff = [PinProtocol]() //PinProtocol type array. can hold any type of objects that conform to this protocol

staff in this case is Array of type PinProtocol, it doesn't know any other info regarding the object created.

Edit:

I just understood your question correctly. Yes, we can declare an array of type in swift by protocol names. And that array can hold objects of the classes that conform to the protocol. Your syntax of declaring array is correct.

let staffAndClients = [PinProtocol]()

Regarding the error The error is, cannot convert value of type [PinProtocol] to to expected argument [MKAnnotation] I looked into documentations, and found out that the method addAnnotations() takes array of AnyObject as follow:

func addAnnotations(annotations: [AnyObject]!) //in MKMapView 

Now the tricky part is, MKAnnotation doesn't inherit from AnyObject, but instead from NSObjectProtocol ie:

protocol MKAnnotation : NSObjectProtocol

Whereas AnyObject also inherit from NSObjectProtocol, making it sibling of MKAnnotation that's why you're getting error, because you cannot pass object with types of MKAnnotation, because they're not AnyObject in their parent hierarchy.

2
votes

After dealing with the hassle of MapViewController's data source being

var pins = [AnyObject]()

I came to the realisation that I could easily avoid all the associated issues of type checking and use the preferred array of PinProtocols

var pins = [PinProtocol]()

The workaround was simply

func addToMap() {
    let mappingPins = pins.map{ $0 as AnyObject }
    mapView.addAnnotations(mappingPins as! [MKAnnotation])
    mapView.showAnnotations(mappingPins as! [MKAnnotation], animated: true)
}