I'm finding that if I try to lookup a generic subclass of an NSObject by its name NSClassFromString, it only succeeds when that subclass was declared at the top level:
// This always works, so long as it's declared at the top level:
class Solid<T>: NSObject { }
print(NSClassFromString(Solid<Int>.className())) // Optional(Solid<Swift.Int>)
// This always fails:
func someFunction() {
class Ephemeral<T>: NSObject { }
print(NSClassFromString(Ephemeral<Int>.className())) // nil
}
someFunction()
The second example works just fine if the type is made non-generic.
Is this intentional and expected behavior, or is this a bug?
That function has a very entertaining doc comment:
/**
* Perform Objective-C initialization of a Swift class.
* Do not call this function. It is provided for the Swift runtime's use only
* and will change without notice or mercy.
*/
This looks to me like a bug in realizing generic subclasses of NSObject, but only when nested inside of functions — with Swift 5.5.2:
import Foundation
func printClass<T: NSObject>(_ type: T.Type) {
print(type, "=>", String(describing: NSClassFromString(T.className())))
}
class Generic<T>: NSObject {}
printClass(Generic<Int>.self)
// ↪︎ Generic<Int> => Optional(main.Generic<Swift.Int>)
private class PrivateClass: NSObject {}
printClass(PrivateClass.self)
// ↪︎ PrivateClass => Optional(main.(unknown context at $10df76c28).PrivateClass)
class TopLevelClass: NSObject {
class NestedClass: NSObject {}
class NestedGenericClass<T>: NSObject {}
private class NestedPrivateClass: NSObject {}
static func printNestedClasses() {
printClass(NestedClass.self)
// ↪︎ NestedClass => Optional(main.TopLevelClass.NestedClass)
printClass(NestedGenericClass<Int>.self)
// ↪︎ NestedGenericClass<Int> => Optional(main.TopLevelClass.NestedGenericClass<Swift.Int>)
printClass(NestedPrivateClass.self)
// ↪︎ NestedPrivateClass => Optional(main.TopLevelClass.(unknown context at $10df76d60).NestedPrivateClass)
}
}
TopLevelClass.printNestedClasses()
func classesNestedInFunction() {
class FunctionClass: NSObject {}
printClass(FunctionClass.self)
// ↪︎ FunctionClass => Optional(main.(unknown context at $10df76db0).(unknown context at $10df76db8).FunctionClass)
class FunctionGenericClass<T>: NSObject {}
printClass(FunctionGenericClass<Int>.self)
// ↪︎ FunctionGenericClass<Int> => nil
// Can't define classes as `private` to a function.
}
classesNestedInFunction()
Where all other types are being realized automatically (either statically, or at runtime), generic subclasses nested inside of functions specifically appear not to be. This is likely worth a report on bugs.swift.org.