Files
certain/ios/App/RootAnsicht.swift
GuidoandClaude Opus 5 09ec2dc5fd feat: Bildschirmsperre mit Face ID und PIN
Beim ersten Start fuehrt kein Weg an einer sechsstelligen PIN vorbei. Danach
sperrt die App beim Kaltstart und beim Wegschalten; kommt man binnen einer
Minute zurueck, geht sie ohne Nachfrage wieder auf. Face ID zuerst, die PIN
als Rueckfallweg und als einziger Weg ohne Biometrie. Nach fuenf falschen
PINs meldet die App ab.

Gesperrt wird schon bei inactive, nicht erst im Hintergrund - sonst stuenden
die Kontostaende im App-Umschalter.

Von der PIN liegt nur ein gesalzener SHA-256 im Schluesselbund. Die
Gerätemarke selbst bleibt wie bisher abgelegt: Die Sperre verdeckt die
Oberflaeche, sie verschluesselt nichts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-24 18:09:05 +02:00

78 lines
2.7 KiB
Swift
Raw Permalink 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 SwiftUI
/// Welcher Bildschirm zu sehen ist, entscheidet allein `AppZustand.lage`.
/// Jede Ansicht darunter darf davon ausgehen, dass es einen angemeldeten
/// Client gibt.
struct RootAnsicht: View {
@Environment(AppZustand.self) private var zustand
var body: some View {
switch zustand.lage {
case .startet:
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
case .brauchtServer:
ServerAnsicht()
case .brauchtAnmeldung:
AnmeldeAnsicht()
case .brauchtZweitenFaktor:
ZweiFaktorAnsicht()
case .brauchtPasswortwechsel:
PasswortWechselAnsicht()
case .brauchtZweitenFaktorEinrichtung:
ZweitenFaktorEinrichtenHinweis()
case .bereit:
// Reihenfolge mit Absicht: Ohne PIN kommt niemand an die Zahlen,
// und der Sperrbildschirm liegt vor allem anderen.
if !zustand.hatPIN {
PINEinrichtenAnsicht()
} else if zustand.gesperrt {
SperrAnsicht()
} else {
Hauptnavigation()
}
}
}
}
/// Die Reiter. Auf dem iPhone unten, auf dem iPad als Seitenleiste – das
/// erledigt `.sidebarAdaptable` von selbst.
///
/// Reihenfolge nach Häufigkeit, nicht nach Umfang: Das Dashboard beantwortet
/// die Frage, wegen der man die App öffnet. Konten und Einstellungen stehen
/// hinten, weil man sie selten braucht.
struct Hauptnavigation: View {
var body: some View {
TabView {
Tab(String(localized: "reiter.dashboard"), systemImage: "square.grid.2x2") {
NavigationStack { DashboardAnsicht() }
}
Tab(String(localized: "reiter.budget"), systemImage: "chart.pie") {
NavigationStack { BudgetAnsicht() }
}
Tab(String(localized: "reiter.buchungen"), systemImage: "list.bullet.rectangle") {
NavigationStack { BuchungsListe() }
}
Tab(String(localized: "reiter.arbeitsliste"), systemImage: "tray") {
NavigationStack { ArbeitslisteAnsicht() }
}
Tab(String(localized: "reiter.abos"), systemImage: "arrow.triangle.2.circlepath") {
NavigationStack { AboListe() }
}
Tab(String(localized: "reiter.konten"), systemImage: "wallet.bifold") {
NavigationStack { KontenListe() }
}
Tab(String(localized: "reiter.einstellungen"), systemImage: "gearshape") {
NavigationStack { EinstellungenAnsicht() }
}
}
.tabViewStyle(.sidebarAdaptable)
}
}