Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Self.Type cannot be directly converted to AnyClass in extension to objective-c class in swift

I'm trying to create fabric method to create UIViewController with correct nib name (to fix iOS8 default initialiser issue). To do it I have added extension:

extension UIViewController {    
    class func create() -> Self {
        if #available(iOS 9.0, *) {
            return self.init()
        } else {
            let clsName = NSStringFromClass(self).componentsSeparatedByString(".").last!
            return self.init(nibName: clsName, bundle: nil)
        }
    }
}

However compiler issues error: Cannot convert value of type 'Self.Type' to expected argument type 'AnyClass' (aka 'AnyObject.Type') in NSStringFromClass(self).

To fix it another extension method can be added and code rewritten to:

extension UIViewController {
    private class var nibNameForInitializer:String {
        return NSStringFromClass(self).componentsSeparatedByString(".").last!
    }
    class func create_() -> Self {
        if #available(iOS 9.0, *) {
            return self.init()
        } else {
            return self.init(nibName: self.nibNameForInitializer, bundle: nil)
        }
    }
}

However I want to understand the problem with first variant.

As I understand, method returning Self is a kind of generic method. Self can be used in many contexts (e.g. class, struct, protocol, etc), however Self.Type matches AnyClass only for classes.

In my case, compiler should know that Self refers to UIViewController and all its subclasses (since it is inside UIViewController extension), so Self.Type must be convertible to AnyClass.

Do I miss anything, or it is correct behaviour, since compiler doesn't perform any additional type analysis for Self?

like image 573
reqzix Avatar asked Nov 06 '15 12:11

reqzix


2 Answers

This looks like a bug or an (unnecessary) restriction, so you might consider to file a bug report at Apple. It happens only for type methods with return type Self. As a workaround, you can write

let clsName = NSStringFromClass(self as! AnyClass).componentsSeparatedByString(".").last!

which compiles and seems to work as expected.

But there is a simpler way to get the same result:

// Swift 2:
let clsName = String(self)
// Swift 3:
let clsName = String(describing: self)
like image 125
Martin R Avatar answered Nov 15 '22 12:11

Martin R


Perhaps it is a bug, try forcing a downcast, that is how I had to get this to work:

let clsName = NSStringFromClass(self as! AnyClass).componentsSeparatedByString(".").last!
like image 39
Knight0fDragon Avatar answered Nov 15 '22 13:11

Knight0fDragon