Search code examples
iosswiftinitializer

Calling an initializer having only the class name in Swift


I wanted to know if there was a way to call an initializer by only having the class name in Swift.

class Shape {
  let name: String
  var numberOfSides: Int?

  init(name: String) {
    self.name = name
  }
}

Pseudo code:

let shapeClass = stringToClass("Shape")

let shape: Shape = shapeClass(name: "Something")

Solution

  • More than just trying to call a class function, you are trying to call an initializer dynamically. To be able to call an initializer dynamically on a class that you get at runtime, the compile-time type of that class value must be a metatype of a class that has that initializer; but since initializers are not always inherited in Swift, that initializer must be declared required.

    Here we use NSClassFromString to get a class from a string. (I am declaring the classes as @objc with an explicit name, because otherwise Swift class names are mangled from the perspective of Objective-C and it's would be a pain to deal with.) However, it returns AnyClass (i.e. AnyObject.Type), which is a metatype whose values are any class. We want it to be restricted to classes that inherit from Shape, so we can use Shape's initializer, so we cast it to Shape.Type (the metatype whose values are classes that descend from Shape, inclusive).

    I also demonstrate that it is polymorphic as it works with the name of a subclass by simply changing the name.

    import Foundation
    @objc(Shape)
    class Shape {
      let name: String
      var type: String
    
      required init(name: String) {
        self.name = name
        self.type = "Shape"
      }
    }
    @objc(SubShape)
    class SubShape : Shape {
      required init(name: String) {
        super.init(name: name)
        self.type = "SubShape"
      }
    }
    let shapeClass = NSClassFromString("SubShape") as Shape.Type
    let shape: Shape = shapeClass(name: "Something")
    println(shape.type) // prints "SubShape"