I plan to use a string value to select a custom defined class. The desired class object is returned using Bundle.main.classNamed
.
For better understanding what I want to do, here is a short code example with some custom classes:
class Product: NSObject {
var name: String
init(_ name: String) {
self.name = name
}
}
class Apple: Product {
var num: Int
init(_ num: Int) {
self.num = num
super .init("Apple")
}
}
class Melon: Product {
var num: Int
var size: Int
init(_ num: Int, _ size: Int) {
self.num = num
self.size = size
super .init("Melon")
}
}
The class Apple
and Melon
are subclasses of Product
, each with its own initialization method. Given a string value like "1000 Apples"
I want to instantiate an Apple
class. To get the desired class I use Bundle.main.classNamed
like this:
let clsName: String = "Apple"
let nameSpace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
let loadedClass = Bundle.main.classNamed(nameSpace + "." + "Apple") as! NSObject.Type
How do I instantiate loadedClass
?
let apple = loadedClass.init(100)
Gives an Fatal error: Use of unimplemented initializer 'init()' for class 'MyApp.Apple'
let unknownFruit = loadedClass(100)
Gives the Error: Initializing from a metatype value must reference 'init' explicitly
While there are two subclasses with two different initialization methods it should be possible to use loadedClass
with its own init
method, but this doesn't work so far.
Swift 5.2.4 Xcode 11.5
See my comments under the question to get a context for this answer. But I wonder if you are solving a wrong problem, and it would be better for you to switch to a different design. For example without knowing how your product classes are used, I think it may be a good case for enum:
enum Product {
case apple(num: Int)
case melon(num: Int, size: Int)
init?(_ name: String, _ num: Int = 0, _ size: Int = 0) {
switch name.lowercased() {
case "apple":
self = .apple(num: num)
case "melon":
self = .melon(num: num, size: size)
default:
return nil
}
}
}
extension Product: CustomStringConvertible {
var description: String {
switch self {
case .apple(let num):
return "apple(\(num))"
case .melon(let num, let size):
return "melon(\(num) - \(size)"
}
}
}
let clsName: String = "Apple"
let apple = Product(clsName, 100)
print("I am \(apple?.description)") // apple(100)
Anyway, this is more a food for thought, which was too long for comment! Feel free to delete.