4

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")
modusCell
  • 13,151
  • 9
  • 53
  • 80
AJ_1310
  • 3,303
  • 3
  • 19
  • 26
  • what's the point of "having only the class name" if you write the type `Shape` explicitly in the code? – newacct Aug 06 '14 at 08:32
  • It's a representation of what is trying to be accomplished, shapeClass is meant to be a Shape.Type that can call funcs as if it was explicitly Shape(name: "Something"). – AJ_1310 Aug 06 '14 at 15:14
  • 2
    What I'm trying to understand is what possible class names you can put there. You hard-coded the type `Shape` for the variable `shape` in the source code. So we already know that a class that isn't `Shape` or a subclass of `Shape` won't work. In your example, you gave the name `"Shape"`, which seems odd because you already hard-coded somewhere else. Is this meant to work for classes that are subtypes of `Shape`? Because if it is just `Shape` then it doesn't make sense since we already know `Shape` at compile-time. – newacct Aug 06 '14 at 17:46

1 Answers1

7

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"
newacct
  • 119,665
  • 29
  • 163
  • 224