Deine erste SwiftUI-App
SwiftUI beschreibt Oberflächen deklarativ: Du sagst, wie die Ansicht für den aktuellen Zustand aussehen soll, und SwiftUI aktualisiert sie, wenn sich der Zustand ändert. Den Zustand hält eine View mit @State. Eine Unteransicht, die ihn ändern darf, bekommt mit @Binding eine Verbindung dazu, übergeben mit einem Dollarzeichen. Für SwiftUI-Apps brauchst du einen Mac mit Xcode.
Danach kannst du
- verstehst, wie eine SwiftUI-View aus body und Zustand entsteht
- hältst Zustand mit @State und gibst ihn mit @Binding weiter
- baust eine Liste mit Eingabe, Umschalter und Löschen
- weißt, welche Teile Swift und welche SwiftUI sind
Voraussetzungen
Umgebung
Swift 6 mit aktueller Xcode-Version
iOS 16 oder neuer (wegen NavigationStack), entwickelt in Xcode auf macOS
Dieser Code wurde nicht ausgeführt. Auf Swiftlang steht derzeit kein Mac mit Xcode für Prüfungen bereit. Wir haben jede verwendete API und ihre Mindestversion in der Apple-Dokumentation nachgeschlagen (Stand 19.10.2026).
Auf dieser Seite
Bevor du anfängst
Diese Lektion ist anders als die übrigen auf Swiftlang: Der Code wurde nicht ausgeführt. SwiftUI ist ein Framework von Apple und läuft nur auf Apple-Plattformen, und für diese Prüfung stand uns kein Mac mit Xcode zur Verfügung. Wir haben jede verwendete API mit ihrer Mindestversion in der Apple-Dokumentation nachgeschlagen und den Code sorgfältig gelesen. Wenn bei dir etwas nicht kompiliert, schreib uns bitte über die Kontaktseite.
Du brauchst:
- einen Mac mit einer aktuellen Version von Xcode,
- die Grundlagen aus dem Lernpfad, vor allem Structs, Protokolle und Closures.
Ein kostenpflichtiges Entwicklerkonto brauchst du zum Lernen nicht. Die App läuft im Simulator und in der Vorschau von Xcode.
Swift und SwiftUI auseinanderhalten
Alles, was du bisher gelernt hast, ist Swift. SwiftUI ist eine Bibliothek, geschrieben für Swift, die dir Bausteine für Oberflächen gibt: Text, Button, List, TextField und viele mehr. Du erkennst SwiftUI-Code an import SwiftUI und daran, dass Views Structs sind, die das Protokoll View erfüllen. Die Sprachmittel darin kennst du: Structs, berechnete Eigenschaften, Closures, Protokolle mit some.
Das Projekt anlegen
Lege in Xcode ein neues Projekt mit der Vorlage App für iOS an, als Oberfläche SwiftUI und als Sprache Swift. Die genauen Menüs unterscheiden sich zwischen Xcode-Versionen etwas; Apples SwiftUI-Tutorials zeigen den aktuellen Weg mit Bildern. Xcode erzeugt zwei Dateien, die du gleich ersetzt.
Die erste ist der Einstiegspunkt der App:
import SwiftUI
@main
struct LernlisteApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
@main markiert, wo die App startet. WindowGroup ist das Fenster, auf dem iPhone der ganze Bildschirm. Darin steht die erste View.
Die erste View
Ersetze den Inhalt von ContentView.swift durch diese kleine Lernliste:
import SwiftUI
struct Aufgabe: Identifiable {
let id = UUID()
var titel: String
var erledigt = false
}
struct ContentView: View {
@State private var aufgaben: [Aufgabe] = [
Aufgabe(titel: "Swift installieren", erledigt: true),
Aufgabe(titel: "Optionals verstehen"),
]
@State private var neuerTitel = ""
var body: some View {
NavigationStack {
List {
Section {
HStack {
TextField("Neue Aufgabe", text: $neuerTitel)
.onSubmit(hinzufuegen)
Button("Hinzufügen", action: hinzufuegen)
.disabled(bereinigterTitel.isEmpty)
}
}
Section("Offen: \(anzahlOffen)") {
ForEach($aufgaben) { $aufgabe in
AufgabeZeile(aufgabe: $aufgabe)
}
.onDelete { aufgaben.remove(atOffsets: $0) }
}
}
.navigationTitle("Lernliste")
}
}
private var bereinigterTitel: String {
neuerTitel.trimmingCharacters(in: .whitespaces)
}
private var anzahlOffen: Int {
aufgaben.filter { !$0.erledigt }.count
}
private func hinzufuegen() {
guard !bereinigterTitel.isEmpty else { return }
aufgaben.append(Aufgabe(titel: bereinigterTitel))
neuerTitel = ""
}
}
struct AufgabeZeile: View {
@Binding var aufgabe: Aufgabe
var body: some View {
Toggle(aufgabe.titel, isOn: $aufgabe.erledigt)
}
}
#Preview {
ContentView()
}
#Preview zeigt die View direkt in Xcode neben dem Code an. Wenn du etwas änderst, aktualisiert sich die Vorschau. In ihr kannst du die Liste schon benutzen: Aufgaben eintippen, abhaken, nach links wischen zum Löschen.
Wie die Daten fließen
@State macht aufgaben und neuerTitel zu Zustand, den SwiftUI für diese View verwaltet. Ändert sich der Wert, berechnet SwiftUI body neu und aktualisiert die betroffenen Teile des Bildschirms. Apple empfiehlt, @State als private zu deklarieren und in der höchsten View anzulegen, die den Wert braucht (Dokumentation zu State). Laut derselben Seite setzt SwiftUI @State beim Bauen mit Xcode 27 oder neuer als Makro um; an der Schreibweise in deinem Code ändert sich dadurch nichts.
$neuerTitel ist eine Bindung, eine Verbindung zum Zustand. TextField schreibt jeden Tastendruck über sie direkt in neuerTitel.
@Binding in AufgabeZeile heißt: Diese View besitzt die Aufgabe nicht, sie bekommt nur eine Verbindung zu einer Aufgabe, die jemand anderem gehört. Der Toggle schreibt über $aufgabe.erledigt direkt in das Array von ContentView. Deshalb ändert sich die Zahl „Offen“ sofort mit.
ForEach($aufgaben) { $aufgabe in … } läuft über das Array und liefert für jedes Element gleich eine Bindung. Identifiable mit der id sorgt dafür, dass SwiftUI jede Zeile wiedererkennt, auch wenn Elemente hinzukommen oder verschwinden.
Erinnere dich an die Lektion zu Structs und Klassen: Aufgabe ist ein Struct, ein Werttyp. Ohne Bindung bekäme AufgabeZeile eine Kopie, und das Abhaken würde nur die Kopie ändern. Die Bindung ist genau die Brücke, die das verhindert.
Was im Code noch steckt
some Viewist ein Rückgabetyp aus der Lektion zu Protokollen: „irgendeine bestimmte View“. Der genaue Typ wäre lang und ändert sich mit jeder Zeile, die du inbodyschreibst.- Modifier wie
.navigationTitle,.disabledund.onDeletesind Methoden, die eine neue, veränderte View zurückgeben. Die Reihenfolge kann eine Rolle spielen. - Berechnete Eigenschaften wie
anzahlOffenwerden bei jedem neuenbodyfrisch berechnet. Speichere nichts doppelt, was sich aus dem Zustand ableiten lässt. trimmingCharacters(in:)kommt aus Foundation, das SwiftUI mitbringt. Damit lassen sich keine leeren Aufgaben aus Leerzeichen anlegen.
Häufige Fehler
cannot assign to property: 'self' is immutable. Du änderst in einer View eine normale Eigenschaft. Views sind Structs, und body darf sie nicht verändern; ihr Zustand gehört in @State. Diese Meldung kommt vom Swift-Compiler selbst; wir haben sie an einem normalen Struct geprüft (Fehlermeldungen).
Die Anzeige ändert sich nicht. Der Wert liegt nicht in @State oder einer Bindung, sondern in einer normalen Eigenschaft oder einer Kopie. Prüfe, wem die Daten gehören.
Eine Bindung wird nicht gefunden (sinngemäß „cannot find … in scope“). Bei ForEach fehlt das $ vor aufgaben oder vor dem Parameternamen im Closure.
Übungen
- Füge unter der Liste eine Schaltfläche „Erledigte entfernen“ hinzu, die alle abgehakten Aufgaben löscht. Tipp:
removeAll(where:). - Zeige in
AufgabeZeileerledigte Aufgaben durchgestrichen an. Tipp:Texthat den Modifier.strikethrough(_:); du brauchst dann einen eigenen Aufbau stattToggle(_:isOn:)mit Titel. - Speichere die Aufgaben dauerhaft. Das geht über diese Lektion hinaus. Ein guter Ausgangspunkt ist Apples Dokumentation zu SwiftData.