I'm going around in circles trying to get Hashable
to work with multiple struct
that conform to the same protocol
.
I have a protocol SomeLocation
declared like this:
protocol SomeLocation {
var name:String { get }
var coordinates:Coordinate { get }
}
Then I create multiple objects that contain similar data like this:
struct ShopLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
struct CarLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
I can later use these in the same array by declaring:
let locations: [SomeLocation]
The problem is, I create an MKAnnotation
subclass and need to use a custom Hashable
on the SomeLocation
objects.
final class LocationAnnotation:NSObject, MKAnnotation {
let location:SomeLocation
init(location:SomeLocation) {
self.location = location
super.init()
}
}
override var hash: Int {
return location.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
if let annot = object as? LocationAnnotation
{
let isEqual = (annot.location == location)
return isEqual
}
return false
}
This gives me 2 errors:
Value of type 'SomeLocation' has no member 'hashValue' Binary operator
'==' cannot be applied to two 'SomeLocation' operands
So I add the Hashable
protocol to my SomeLocation
protocol:
protocol SomeLocation: Hashable {
...
}
This removes the first error of hashValue not being available, but now I get an error where I declared let location:SomeLocation
saying
Protocol 'SomeLocation' can only be used as a generic constraint because it has Self or associated type requirements
So it doesn't look like I can add Hashable
to the protocol.
I can add Hashable
directly to each struct that implements the SomeLocation
protocol, however that means I need to use code like this and keep updating it every time I might make another object that conforms to the SomeLocation
protocol.
override var hash: Int {
if let location = location as? ShopLocation
{
return location.hashValue
}
return self.hashValue
}
I have tried another way, by making a SomeLocationRepresentable
struct:
struct SomeLocationRepresentable {
private let wrapped: SomeLocation
init<T:SomeLocation>(with:T) {
wrapped = with
}
}
extension SomeLocationRepresentable: SomeLocation, Hashable {
var name: String {
wrapped.name
}
var coordinates: Coordinate {
wrapped.coordinates
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(coordinates)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.name == rhs.name && lhs.coordinates == rhs.coordinates
}
}
however when I try to use this in the LocationAnnotation
class like
let location: SomeLocationRepresentable
init(location:SomeLocation) {
self.location = SomeLocationRepresentable(with: location)
super.init()
}
I get an error
Value of protocol type 'SomeLocation' cannot conform to 'SomeLocation'; only struct/enum/class types can conform to protocols
Is it possible to achieve what I am trying to do? Use objects that all conform to a protocol and use a custom Hashable
to compare one to the other?