# Certain für iOS Native App zum Certain-Backend. Der Code hier ist **nie übersetzt worden** — auf der Maschine, auf der er entstanden ist, gibt es kein Xcode. Rechne beim ersten Bauen mit Fehlern und schick sie mir; das ist eingeplant. ## Projekt anlegen 1. Xcode → neues Projekt → **App**, Schnittstelle **SwiftUI**, Sprache **Swift**. 2. Mindestversion auf **iOS 26** stellen. 3. Als Geräte **iPhone und iPad** ankreuzen. 4. Die Datei `ContentView.swift`, die Xcode anlegt, **löschen** — `App/` bringt einen eigenen Einstiegspunkt mit. 5. Die Ordner aus `ios/` per Ziehen ins Projekt übernehmen, mit *Create groups* (nicht *folder references*). 6. `Texte/de.lproj` und `Texte/en.lproj` übernehmen und unter *Project → Info → Localizations* **Deutsch** und **Englisch** eintragen. Deutsch ist die Grundsprache. Der Ordner `Tests/` gehört in ein Test-Ziel, nicht in das App-Ziel. ## 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 | | `Konten/` | Kontenliste (die erste Fachansicht) | | `Texte/` | Übersetzungen | | `Tests/` | Reine Logiktests, ohne Netz | ## 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. **Nicht geprüft:** Ob das Ganze übersetzt. Kein Xcode auf der Maschine, auf der es entstanden ist. ### Wo es beim ersten Bauen am ehesten klemmt Nach abnehmender Wahrscheinlichkeit: 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.