Refine app structure for persisted sessions and test coverage
CI / test (push) Has been cancelled

This commit is contained in:
2026-04-18 12:29:32 +02:00
parent 243029c798
commit d534964601
18 changed files with 1850 additions and 1162 deletions
+50 -3
View File
@@ -23,6 +23,7 @@ final class AppViewModel: ObservableObject {
private var hasBootstrapped = false
private let service: IDPServicing
private let notificationCoordinator: NotificationCoordinating
private let appStateStore: AppStateStoring
private let launchArguments: [String]
private var preferredLaunchSection: AppSection? {
@@ -40,10 +41,12 @@ final class AppViewModel: ObservableObject {
init(
service: IDPServicing = MockIDPService(),
notificationCoordinator: NotificationCoordinating = NotificationCoordinator(),
appStateStore: AppStateStoring = UserDefaultsAppStateStore(),
launchArguments: [String] = ProcessInfo.processInfo.arguments
) {
self.service = service
self.notificationCoordinator = notificationCoordinator
self.appStateStore = appStateStore
self.launchArguments = launchArguments
}
@@ -79,14 +82,17 @@ final class AppViewModel: ObservableObject {
guard !hasBootstrapped else { return }
hasBootstrapped = true
restorePersistedState()
isBootstrapping = true
defer { isBootstrapping = false }
notificationPermission = await notificationCoordinator.authorizationStatus()
do {
let bootstrap = try await service.bootstrap()
suggestedPairingPayload = bootstrap.suggestedPairingPayload
manualPairingPayload = bootstrap.suggestedPairingPayload
notificationPermission = await notificationCoordinator.authorizationStatus()
manualPairingPayload = session?.pairingCode ?? bootstrap.suggestedPairingPayload
if launchArguments.contains("--mock-auto-pair"),
session == nil {
@@ -97,7 +103,9 @@ final class AppViewModel: ObservableObject {
}
}
} catch {
errorMessage = "Unable to prepare the app."
if session == nil {
errorMessage = "Unable to prepare the app."
}
}
}
@@ -144,6 +152,7 @@ final class AppViewModel: ObservableObject {
let result = try await service.signIn(with: normalizedRequest)
session = result.session
apply(snapshot: result.snapshot)
persistCurrentState()
notificationPermission = await notificationCoordinator.authorizationStatus()
selectedSection = .overview
errorMessage = nil
@@ -200,6 +209,7 @@ final class AppViewModel: ObservableObject {
do {
let snapshot = try await service.identify(with: normalizedRequest)
apply(snapshot: snapshot)
persistCurrentState()
errorMessage = nil
isScannerPresented = false
} catch let error as AppError {
@@ -218,6 +228,7 @@ final class AppViewModel: ObservableObject {
do {
let snapshot = try await service.refreshDashboard()
apply(snapshot: snapshot)
persistCurrentState()
errorMessage = nil
} catch {
errorMessage = "Unable to refresh the dashboard."
@@ -238,6 +249,7 @@ final class AppViewModel: ObservableObject {
do {
let snapshot = try await service.simulateIncomingRequest()
apply(snapshot: snapshot)
persistCurrentState()
selectedSection = .requests
errorMessage = nil
} catch {
@@ -271,6 +283,7 @@ final class AppViewModel: ObservableObject {
do {
let snapshot = try await service.markNotificationRead(id: notification.id)
apply(snapshot: snapshot)
persistCurrentState()
errorMessage = nil
} catch {
errorMessage = "Unable to update the notification."
@@ -278,6 +291,7 @@ final class AppViewModel: ObservableObject {
}
func signOut() {
appStateStore.clear()
session = nil
profile = nil
requests = []
@@ -298,12 +312,45 @@ final class AppViewModel: ObservableObject {
? try await service.approveRequest(id: request.id)
: try await service.rejectRequest(id: request.id)
apply(snapshot: snapshot)
persistCurrentState()
errorMessage = nil
} catch {
errorMessage = "Unable to update the identity check."
}
}
private func restorePersistedState() {
guard let state = appStateStore.load() else {
return
}
session = state.session
manualPairingPayload = state.session.pairingCode
apply(
snapshot: DashboardSnapshot(
profile: state.profile,
requests: state.requests,
notifications: state.notifications
)
)
}
private func persistCurrentState() {
guard let session, let profile else {
appStateStore.clear()
return
}
appStateStore.save(
PersistedAppState(
session: session,
profile: profile,
requests: requests,
notifications: notifications
)
)
}
private func apply(snapshot: DashboardSnapshot) {
profile = snapshot.profile
requests = snapshot.requests.sorted { $0.createdAt > $1.createdAt }