Der Plan lag bisher ausserhalb des Repositories und waere beim Geraetewechsel verloren gewesen. ios/UEBERGABE.md haelt jetzt alles fest: die Entscheidungen des Nutzers, die Eigenheiten des Backends (Cookie-Anmeldung, Zeitstempel ohne Zone, Uebertraege in zwei Mechanismen), was Stufe 1 enthaelt, was die drei offenen Backend-Ergaenzungen sind und was die Stufen 2 bis 7 bringen. Dazu die Konventionen des Projekts und die Fallen, die mich Zeit gekostet haben - damit sie niemand zweimal findet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Wer hier weiterarbeitet, liest zuerst 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, wie man den Code ins Projekt bekommt.
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.