What is `classNamed(_:)` for?

There is a such method in Bundle:
func classNamed(_ className: String) -> AnyClass?

The description says it loads the Class object for className. It's, obviously, an Objective-C stuff. I started from Objective-C but didn't used it, preferring NSClassFromString.

Now I suddenly tested it in various applications. I was surprised that it doesn't work in iOS apps neither in Playground:

import Foundation

class TalkingFruit {
  func greet() {
    print("Hello, playground")
  }
}

@objc class LagacyFruit: NSObject {
}

print(Bundle.main.classNamed("TalkingFruit") ?? "no class")
// no class
print(Bundle.main.classNamed("LegacyFruit") ?? "no class")
// no class
print(Bundle.main.classNamed("NSObject") ?? "no class either")
// no class either

And now I have a question: Does it even work? And how it's supposed to be used? Working use case example would be great.

Replies

I fouine this which gives a sound explanation of what's going on, when it works and when it doesn't.

https://stackoverflow.com/questions/58113227/bundle-classnamed-classname-string-fails-but-nsclassfromstring-work

// This works:

let b1 = Bundle(path: "/System/Library/Frameworks/Foundation.framework")!
print(b1.load()) // true
let c1: AnyClass? = b1.classNamed("NSString")
print(c1 as Any) // Optional(NSString)

// This does not work

FileManager.default.changeCurrentDirectoryPath("/System/Library/Frameworks")
let b2 = Bundle(path: "Foundation.framework")!
print(b2.load()) // true
let c2: AnyClass? = b2.classNamed("NSString")
print(c2 as Any) // nil

The problem here is namespacing. Consider this code, running in an iOS app named Test699152:

@objc
class LegacyFruit: NSObject {
}

func test() {
    let c1: AnyClass? = Bundle.main.classNamed("LegacyFruit")
    print(c1 != nil)    // prints false
    let c2: AnyClass? = Bundle.main.classNamed("Test699152.LegacyFruit")
    print(c2 != nil)    // prints true
}

The first call fails because the correct class name, from an Objective-C perspective, is Test699152.LegacyFruit.

If you want the first call to succeed, pin the name using the @objc attribute:

@objc(LegacyFruit)
class LegacyFruit: NSObject {
}

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"