Using "self" in class extension functions in Swift

I am looking to pull an instance of a UIView subclass from Nib.

I would like to be able to call MyCustomView.instantiateFromNib () and have an instance of MyCustomView. I am almost ready to simply port the working Objective-C code that I have through the bridge header, but decided that I would try to use the idiomatic approach first. That was two hours ago.

extension UIView { class func instantiateFromNib() -> Self? { let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil) for topLevelObject in topLevelObjects { if (topLevelObject is self) { return topLevelObject } } return nil } } 

Now if (topLevelObject is self) { wrong, because "The expected type after" is equal to "." What I tried after that says a lot about what I donโ€™t understand about a system like Swift.

  • if (topLevelObject is self) {
  • if (topLevelObject is self.dynamicType) {
  • if (topLevelObject is self.self) {
  • Millions of other options are not mistaken .

Any understanding is understood.

+8
swift
Jul 01 '15 at 15:25
source share
2 answers

Using the approach from How to instantiate subclasses of a managed entity in the NSManagedObject Swift extension? you can define a generic helper method that maps the type self from the calling context:

 extension UIView { class func instantiateFromNib() -> Self? { return instantiateFromNibHelper() } private class func instantiateFromNibHelper<T>() -> T? { let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil) for topLevelObject in topLevelObjects { if let object = topLevelObject as? T { return object } } return nil } } 

This compiles and works as expected in my quick test. If MyCustomView is your subclass of UIView , then

 if let customView = MyCustomView.instantiateFromNib() { // `customView` is a `MyCustomView` // ... } else { // Not found in Nib file } 

provides an instance of MyCustomView , and the type is automatically displayed.




Update for Swift 3:

 extension UIView { class func instantiateFromNib() -> Self? { return instantiateFromNibHelper() } private class func instantiateFromNibHelper<T>() -> T? { if let topLevelObjects = Bundle.main.loadNibNamed("CustomViews", owner: nil, options: nil) { for topLevelObject in topLevelObjects { if let object = topLevelObject as? T { return object } } } return nil } } 
+14
Jul 01 '15 at 16:52
source share

I believe the conditional expression you are looking for is topLevelObject.dynamicType == self

By combining this with unsafeBitCast (which, according to Appleโ€™s own documentation, "violates Swift type system guarantees") , we can force downcast topLevelObject to self . This should be safe, because we have already seen that topLevelObject is the same type as self

This is one way to get around the helper method using the generics described by Martin R.

 extension UIView { class func instantiateFromNib() -> Self? { let bundle = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil) for topLevelObject in topLevelObjects { if topLevelObject.dynamicType == self { return unsafeBitCast(topLevelObject, self) } } return nil } } 

Note that Apple also says in its documentation for unsafeBitCast :

There is almost always a better way to do something.

So be careful!

+2
Apr 17 '16 at 18:41
source share



All Articles