Swift 5: Implementieren Sie generische Array-Operationen für Strukturen, die Equatable with protocol implementieren
Ich habe 2 einfache Strukturen in meiner App, die einem Protokoll entsprechen, um einige generische Operationen zu ermöglichen, die ich auf beide Typen anwenden muss:
import UIKit
protocol Track {
var url: URL { get set }
}
struct TrackFromMediaFile: Track {
var url: URL
}
struct TrackFromAsset: Track {
var url: URL
}
Jetzt habe ich eine Liste dieser Strukturen:
var tracks = [Track]()
tracks.append(TrackFromMediaFile(url: URL(string: "http://www.remotefile")!))
tracks.append(TrackFromAsset(url: URL(string: "local://localfile")!))
Ich muss den Index eines bestimmten Titels in dieser Titelliste finden:
// throws:
// Value of protocol type 'Track' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols
var track = tracks[0]
tracks.firstIndex(of: track)
Ich habe versucht, Ansätze von zu folgen https://stackoverflow.com/a/46719045/319905 und https://developer.apple.com/videos/play/wwdc2015/408/ ohne Erfolg konnte ich diese nicht an mein Beispiel anpassen.
Wie kann ich die generelle Behandlung von Spurarrays implementieren?
Antworten
Es ist nicht ganz klar, welche Art von Gleichheit Sie suchen. Wenn Sie sowohl die Typgleichheit als auch die Wertgleichheit überprüfen möchten, gehen Sie folgendermaßen vor:
tracks.firstIndex(where: {
type(of: $0) == type(of: track) && $0.url == track.url
})
Wenn Sie nur überprüfen möchten, ob dies urlgleich ist, gehen Sie folgendermaßen vor:
tracks.firstIndex(where: { $0.url == track.url })
In Swift bedeutet Equatable, dass ein Wert mit anderen Werten desselben Typs verglichen werden kann. Sie gilt nicht für Werte unterschiedlichen Typs. Ein Protokoll ist kein Typ, es beschreibt Typen, daher kann Track selbst nicht gleichwertig sein.
Wenn Sie dies häufig tun, ist es oft bequem auszudrücken, "in der Lage zu sein, die Gleichheit mit anderen Mitgliedern des Protokolls zu überprüfen". Das ist nicht gleichwertig, aber es ist sicherlich etwas, das Sie ausdrücken können. Es sieht aus wie das:
protocol Track {
var url: URL { get set }
func isEqual(to: Track) -> Bool
}
Für Track-konforme Typen, die bereits Equatable sind, können Sie eine Standardimplementierung bereitstellen:
extension Track where Self: Equatable {
func isEqual(to other: Track) -> Bool {
guard let other = other as? Self else { return false }
return self == other
}
}
Und markieren Sie Ihre konformen Typen als gleichwertig (swift erstellt automatisch Konformitäten):
struct TrackFromMediaFile: Track, Equatable { ... }
struct TrackFromAsset: Track, Equatable { ... }
Damit ist der Code etwas schöner:
tracks.firstIndex(where: { $0.isEqual(to: track) })
Und wenn Sie das viel tun, können Sie es natürlich noch ein bisschen schöner machen:
extension Collection where Element == Track {
func firstIndex(of track: Track) {
firstIndex(where: { $0.isEqual(to: track) })
}
}
Und damit können Sie die Syntax haben, mit der Sie begonnen haben:
tracks.firstIndex(of: track)