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

reqzix picture reqzix · Nov 6, 2015 · Viewed 10.8k times · Source

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?

Answer

Martin R picture Martin R · Nov 6, 2015

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)