Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast to right generic from array in Swift

I have a Protocol called Composite.

This protocol has an array composites: [Composite]

I also have a generic subclass GenericSubclass<T>: Composite

When iterating over the array the best I can come up with looks like this:

for item in composites {
    if let item = item as? GenericSubclass<A> {
        let sc = SomeOtherClass<A>
    } else if let item = item as? GenericSubclass<B> {
        let sc = SomeOtherClass<B>
    } //and so on...
}

Is there any way to get a hold of GenericSubclass without specifying the Generic? In my use case there is absolutely no need for me to know about the T. I just have to instantiate another class with the same generic type.

Any help is much appreciated.

like image 633
ph1lb4 Avatar asked Feb 13 '17 13:02

ph1lb4


1 Answers

It's not clear what you're trying to accomplish with the "generic" (pun intended) class names you've chosen. I don't think there's a way to directly accomplish what you want. I.e. you can't just leave it as a generic T because the compiler needs some way to determine what T will be in use at runtime.

However, one way to solve the issue is to hoist the API into the Composite protocol:

protocol Composite {
    var composites: [Composite] { get set }
    func otherClass() -> OtherProtocol
}

protocol OtherProtocol { }

class GenericSubclass<T>: Composite {
    var composites: [Composite] = []

    func otherClass() -> OtherProtocol {
        return SomeOtherClass<T>()
    }
}

class SomeOtherClass<T>: OtherProtocol {}

So now when you implement your loop, you can rely on the fact that since each element is a Composite, you know it must provide an instance of OtherProtocol via the otherClass() method:

var c = GenericSubclass<Int>()
c.composites = [GenericSubclass<Double>(), GenericSubclass<Int>(), GenericSubclass<Character>()]

for item in c.composites {
    let sc = item.otherClass()
    print(sc)
}

Alternatively, if only GenericSubclass should vend an OtherProtocol, you can make the return type Optional and define an extension for all the other implementations of Composite:

protocol Composite {
    var composites: [Composite] { get set }
    func optionalClass() -> OtherProtocol?
}

extension Composite {
    func optionalClass() -> OtherProtocol? {
        return nil
    }
}
like image 101
Dave Weston Avatar answered Oct 28 '22 18:10

Dave Weston