Files
certain/ios/Netzwerk/Lader.swift
T
ScarriffleandClaude Opus 5 6b86a6bf91 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>
2026-10-20 16:20:03 +02:00

58 lines
2.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
import Observation
/// Holt eine Liste, legt sie in den Zwischenspeicher und weiss, was bei einem
/// Fehlschlag noch da ist.
///
/// Jede Listenansicht braucht dasselbe: laden, Fehler zeigen, zum Aktualisieren
/// herunterziehen, offline den letzten Stand behalten. Einmal hier, statt in
/// jeder Ansicht neu.
@MainActor
@Observable
final class Lader<A: Codable & Sendable> {
private(set) var zustand: Ladezustand<A> = .laedt
private let schluessel: String
private let holen: () async throws -> A
/// - Parameter schluessel: Name im Zwischenspeicher. Der Endpunktpfad ist
/// eine gute Wahl – er ist eindeutig und beim Suchen lesbar.
init(schluessel: String, holen: @escaping () async throws -> A) {
self.schluessel = schluessel
self.holen = holen
}
/// Lädt. Beim ersten Mal wird ein vorhandener Stand sofort angezeigt,
/// damit die Liste nicht leer aufblitzt, während der Abruf läuft.
func lade(zeigeGemerktesSofort: Bool = true) async {
if zeigeGemerktesSofort, zustand.inhalt == nil,
let gemerkt = Zwischenspeicher.hole(A.self, unter: schluessel) {
zustand = .ausSpeicher(gemerkt.wert, geholtAm: gemerkt.geholtAm,
fehler: .keineVerbindung(""))
} else if zustand.inhalt == nil {
zustand = .laedt
}
do {
let frisch = try await holen()
Zwischenspeicher.lege(frisch, unter: schluessel)
zustand = .da(frisch)
} catch let fehler as APIFehler {
if let gemerkt = Zwischenspeicher.hole(A.self, unter: schluessel) {
zustand = .ausSpeicher(gemerkt.wert, geholtAm: gemerkt.geholtAm,
fehler: fehler)
} else {
zustand = .fehler(fehler)
}
} catch {
zustand = .fehler(.keineVerbindung(error.localizedDescription))
}
}
/// Fürs Herunterziehen: kein Zwischenzustand, die alte Liste bleibt
/// stehen, bis die neue da ist.
func aktualisiere() async {
await lade(zeigeGemerktesSofort: false)
}
}