I've read through the relevant sections of Apple's Swift iBook (Type Casting & Protocols) but I can seem to find a way to specify that an object is an instance of a particular class that conforms to a specific protocol.
As an example in tableView(_: , cellForRowAt: )
I would like to cast the cell returned by tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath)
as being a subclass of UITableViewCell
that conforms to the RLMEntityCapableCell
protocol (Just specifies that conformers have a variable called item
that is an instance of Object
, or one of its subclasses).
This route works but the double casting seems excessive:
protocol RLMEntityCapableCell: class {
var item: Object { get set }
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) as! RLMEntityCapableCell // Cast here so we can set item
cell.item = items[indexPath.row]
return cell as! UITableViewCell // Cast again so the return type is right…
}
This other approach:
var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath)
as! RLMEntityCapableCell, UITableViewCell
gives this error:
type annotation missing in pattern
so clearly isn't the right way to do it either.
I would prefer to specify that in order to conform to the protocol an object has to inherit from either UITableViewCell
or UICollectionViewCell
but the base of a protocol can only be limited to the class type and no further.
Edit:
The idea here is to have a generic data source for Realm objects that leverages generics much like Array
and Dictionary
do. The cells used in each table view would be specific to the entity to be displayed but the data source would only know that the cell would be a subclass of UITableViewCell
that conforms to RLMEntityCapableCell
. All the data source needs to worry about is telling the cell what instance (that would always be a subclass of Object
) it needs to display, the cell would take it from there and configure itself as needed.
UITableViewCell
doesn't have anitem
property, so what you probably want to do is create a subclass of it that conforms to your protocol, and then castcell
as that.Notes:
1.
dequeueReusableCell
returns an optional and will almost certainly return nil in some cases, so don't force unwrap it.2. You'll need to do at least one of the following: make
item
optional, provide a default value, or add an initializer function.No, this isn't possible... yet.
The next Swift release (version 4) might bring what you are looking for, a new feature called Class and Subtype Existentials:
You could then say:
But, of course, you won't be able to use this to add a superclass requirement to your
RLMEntityCapableCell
protocol (as you initially wished). We may need to wait for Swift 5 for that :)Some other examples using the above Class and Subtype Existentials (Swift 4) feature:
and: