Unable to cast between types

In an extranal library that I pull in via a pod I've got this class defined.


open class ExpandableDatePickerCell : UITableViewCell {
    public static func reusableCell(for indexPath: IndexPath, in tableView: UITableView) -> ExpandableDatePickerCell {
        return tableView.dequeueReusableCell(withIdentifier: ExpandableDatePickerCell.identifier, for: indexPath) as! ExpandableDatePickerCell
    }
}


Obviously there's some other stuff, that's the interesting method. Then in my project I inherit from that cell.


final class UserProfileDatePickerCell : ExpandableDatePickerCell {


When I hit tableView(_:cellForRowAt:) I'm trying to now get one of those cells, but it's crashing. I call it like so:


let cell = UserProfileDatePickerCell.reusableCell(for: indexPath, in: tableView) as! UserProfileDatePickerCell


But it's getting a SIGABRT with the message:


Could not cast value of type 'ExpandableDatePicker.ExpandableDatePickerCell' (0x103ed3e60) to 'TeamKnect.UserProfileDatePickerCell' (0x103807c78).


I don't understand why it can't do the cast since it's a child.

Accepted Reply

QuinceyMorris wrote:

Yes, except that because this is crossing a bundle boundary …

Ah, yes, missed that bit.

Gargoyle wrote:

How would I modify the signature of that method where I could optionally pass in a different class to use if they decided to subclass the time zone cell, for example?

Do you need to?

register(_:forCellReuseIdentifier:)
allows you to replace a registration (by calling it again with the same reuse identifier), so why not have your
viewDidLoad()
call
registerCells()
and then replace the registrations it wants to override?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Well, you can't *cast* a superclass to a subclass, because the object (of the superclass) doesn't actually have the additional behavior of the subclass.


What you're going to have to do is "tell" the reusableCell method what subclass to create.

What you're going to have to do is "tell" the reusableCell method what subclass to create.

Specifically,

dequeueReusableCell(withIdentifier:for:)
returns a cell of the type determined by looking up the
identifier
parameter in the registered cell classes for table. In most cases you set this up in the interface editor by:
  1. Adding a prototype cell to the table view

  2. Using the identity inspector (View > Utilities > Identify Inspector) to set the class for it

  3. Using the attributes inspector (View > Utilities > Attributes Inspector) to set the reuse identifier for it

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes, except that because this is crossing a bundle boundary, it's an API decision whether to supply an identifier as a parameter (which needs to be managed depending where the table is defined) or a class (which might mean using registerClass in the framework). I don't think we got enough information to make it clear what the best approach might be.

So the library was registering the table cells to make things easier on the caller, so it has a function like so:


static func registerCells() {
    tableView.register(ExpandableDatePickerCell.self, forCellReuseIdentifier: ExpandableDatePickerCell.identifier)
    tableView.register(ExpandableDatePickerTimeZoneCell.self, forCellReuseIdentifier: ExpandableDatePickerTimeZoneCell.identifier)
}


And then in viewDidLoad() of the caller I can just call that. How would I modify the signature of that method where I could optionally pass in a different class to use if they decided to subclass the time zone cell, for example?

QuinceyMorris wrote:

Yes, except that because this is crossing a bundle boundary …

Ah, yes, missed that bit.

Gargoyle wrote:

How would I modify the signature of that method where I could optionally pass in a different class to use if they decided to subclass the time zone cell, for example?

Do you need to?

register(_:forCellReuseIdentifier:)
allows you to replace a registration (by calling it again with the same reuse identifier), so why not have your
viewDidLoad()
call
registerCells()
and then replace the registrations it wants to override?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Sweet! I didn't know you could just re-register over the existing name with another class. Thanks!