# Certain für iOS Native App zum Certain-Backend. > **Wer hier weiterarbeitet, liest zuerst [UEBERGABE.md](UEBERGABE.md).** Dort > steht, was entschieden ist, was fertig ist, was als Nächstes dran ist und > welche Eigenheiten das Backend hat. Dieses README erklärt nur das Projekt. ## Bauen und starten Einfach `Certain.xcodeproj` öffnen — es liegt mit im Repository. Die Wahrheit ist aber `project.yml`: eine XcodeGen-Spezifikation, aus der das Projekt entsteht. **Wer eine Swift-Datei anlegt, läuft danach einmal `xcodegen generate`**, sonst kennt Xcode sie nicht. Ein neuer *Ordner* will ausserdem eine Zeile unter `sources:`. ``` brew install xcodegen # einmalig cd ios && xcodegen generate xcodebuild -project Certain.xcodeproj -scheme Certain \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build xcodebuild -project Certain.xcodeproj -scheme Certain \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' test ``` Festgelegt sind dort: Bündelkennung `com.scarriffle.certain`, Team `PP34X97WS3`, iPhone und iPad, Grundsprache Deutsch, **iOS 26** als Mindestversion, Sprachfassung Swift 5. Wer mit einem anderen Apple-Konto baut, trägt seine Team-ID in `project.yml` ein. **Nur https.** App Transport Security steht auf der Voreinstellung, ein Server unter `http://…` wird also abgewiesen — auch `http://localhost` beim Entwickeln. Das ist Absicht: über die Verbindung geht die Gerätemarke, ein Vollzugang. Wer eine Ausnahme braucht, trägt sie bewusst in `project.yml` ein. ## Was wohin gehört | Ordner | Inhalt | |---|---| | `App/` | Einstiegspunkt, Navigation, globaler Zustand | | `Netzwerk/` | Client, Endpunkte, Fehler, Offline-Ablage | | `Modelle/` | Codable-Typen, eins zu eins zu den Backend-Schemas | | `Bausteine/` | Wiederverwendetes: Geld, Farben, Symbole, Zustandsansichten | | `Anmeldung/` | Server-Adresse, Anmeldung, zweiter Faktor, Passwortwechsel | | `Dashboard/` | Die Übersicht: vier feste Karten | | `Budget/` | Soll gegen Ist, Monat für Monat | | `Buchungen/` | Buchungsliste mit Suche und Filtern | | `Abos/` | Verträge und Abos | | `Konten/` | Kontenliste | | `Texte/` | Übersetzungen, Deutsch und Englisch zeilengleich | | `Tests/` | Logiktests ohne Netz, plus die Modellprüfung gegen `Fixtures/antworten.json` | ## Wie die Anmeldung funktioniert Das Backend kennt für die Weboberfläche nur ein Sitzungs-Cookie. Für die App wurde eine **Gerätemarke der Reichweite `app`** ergänzt (`backend/app/models.py`, `TokenScope`). Der Ablauf: 1. Nutzer gibt die Server-Adresse ein. Die App prüft mit `GET /api/version`, ob dort überhaupt ein Certain läuft. 2. Nutzer meldet sich mit Benutzername und Passwort an — über das normale Cookie, inklusive zweitem Faktor und Pflicht-Passwortwechsel, falls fällig. 3. Noch in derselben Sitzung legt die App über `POST /api/inbox/tokens` mit `scope: "app"` eine Marke an und legt deren Klartext im **Schlüsselbund** ab. 4. Danach läuft **alles** über den Header `X-Certain-Token`. Das Cookie wird verworfen. Damit gibt es kein Passwort auf dem Gerät und keinen Ablauf nach 30 Tagen. Die Marke lässt sich am Rechner unter Einstellungen → Gerätemarken einzeln widerrufen; danach sperrt sie sofort. Eine App-Marke kann **keine weitere Marke ausstellen** — das geht nur mit einer echten Anmeldung. Sonst hätte ein Dieb sich Ersatzschlüssel ausgestellt und das Widerrufen wäre wirkungslos. ## Später: Erweiterungen Teilen-Menü und Kurzbefehle kommen in Stufe 6. Beide brauchen Zugriff auf dieselbe Marke wie die App. Dafür ist im Projekt nötig: - eine **App-Gruppe** (`group.`) für die Server-Adresse, - eine **geteilte Schlüsselbund-Gruppe** für die Marke selbst. `Netzwerk/Ablage.swift` liest beides bereits aus Konstanten, die du an einer Stelle setzt (`Ablage.gruppe`). Solange keine Erweiterung existiert, funktioniert alles auch ohne App-Gruppe. ## Was die App bewusst nicht kann - **Bankkonten verbinden.** Die Bank leitet auf eine feste Web-Adresse zurück, die bei Enable Banking registriert ist. Abrufen und Zuordnen geht nativ, das Herstellen der Verbindung bleibt im Browser. - **Postfach einrichten** und **Sicherung/Wiederherstellung.** Gehört an den Rechner. - **Benutzerverwaltung.** Administration für Mehrbenutzerbetrieb. ## Was geprüft ist — und was nicht **Geprüft:** Jedes Feld der Modelle wurde gegen echte Antworten eines laufenden Backends verglichen (`/api/version`, `/api/auth/me`, `/api/accounts`, `/api/settings`, `/api/inbox/tokens`). Alle Namen stimmen überein. Dabei kam heraus, dass `created_at` ohne Zeitzone und mit Mikrosekunden kommt (`2026-10-20T14:18:15.780705`) — dafür hat `Zeitpunkt` einen dritten Leser, und `GrundlagenTests` deckt den Fall ab. **Ebenfalls geprüft:** Dass es übersetzt. Ein einziger Fehler in 2600 blind geschriebenen Zeilen (`[:]` statt `[]` in `CertainClient.schickeDatei`), danach keine Warnung im eigenen Code, 28 Tests grün, Start im Simulator bis zur Serverabfrage. **Nicht geprüft:** Der Weg gegen ein echtes Backend — anmelden, zweiter Faktor, Marke anlegen, Konten laden. ### Wo es beim ersten Bauen am ehesten klemmte Nichts davon trat ein; die Liste bleibt als Landkarte der heiklen Stellen. 1. **`@testable import Certain`** in `Tests/GrundlagenTests.swift` — muss auf den Namen deines App-Ziels zeigen. Heisst es anders, anpassen. 2. **`Lader` ist eine generische `@Observable`-Klasse.** Das geht, ist aber die ungewöhnlichste Konstruktion hier. Falls der Übersetzer murrt: Die Klasse wird bisher nur an einer Stelle benutzt (`KontenListe`). 3. **Nebenläufigkeit.** `AppZustand` ist `@MainActor`, `CertainClient` ein Actor. Unter strikter Nebenläufigkeitsprüfung kann es an den Übergängen Warnungen setzen. 4. **`Tab` und `.tabViewStyle(.sidebarAdaptable)`** in `RootAnsicht` setzen iOS 18 voraus. Bei iOS 26 als Mindestversion kein Problem. 5. **Die Tests nutzen Swift Testing** (`import Testing`, `@Test`, `#expect`), nicht XCTest. Xcode 16 und neuer kann beides im selben Ziel. Schick mir die Fehler, dann räumen wir sie weg. ## Stand Stufe 1: Gerüst, Anmeldung samt zweitem Faktor und Passwortwechsel, Kontenliste. 21 Swift-Dateien, rund 2600 Zeilen. Siehe den Plan im Repository für die weiteren Stufen. Noch nicht in der App, obwohl in Stufe 1 erreichbar: Einen zweiten Faktor **einrichten** geht nur im Browser. Wer ein Konto hat, für das die Richtlinie MFA verlangt, sieht deshalb einen Hinweis mit Link statt eines Einrichtungs- Assistenten. Das kommt mit den Einstellungen in Stufe 5.