Es klingt vielleicht kontraintuitiv, warum ich das getan habe, aber in meiner App RemafoX musste ich den Binding-Typ, der mit SwiftUI ausgeliefert wird, um Aenderungen in Views an Daten zu binden, dem Equatable-Protokoll konform machen, damit ich ein Binding-Objekt in meiner Datenschicht herumreichen konnte. Die Implementierung der Extension sah so aus:
extension Binding: Equatable where Value: Equatable {
static func == (left: Binding<Value>, right: Binding<Value>) -> Bool {
left.wrappedValue == right.wrappedValue
}
}Obwohl ich mit dieser Loesung nicht zu 100% zufrieden war, funktionierte sie einwandfrei, als ich sie urspruenglich entwickelte – also habe ich sie ausgeliefert. Aber dann begann mit irgendeinem macOS-Update ein Bug in alle Picker-Views meiner App einzuschleichen. Die Picker funktionierten zwar die meiste Zeit noch, verhielten sich aber manchmal seltsam. Das einzige Verhalten, das ich immer reproduzieren konnte, war: Wenn ich einen ausgewaehlten Wert programmatisch auf das Binding setzte und der Nutzer ihn dann spaeter auf einen anderen Wert aenderte, wurden beide Werte mit einem Haekchen im Picker-Dropdown angezeigt – was wahrscheinlich ein Bug irgendwo in SwiftUI ist:

Es hat mich Stunden des Code-Auskommentierens gekostet, um die Ursache zu finden, und es stellte sich heraus, dass es die oben erwaehnte Binding-Extension war. Irgendwie scheint ihre Definition in meiner App die interne Implementierung der Picker-View in SwiftUI beeinflusst zu haben. Und wenn man das weiss – wer weiss, welche anderen Seiteneffekte sie moeglicherweise verursacht hat oder in zukuenftigen System-Updates verursachen koennte? Ich musste also eindeutig eine bessere Loesung finden, die das Verhalten in SwiftUI-Views nicht beeinflussen sollte.
Der Grund, warum ich Binding Equatable-konform machen wollte, war, dass ich Anforderungen an meine Datenschicht hatte – naemlich bestimmte State-Typen in der TCA-Architektur –, die verlangten, dass alle Daten, die ich darin speichere, ebenfalls Equatable konformieren. Etwas wie:
struct AppState: Equatable {
// other properties
var configFile: Binding<ConfigFile>
}Moechtest du hier deine Werbung sehen? Kontaktiere mich unter [email protected].
Die Loesung, die ich gefunden habe, ist recht einfach, auch wenn ich erst “lernen” musste, wie man einen Property Wrapper schreibt, da es das erste Mal war, dass ich einen eigenen brauchte. Aber es war unkompliziert – ich glaube, du wirst es verstehen, auch wenn du noch nie einen geschrieben hast:
@propertyWrapper
public struct EquatableBinding<Wrapped: Equatable>: Equatable {
public var wrappedValue: Binding<Wrapped>
public init(wrappedValue: Binding<Wrapped>) {
self.wrappedValue = wrappedValue
}
public static func == (left: EquatableBinding<Wrapped>, right: EquatableBinding<Wrapped>) -> Bool {
left.wrappedValue.wrappedValue == right.wrappedValue.wrappedValue
}
}Die einzige Anforderung des @propertyWrapper ist die wrappedValue-Property, und weil ich diesen Wrapper in meiner gesamten modularisierten Anwendung teilen wollte, musste ich auch einen public Initializer schreiben – aber alles ist unkompliziert. Die ==-Funktion ist die einzige Anforderung des Equatable-Protokolls und auch sie ist unkompliziert.
Damit kann ich jetzt alle meine Binding-Properties mit @EquatableBinding markieren:
struct AppState: Equatable {
// other properties
@EquatableBinding<ConfigFile>
var configFile: Binding<ConfigFile>
}Und das war’s – der Typ AppState ist jetzt vollstaendig Equatable, das seltsame Dropdown-Problem ist geloest, und es besteht kein Risiko fuer Seiteneffekte, weil ich keine bestehenden Typen aus anderen Frameworks mit neuen Protokollen konform mache. Stattdessen habe ich einfach einen neuen Typ eingefuehrt, von dem andere Frameworks nicht einmal wissen – sie koennen also nicht davon betroffen sein.
Die Lektion daraus: Erweitere nie Typen, die du nicht besitzt, um Protokolle, die du nicht besitzt. Es gibt sogar einen Swift-Vorschlag, der das zu einer Compiler-Warnung machen soll. Beachte, dass die Loesung nicht immer ein Property Wrapper ist. Es gibt viele Moeglichkeiten, eigene Typen einzubeziehen, wenn man Protokolle konformiert – vergiss einfach nicht, es zu tun.
Was ist RemafoX? Eine native Mac-App, die sich in Xcode integriert, um beim Uebersetzen deiner App zu helfen. Jetzt herunterladen, um beim Entwickeln Zeit zu sparen und Lokalisierung einfach zu machen.

