taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 05428f8e53f24cac87b50f22a17d100f3ef9e242
parent b03fc2d6d9dff3a5bc2d74009aef0d9f0b7c9b67
Author: Marc Stibane <marc@taler.net>
Date:   Sun,  1 Mar 2026 21:47:50 +0100

Error handling

Diffstat:
MTalerWallet1/Views/Actions/Banking/ManualWithdraw.swift | 4++--
MTalerWallet1/Views/Main/ErrorView.swift | 74++++++++++++++++++++++++++++++++++++--------------------------------------
MTalerWallet1/Views/Main/MainView.swift | 5+++--
MTalerWallet1/Views/Sheets/QRSheet.swift | 11+++++++----
MTalerWallet1/Views/Sheets/Sheet.swift | 2+-
MTalerWallet1/Views/Transactions/TransactionSummaryV.swift | 3++-
6 files changed, 51 insertions(+), 48 deletions(-)

diff --git a/TalerWallet1/Views/Actions/Banking/ManualWithdraw.swift b/TalerWallet1/Views/Actions/Banking/ManualWithdraw.swift @@ -1,5 +1,5 @@ /* - * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. + * This file is part of GNU Taler, ©2022-26 Taler Systems S.A. * See LICENSE.md */ /** @@ -94,7 +94,7 @@ struct ManualWithdraw: View { exchange: exchange) } else { // should never happen, we either have an exchange or a balance let title = "ManualWithdrawContent: Cannot determine scope" // should not happen, so no L10N - ErrorView(title: title, message: nil, copyable: true) { + ErrorView(stack.push("ManualWithdraw"), title: title, message: nil, copyable: true) { dismissTop(stack.push()) } } diff --git a/TalerWallet1/Views/Main/ErrorView.swift b/TalerWallet1/Views/Main/ErrorView.swift @@ -1,5 +1,5 @@ /* - * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. + * This file is part of GNU Taler, ©2022-26 Taler Systems S.A. * See LICENSE.md */ /** @@ -14,20 +14,24 @@ enum ErrorData { } struct ErrorView: View { + let stack: CallStack var title: String var message: String? = nil var copyable: Bool var onDismiss: () -> Void - init(title: String, message: String? = nil, copyable: Bool, onDismiss: @escaping () -> Void) { + init(_ stack: CallStack, title: String, message: String? = nil, copyable: Bool, onDismiss: @escaping () -> Void) { + self.stack = stack self.title = title self.message = message self.copyable = copyable self.onDismiss = onDismiss } - init(error: Error, devMode: Bool, onDismiss: @escaping () -> Void) { + init(_ stack: CallStack, error: Error, devMode: Bool, onDismiss: @escaping () -> Void) { + let internetError = String(localized: "Network problem") + let internetMessage = String(localized: "Please check your internet connection and try again.") let walletCoreError = String(localized: "Internal core error") let initializationError = String(localized: "Initialization error") let serializationError = String(localized: "Serialization error") @@ -35,36 +39,42 @@ struct ErrorView: View { let unknownError = String(localized: "Unknown error") switch error { - case let walletError as WalletBackendError: - switch walletError { - case .walletCoreError(let error): - if let json = error?.toJSON(), devMode { - self.init(title: walletCoreError, message: json, copyable: true, onDismiss: onDismiss) - } else if let hint = error?.errorResponse?.hint ?? error?.hint { - self.init(title: walletCoreError, message: hint, copyable: false, onDismiss: onDismiss) - } else if let message = error?.message { - self.init(title: walletCoreError, message: message, copyable: false, onDismiss: onDismiss) - } else { - self.init(title: walletCoreError, copyable: false, onDismiss: onDismiss) + case let walletError as WalletBackendError: + switch walletError { + case .walletCoreError(let walletBackendResponseError): + if let responseError = walletBackendResponseError { + if responseError.code == 7001 { + self.init(stack, title: internetError, message: internetMessage, copyable: false, onDismiss: onDismiss) + break + } + } + if let json = walletBackendResponseError?.toJSON(), devMode { + self.init(stack, title: walletCoreError, message: json, copyable: true, onDismiss: onDismiss) + } else if let hint = walletBackendResponseError?.errorResponse?.hint ?? walletBackendResponseError?.hint { + self.init(stack, title: walletCoreError, message: hint, copyable: false, onDismiss: onDismiss) + } else if let message = walletBackendResponseError?.message { + self.init(stack, title: walletCoreError, message: message, copyable: false, onDismiss: onDismiss) + } else { + self.init(stack, title: walletCoreError, copyable: false, onDismiss: onDismiss) + } + case .initializationError: + self.init(stack, title: initializationError, copyable: false, onDismiss: onDismiss) + case .serializationError: + self.init(stack, title: serializationError, copyable: false, onDismiss: onDismiss) + case .deserializationError: + self.init(stack, title: deserializationError, copyable: false, onDismiss: onDismiss) } - case .initializationError: - self.init(title: initializationError, copyable: false, onDismiss: onDismiss) - case .serializationError: - self.init(title: serializationError, copyable: false, onDismiss: onDismiss) - case .deserializationError: - self.init(title: deserializationError, copyable: false, onDismiss: onDismiss) - } - default: - self.init(title: unknownError, message: error.localizedDescription, copyable: false, onDismiss: onDismiss) + default: + self.init(stack, title: unknownError, message: error.localizedDescription, copyable: false, onDismiss: onDismiss) } } - init(data: ErrorData, devMode: Bool, onDismiss: @escaping () -> Void) { + init(_ stack: CallStack, data: ErrorData, devMode: Bool, onDismiss: @escaping () -> Void) { switch data { case .message(let title, let message): - self.init(title: title, message: message, copyable: false, onDismiss: onDismiss) + self.init(stack, title: title, message: message, copyable: false, onDismiss: onDismiss) case .error(let error): - self.init(error: error, devMode: devMode, onDismiss: onDismiss) + self.init(stack, error: error, devMode: devMode, onDismiss: onDismiss) } } @@ -111,15 +121,3 @@ struct ErrorView: View { } } } - -struct ErrorSheet_Previews: PreviewProvider { - static let error = WalletBackendError.walletCoreError(WalletBackendResponseError( - code: 7025, when: Timestamp.now(), - hint: "A KYC step is required before withdrawal can proceed", - message: "A KYC step is required before withdrawal can proceed")) - - static var previews: some View { - ErrorView(error: error, devMode: true, onDismiss: {}) - ErrorView(error: error, devMode: false, onDismiss: {}) - } -} diff --git a/TalerWallet1/Views/Main/MainView.swift b/TalerWallet1/Views/Main/MainView.swift @@ -99,7 +99,7 @@ struct MainView: View { if (!showScanner && urlToOpen == nil) { if let error2 = model.error2 { - ErrorView(data: error2, devMode: developerMode) { + ErrorView(stack.push("Main"), data: error2, devMode: developerMode) { model.setError(nil) }.interactiveDismissDisabled() .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all)) @@ -152,7 +152,8 @@ struct MainView: View { // show launch animation until either ready or error switch controller.backendState { case .ready: mainContent - case .error: ErrorView(title: "", // TODO: String(localized: ""), + case .error: ErrorView(stack.push("mainGroup"), + title: "", // TODO: String(localized: ""), copyable: true) {} default: LaunchAnimationView() } diff --git a/TalerWallet1/Views/Sheets/QRSheet.swift b/TalerWallet1/Views/Sheets/QRSheet.swift @@ -1,5 +1,5 @@ /* - * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. + * This file is part of GNU Taler, ©2022-26 Taler Systems S.A. * See LICENSE.md */ /** @@ -50,7 +50,8 @@ struct QRSheet: View { urlToOpen: $urlToOpen) // !!! will set @Binding to nil } else { // let _ = print(scannedURL) // TODO: error logging - ErrorView(title: String(localized: "Scanned QR is no talerURI"), + ErrorView(stack.push(), + title: String(localized: "Scanned QR is no talerURI"), message: scannedURL.absoluteString, copyable: true) { scannedSomething = false @@ -58,9 +59,11 @@ struct QRSheet: View { } } } else { - ErrorView(title: String(localized: "Scanned QR is no URL"), + ErrorView(stack.push(), + title: String(localized: "Scanned QR is no URL"), message: scannedCode, - copyable: true) { + copyable: true + ) { scannedSomething = false dismissTop(stack.push()) } diff --git a/TalerWallet1/Views/Sheets/Sheet.swift b/TalerWallet1/Views/Sheets/Sheet.swift @@ -56,7 +56,7 @@ struct Sheet: View { .accessibilityValue(idString) } if let error2 = model.error2 { - ErrorView(data: error2, devMode: developerMode) { + ErrorView(stack.push(), data: error2, devMode: developerMode) { model.setError(nil) logger.log("ErrorSheet dismissTop") dismissTop(stack.push()) diff --git a/TalerWallet1/Views/Transactions/TransactionSummaryV.swift b/TalerWallet1/Views/Transactions/TransactionSummaryV.swift @@ -416,7 +416,8 @@ struct TransactionSummaryV: View { badge: CONFIRM_BANK) } } } } @unknown default: - ErrorView(title: "Unknown withdrawal type", // should not happen, so no L10N + ErrorView(stack.push(), + title: "Unknown withdrawal type", // should not happen, so no L10N message: withdrawalDetails.type.rawValue, copyable: true) { dismissTop(stack.push())