5

In Swift 3, I had a snipped of code that called into Objective-C runtime to check if a certain class is present.

guard let managerClass = NSClassFromString("ASIdentifierManager") as? NSObjectProtocol else {
    return nil
}

This returns AnyClass instance and it is castable to NSObjectProtocol - so I could call methods like responds and perform to work with selectors.

In Swift 4, this still works at runtime, but will emit a warning:

Cast from 'AnyClass?' (aka 'Optional') to unrelated type 'NSObjectProtocol' always fails

Oddly it still works, but I am worried a certain Swift version will kill the code.

What is the proper way to get rid of this warning? To what should I cast the object, to be able to perform selectors on it dynamically?

Orkhan Alikhanov
  • 9,122
  • 3
  • 39
  • 60
Legoless
  • 10,942
  • 7
  • 48
  • 68

2 Answers2

8

Looks like you need to cast from AnyObject.Type to AnyObject first to convince the compiler that you're talking about an instance (albeit an instance of a metaclass, as you know):

    guard
        let klass = NSClassFromString("ASIdentifierManager"),
        let managerClass = klass as AnyObject as? NSObjectProtocol else
    {
        return nil
    }

I'm not sure whether this should be considered a compiler bug or not. I'd bet not, and that there's some Swiftian reason that NSObjectProtocol does not apply to ObjC type objects when in a Swift context.

jscs
  • 63,694
  • 13
  • 151
  • 195
0

You should cast to NSObjectProtocol.Type:

guard let managerClass = NSClassFromString("ASIdentifierManager") as? NSObjectProtocol.Type else {
    return nil
}
Orkhan Alikhanov
  • 9,122
  • 3
  • 39
  • 60
  • 2
    This is incorrect. While you can do this cast without the warning, you can no longer use `responds` and `perform` methods, as this is a type, not an instance anymore. – Legoless Aug 30 '17 at 13:55
  • This is correct. `NSClassFromString` <- returns `class` not `an instance`. If you want instance you have to create it. – Orkhan Alikhanov Aug 30 '17 at 13:56
  • 1
    In Objective-C type class also is an instance of `class` class. Each class instance has a meta class. So it is incorrect. I know it is confusing, but it is effectively how Objective-C runtime works. – Legoless Aug 30 '17 at 13:58
  • @Legoless You want to call `class method` through `perform selector`? – Orkhan Alikhanov Aug 30 '17 at 14:01
  • 1
    Exactly, yes! The code I had worked in Swift 3 and is semantically correct. The code actually still works, but will emit warning described in the question. – Legoless Aug 30 '17 at 14:09
  • Did you actually try my code? It should work I guess – Orkhan Alikhanov Aug 30 '17 at 14:37
  • 1
    Yes, it doesn't work, because there's no way to call any Objective-C runtime methods on that meta type. – Legoless Aug 30 '17 at 15:10