How to Implement a Swift Protocol Across Structs with Conflicting Property Names

How to implement a Swift protocol across structs with conflicting property names

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.

swift protocol conformance when same property name is optional

There is no elegant solution to your problem other than renaming the conflicting property on the conforming type.

Swift doesn't allow 2 properties of the same name to exist on a type even if their types are different. On the other hand, Int? and Int are completely different types, so you cannot have trackNumber: Int fulfil the protocol requirement of trackNumber: Int?.

The only solution (other than changing the type in either the protocol or the struct) is to rename the non-Optional property in SpotifyTrack and make an optional computed property of the same name returning the non-optional one.

protocol Track {
var trackNumber: Int? { get }
}
struct SpotifyTrack {
private let _trackNumber: Int
}
extension SpotifyTrack: Track {
var trackNumber: Int? { _trackNumber }
}

How can I explicitly implement a protocol in swift? If it is impossible, why?

Protocol extensions basically already do what you're describing:

protocol Cat {
}
extension Cat {
func quack() {
print("meow")
}
}
class Duck : Cat {
func quack() {
print("quack")
}
}
let d = Duck()
d.quack() // quack
(d as Cat).quack() // meow

Swift Struct with Lazy, private property conforming to Protocol

Because accessing the lazy data variable mutates AStruct, any access to it must be marked as also mutating the struct. So you need to write:

struct AStruct : Example {
// ...
var var1: String {
// must declare get explicitly, and make it mutating:
mutating get {
// act of looking at data mutates AStruct (by possibly initializing data)
return self.data.var1
}
}

var varArray:[String] {
mutating get {
return self.data.varArray
}
}
}

However, you'll find now that Swift complains you aren't conforming to Example, because its get var1 isn't marked as mutating. So you'd have to change it to match:

protocol Example {
var var1:String { mutating get }
var varArray:[String] { mutating get }
}

How to resolve conflict between struct and module names?

The following scenario resolves name conflict between struct and module for second case which contains A, B, C projects. This solution is based on rule that we can edit only C project. Because C is our client of hypothetically third-party unchangeable modules A and B.

  1. Add new Swift file into C project with code below
import struct A.B
typealias StructB = B

  1. Change code in Src.swift to the following
import B
let structB: StructB? = nil

struct X { }
typealias Y = B.X

Result is succeeded compilation without error:

'X' is not a member type of 'B'

And we can use struct B from module A and struct from module B in single file. The name conflict has been resolved.


The second one scenario resolves conflict in the first case with Data.

  1. Replace
import Foundation

with

import struct Foundation.Date
import struct Foundation.UUID

  1. Be sure that Foundation isn't imported in module level. In my case I removed #import <UIKit/UIKit.h> from Data.h. And replaced FOUNDATION_EXPORT with extern to do Data.h compilable.

Conflicting with parent property description in Swift

As vadian suggested in comments, I decided to use Codable protocol instead of EVReflection as Codable is way easy to use. I read this link for Codable and this video link for parsing json data.

Hashable struct with interchangeable properties?

This is how Hasher works

https://developer.apple.com/documentation/swift/hasher

However, the underlying hash algorithm is designed to exhibit
avalanche effects: slight changes to the seed or the input byte
sequence will typically produce drastic changes in the generated hash
value.

So the problem here in hash(into:) func

Since the sequence matters combine operation is not commutative. You should find some other function to be a hash for this struct. In your case the best option is

    func hash(into hasher: inout Hasher) {
hasher.combine(leftOperand & rightOperand)
}

As @Martin R pointed out to have less collisions it's better to use ^

    func hash(into hasher: inout Hasher) {
hasher.combine(leftOperand ^ rightOperand)
}


Related Topics



Leave a reply



Submit