Search code examples
swiftgenericsprotocolsdowncast

swift 3 downcast to dynamic class


I am trying to create a couple of objects which are dependent one to each other and they mush have a method to downcast directly the concrete class of the other object. Something like this:

protocol aProt
{
    var bVar:bProt! { get set }
}

protocol bProt
{
    var aVar:aProt!  { get set }
}

class a: aProt
{
    var bVar: bProt!
    func bConcrete() -> b {
        return bVar as! b
    }
}

class b: bProt
{
    var aVar: aProt!
    func aConcrete() -> a {
        return aVar as! a
}

Now, the problem is that I want this behavior (func aConcrete(),func bConcrete()) to be inherited by the subclasses of a and b. Then I thought the perfect way of doing this was using generics, but... There's no way of doing this.

class a: aProt { var bVar: bProt! func bConcrete() -> T { return bVar as! T } }

class b: bProt
{
    var aVar: aProt!
    func aConcrete<T>() -> T {
        return aVar as! T
}

You can do it but when you have to use it you must downcast the variable anyway, so there is no way of doing it in a clean manner:

let aObject = a()
let bSubclassObject = a.bConcrete() // The compiler complains it cannot infer the class of T
let bSubclassObject = a.bConcrete() as! bSubclass // this works, but this is exactly which I wanted to avoid... :(

Solution

  • Define the generic function and add where to T:

    protocol aProt {
        var bVar: bProt! { get set }
    }
    
    protocol bProt {
        var aVar:aProt!  { get set }
    }
    
    class a: aProt {
        var bVar: bProt!
        func bConcrete<T: b>(_ type: T.Type) -> T? {
            return bVar as? T
        }
    }
    
    class b: bProt {
        var aVar: aProt!
        func aConcrete<T: a>(_ type: T.Type) -> T? {
            return aVar as? T
        }
    }
    
    class a1: a { }
    class b1: b {
        var fullName: String = "new object"
    }
    
    let aObj = a()
    aObj.bVar = b1()
    let bObj = aObj.bConcrete(b1.self)
    bObj?.fullName
    

    According to your requirement, calls bConcrete(b1.self) might still not good enough, but at least you need to know what type of data you are expecting to return.