Reputation: 1194
In order to reduce cut-and-paste code in this app, I'm trying to pass class names around in order to tell a method which way it should process some data. I have something like the following:
class MyClass : NSObject {
var name : String = ""
}
class OneClass : MyClass {
override init() {
super.init()
self.name = "One"
}
}
class TwoClass : MyClass {
override init() {
super.init()
self.name = "Two"
}
}
class Thing : NSObject {
func doStuff(withClass cls: AnyClass) -> String {
let x = cls.init()
return x.name
}
}
let z = Thing()
print(z.doStuff(withClass: OneClass))
print(z.doStuff(withClass: TwoClass))
Passing withClass cls: AnyClass
the parser pushed me to change let x = cls()
to let x = cls.init()
. But I've got an Expected member name or constructor call after type name
error for the last two lines. The recommended fixes both cause other problems.
The first suggestion, adding the ()
constructor after the class name, causes new errors on those lines: Cannot convert value of type 'OneClass' to expected argument type 'AnyClass' (aka 'AnyObject.Type')
Taking the second suggestion and changing them to OneClass.self
and TwoClass.self
gets rid of the parser errors, but when I execute the code it just runs forever.. never erroring out, and never completing.
I found a recommendation elsewhere that suggests I should change the Thing.doStuff()
parameters to expect MyClass
instead of AnyClass
, but that causes another set of new problems.
First, the parser starts complaining about the cls.init()
call, and the series of fixes it suggests eventually lead to something that makes no sense: let x = cls.type(of:;; init)()
. The parser ends up in a suggestion loop where it keeps adding more semi-colons in the middle of the statement.
Second, I'm back to type mismatch errors on the calls to doStuff()
in the last two lines: Cannot convert value of type 'OneClass.Type' to expected argument type 'MyClass'
.
There's obviously something I'm not getting here about passing types as arguments, but none of the googling I've done has landed me on something that explains the problems I'm seeing.
Upvotes: 0
Views: 115
Reputation: 24341
To get this working, you must call init
on cls
after typecasting it to NSObject.Type
. Also, x.name
only works if cls
Class type contains that particular property. This is the reason x
is then typecasted to MyClass
.
class Thing : NSObject
{
func doStuff(withClass cls: AnyClass) -> String?
{
let x = (cls as? NSObject.Type)?.init()
if let x = x as? MyClass
{
return x.name
}
return nil
}
}
Call doStuff
with ClassType.self
print(z.doStuff(withClass: OneClass.self))
print(z.doStuff(withClass: TwoClass.self))
Let me know if you still face any issues.
Upvotes: 1
Reputation: 285079
How about the generic Swift way.
The code constrains the generic type T
to MyClass
since it must have a name
property.
class MyClass : NSObject {
var name : String
override required init() {
self.name = ""
super.init()
}
}
class OneClass : MyClass {
required init() {
super.init()
self.name = "One"
}
}
class TwoClass : MyClass {
required init() {
super.init()
self.name = "Two"
}
}
class Thing : NSObject {
func doStuff<T : MyClass>(withClass cls: T.Type) -> String {
let x = cls.init()
return x.name
}
}
let z = Thing()
print(z.doStuff(withClass: OneClass.self))
print(z.doStuff(withClass: TwoClass.self))
Or use a protocol.
protocol Nameable {
var name : String { get }
init()
}
class MyClass : NSObject, Nameable { ...
...
class Thing : NSObject {
func doStuff<T : Nameable>(withClass cls: T.Type) -> String {
let x = cls.init()
return x.name
}
}
Upvotes: 1