class A {
let val : Int
init(val: Int) {
self.val = val
}
}
I have these 3 strings:
let className = "A"
let argName = "val"
let argValue = "4"
How can I call A(val:4) from using these 3 strings?
Since you've noted in the comments that the types will all be subclasses of some supertype, then that supertype can handle all the dispatching. In Cocoa, this is a pretty common pattern known as a class cluster.
class SuperA {
enum SuperAError: Error {
case cannotConstruct
}
static func create(className: String, argName: String, argValue: String) throws -> SuperA {
switch className {
case "A":
guard argName == "val",
let value = Int(argValue)
else { throw SuperAError.cannotConstruct }
return A(val: value)
default:
throw SuperAError.cannotConstruct
}
}
}
Now, I don't particularly like this approach. This kind of subclassing tends to be poor Swift. Swift is fine with classes when you require a reference type, but it doesn't favor subclassing. I'd do this with a Buildable protocol and a Builder:
enum BuildableError: Error {
case unknownType
case badParameters
}
protocol Buildable {
init(argName: String, argValue: String) throws
// ... and the rest of the methods you require ...
}
struct A {
var val: Int
}
extension A: Buildable {
init(argName: String, argValue: String) throws {
guard argName == "val", let value = Int(argValue) else {
throw BuildableError.badParameters
}
self.init(val: value)
}
}
final class Builder {
var buildables: [String: Buildable.Type] = [:]
func build(className: String, argName: String, argValue: String) throws -> Buildable {
guard let buildable = buildables[className] else {
throw BuildableError.unknownType
}
return try buildable.init(argName: argName, argValue: argValue)
}
}
let builder = Builder()
builder.buildables["A"] = A.self
builder.build(className: "A", argName: "val", argValue: "4")
If this leads to duplicated code, there are straightforward ways to address that with other protocols. For example, if many of your types had init(val: Int), they could share code with another protocol:
protocol ValIntBuildable: Buildable {
init(val: Int)
}
extension ValIntBuildable {
init(argName: String, argValue: String) throws {
guard argName == "val", let value = Int(argValue) else {
throw BuildableError.badParameters
}
self.init(val: value)
}
}
extension A: ValIntBuildable {}
In native Swift alone, you can't. Swift is not dynamic in such a way that you can instantiate an arbitrary class based on a string name of the class, and so forth. Objective-C is dynamic and has ways to do this, so if this kind of thing is important to you, make A an NSObject subclass and write this part of the code in Objective-C (or use equivalent Cocoa/objc-runtime calls).
A(val: 4), what would be the next thing you'd do with that, since you don't know anything about the returned value (such as what properties it has)? There's no way to do what you've literally said here, but there are quite a few ways to achieve what you're hinting at. The solution that makes sense depends on the goal, though.