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>
5.3 KiB
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
- Xcode → neues Projekt → App, Schnittstelle SwiftUI, Sprache Swift.
- Mindestversion auf iOS 26 stellen.
- Als Geräte iPhone und iPad ankreuzen.
- Die Datei
ContentView.swift, die Xcode anlegt, löschen —App/bringt einen eigenen Einstiegspunkt mit. - Die Ordner aus
ios/per Ziehen ins Projekt übernehmen, mit Create groups (nicht folder references). Texte/de.lprojundTexte/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:
- Nutzer gibt die Server-Adresse ein. Die App prüft mit
GET /api/version, ob dort überhaupt ein Certain läuft. - Nutzer meldet sich mit Benutzername und Passwort an — über das normale Cookie, inklusive zweitem Faktor und Pflicht-Passwortwechsel, falls fällig.
- Noch in derselben Sitzung legt die App über
POST /api/inbox/tokensmitscope: "app"eine Marke an und legt deren Klartext im Schlüsselbund ab. - 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:
@testable import CertaininTests/GrundlagenTests.swift— muss auf den Namen deines App-Ziels zeigen. Heisst es anders, anpassen.Laderist 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).- Nebenläufigkeit.
AppZustandist@MainActor,CertainClientein Actor. Unter strikter Nebenläufigkeitsprüfung kann es an den Übergängen Warnungen setzen. Tabund.tabViewStyle(.sidebarAdaptable)inRootAnsichtsetzen iOS 18 voraus. Bei iOS 26 als Mindestversion kein Problem.- 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.