Home > Blockchain >  Swift, use generic property with different specific types - Reference to generic type requires argum
Swift, use generic property with different specific types - Reference to generic type requires argum

Time:04-15

How would one go about assigning an instance of a class which can have a generic type, but you don't know what type that is until run time?

For example.

We have a protocol and enums that conform to it like this:

protocol Stage: CaseIterable, Hashable {
    var fooBarLength: Int { get }
}

enum FirstStage: String, Stage {
    var fooBarLength: Int { 10 }
    case section1
    case section2
}

enum SecondStage: String, Stage {
    var fooBarLength: Int { 10 }
    case section1
    case section2
    case section3
}

Next we have some kind of controller that uses the protocol as a generic type... comme ça...

class FooBarController<StageType: Stage>: UIViewController {
    private var stages: [StageType: Float] = [:]
}

Then used like this:

func fooBarScreen(boop: SomethingThatKnowsAboutTheStages) {
    var fooBarController: FooBarController // <---- how do I define this????

    if boop.someCondition() {
        fooBarController = FooBarController<FirstStage>()
    } else {
        fooBarController = FooBarController<SecondStage>()
    }
}

In Java / Kotlin I could just do this as it is above, how do I achieve the same thing in Swift?

Currently I get

"Reference to generic type 'FooBarController' requires arguments in <...>"

Secondary Question

Is there a more generic way than having to use that if-statement here? Ideally I would like the fooBarScreen method to not care about the generic type and just have SomethingThatKnowsAboutTheStages provide the type for me.

CodePudding user response:

You can specify a protocol for providing Stage types like so:

protocol StageProvider {
    
    associatedtype T: Stage
    
    func getType() -> T.Type
    
}

Then make your SomethingThatKnowsAboutTheStages or any other one conform this protocol:

class SomethingThatKnowsAboutTheStages: StageProvider {
    
    typealias T = SecondStage
    
    func getType() -> T.Type {
        SecondStage.self
    }
    
}

Add an initializer for your FooBarController:

class FooBarController<StageType: Stage>: UIViewController {
    
    convenience init(stage: StageType.Type) {
        self.init()
    }
    
}

And finally use all these:

func fooBarScreen<T: StageProvider>(boop: T) {
    let controller = FooBarController(stage: boop.getType())
}
  • Related