1

Suppose I have a simple factory which returns various subclasses of a custom ModelObject class like:

class func testModelObject(className: String) -> ModelObject
{
   let obj = // instance of the subclass of ModelObject specified by className
   return obj
}

Is there a way to do this? Will Swift freak out when I try to call any methods of that object? Should I have something else for my return type?

GoldenJoe
  • 7,874
  • 7
  • 53
  • 92
  • 1
    Why should it take a string instead of a `ModelObject.Type`? – kennytm Apr 11 '17 at 04:08
  • Clarified my question. ModelObject is a base class. – GoldenJoe Apr 11 '17 at 04:12
  • 2
    I mean why not use something like this https://i.stack.imgur.com/yXsS1.png (sorry for the screenshot but inline-code in comment is ugly) – kennytm Apr 11 '17 at 04:49
  • The example I posted is oversimplified. For project-specific reasons I would have preferred using a string to define the type. I moved some code around though, and your idea of specifying a Metatype does work. If it's not possible to define a metatype with a string, you should post your code as an answer and explain why it can't be done so I can mark the solution. – GoldenJoe Apr 11 '17 at 09:21

1 Answers1

3

For best type safety, you should let testModalObject to accept a meta-type like:

class ModelObject {
    required init() {}
}

class Subclass: ModelObject {
    required init() { super.init() }
}

func testModalObject(_ type: ModelObject.Type) -> ModelObject {
    return type.init()
}

testModalObject(Subclass.self)

If you really need a string input, you will need to rely on the Objective-C runtime and do some casting (see how to create instance of a class from a string in swift 3):

@objc
class ModelObject: NSObject {     // <---
    required override init() {}
}

@objc(MOSubclass)    // <-- tell ObjC the name is "MOSubclass" instead of "????.Subclass".
class Subclass: ModelObject {
    required init() { super.init() }
}

func testModalObject(_ typeName: String) -> ModelObject? {
    let cls = NSClassFromString("MO\(typeName)") as? ModelObject.Type
    return cls?.init()
}

testModalObject("Subclass")!
Community
  • 1
  • 1
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005