SwiftUI verspricht, dass deine Oberfläche immer den aktuellen Zustand deiner App widerspiegelt. Damit das funktioniert, musst du verstehen, wie SwiftUI mit Zustand umgeht. Das State Management ist der schwierigste Teil beim Einstieg – nicht weil es kompliziert wäre, sondern weil es viele Property-Wrapper gibt, die auf den ersten Blick ähnlich aussehen. Dieser Artikel räumt damit auf und zeigt dir, wann du welches Werkzeug brauchst.
Das Grundprinzip: deklarative Oberflächen
Bei klassischem UIKit beschreibst du Schritt für Schritt, wie sich deine Oberfläche ändern soll: Du nimmst dir ein Label, setzt einen neuen Text, blendest eine View ein. Das ist imperativ. SwiftUI dreht den Spieß um: Du beschreibst, wie deine Oberfläche bei einem gegebenen Zustand aussehen soll, und das Framework kümmert sich darum, die Darstellung zu aktualisieren, sobald sich der Zustand ändert. Das nennt man deklarativ.
Der Kern ist eine einfache Gleichung: View = f(State). Deine View ist eine Funktion ihres Zustands. Änderst du den Zustand, berechnet SwiftUI das body-Property neu und gleicht nur die tatsächlich veränderten Teile ab. Damit SwiftUI weiß, welche Daten überhaupt zu diesem Zustand gehören, markierst du sie mit Property-Wrappern.
@State: lokaler Zustand einer View
@State ist der Einstiegspunkt. Du verwendest ihn für einfache Werte, die zu genau einer View gehören und ihren Lebenszyklus nicht überdauern müssen – etwa ob ein Schalter an ist, welcher Text in einem Textfeld steht oder ob ein Sheet gerade angezeigt wird.
- SwiftUI speichert den Wert außerhalb der View-Struktur, denn Views sind Wertetypen und werden ständig neu erzeugt. Der Zustand muss diese Neuberechnungen überleben.
- Schreibst du auf eine @State-Property, markiert SwiftUI die View als „veraltet“ und berechnet ihren Body neu.
- Halte @State-Properties
private. Sie sind ein Implementierungsdetail der View und sollten nicht von außen gesetzt werden.
Eine typische Faustregel: Wenn nur diese eine View den Wert kennt und ändert, ist @State richtig. Sobald eine Kind-View denselben Wert lesen und schreiben können soll, kommt @Binding ins Spiel.
@Binding: geteilter Schreibzugriff
Ein @Binding ist eine Referenz auf einen Zustand, der woanders lebt – meist in der Eltern-View. Mit einem Binding kann eine Kind-View einen Wert lesen und verändern, ohne ihn selbst zu besitzen. Das klassische Beispiel ist ein eigenes Eingabefeld oder ein Toggle, der seinen Zustand an die Eltern-View zurückgibt.
Du erzeugst ein Binding aus einer @State-Property mit dem Dollar-Präfix, etwa $istAktiv. Dieses Dollarzeichen liefert die sogenannte „projected value“ des Wrappers – die Brücke zwischen Besitzer und Nutzer des Zustands. So bleibt der Wert an einer einzigen Stelle gespeichert (Single Source of Truth), kann aber an mehreren Stellen bearbeitet werden, ohne dass Kopien auseinanderlaufen.
Vom alten ObservableObject zum neuen @Observable
Für Zustand, der mehrere Views umspannt oder echte Geschäftslogik enthält, brauchst du ein Modell-Objekt. Hier gab es 2023 mit dem Observation-Framework einen wichtigen Umbruch.
Der klassische Weg: ObservableObject
Früher (und weiterhin gültig bei älteren Deployment-Targets) definierst du eine Klasse, die das Protokoll ObservableObject erfüllt, und markierst jede beobachtbare Property mit @Published. In der View hältst du das Objekt dann mit @StateObject (wenn die View das Objekt besitzt) oder @ObservedObject (wenn es von außen kommt). Der Nachteil: SwiftUI beobachtet das gesamte Objekt. Ändert sich irgendein @Published-Wert, werden potenziell alle Views aktualisiert, die das Objekt nutzen – auch solche, die den geänderten Wert gar nicht anzeigen.
Der moderne Weg: das @Observable-Makro
Seit iOS 17 gibt es das Makro @Observable. Du schreibst es einfach vor deine Klasse, und das war's – kein @Published mehr, kein ObservableObject-Protokoll. Der große Vorteil liegt unter der Haube: SwiftUI verfolgt nun auf Property-Ebene, welche Werte eine View tatsächlich liest. Aktualisiert wird nur, was sich wirklich auf die Darstellung auswirkt. Das verbessert die Performance bei komplexen Datenmodellen spürbar und reduziert versehentliche Neuberechnungen.
In der View hältst du ein @Observable-Modell, das du selbst erzeugst, weiterhin mit @State – das ist die neue, vereinheitlichte Schreibweise. Bekommst du das Objekt von außen gereicht, übergibst du es als normale Property. Brauchst du ein Binding auf eine seiner Eigenschaften, funktioniert das Dollar-Präfix wie gewohnt.
@Environment: Werte durch den View-Baum reichen
Manche Daten brauchst du an vielen Stellen tief im View-Baum – etwa das Farbschema, einen angemeldeten Benutzer oder einen Service. Sie als Parameter durch jede Zwischenebene durchzureichen, wäre mühsam. Dafür gibt es @Environment.
- Du legst einen Wert mit dem Modifier
.environment(...)auf einer übergeordneten View ab. - Jede beliebige Kind-View liest ihn dann mit
@Environmentaus, ohne dass die dazwischenliegenden Views davon wissen müssen. - SwiftUI bringt viele Standard-Environment-Werte mit, etwa
\.colorScheme,\.localeoder\.dismiss.
Mit dem Observation-Framework kannst du auch eigene @Observable-Objekte sauber über die Environment verteilen, was die alte Kombination aus @EnvironmentObject weitgehend ablöst.
Die wichtigste Regel: Single Source of Truth
Egal welchen Wrapper du wählst – der rote Faden bleibt immer derselbe: Jeder Zustand sollte genau eine Quelle der Wahrheit haben. Kopiere Werte nicht in mehrere @State-Properties, die du dann synchron halten musst. Stattdessen besitzt eine Stelle den Wert, und alle anderen greifen über Bindings oder die Environment darauf zu. Driften Daten auseinander, sind das fast immer die Stellen, an denen jemand diese Regel verletzt hat.
Eine praktische Heuristik für die Wahl des richtigen Werkzeugs:
- Einfacher Wert, nur diese View? → @State
- Kind-View soll denselben Wert ändern? → @Binding
- Geschäftslogik oder geteilter Zustand über mehrere Views? → @Observable-Klasse mit @State
- Wert tief im Baum oder global verfügbar? → @Environment
Häufige Stolperfallen
Ein klassischer Fehler ist die Verwechslung von @StateObject und @ObservedObject in der alten Welt: Erzeugt eine View ein Objekt mit @ObservedObject, wird es bei jeder Neuberechnung verworfen und neu angelegt – der Zustand geht verloren. Wer dagegen das neue @Observable-Makro mit @State nutzt, umgeht diese Falle. Ebenso häufig: zu viel in ein einziges Modell packen. Lieber mehrere kleine, fokussierte @Observable-Klassen als ein riesiges Allwissendes-Objekt, das bei jeder Änderung halbe Bildschirme neu zeichnet.
Achte außerdem auf Identität in Listen. Verwende stabile Identifier (etwa über Identifiable), damit SwiftUI Zeilen korrekt wiederverwenden kann, anstatt sie bei jeder Änderung komplett neu aufzubauen.
Nächste Schritte
State Management ist die Grundlage, auf der alles andere in SwiftUI aufbaut. Sobald du verinnerlicht hast, dass deine View nur eine Funktion ihres Zustands ist, fügt sich vieles zusammen. Wenn du mit den Grundlagen der Sprache und des Frameworks noch nicht ganz sicher bist, lohnt sich ein Blick in unsere Einführung zu SwiftUI-Grundlagen für die iOS-Entwicklung. Wer aus der Cross-Platform-Welt kommt, findet im Vergleich Flutter vs. SwiftUI eine hilfreiche Einordnung.
Praktisch wird es vor allem beim Tippen von Code. Brauchst du beim Entwickeln schnelle Hilfsmittel – etwa zum Generieren von Beispiel-IDs, zum Umrechnen von Werten oder zum Formatieren von JSON aus einer API-Antwort – findest du im Webtools-Bereich eine ganze Sammlung praktischer Helfer für den Entwickleralltag.
Häufige Fragen
Wann nutze ich @State und wann @Observable?
@State ist für einfache, lokale Werte einer einzelnen View gedacht (Bool, String, Int, Enum). Sobald du echte Logik, mehrere zusammenhängende Eigenschaften oder geteilten Zustand über mehrere Views hast, definierst du eine Klasse mit @Observable und hältst sie in der besitzenden View mit @State.
Brauche ich @Published noch?
Nur, wenn du das klassische ObservableObject-Protokoll für ältere iOS-Versionen verwendest. Mit dem @Observable-Makro (ab iOS 17) entfällt @Published komplett – jede gespeicherte Property wird automatisch beobachtet.
Was bedeutet das Dollarzeichen vor einer Property?
Das Dollar-Präfix liefert die „projected value“ eines Property-Wrappers. Bei @State und @Observable-Eigenschaften ist das ein Binding, das du an Kind-Views weitergeben kannst, damit diese den Wert nicht nur lesen, sondern auch verändern können.
Warum aktualisiert sich meine View nicht?
Meist liegt es daran, dass der geänderte Wert nicht mit einem beobachtbaren Wrapper markiert ist oder dass du ein Objekt mit @ObservedObject statt @StateObject erzeugst und es so bei jeder Neuberechnung verlierst. Prüfe, ob die Quelle der Wahrheit korrekt deklariert ist und ob die View den Wert tatsächlich liest.