Swift
NSClassFromString
Swift programming
iOS development
Objective-C interoperability

Swift language NSClassFromString

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Swift is strongly typed, but Apple platforms still expose parts of the Objective-C runtime for cases where type information must be discovered dynamically. NSClassFromString is one of those bridging tools: it lets you ask the runtime for a class by name and then decide whether that class can be used in a safe way.

What NSClassFromString Returns

NSClassFromString takes a class name as a string and returns an optional AnyClass. If the runtime can find a class with that name, you get the metatype back. If not, you get nil.

That makes it useful for plugin-like systems, storyboard-related work, optional framework integration, and migration code that still needs to talk to Objective-C style APIs.

In Swift, the returned value is intentionally generic. You usually cast it to a more specific metatype before instantiating or calling class methods.

Looking Up a Class by Module and Name

For Swift classes, the runtime name usually includes the module name. In an app target called MyApp, a class named SettingsViewController is usually looked up as MyApp.SettingsViewController.

swift
1import Foundation
2import UIKit
3
4if let cls = NSClassFromString("MyApp.SettingsViewController") as? UIViewController.Type {
5    let viewController = cls.init()
6    print("Loaded \(type(of: viewController))")
7} else {
8    print("Class not found")
9}

Two things matter here. First, the string must match the runtime name exactly. Second, the cast to UIViewController.Type gives you a type that can actually be initialized and used safely in UI code.

Making a Swift Class Visible to the Runtime

Not every pure Swift type is visible in the same way as Objective-C classes. Runtime lookup works best with classes that inherit from NSObject, UIViewController, or another Objective-C compatible base class.

swift
1import Foundation
2
3@objc(UserProfile)
4final class UserProfile: NSObject {
5    @objc func greeting() -> String {
6        "Hello"
7    }
8}
9
10if let cls = NSClassFromString("UserProfile") as? UserProfile.Type {
11    let instance = cls.init()
12    print(instance.greeting())
13}

Using @objc can expose a stable runtime name. That is helpful when you need to interoperate with Objective-C code or when you want to avoid depending on a module-qualified name in a string.

A Safer Pattern for Dynamic Instantiation

Dynamic lookup is inherently string-based, so it is best kept behind a small helper that validates the result once.

swift
1import UIKit
2
3func instantiateViewController(named className: String) -> UIViewController? {
4    guard
5        let cls = NSClassFromString(className) as? UIViewController.Type
6    else {
7        return nil
8    }
9
10    return cls.init()
11}
12
13let screen = instantiateViewController(named: "MyApp.SettingsViewController")
14print(screen as Any)

This keeps the rest of your codebase from spreading runtime strings everywhere. If naming rules change later, you only have one place to update.

Common Pitfalls

The most common problem is forgetting the module name. In Swift, "SettingsViewController" often fails while "MyApp.SettingsViewController" succeeds.

Another pitfall is expecting structs, enums, or unrelated pure Swift classes to behave like Objective-C runtime classes. NSClassFromString is for class lookup, and it works best with Objective-C compatible types.

Initialization is another source of bugs. Even after a successful cast, the class must support the initializer you plan to call. A UIViewController subclass with a custom setup path may not behave correctly with plain init().

Finally, avoid building architecture around raw class-name strings when a protocol registry or explicit factory would be clearer. Dynamic lookup is useful, but it trades compile-time safety for runtime flexibility.

Summary

  • 'NSClassFromString lets Swift ask the Objective-C runtime for a class by name.'
  • The returned value is usually cast to a specific metatype before use.
  • Swift class names often need the module prefix, such as MyApp.SettingsViewController.
  • Objective-C compatibility matters, so classes that inherit from NSObject or UIKit classes work best.
  • Keep string-based lookup isolated so the rest of the code remains type-safe and maintainable.

Course illustration
Course illustration

All Rights Reserved.