> On Nov 13, 2016, at 1:55 AM, Rick Mann <rm...@latencyzero.com> wrote:
>
>
>> On Nov 12, 2016, at 22:47 , David Sweeris <daveswee...@mac.com> wrote:
>>
>>
>>> On Nov 13, 2016, at 00:38, Rick Mann via swift-users
>>> <swift-users@swift.org> wrote:
>>>
>>> So, it seems there's still no way to do something like instantiate a class
>>> given only its name (as a String)?
>>>
>>> In my situation, I have a number of subclasses (or protocol
>>> implementations), which parallel some other class hierarchy. I need to
>>> dynamically create one based on the name of another. I don't want the
>>> latter classes to "know" anything about the former.
>>
>> Not that I know of... If this is all your code, can you fake it by switching
>> over the type's name?
>
> Yeah, it just happens in a few places, would be nice to write the code once
> and then just ensure classes exist. I know I could do it subclassing
> NSObject, but Swift really ought to have proper reflection.
Reflection is on the todo list (although I can’t recall if it’s in-scope for
Swift 4).
There’s also the possibility of putting the names of the classes you want to
instantiate into an enum. In fact, if all the classes in question have a common
superclass, you could this:
class MyAwesomeSuperClass { required init() {} }
class MyAwesomeSubClass : MyAwesomeSuperClass {}
class Whatever : MyAwesomeSuperClass {}
class Etc : Whatever {}
enum ClassNames : String {
//Unfortunately the value of each case has to be a literal, so
//you can’t just say `case etc = "\(Etc.self)"`
case myAwesomeSuperClass = "MyAwesomeSuperClass"
case myAwesomeSubClass = "MyAwesomeSubClass"
case whatever = "Whatever"
case etc = "Etc"
init(_ instance: MyAwesomeSuperClass) { self = ClassNames.init(rawValue:
"\(type(of: instance))")! }
init(_ type: MyAwesomeSuperClass.Type) { self = ClassNames.init(rawValue:
"\(type)")! }
var type: MyAwesomeSuperClass.Type {
switch self {
case .myAwesomeSuperClass: return MyAwesomeSuperClass.self
case .myAwesomeSubClass: return MyAwesomeSubClass.self
case .whatever: return Whatever.self
case .etc: return Etc.self
}
}
}
var etc = Etc()
var className = ClassNames(etc)
var newEtc = className.type.init()
The same trick works if all the types in question conform to the same protocol,
too:
protocol MyAwesomeProtocol {
init()
func toIntMax() -> IntMax
}
extension Int : MyAwesomeProtocol {}
struct Five : MyAwesomeProtocol { func toIntMax() -> IntMax { return 5 } }
enum ProtocolNames : String {
case five = "Five"
case int = "Int"
init(_ instance: MyAwesomeProtocol) { self = ProtocolNames.init(rawValue:
"\(type(of: instance))”)! }
init(_ type: MyAwesomeProtocol.Type) { self = ProtocolNames.init(rawValue:
"\(type)")! }
var type: MyAwesomeProtocol.Type {
switch self {
case .five: return Five.self
case .int: return Int.self
}
}
}
var five = Five()
var fiveName = ProtocolNames(five)
var newFive = fiveName.type.init()
Either way, though, you’ll have to do something like if let nf = newFive as?
Int {…} if you want to use any functionality that isn’t explicitly in the
superclass or protocol.
It certainly involves some boilerplate code, but it also has the advantages of
validating the name before you try to use it and ensuring that any switch
statements can actually handle all the classes.
- Dave Sweeris
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users