Pride Chung
Pride Chung

Reputation: 573

Can't use concrete subclass to implement a property in protocol in Swift

I'm doing something similar to the Bridge Pattern in JAVA, DriverType is a protocol requires a property named vehicle to be a Drivable object, Drivable is also a protocol and is adopted by the class 'Car'

protocol Drivable {
    var speed: Double { get }
}

protocol DriverType {
    var vehicle: Drivable { get }
}

class Car: Drivable {
    var speed = 80.0;
    var brand = "BMW"
}

class Driver: DriverType {

    var vehicle: Car = Car() //Error: Type 'Driver' does not conform to protocol 'DriverType'

    // This one works, but I have to downcast the property to 'Car' everytime I use it.
    var vehicle: Drivable = Car() //Type 'Driver' does not conform to protocol 'DriverType'
}

When I implement the Driver class, it's very natural to declare vehicle property as a Car. But then I ran into the problem that compiler thinks Driver doesn't conform to DriverType even though Car conforms to Drivable perfectly.

UPDATE:

@Antonio 's answer is solid, but this is what I currently settled, it doesn't involve generic with the class.

protocol Drivable {
    var speed: Double { get }
    init()
}

protocol DriverType {
    func vehicle() -> Drivable
}

class Car: Drivable {
    var speed = 80.0;
    var brand = "BMW"
    required init() {}
}

class Driver: DriverType {
    private var m_vehicle: Car = Car()
    func vehicle() -> Drivable {
        return m_vehicle
    }

    // Inside Driver class I use the m_vehicle property directly
}

Upvotes: 3

Views: 1275

Answers (1)

Antonio
Antonio

Reputation: 72750

I think the compiler error is misleading. DriverType states that any class adopting it must expose a vehicle property with Drivable type, not a property with a class type adopting the Drivable type.

I would solve this issue by defining both the DriverType protocol and the Car class using generics:

protocol Drivable {
    var speed: Double { get }
    init()
}

protocol DriverType {
    typealias T: Drivable
    var vehicle: T { get }
}

class Car: Drivable {
    var speed = 80.0;
    var brand = "BMW"
    required init() {}
}

class Driver<T: Drivable>: DriverType {   
    var vehicle: T = T() 
}

This explicitly states that classes adopting DriverType must expose a vehicle property whose type is any class adopting the Drivable protocol.

Upvotes: 1

Related Questions