Press "Enter" to skip to content

SwiftUI Best Practices: Dynamische Binding-Generierung

Bindings spielen in SwiftUI eine enorm wichtige Rolle. Mit ihrer Hilfe geben wir eine Referenz eines Status an eine View weiter. Von dieser View aus kann die zugehörige Information ausgelesen und verändert werden. Zudem sind Bindings enorm flexibel und lassen sich mithilfe eines entsprechenden Initializers (init(get:set:)) ganz den eigenen Bedürfnissen entsprechend erzeugen.

Das Problem mit Optionals

Viele Views in SwiftUI setzen auf Bindings. Nehmen wir als Beispiel TextEditor. Die View benötigt ein Binding vom Typ String. Was aber, wenn der zugrundeliegende Status, den man als Binding nutzen möchte, ein Optional ist? Dazu ein Beispiel:

Dieser Code lässt sich nicht kompilieren, da TextEditor als Binding zwingend einen konkreten Wert vom Typ String benötigt, nicht wie im gezeigten Fall ein Optional. Eine potentielle Lösung zeigt das folgende Konstrukt:

Hier erzeuge ich ein passendes Binding mithilfe des Initializers init(get:set:). Das basiert immer auf einem validen Wert vom Typ String, indem als Fallback schlicht ein leerer String als Wert zurückgegeben wird, sollte text nil entsprechen.

Das Problem: Derartige Binding-Konstrukte können sich in umfangreichen SwiftUI-Projekten schnell wiederholen. Das trifft insbesondere zu, wenn man für die Datenbasis ein Framework wie Core Data nutzt, das von Haus aus viele Eigenschaften als Optionals umsetzt.

Die Lösung: Eine Binding-Extension

Um solch umfangreiche (und optisch auch nicht sehr ansprechende) Konstrukte zu vermeiden, könnt ihr eine Extension für den Typ Binding erstellen. Denn das gezeigte Konstrukt baut immer auf zwei Faktoren auf:

  • einem bestehenden Binding auf Basis eines Optionals
  • einem Fallback-Wert

Im gezeigten Beispiel ist das bestehende Binding $text, der Fallback-Wert der leere String. Um daraus ein passendes Binding mit konkretem Wert zu generieren, könnte man den Typ Binding um folgende Methode ergänzen:

Diese neue Methode nimmt als Parameter ein Binding mit optionalem String sowie einen Fallback entgegen (der allerdings bereits einen Standardwert in Form eines leeren Strings erhält). Die Methode generiert daraufhin ein neues Binding, das immer einen validen String zurück liefert; entweder den Wert des optionalen Bindings oder den des Fallbacks.

Die Anwendung dieser neuen Methode im vorherigen Beispiel demonstriert das nachfolgende Listing:

Das war’s! Auf diese Art und Weise können nun mit einem einzigen Befehl Bindings auf Basis eines optionalen Strings in Bindings mit einem konkreten String umgewandelt werden.

Es lebe die Dynamik!

Doch es geht noch besser! Warum diese Funktion auf Strings beschränken? Mithilfe einer Generic Function erweitert man die Funktionalität auf alle beliebigen Typen. Die entsprechende Implementierung zeigt das folgende Listing:

Die erste Methode convertOptionalValue(_:fallback:) nimmt ein beliebiges Binding mit einem optionalen Wert entgegen. Der Fallback muss dem Typ des optionalen Werts entsprechen. So ist sichergestellt, dass jedes Binding auf Basis eines Optionals in ein Binding mit konkretem Wert umgewandelt werden kann.

Die zuvor erstellte Methode convertOptionalString(_:fallback:) nutzt diese neue Generic Function, was den Code der Binding-Extension noch übersichtlicher macht. Denn convertOptionalString(_:fallback:) wird nicht überflüssig. Da sie speziell für Strings einen Standard-Fallback definiert, kann sie überall dort genutzt werden, wo dieser Fallback angemessen ist. So ist es auch denkbar, ähnliche Methoden wie convertOptionalString(_:fallback:) auch für andere häufig benötigte Typen zu erstellen, beispielsweise für Date:

Fazit

Arbeitet man in einem Projekt viel mit Daten auf Basis von Optionals, kann die Arbeit mit Binding mühselig werden. Wenn es nur darum geht, als Alternative für einen nicht vorhandenen Wert eines Bindings einen frei definierbaren Standardwert zu nutzen, kann man sich mit der gezeigten Binding-Extension enorm viel Code-Schreiberei sparen. Da die Basismethode der Extension generisch ist, lässt sie sich mit jedem beliebigen Typ verwenden.

Euer Thomas

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Impressum

Thomas Sillmann
Kettererstraße 6
D-63739 Aschaffenburg
E-Mail: contact@thomassillmann.de
Mobil: +49 (0) 151 65125650
Web: https://www.thomassillmann.de/

Inhaltlich Verantwortlicher gemäß §55 Abs. 2 RStV: Thomas Sillmann (Anschrift siehe oben)

Haftungshinweis: Trotz sorgfältiger inhaltlicher Kontrolle übernehme ich keine Haftung für die Inhalte externer Links. Für die Inhalte der verlinkten Seiten sind ausschließlich deren Betreiber verantwortlich.

Kontakt und soziale Netzwerke

Copyright © 2020 · Thomas Sillmann