3

I've had a VehicleModels framework with classes like Car, Bike, Plane. In the other framework VehicleInventory I needed to print customised descriptions (specific to the second framework) in a table. So I have added protocol DescriptableVehicle with method describe(). Then I've added protocol extensions for all the vehicles like:

extension Car: DescriptableVehicle {
  func describe() -> String {
    return "Car: \(self.vin)" // returns formatted vehicle number
  }
}

However, assumptions have changed and now I do not expose concrete classes from my vehicles framework. Instead, I expose protocols like CarProtocol, BikeProtocol, so that in general I have the same information.

The problem is that I can't use protocol extensions anymore (or at least not in that shape) because extension of protocol in opposite to extension of class cannot have an inheritance clause.

Any idea how I can tackle the problem to not modify my usages to much? Initially, I thought couple where clauses on protocols plus couple casts will make the deal, however without access to classes it doesn't help. I have also tried Adapters and type erasure but either I am using it badly or it serves a different purpose.

To illustrate problem I have prepared repository: https://github.com/wedkarz/SwiftProtocolExtensionsProblem

There are two playgrounds. V1 is what I used to have and which was working. V2 contains what I have now and what I am trying to make working.

In real life, class PrivateFramework is a separate framework and protocols: VehicleProtocol, CarProtocol, BikeProtocol, PlaneProtocol are part of it, but DescriptableVehicle is not a part of PrivateFramework, so it cannot be used inside. The example illustrates a problem with accesses to concrete types and a problem with its extensions.

You can see there are extensions commented out because I cannot use them anymore. The goal is to make the collection of [VehicleProtocol] printing its contents in a similar fashion it was working previously.

Fishman
  • 1,737
  • 1
  • 25
  • 42
  • You should provide more code, it's quite unclear what you have and what the issue with it is. – Alexander Nov 22 '17 at 16:24
  • Ok, I will. Meanwhile, could you highlight what is unclear? – Fishman Nov 22 '17 at 19:58
  • I just can't visualize what you currently have, what your goal is, and what is giving you trouble. Seeing a MVE of the code will help – Alexander Nov 22 '17 at 20:28
  • I have prepared repository that somehow illustrates my problem. http://github.com/wedkarz/SwiftProtocolExtensionsProblem, let me know if I something is still unclear – Fishman Nov 23 '17 at 09:16

1 Answers1

0

Does this answer your question?

class PrivateFramework {
    private class AbstractVehicle: VehicleProtocol {
        var name: String { return "Abstract vehicle name" }
    }

    private class Car: AbstractVehicle, CarProtocol {
        override var name: String { return "Car vehicle name" }
        let vin: String = "ABCDEFGH VIN"
    }

    private class Bike: AbstractVehicle, BikeProtocol {
        override var name: String { return "Bike vehicle name" }
        let saddle: String = "Comforable saddle name"
    }

    private class Plane: AbstractVehicle, PlaneProtocol {
        override var name: String { return "Plane vehicle name" }
        let numberOfEngines: Int = 4
    }

    func produceVehicles() -> [DescriptableVehicle] {
        let car = Car()
        let bike = Bike()
        let plane = Plane()
        return [car, bike, plane]
    }
}

protocol VehicleProtocol {
    var name: String { get }
}

protocol DescriptableVehicle: VehicleProtocol {
    func describe() -> String
}



protocol CarProtocol: DescriptableVehicle {
    var vin: String { get }
}

protocol BikeProtocol: DescriptableVehicle {
    var saddle: String { get }
}

protocol PlaneProtocol: DescriptableVehicle {
    var numberOfEngines: Int { get }
}




extension DescriptableVehicle where Self: CarProtocol {
    func describe() -> String { return "Car, \(self.name), \(self.vin)" }
}

extension DescriptableVehicle where Self: BikeProtocol {
    func describe() -> String { return "Bike, \(self.name), \(self.saddle)" }
}

extension DescriptableVehicle where Self: PlaneProtocol {
    func describe() -> String { return "Plane, \(self.name), \(self.numberOfEngines)" }
}

let vehicles: [DescriptableVehicle] = PrivateFramework().produceVehicles()

vehicles.forEach { print($0.describe()) }
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • No. `DescriptableVehicle` protocol is not a part of Private framework, so you cannot use it inside like `func produceVehicles() -> [DescriptableVehicle]`. It is the other framework functionality. My example is simplified a little, but I have described the rules. – Fishman Nov 23 '17 at 19:32
  • @Fishman Well then you'll need to return a `[VehicleProtocol]` and cast the contents to get those instances which are `VehicleProtocol`, but also `DescriptableVehicle` – Alexander Nov 23 '17 at 19:54
  • But here is the catch, you’ll get nil when you try to cast. – Fishman Nov 23 '17 at 22:17
  • @Fishman Of course, if the class didn't originally conform to `DescriptableVehicle`, and no one extended it to conform, what would you expect to happen? You would be trying to call `DescriptableVehicle` method on a type that hasn't had it defined – Alexander Nov 24 '17 at 00:41
  • Hence is the problem. Originally I could extend concrete types. Now I have interfaces. How to deal with it (without changing Private framework) - how to transform my "used to be" class extensions, so that I do not have to rewrite everything. – Fishman Nov 24 '17 at 06:38
  • @Fishman So `CarProtocol`/`BikeProtocol`/`PlaneProtocol` are not part of the `PrivateFramework`, but the framework has access to them, but `DescriptableVehicle` is not part of the framework and the framework doesn't have access to it? – Alexander Nov 24 '17 at 14:20
  • :/ It is exactly as I have described in the issue. ‘Car/Bike/Plane/VehicleProtocol’ **are** parts of private framework, publicly visible outside. ‘DescriptableVehicle’ **is not** a part of framework. – Fishman Nov 24 '17 at 16:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159769/discussion-between-alexander-and-fishman). – Alexander Nov 24 '17 at 17:43