Every iOS developers know that Objective-C is superset of C with powerful dynamic type feature of SmallTalk. Dynamic type feature of Objective-C can be utilised well when you want to initiate an object from a string at run time.

id object = [NSClassFromString(@”Foo”) new];

This way is totally different with create an object from a type that is well checked at compile time.
Foo *foo = [Foo new];

Swift is new kid on the block. It offers many cool features such as functional programming, generic type…but at the moment (Dec 2014), it does not yet support dynamic type feature as Objective-C. In this post I will show a method to “Make Swift dynamic as Objective-C”

Because we can create a Swift class that inherits Objective-C, we will make a class in Swift can be dynamically initiated. This class must inherit from NSObject.The reverse is not true. We cannot create Objective-C class that inherits Swift class.

I extended NSObject by creating a new category NSObject+Swift.h and NSObject+Swift.m .

Method 1: prefix class with keyword @objc

Prefix a class declaration by @objc to expose it to Objective-C code.

import Foundation
@objc(Base) class Base : NSObject {
    override var description : String {return "I am a Base"}
    func say() {
        println("Say base...")
    }
}
@objc(Foo) class Foo : Base {
    override var description : String {return "I am a Foo"}
    override func say() {
        println("Say foo...")
    }
}
@objc(Bar) class Bar : Base {
    override var description : String {return "I am a Bar"}
    override func say() {
        println("Say bar...")
    }
}

In NSObject+Swift.m, I create a class method + (id)createObject: (NSString*) name . A method - (id)swift_performSelector:(SEL)selector will call a method that has name as input parameter.

+ (id)createObject: (NSString*) name {
    return [NSClassFromString(name) new];
}
- (id)swift_performSelector:(SEL)selector
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    return [self performSelector:selector];
#pragma clang diagnostic pop
}

Now I can initiate Foo or Bar object by providing class name as String

var foo = NSObject.createObject("Foo") as NSObject
//Execute method "say" of object foo. Method "say" is not type-safe checked in compile time
if foo.respondsToSelector("say") {
  foo.swift_performSelector("say")            
}

Method 2: Do not use keyword @objc to prefix class

import Foundation
class Besa: NSObject {
    func say() {
        println("Say base...")
    }
}

class Boo: Besa {
    override func say() {
        println("Say boo...")
    }
}

class Far: Besa {
    override func say() {
        println("Say far...")
    }
}

In NSObject+Swift.m, I created another class method + (id)createObject2: (NSString*) name.
In this method, I need to prefix application name to class name otherwise iOS cannot find class type: AppName.ClassName

//name is name of class that is not prefixed by @objc
+ (id)createObject2: (NSString*) name {
    NSString* appName = [[NSBundle mainBundle].infoDictionary objectForKey:@"CFBundleName"];
    return [NSClassFromString([NSString stringWithFormat:@"%@.%@", appName, name]) new];
}

The rest are same as method 1. Now you can make Swift has dynamic feature like Objective-C
Get full demo source code in below

Học lập trình di động trực tuyến

Credits
Part of this code, I borrowed from https://github.com/tokorom/performSelector-swift

Khóa học lập trình di động tại Techmaster:

Để cài đặt MacOSX lên phần cứng không phải Apple liên hệ chuyên gia cài Hackintosh:

  • Nguyễn Minh Sơn: 01287065634
  • Huỳnh Minh Sơn: 0936225565
  • Website: caidatmacos.com