7
votes

I have a protocol that uses an associated type, as such:

protocol Populatable {
    typealias T
    func populateWith(object: T)
}

and classes that implement the protocol:

class DateRowType: Populatable {
    func populateWith(object: NSDate) {
        print(object.description)
    }
}

class StringRowType : Populatable {
    func populateWith(object: String) {
        print(object)
    }
}

but when I try to cast or test for conformance, like this:

let drt = DateRowType()
let srt = StringRowType()

let rowTypes = [drt, srt]
let data = [NSDate(), "foo"]

for (i, p: Populatable) in enumerate(rowTypes) {
    p.populateWith(data[i])
}

I get the error:

Protocol 'Populatable' can only be used as a generic constraint because it has Self or associated type requirements

What's the correct way to test if the object conforms to the Populatable protocol?

Note: all the code required to try this out is contained in the question, just copy the code blocks into a playground.

2
I want to have other Row Type classes that take in other types as a parameter to the populate function. Right now the events array is heterogeneous but in the future could contain a number of types that implement the Populatable protocol.Senior
Each row will be populated by a different type, so I want my protocol to be generic so that one row can implement it as populate(object: Event), another could be populate(object: Team), etc. Without being generic I'd have to make the protocol use populate(object: AnyObject) which removes a lot of the fun of using Swift.Senior
populate doesn't take a Populatable, it is contained within the Populatable protocol. Populatable defines a method on another class, in this case an NSObject for using as WKInterfaceTable controllers.Senior
There were other comments and answers here but have been deleted by the poster. I had said that making a generic function was the solution, but in fact it is not. I still need typealiases. I'll post a Swift playground to clarify.Senior

2 Answers

0
votes

As the error says, you cannot cast it to Populatable here. I think the correct way is to cast it to EventRowType.

if let rowController = self.table.rowControllerAtIndex(i) as? EventRowType {

And you already tested that 'EventRowType' class conforms 'Populatable' protocol. Because if the EventRowType doesn't have function named 'populate', swift compiler says,

Type 'EventRowType' does not conform to protocol 'Populatable'

0
votes

I don't think you will be able to go generic the whole way, unless possibly by using AnyObject and testing the class of the parameter in each populateWith function.

But this will work:

for (i, p) in enumerate(rowTypes) {
    if let dateRow = p as? DateRowType {
        dateRow.populateWith(data[i] as! NSDate)
    }
    else if let stringRow = p as? StringRowType {
        stringRow.populateWith(data[i] as! String)
    }
}

You will just need to expand this for every Populatable class you add.