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>
59 lines
2.1 KiB
Swift
59 lines
2.1 KiB
Swift
import SwiftUI
|
|
|
|
/// Der allererste Bildschirm: Wo steht dein Certain?
|
|
///
|
|
/// Die Adresse wird nicht blind übernommen, sondern mit `GET /api/version`
|
|
/// geprüft. Das erspart die Sorte Fehlersuche, bei der man sich eine Stunde
|
|
/// fragt, warum die Anmeldung nicht geht, und am Ende ein Buchstabe fehlte.
|
|
struct ServerAnsicht: View {
|
|
@Environment(AppZustand.self) private var zustand
|
|
@State private var eingabe = ""
|
|
@FocusState private var imFeld: Bool
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Form {
|
|
Section {
|
|
TextField(String(localized: "server.platzhalter"), text: $eingabe)
|
|
.textContentType(.URL)
|
|
.keyboardType(.URL)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
.focused($imFeld)
|
|
.submitLabel(.go)
|
|
.onSubmit { pruefe() }
|
|
} header: {
|
|
Text(String(localized: "server.ueberschrift"))
|
|
} footer: {
|
|
Text(String(localized: "server.erklaerung"))
|
|
}
|
|
|
|
if let meldung = zustand.meldung {
|
|
Section {
|
|
Label(meldung, systemImage: "exclamationmark.triangle")
|
|
.foregroundStyle(.red)
|
|
}
|
|
}
|
|
|
|
Section {
|
|
Button(action: pruefe) {
|
|
if zustand.beschaeftigt {
|
|
ProgressView()
|
|
} else {
|
|
Text(String(localized: "server.verbinden"))
|
|
}
|
|
}
|
|
.disabled(eingabe.trimmingCharacters(in: .whitespaces).isEmpty
|
|
|| zustand.beschaeftigt)
|
|
}
|
|
}
|
|
.navigationTitle(String(localized: "server.titel"))
|
|
.onAppear { imFeld = true }
|
|
}
|
|
}
|
|
|
|
private func pruefe() {
|
|
Task { _ = await zustand.pruefeUndMerkeServer(eingabe) }
|
|
}
|
|
}
|