-1

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.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
kaylanx
  • 908
  • 1
  • 8
  • 11
  • Where is `fooBarScreen` method declared? – Leo Dabus Apr 14 '22 at 22:49
  • Are you sure you can do this in Java? Making this would work would require covariant generics, which AFAIK Java doesn’t support ([e.g.](https://stackoverflow.com/questions/2660827/java-generics-covariance)). If this were possible, hypothetically, the issue is this: `fooBarController` can be one of two different types, depending on a runtime condition (`boop.someCondition`). Generally that leads to issues like this: `let x = fooBarController.stages`, what’s the static type of x? In this case that isn’t an issue because `stages`, but you see the point – Alexander Apr 14 '22 at 23:32
  • @Alexander yep absolutely works in Java... https://gist.github.com/kaylanx/877d8d77094ff3e7820f5ef1139966f1 – kaylanx Apr 15 '22 at 09:27
  • @LeoDabus just in another class – kaylanx Apr 15 '22 at 09:31
  • @kaylanx Huh, what the heck, you can just omit the generic type parameters in Java? I've never seen this before... what would that even mean? Does it act as if you had `FooBarController`? – Alexander Apr 15 '22 at 12:28

1 Answers1

2

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())
}
grigorevp
  • 631
  • 6
  • 13