I'm trying to write a protocol that reconciles two different structs that describe the same concept, a stop of some kind. Both have a Code
, Description
, and Latitude
& Longitude
coordinates, but for one type, the Description
could be nil
, and for the other type, the coordinates might be nil
.
How can I write a single protocol that reconciles these two structs?
Here's my protocol:
protocol Stop {
var Code : String { get }
var Description : String { get }
var Latitude : Double { get }
var Longitude : Double { get }
}
And the two types of stops:
struct BusStop : Stop { // Compiler error: doesn't implement Description
var Code : String
var Description : String?
var Latitude : Double
var Longitude : Double
// Various other properties
}
struct TrainStop : Stop { // Compiler error: doesn't implement Latitude or Longitude
var Code : String
var Description : String
var Latitude : Double?
var Longitude : Double?
// Various other properties
}
In C# (my mother tongue), I would write an explicit interface implementation like so (pseudo-code):
// At the end of the BusStop struct
var Stop.Description : String { return Description ?? string.Empty }
// At the end of the TrainStop struct
var Stop.Latitude : Double { return Latitude ?? 0 }
var Stop.Longitude : Double { return Longitude ?? 0 }
However, I'm not aware of any similar functionality in Swift. Given that I'm unable to change the existing property definitions of BusStop
and TrainStop
, how can I write the Stop
protocol so that it wraps around both structs and returns the properties when available?
The desired feature from explicit interface implementations is that they are statically dispatched, right? If you use Description
on a BusStop
, it will be an optional string, but if you use Description
on a Stop
, it will be a non-optional string.
In Swift, extension members are statically dispatched, so you can make use of this to achieve something similar:
extension Stop where Self == BusStop {
// Since the type of "self" here is BusStop, "Description" refers to the one declared in BusStop
// not this one here, so this won't cause infinite recursion
var Description : String { return self.Description ?? "" }
}
extension Stop where Self == TrainStop {
var Latitude: Double { return self.Latitude ?? 0 }
var Longitude: Double { return self.Longitude ?? 0 }
}
This code shows that this works:
let busStop = BusStop(Code: "ABC", Description: "My Bus Stop", Latitude: 0, Longitude: 0)
print(type(of: busStop.Description)) // Optional<String>
let stop: Stop = busStop
print(type(of: stop.Description)) // String
However, I still don't think this is good Swift code. It is often bad to just directly translate an API from one language to another. If I were you, I would make Longitude
, Latitude
and Description
in Stop
to be all optionals.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With