0
votes

I have a class that intends to return a class which conforms to PanelControllerProtocol. The problem I'm having is taking the anyClass object and converting it to T.

func classFromBundle<T: PanelControllerProtocol>(className: String) -> T {
    let bundleClassString = classBundleAndName(className)
    var anyClass : AnyClass = NSClassFromString(bundleClassString)
}

If I try this:

var panelControllerType = anyClass as T

I get:

"Cannot cast from concrete type 'AnyClass' to type variable 'T' for which it does not match constraints".

Attempting to simply return anyClass gives me the error:

'AnyClass' is not convertible to 'T'

Trying to change my method name to this:

func classFromBundle<T where T: AnyClass, T: PanelControllerProtocol>(className: String) -> T

Gives the error:

Type 'T' constrained to non-protocol type 'AnyClass'

Also, once I figure out how to return the correct object, I'm not sure how to call the class. Currently, calling the above method like so:

classFromBundle("Test")

Gives the error:

Cannot convert the expression's type 'NSString' to type 'String'

1

1 Answers

0
votes

You can use the following code:

var instance: AnyObject? = NSClassFromString(bundleClassString)
let actualClassInstance = instance as T

return actualClassInstance

but, as you might guess, if the class type is not found, that generates a runtime exception.

A better solution is to let your function return an optional T, and account for NSClassFromString to fail creating an instance of the T class:

var actualClassInstance: T?

var instance: AnyObject? = NSClassFromString(bundleClassString)

if instance != nil {
    actualClassInstance = instance as? T
}

return actualClassInstance

Note that NSClassFromString, accordingly to the documentation:

Returns: The class object named by aClassName, or nil if no class by that name is currently loaded.

is returning the wrong type AnyObject!, whereas it should be AnyObject?

To call the function, you just have to tell the compiler what T is - and you do it by inferring the type from the variable the function result is assigned to:

let concreteClass: ConcreteClass = classFromBundle("ConcreteClass")
                   ^^^^^^^^^^^^^
                   This tells the compiler what T is

If you are using the 2nd (safer) version of the code, then you should declare the variable as optional:

let concreteClass: ConcreteClass? = classFromBundle("ConcreteClass")