8

I could instantiate class from string in ObjC.For example,I have defined new DBCell class through subclassing UITableViewCell,and I instantiate class from name with these codes:

 DBCell *cell=[tableView dequeueReusableCellWithIdentifier:cellClassname];
    if (cell==nil) {
        Class cellClass=NSClassFromString(cellClassname);
        cell=[cellClass alloc];
        cell=[cell initWithStyle:cellStyle reuseIdentifier:cellClassname];
    }

Now I need migrate codes to Swift,I have redefined DBCell class in Swift:

 class DBCell: UITableViewCell {
    var label:String?
    var key:String?
    var managedObject:NSManagedObject?
    var delegate:AnyObject?
    convenience override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        self.init(style: style, reuseIdentifier: reuseIdentifier)
        self.textLabel.font=UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1)
        self.backgroundColor=UIColor.blackColor()
        self.textLabel.backgroundColor=UIColor.blackColor()
    }
    }

But how can I instantiate class and invoke the appropriate init function?

2
  • I feel that your problem is caused by bad architecture, mainly the lack of properly used polymorphism. Could you please show more of your original obj-c code? What's the difference between different cells?
    – Sulthan
    Sep 16, 2014 at 15:54
  • @Sulthan This is a pretty common Cocoa pattern. What do you mean by "properly used polymorphism" in this case? The point is that the cell names are stored in the storyboard, which is quite convenient and useful. The question is how to do that while maintaining Swift type safety (and it's a reasonable question).
    – Rob Napier
    Sep 16, 2014 at 17:30

1 Answer 1

17

Swift is more statically typed, which makes it much safer at runtime. For instance, if any of the strings are wrong, you'll crash (or at best quietly do nothing, which can be worse than crashing) and the compiler can't catch it for you.

OK, but how do you deal with this pattern? The best way, IMO, in Swift is to be explicit and create a mapping.

let cellMapping = [
  "DBCell": DBCell.self,
]

if let cellClass = cellMapping[cellClassName] {
  let cell = cellClass(style: cellStyle, reuseIdentifier: cellClassName)
  // use cell
}

Now when you refactor and change the name of DBCell, but miss the string in the Storyboard, everything will just keep working as expected, rather than crashing or quietly failing like it would in ObjC.

Swift is really smart about these mappings, too. The type of cellMapping above is [String: DBCell.Type], so if you had a special init for DBCell, it would be available. But if your dictionary looked like:

let cellMapping = [
  "DBCell": DBCell.self,
  "OtherCell": UITableViewCell.self
]

then the type is [String: UITableViewCell.Type]. Swift automatically figures out the most specific class that covers all the values. If you added a type into this list that didn't have some method you actually use, Swift can catch that at compile time. And if you made a mistake and added something that isn't even a table view cell:

let cellMapping = [
  "DBCell": DBCell.self,
  "OtherCell": UITableViewCell.self,
  "BadCell": UICollectionViewCell.self
]

then Swift will give you a compile time warning that you've created an AnyObject. That's really nice in writing safe but flexible code, all for the cost of a dictionary.

4
  • Is there any way to create a cell mapping that will make this work with different types? Mar 16, 2015 at 5:02
  • I don't understand the question. You should ask a new SO question with details of your specific problem.
    – Rob Napier
    Mar 16, 2015 at 5:08
  • ["\(DBCell.self)" : DBCell.self] Less "stringly typed"
    – Sentry.co
    Aug 23, 2017 at 9:04
  • @GitSync That would be a dangerous change. You want the string in the dictionary to be a literal string. It has to exactly match what is in the Storyboard (even if the name of the class changes). If you compute its name, then a refactor that changes the name of DBCell would make it mismatch Storyboard, defeating the whole point. Your change relies on the assumption that the string in the Storyboard is the same as the description of the class; using a literal string does not impose that assumption.
    – Rob Napier
    Aug 23, 2017 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.