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 UICollectionViewCell
s 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