Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiate class from protocol type

Tags:

swift

I am writing method which takes a type which conforms to a protocol and instantiates an instance of this class. When I build it, the compiler crashes with a segfault. I appreciate that this points to a compiler bug 99% of the time, but I am interested to see if what I'm trying to do is logically correct or am I just throwing absolute nonsense at the compiler and I shouldn't be surprised to see it crash.

Here is my code

protocol CreatableClass {
    init()
}

class ExampleClass : CreatableClass {
    required init() {

    }
}

class ClassCreator {
    class func createClass(classType: CreatableClass.Type) -> CreatableClass {
        return classType()
    }
}

ClassCreator.createClass(ExampleClass.self)

I also tried to rule out passing a Type as a method parameter as being the root of the problem and the following code also crashes the compiler:

protocol CreatableClass {
    init()
}

class ExampleClass : CreatableClass {
    required init() {

    }
}


let classType: CreatableClass.Type = CreatableClass.self
let instance = classType()

So - is this just a straightforward compiler bug and does what I am trying to do seem reasonable, or is there something in my implementation that is wrong?

Edit:

This can be achieved using generics as shown @Antonio below but unfortunately i believe that isn't useful for my application.

The actual non-dumbed down use-case for doing this is something like

protocol CreatableClass {}
protocol AnotherProtocol: class {}

class ClassCreator {
    let dictionary: [String : CreatableClass]

    func addHandlerForType(type: AnotherProtocol.Type, handler: CreatableClass.Type) {
        let className: String = aMethodThatGetsClassNameAsAString(type)
        dictionary[className] = handler()
    }

    required init() {}
}
like image 931
Stephen Groom Avatar asked Mar 01 '26 22:03

Stephen Groom


1 Answers

I usually do that by defining a generic method. Try this:

class func createClass<T: CreatableClass>(classType: T.Type) -> CreatableClass {
    return classType()
}

Update

A possible workaround is to pass a closure creating a class instance, rather than passing its type:

class ClassCreator {
    class func createClass(instantiator: () -> CreatableClass) -> (CreatableClass, CreatableClass.Type) {
        let instance = instantiator()
        let classType = instance.dynamicType

        return (instance, classType)
    }
}

let ret = ClassCreator.createClass { ExampleClass() }

The advantage in this case is that you can store the closure in a dictionary for example, and create more instances on demand by just knowing the key (which is something in 1:1 relationship with the class name).

I used that method in a tiny dependency injection framework I developed months ago, which I realized it works only for @objc-compatible classes only though, making it not usable for my needs...

like image 167
Antonio Avatar answered Mar 03 '26 19:03

Antonio



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!