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>
120 lines
5.3 KiB
Markdown
120 lines
5.3 KiB
Markdown
# 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.
|