Swift class introspection & generics

IntrospectionSwift

Introspection Problem Overview


I am trying to dynamically create a class instance based type using generics, however I am encountering difficulty with class introspection.

Here are the questions:

  • Is there a Swift-equivalent to Obj-C's self.class?
  • Is there a way to instantiate a class using the AnyClass result from NSClassFromString?
  • Is there a way to get AnyClass or otherwise type information strictly from a generic parameter T? (Similar to C#'s typeof(T) syntax)

Introspection Solutions


Solution 1 - Introspection

Well, for one, the Swift equivalent of [NSString class] is .self (see Metatype docs, though they're pretty thin).

In fact, NSString.class doesn't even work! You have to use NSString.self.

let s = NSString.self
var str = s()
str = "asdf"

Similarly, with a swift class I tried...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

Hmm… the error says:

> Playground execution failed: error: :16:1: error: constructing an object of class type 'X' with a metatype value requires an '@required' initializer > > Y().me() > ^ > :3:7: note: selected implicit initializer with type '()' > class X { > ^

It took me a while to figure out what this means… turns out it wants the class to have a @required init()

class X {
    func me() {
        println("asdf")
    }
    
    required init () {
        
    }
}

let Y = X.self

// prints "asdf"
Y().me()

Some of the docs refer to this as .Type, but MyClass.Type gives me an error in the playground.

Solution 2 - Introspection

Here's how to use NSClassFromString. You have to know the superclass of what you're going to end up with. Here are a superclass-subclass pair that know how to describe themselves for println:

@objc(Zilk) class Zilk : NSObject {
    override var description : String {return "I am a Zilk"}
}

@objc(Zork) class Zork : Zilk {
    override var description : String {return "I am a Zork"}
}

Notice the use of the special @obj syntax to dictate the Objective-C munged name of these classes; that's crucial, because otherwise we don't know the munged string that designates each class.

Now we can use NSClassFromString to make the Zork class or the Zilk class, because we know we can type it as an NSObject and not crash later:

let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"

And it's reversible; println(NSStringFromClass(anObject.dynamicType)) also works.


Modern version:

    if let aClass = NSClassFromString("Zork") as? NSObject.Type {
        let anObject = aClass.init()
        print(anObject) // "I am a Zork"
        print(NSStringFromClass(type(of:anObject))) // Zork
    }

Solution 3 - Introspection

If I'm reading the documentation right, if you deal with instances and e.g. want to return a new instance of the same Type than the object you have been given and the Type can be constructed with an init() you can do:

let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()

I quickly tested it with String:

let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")

which worked fine.

Solution 4 - Introspection

In swift 3

object.dynamicType

is deprecated.

Instead use:

type(of:object)

Solution 5 - Introspection

Swift implementation of comparing types

protocol Decoratable{}
class A:Decoratable{}
class B:Decoratable{}
let object:AnyObject = A()
object.dynamicType is A.Type//true
object.dynamicType is B.Type//false
object.dynamicType is Decoratable.Type//true

NOTE: Notice that it also works with protocols the object may or may not extend

Solution 6 - Introspection

Finally got something to work. Its a bit lazy but even the NSClassFromString() route did not work for me...

import Foundation

var classMap = Dictionary<String, AnyObject>()

func mapClass(name: String, constructor: AnyObject) -> ()
{
    classMap[name] = constructor;
}

class Factory
{
    class func create(className: String) -> AnyObject?
    {
        var something : AnyObject?
        
        var template : FactoryObject? = classMap[className] as? FactoryObject
        
        if (template)
        {
            let somethingElse : FactoryObject = template!.dynamicType()
            
            return somethingElse
        }
        
        return nil
    }
}


 import ObjectiveC

 class FactoryObject : NSObject
{
    @required init() {}
//...
}

class Foo : FactoryObject
{
    class override func initialize()
    {
        mapClass("LocalData", LocalData())
    }
    init () { super.init() }
}

var makeFoo : AnyObject? = Factory.create("Foo")

and bingo, "makeFoo" contains a Foo instance.

The downside is your classes must derrive from FactoryObject and they MUST have the Obj-C +initialize method so your class gets automagically inserted in the class map by global function "mapClass".

Solution 7 - Introspection

Here is another example showing class hierarchy implementation, similar to accepted answer, updated for the first release of Swift.

class NamedItem : NSObject {
    func display() {
        println("display")
    }
    
    required override init() {
        super.init()
        println("base")
    }
}

class File : NamedItem {
    required init() {
        super.init()
        println("folder")
    }
}

class Folder : NamedItem {
    required init() {
        super.init()
        println("file")
    }
}

let y = Folder.self
y().display()
let z = File.self
z().display()

Prints this result:

base
file
display
base
folder
display

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionErikView Question on Stackoverflow
Solution 1 - IntrospectionJiaaroView Answer on Stackoverflow
Solution 2 - IntrospectionmattView Answer on Stackoverflow
Solution 3 - IntrospectionmonkeydomView Answer on Stackoverflow
Solution 4 - IntrospectionJ.beenieView Answer on Stackoverflow
Solution 5 - IntrospectionSentry.coView Answer on Stackoverflow
Solution 6 - IntrospectionMartin-Gilles LavoieView Answer on Stackoverflow
Solution 7 - IntrospectionpossenView Answer on Stackoverflow