feat: Erste Stufe der iOS-App - Anmeldung und Kontenliste

Neuer Ordner ios/ mit dem Geruest: Netzwerkschicht, Modelle, Offline-Ablage,
Texte in Deutsch und Englisch, Navigation, der komplette Anmeldeweg (Server
waehlen, anmelden, zweiter Faktor, Pflicht-Passwortwechsel) und als erste
Fachansicht die Kontenliste.

Die Anmeldung laeuft einmal ueber das Sitzungs-Cookie, holt sich dabei eine
App-Marke und legt sie in den Schluesselbund. Danach traegt nur noch der
Header. Kein Passwort auf dem Geraet, kein Ablauf nach 30 Tagen.

Der Code ist nie uebersetzt worden - hier gibt es kein Xcode. Geprueft ist
dafuer jedes Modellfeld gegen echte Antworten des laufenden Backends; dabei
kam heraus, dass created_at ohne Zeitzone und mit Mikrosekunden kommt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-20 16:20:03 +02:00
co-authored by Claude Opus 5
parent 8a7545bf5b
commit 6b86a6bf91
25 changed files with 2908 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
# 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.<deine-bundle-id>`) 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.