4

I want to instantiate a class by using a string but I'm having problems. That's what I try to do:

let aClass = NSClassFromString("MyApp.Outer.Inner") as! SomeProtocol.Type
....
public class Outer {
    public class Inner: SomeProtocol {
        init() {...}
    }
}

That's crashing (because NSClassFromString returns nil

But that's working:

let aClass = NSClassFromString("MyApp.Inner") as! SomeProtocol.Type
....
public class Inner: SomeProtocol {
    init() {...}
}

Doesn't NSClassFromString work for nested classes?


Background: I have many "inner" classes that I use as templates to create objects (instead of storing them in .plist files, which is kind of annoying). I want to encapsulate them in an "Outer" class to have them better organised.

Thanks for any help :)

EDIT: I saw the question that this seems to be a duplicate of. But that's not a viable solution:

(Note this snippet won't run by itself -- the class A.B has to be referenced at least once somewhere in your process in order to be registered with the ObjC runtime. This could be as simple as putting let t = A.B.self somewhere in your app's startup code.)

I'm doing this approach with NSClassFromString because I want to avoid adding too much stuff for every inner class I create. I don't want to constantly add this to my startup code.

3
  • I'm wondering since NSClassFromString is a Obj-C legacy thing, it can't do something thats uniquely Swifty (nested classes) ? Commented Aug 16, 2017 at 17:55
  • "I'm doing this approach with NSClassFromString because I want to avoid adding too much stuff for every inner class I create. I don't want to constantly add this to my startup code" I'm sorry, but in my view resorting to NSClassFromString for this purpose is a Bad Smell. You should rethink your entire strategy (in my view). Commented Aug 16, 2017 at 19:15
  • That's the best way I could think of trying to access many classes in the most dynamic way possible. I use these in the following way: I have a game where you can unlock things (new items for example). These things have attributes (level, name, ...) that I need to grab from somewhere in order to create a new database object. I grab these attributes from a "template class" (the inner classes I talked about) that have these attributes. I couldn't think of a better way to access the classes, than using a string and NSClassFromString Commented Aug 16, 2017 at 19:51

1 Answer 1

4

If you run this code:

import Foundation

class Outer: NSObject {
    class Inner: NSObject {}
}

print(NSStringFromClass(Outer.Inner.self))

You will get something like the following:

_TtCC8Untitled5Outer5Inner

As you can see, embedded classes have their names mangled differently from how you'd expect. I believe the mangling format is undocumented, as well, so you can't rely on this not changing in some future Swift release.

If you need to be able to access a class using its name in the Objective-C runtime, you can use the @objc attribute to give it a custom Objective-C name. Unfortunately, the @objc keyword doesn't allow dots, but you can use underscores for a similar effect:

class Outer: NSObject {
    @objc (Outer_Inner) class Inner: NSObject {}
}

EDIT: I should clarify that even with the @objc keyword specified, the inner class will still need to be accessed by Swift code before it will be available to the Objective-C runtime, and thus before functions like NSClassFromString() will work.

Sign up to request clarification or add additional context in comments.

3 Comments

Yeah, I know that (my question got marked as duplicate because there is a question with this exact answer as well). But this just moves my problem to another point. This way, I still have to manually add a line for every Inner class I write
You have to do that anyway, though, because as far as the Objective-C runtime, and thus, functions like NSClassFromString() are concerned, the inner class doesn't exist until something on the Swift side references it.
Hm, sound logical. Though, then I have to find a complete different approach because this way it isn't solving anything (I only do this to create these classes more conveniently)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.