Lernziel
Nach dieser Lektion verstehst du den grundlegenden Unterschied zwischen imperativer und deklarativer Programmierung und weißt, warum SwiftUI deklarativ arbeitet. Du kannst eine einfache View lesen, als Beschreibung verstehen und erklären, was beim Ändern von Daten passiert.
Willkommen in Stufe 2
In Stufe 1 hast du die Sprache Swift gelernt. Ab jetzt bekommt dein Code eine Oberfläche: Texte, Bilder, Buttons, Listen. Bevor wir aber Views bauen, müssen wir über eine Denkweise sprechen. Sie ist der wichtigste Unterschied zwischen SwiftUI und fast allem, was es vorher gab, und wer sie verinnerlicht hat, versteht SwiftUI doppelt so schnell.
Das Problem: Die Oberfläche aktuell halten
Stell dir eine einfache Wetter-App vor. Sie zeigt eine Temperatur. Ist es unter 0 Grad, soll der Text blau sein und daneben ein Schneeflocken-Symbol stehen. Darüber ist er orange, mit einer Sonne.
Jetzt ändert sich die Temperatur von 3 auf −2 Grad. Was muss passieren?
- Der Text muss von „3 °C“ auf „−2 °C“ geändert werden.
- Die Farbe muss von Orange auf Blau wechseln.
- Das Symbol muss von der Sonne zur Schneeflocke wechseln.
Vergisst du einen dieser drei Schritte, zeigt die App Unsinn: „−2 °C“ in Orange mit einer Sonne. Und das ist nur eine Zahl. Echte Apps haben Hunderte solcher Abhängigkeiten.
Der imperative Weg: Befehle erteilen
Früher, und in vielen anderen Frameworks bis heute, hat man Oberflächen imperativ gebaut. Imperativ kommt vom lateinischen imperare, befehlen. Du sagst dem Computer Schritt für Schritt, was er tun soll:
// Pseudocode im imperativen Stil
func temperatureChanged(to newValue: Int) {
label.text = "\(newValue) °C"
if newValue < 0 {
label.textColor = .blue
icon.image = snowflake
} else {
label.textColor = .orange
icon.image = sun
}
}Das funktioniert. Aber du bist dafür verantwortlich, bei jeder Änderung jede betroffene Stelle zu aktualisieren. Kommt ein neues Element dazu, etwa eine Warnung bei Glatteis, musst du daran denken, es überall mitzupflegen: beim Start, bei jeder Änderung, beim Zurücksetzen. Mit der Zeit wird das ein Netz aus Befehlen, in dem sich leicht Fehler verstecken.
Ein Bild für den imperativen Stil
Stell dir vor, du gibst einem Maler Anweisungen per Telefon: „Nimm den Pinsel. Tauch ihn in Blau. Male oben links einen Strich. Jetzt übermale den roten Fleck.“ Er sieht das Bild nicht, er folgt nur deinen Befehlen. Vergisst du eine Anweisung, bleibt ein Fehler im Bild.
Der deklarative Weg: Beschreiben, was sein soll
SwiftUI arbeitet deklarativ. Deklarieren heißt erklären, festlegen. Du sagst nicht, was der Computer tun soll, sondern beschreibst, wie das Ergebnis aussehen soll, abhängig von den Daten:
struct WeatherView: View {
let temperature: Int
var body: some View {
HStack {
Image(systemName: temperature < 0 ? "snowflake" : "sun.max.fill")
Text("\(temperature) °C")
}
.foregroundStyle(temperature < 0 ? .blue : .orange)
.font(.largeTitle)
}
}Lies den body laut: „Die View ist eine waagrechte Reihe aus einem Symbol und einem Text. Das Symbol ist eine Schneeflocke, wenn es unter null hat, sonst eine Sonne. Die Farbe ist Blau bei Frost, sonst Orange.“
Da steht kein einziger Befehl wie „ändere“ oder „setze“. Nur eine Beschreibung. Ändert sich temperature, ruft SwiftUI body einfach neu auf, bekommt eine neue Beschreibung und sorgt selbst dafür, dass der Bildschirm passt.
Ein Bild für den deklarativen Stil
Diesmal gibst du dem Maler eine Skizze: „So soll das Bild aussehen.“ Ändert sich etwas, gibst du ihm eine neue Skizze. Wie er vom alten Bild zum neuen kommt, welche Stellen er übermalt und welche er stehen lässt, ist seine Sache. Er ist darin sehr gut.
Die Formel, die alles erklärt
In Lektion 2.9 wirst du eine Formel sehen, die SwiftUI auf den Punkt bringt:
UI = f(Zustand)
Die Oberfläche ist eine Funktion der Daten. Wie eine mathematische Funktion: Gleiche Eingabe ergibt immer die gleiche Ausgabe. Bei temperature = −2 sieht die View immer gleich aus, egal ob die Temperatur vorher 3 oder 30 war.
Das hat enorme Vorteile:
- Keine vergessenen Updates. Es gibt nur eine Beschreibung, und sie gilt immer.
- Keine widersprüchlichen Zustände. „−2 °C in Orange“ kann gar nicht entstehen.
- Leicht zu lesen. Du siehst auf einen Blick, wie die Oberfläche in jeder Situation aussieht.
- Leicht zu testen. Für eine Vorschau übergibst du einfach bestimmte Daten und siehst das Ergebnis.
Was du schon kennst
Erinnerst du dich an Stufe 1? Dort hast du alle Bausteine gelernt, die diese Denkweise möglich machen:
| Baustein | Rolle in SwiftUI | Lektion |
|---|---|---|
| Structs | Jede View ist eine leichte Struct, die SwiftUI ständig neu erzeugen kann | 1.12 |
| Berechnete Eigenschaften | body wird bei jedem Zugriff neu berechnet | 1.12 |
Ternärer Operator und if | Je nach Daten unterschiedliche Teile beschreiben | 1.4 |
| Protokolle | Alles, was View erfüllt, lässt sich kombinieren | 1.15 |
| Closures | Inhalte von Stacks und Aktionen von Buttons | 1.11 |
some View | Der genaue Typ der Beschreibung bleibt verborgen | 1.18 |
Jetzt siehst du, warum wir so viel Zeit mit Swift verbracht haben.
Bedingungen in der Beschreibung
Weil body eine Beschreibung ist, kannst du darin ganz natürlich Bedingungen verwenden:
struct GreetingView: View {
let name: String?
var body: some View {
VStack {
if let name {
Text("Hallo, \(name)!")
} else {
Text("Hallo, Gast!")
Text("Bitte melde dich an.")
.foregroundStyle(.secondary)
}
}
}
}Das liest sich wie: „Gibt es einen Namen, zeig eine persönliche Begrüßung. Sonst zeig eine allgemeine Begrüßung und einen Hinweis.“ Optional Binding aus Lektion 1.9 funktioniert hier genauso.
Achtung
In body stehen nur Beschreibungen, keine Aktionen. Ein print(...) oder eine Änderung von Daten hat dort nichts verloren. body kann sehr oft aufgerufen werden, wann immer SwiftUI es für nötig hält. Aktionen gehören in Buttons, Gesten oder andere Reaktionen. Das lernst du ab Lektion 2.9.
Wer macht was?
Am Anfang hilft es, die Aufgaben klar zu trennen:
| Deine Aufgabe | Aufgabe von SwiftUI |
|---|---|
| Die Daten verwalten | Erkennen, dass sich Daten geändert haben |
| Beschreiben, wie die Oberfläche bei bestimmten Daten aussieht | body neu aufrufen |
| Daten ändern, wenn der Nutzer etwas tut | Alte und neue Beschreibung vergleichen |
| Nur die geänderten Stellen auf dem Bildschirm aktualisieren | |
| Änderungen auf Wunsch animieren |
Du konzentrierst dich auf das Was, SwiftUI kümmert sich um das Wie.
Ein Gedankenexperiment
Bevor du weiterliest, versuch einmal Folgendes: Nimm eine App, die du oft benutzt, etwa die Wetter-App oder die Nachrichten-App, und beschreib einen Bildschirm in Worten, so wie wir es oben getan haben:
- „Ganz oben steht der Ortsname, darunter groß die Temperatur …“
- „Gibt es eine Unwetterwarnung, steht darunter ein roter Kasten …“
- „Dann folgt eine Liste mit den nächsten Tagen, pro Tag eine Zeile mit …“
Merkst du, dass du automatisch deklarativ beschreibst? Genau so denken Menschen über Oberflächen. SwiftUI lässt dich deinen Code genauso schreiben.
Typische Denkfehler
„Ich muss die View ändern“
Anfänger suchen oft nach einem Befehl wie text.setColor(.red). So etwas gibt es in SwiftUI nicht. Du änderst nie die View, du änderst die Daten, und die Beschreibung reagiert darauf.
„Ich speichere mir die View in einer Variable und passe sie später an“
let label = Text("Hallo")
label.foregroundStyle(.red) // ändert label nicht!Ein Modifier verändert die View nicht. Er erzeugt eine neue Beschreibung (Lektion 2.4). Deshalb schreibt man Modifier direkt in die Beschreibung, in einer Kette.
„body wird nur einmal aufgerufen“
Falsch. body kann jederzeit und beliebig oft neu aufgerufen werden. Rechne nie damit, dass Code in body nur einmal läuft.
FAQ
Anfänger
Was bedeutet das überhaupt?Ist deklarativ immer besser als imperativ? Für Benutzeroberflächen in den allermeisten Fällen ja. Innerhalb deiner Logik, etwa in einer Funktion, die etwas berechnet, schreibst du aber weiterhin ganz normalen Swift-Code mit Schleifen und Befehlen. Deklarativ ist vor allem die Beschreibung der Oberfläche.
Muss ich verstehen, wie SwiftUI die Unterschiede findet? Am Anfang nicht. Es reicht zu wissen, dass SwiftUI das zuverlässig und schnell macht. Wie es genau funktioniert, ist Thema der Stufe 4 (Lektionen 4.1 bis 4.3).
Gibt es SwiftUI auch für Android oder das Web? Nein, SwiftUI ist Apples Framework für Apple-Plattformen. Das deklarative Prinzip ist aber inzwischen überall verbreitet: Jetpack Compose für Android, React und Vue im Web und Flutter funktionieren sehr ähnlich. Was du hier lernst, hilft dir also auch anderswo.
Fortgeschritten
Warum verhält es sich so?Was ist mit UIKit? UIKit ist Apples älteres, imperatives Framework für iPhone-Oberflächen. Viele bestehende Apps nutzen es noch, und manche Spezialfälle löst man bis heute damit. In Lektion 4.15 lernst du, wie du UIKit-Komponenten in SwiftUI einbindest.
Ist es nicht verschwenderisch, ständig neue Beschreibungen zu erzeugen? Views sind winzige Structs ohne eigenen Speicher für Pixel oder Grafik. Sie zu erzeugen ist extrem günstig. Teuer wäre es, den ganzen Bildschirm neu zu zeichnen, und genau das vermeidet SwiftUI, indem es nur die Unterschiede überträgt.
Profi
Was passiert unter der Haube?Wo bleiben die „echten“ Bildschirmelemente?
SwiftUI pflegt intern einen eigenen, langlebigen Baum aus Darstellungsobjekten und einen Attribut-Graphen, der festhält, welche Daten welche Teile beeinflussen. Deine View-Structs sind nur die Eingabe für diesen Graphen. Ändert sich ein Wert, markiert SwiftUI die abhängigen Teile als veraltet, ruft nur deren body neu auf und überträgt die Unterschiede in die Darstellung.
Was bedeutet „Single Source of Truth“?
Jede Information sollte genau eine Quelle haben, von der alle anderen Stellen ableiten. Im Wetter-Beispiel ist temperature die einzige Quelle. Farbe und Symbol werden daraus berechnet, statt separat gespeichert zu werden. Dieses Prinzip begleitet dich ab Lektion 2.9 durch den ganzen Datenfluss in SwiftUI.
Übungen
Übung 1: Imperativ oder deklarativ?
Entscheide für jede Aussage, ob sie imperativ oder deklarativ formuliert ist:
- „Setz die Hintergrundfarbe auf Rot.“
- „Der Hintergrund ist rot, wenn ein Fehler vorliegt.“
- „Blende den Ladekreisel aus und zeig die Liste an.“
- „Während geladen wird, ist ein Ladekreisel zu sehen, sonst die Liste.“
Lösung Übung 1Erst selbst probieren!
- imperativ, ein Befehl
- deklarativ, eine Beschreibung abhängig von Daten
- imperativ, zwei Befehle nacheinander
- deklarativ, eine Beschreibung für jeden Zustand
Übung 2: Beschreiben statt befehlen
Schreib den folgenden imperativen Pseudocode als deklarative Beschreibung in Worten um:
wenn Akku sich ändert:
setze Text auf "\(akku) %"
wenn akku < 20: setze Farbe rot, zeige Warnsymbol
sonst: setze Farbe grün, verstecke WarnsymbolLösung Übung 2Erst selbst probieren!
„Die View zeigt den Akkustand in Prozent. Liegt er unter 20, ist der Text rot und daneben steht ein Warnsymbol. Sonst ist der Text grün und es gibt kein Warnsymbol.“
In SwiftUI sähe das etwa so aus:
HStack {
Text("\(battery) %")
if battery < 20 {
Image(systemName: "exclamationmark.triangle.fill")
}
}
.foregroundStyle(battery < 20 ? .red : .green)Übung 3: Widersprüchliche Zustände
Eine App speichert drei getrennte Werte: isLoading: Bool, errorMessage: String? und items: [String]. Nenne zwei Kombinationen, die keinen Sinn ergeben. Wie würdest du den Zustand stattdessen modellieren? (Tipp: Lektion 1.14)
Lösung Übung 3Erst selbst probieren!
Unsinnige Kombinationen sind zum Beispiel:
isLoading == trueund gleichzeitig eineerrorMessageerrorMessagegesetzt und trotzdemitemsgefüllt, aus einem früheren Laden
Besser ist ein Enum mit Associated Values, das genau einen Zustand zulässt:
enum LoadingState {
case loading
case failure(message: String)
case loaded(items: [String])
}Die View beschreibt dann mit einem switch für jeden Fall, wie sie aussieht. Unmögliche Zustände können gar nicht erst entstehen.
Zusammenfassung
- Imperativ heißt: Schritt für Schritt befehlen, was sich ändern soll.
- Deklarativ heißt: beschreiben, wie es abhängig von den Daten aussehen soll.
- SwiftUI ist deklarativ: UI = f(Zustand).
- Du änderst nie die View, sondern die Daten. SwiftUI ruft
bodyneu auf und aktualisiert den Bildschirm. - In
bodystehen nur Beschreibungen, keine Aktionen. - Bedingungen wie
if,if letund der ternäre Operator machen Beschreibungen flexibel.
Spickzettel
struct MyView: View {
let value: Int // Daten
var body: some View { // Beschreibung
Text("\(value)")
.foregroundStyle(value < 0 ? .blue : .orange)
if value > 100 {
Text("Sehr hoch!")
}
}
}