Swift I

As I am rebuilding my app in swift, might as well share things as I come across them. To my surprise, some really basic stuff have changed and don’t behave as they did in Objective-C.

Class Name

To load UICollectionViewCells from NIB, I use a universal category/extension that loads the nib from class name. Of course, the easiest way to this was:

+ (UINib *)nib {
    NSString *nibName = NSStringFromClass(self);
    return [UINib nibName:nibName bundle:nil];
}

The reason we can use self directly is because self represents the caller class in Objective-C. So, even though this is a category on UICollectionViewCell, the result string from class will be resolved appropriately when we say [MyCollectionViewCell nib].

So, simple enough, let’s try converting the code to swift:

class func nib() -> UINib {
    let nibName = NSStringFromClass(self)
    return UINib(nibName: nibName, bundle: nil)
}

Yay, that was easy… Not. That doesn’t work, and it fails. Let’s print the value of nibName and see why: MyProject.MyClass. In swift, due to the existence of namespaces, the class is prefixed with it’s target namespace. Unless you want to rename all your XIBs, and use the new convention, I suggest you add a global function that extracts the class name from the verbose form. Something like:

var classString = NSStringFromClass(self)
let range = classString.rangeOfString(".", options: .CaseInsensitiveSearch, range: Range<String.Index>(start:classString.startIndex, end: classString.endIndex), locale: nil)

var identifier = ""
if let dotRange = range {
    identifier = classString.substringFromIndex(dotRange.endIndex)
}
else {
    assertionFailure("Couldn't resolve class: \(classString)")
}

return identifier