gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-ios] 09/17: Implement error handling all around (+refactori


From: gnunet
Subject: [taler-taler-ios] 09/17: Implement error handling all around (+refactoring)
Date: Thu, 11 Apr 2024 23:13:41 +0200

This is an automated email from the git hooks/post-receive script.

marc-stibane pushed a commit to branch master
in repository taler-ios.

commit f7c55f50bb77ff701ddbd942808231ca71478d65
Author: Iván Ávalos <avalos@disroot.org>
AuthorDate: Mon Mar 11 14:10:53 2024 -0600

    Implement error handling all around (+refactoring)
---
 TalerWallet1/Backend/WalletBackendError.swift      | 20 +++---
 TalerWallet1/Backend/WalletCore.swift              | 10 ++-
 TalerWallet1/Controllers/Controller.swift          | 28 ++------
 TalerWallet1/Helper/Encodable+toJSON.swift         | 12 ++--
 TalerWallet1/Model/Model+Balances.swift            | 13 ++--
 TalerWallet1/Model/Model+Exchange.swift            | 27 +++-----
 TalerWallet1/Model/Model+Pending.swift             | 12 ++--
 TalerWallet1/Model/Model+Transactions.swift        | 18 ++---
 TalerWallet1/Model/WalletModel.swift               | 13 +---
 TalerWallet1/Views/Balances/BalancesListView.swift | 24 +++++--
 .../Views/Balances/BalancesSectionView.swift       | 28 ++++++--
 TalerWallet1/Views/Banking/ExchangeListView.swift  | 13 +++-
 TalerWallet1/Views/Banking/ManualWithdraw.swift    | 10 ++-
 TalerWallet1/Views/HelperViews/BarGraph.swift      | 13 +++-
 TalerWallet1/Views/Main/MainView.swift             | 24 ++++---
 TalerWallet1/Views/Peer2peer/RequestPayment.swift  | 10 +--
 TalerWallet1/Views/Peer2peer/SendAmount.swift      | 10 +--
 TalerWallet1/Views/Sheets/ErrorSheet.swift         | 80 ++++++++++++----------
 .../Sheets/P2P_Sheets/P2pReceiveURIView.swift      |  7 +-
 .../Views/Sheets/Payment/PaymentView.swift         |  5 +-
 TalerWallet1/Views/Sheets/Sheet.swift              | 20 +++++-
 .../WithdrawBankIntegrated/WithdrawURIView.swift   |  8 ++-
 TalerWallet1/Views/Sheets/WithdrawExchangeV.swift  | 25 +++----
 23 files changed, 235 insertions(+), 195 deletions(-)

diff --git a/TalerWallet1/Backend/WalletBackendError.swift 
b/TalerWallet1/Backend/WalletBackendError.swift
index 9b4cea4..785cc69 100644
--- a/TalerWallet1/Backend/WalletBackendError.swift
+++ b/TalerWallet1/Backend/WalletBackendError.swift
@@ -13,38 +13,38 @@ enum WalletBackendError: Error {
     case initializationError
     case serializationError
     case deserializationError
-    case walletCoreError
+    case walletCoreError(WalletBackendResponseError?)
 }
 
 /// Information supplied by the backend describing an error.
 struct WalletBackendResponseError: Codable {
     /// Numeric error code defined defined in the GANA gnu-taler-error-codes 
registry.
-    var talerErrorCode: Int
+    var code: Int
     
     /// English description of the error code.
-    var talerErrorHint: String
+    var hint: String
     
     /// English diagnostic message that can give details for the instance of 
the error.
-    var message: String
-    
+    var message: String? = nil
+
     /// Error details, type depends on `talerErrorCode`.
-    var details: Data?
+    var details: Data? = nil
 }
 
 extension WalletCore {
     static func serializeRequestError() -> WalletBackendResponseError {
-        return WalletBackendResponseError(talerErrorCode: -1, talerErrorHint: 
"Could not serialize request.", message: "")
+        return WalletBackendResponseError(code: -1, hint: "Could not serialize 
request.", message: "")
     }
     
     static func parseResponseError() -> WalletBackendResponseError {
-        return WalletBackendResponseError(talerErrorCode: -2, talerErrorHint: 
"Could not parse response.", message: "")
+        return WalletBackendResponseError(code: -2, hint: "Could not parse 
response.", message: "")
     }
     
     static func parseFailureError() -> WalletBackendResponseError {
-        return WalletBackendResponseError(talerErrorCode: -3, talerErrorHint: 
"Could not parse error detail.", message: "")
+        return WalletBackendResponseError(code: -3, hint: "Could not parse 
error detail.", message: "")
     }
 
     static func walletError() -> WalletBackendResponseError {
-        return WalletBackendResponseError(talerErrorCode: -4, talerErrorHint: 
"Error detail.", message: "")
+        return WalletBackendResponseError(code: -4, hint: "Error detail.", 
message: "")
     }
 }
diff --git a/TalerWallet1/Backend/WalletCore.swift 
b/TalerWallet1/Backend/WalletCore.swift
index 1810755..1c10c71 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -62,7 +62,7 @@ class WalletCore: QuickjsMessageHandler {
         let operation: String?
         let id: UInt?
         let result: AnyCodable?
-        let error: AnyCodable?  // should be WalletBackendResponseError?
+        let error: WalletBackendResponseError?
         let payload: AnyCodable?
     }
 
@@ -109,12 +109,10 @@ extension WalletCore {
                 let jsonData = try JSONEncoder().encode(walletError)
                 logger.error("wallet-core sent back an error for request 
\(requestId, privacy: .public)")
                 symLog.log("id:\(requestId)  \(walletError)")
-                // TODO: decode jsonData to WalletBackendResponseError - or 
HTTPError
-                completion(requestId, timeSent, jsonData, 
WalletCore.walletError())
+                completion(requestId, timeSent, jsonData, walletError)
             } catch {        // JSON encoding of response.result failed / 
should never happen
                 symLog.log(decoded)
                 logger.error("cannot encode wallet-core Error")
-                // TODO: show error alert
                 completion(requestId, timeSent, nil, 
WalletCore.parseFailureError())
             }
         } else {             // JSON decoding of error message failed
@@ -409,7 +407,7 @@ extension WalletCore {
                 let millisecs = timeUsed.milliseconds
                 self.logger.info("Request \"id\":\(requestId, privacy: 
.public) took \(millisecs, privacy: .public) ms")
                 var err: Error? = nil
-                if let json = result {
+                if let json = result, error == nil {
                     do {
                         let decoded = try 
JSONDecoder().decode(T.Response.self, from: json)
                         continuation.resume(returning: (decoded, requestId))
@@ -439,7 +437,7 @@ extension WalletCore {
                     } else {
                         self.lastError = nil
                     }
-                    err = WalletBackendError.walletCoreError
+                    err = WalletBackendError.walletCoreError(error)
                 }
                 continuation.resume(throwing: err ?? 
TransactionDecodingError.invalidStringValue)
             }
diff --git a/TalerWallet1/Controllers/Controller.swift 
b/TalerWallet1/Controllers/Controller.swift
index 03c6997..2045c8f 100644
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -87,31 +87,15 @@ class Controller: ObservableObject {
     }
 
     @MainActor
-    func getInfo(from exchangeBaseUrl: String, model: WalletModel) async -> 
CurrencyInfo? {
-        if let exchange = await model.getExchangeByUrl(url: exchangeBaseUrl) {
-//            let scopeInfo = exchange.scopeInfo
-//            if let info = hasInfo(for: scopeInfo.currency) {
-//                return info
-//            }
-//            do {
-//                let info = try await model.getCurrencyInfoM(scope: 
scopeInfo, delay: 0)
-//                await setInfo(info)
-//                return info
-//            } catch {
-//                return nil
-//            }
-
-            let scopeInfo = exchange.scopeInfo
+    func getInfo(from exchangeBaseUrl: String, model: WalletModel) async 
throws -> CurrencyInfo? {
+        let exchange = try await model.getExchangeByUrl(url: exchangeBaseUrl)
+        if let scopeInfo = exchange.scopeInfo {
             if let info = hasInfo(for: scopeInfo.currency) {
                 return info
             }
-            do {
-                let info = try await model.getCurrencyInfoM(scope: scopeInfo, 
delay: 0)
-                await setInfo(info)
-                return info
-            } catch {
-                return nil
-            }
+            let info = try await model.getCurrencyInfoM(scope: scopeInfo, 
delay: 0)
+            await setInfo(info)
+            return info
         } else {
             // TODO: Error "Can't get Exchange Info"
         }
diff --git a/TalerWallet1/Helper/Encodable+toJSON.swift 
b/TalerWallet1/Helper/Encodable+toJSON.swift
index 265148d..58fac4e 100644
--- a/TalerWallet1/Helper/Encodable+toJSON.swift
+++ b/TalerWallet1/Helper/Encodable+toJSON.swift
@@ -1,11 +1,7 @@
-//
-//  Codable+toJson.swift
-//  TalerWallet
-//
-//  Created by Ivan Avalos on 08/03/24.
-//  Copyright © 2024 Taler. All rights reserved.
-//
-
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
 import Foundation
 
 extension Encodable {
diff --git a/TalerWallet1/Model/Model+Balances.swift 
b/TalerWallet1/Model/Model+Balances.swift
index 6d4be7f..c941792 100644
--- a/TalerWallet1/Model/Model+Balances.swift
+++ b/TalerWallet1/Model/Model+Balances.swift
@@ -55,18 +55,13 @@ fileprivate struct Balances: WalletBackendFormattedRequest {
 extension WalletModel {
     /// fetch Balances from Wallet-Core. No networking involved
     @MainActor func balancesM(_ stack: CallStack)
-      async -> [Balance] {          // M for MainActor
+      async throws -> [Balance] {          // M for MainActor
         await semaphore.wait()
         defer { semaphore.signal() }
         if cachedBalances == nil {
-            do {
-                let request = Balances()
-                let response = try await sendRequest(request, ASYNCDELAY)
-                cachedBalances = response.balances
-            } catch {
-                logger.error("balancesM failed: \(error)")
-                // TODO: show error
-            }
+            let request = Balances()
+            let response = try await sendRequest(request, ASYNCDELAY)
+            cachedBalances = response.balances
         } else {
             logger.trace("returning cached Balances")
         }
diff --git a/TalerWallet1/Model/Model+Exchange.swift 
b/TalerWallet1/Model/Model+Exchange.swift
index 522b84d..67bd05a 100644
--- a/TalerWallet1/Model/Model+Exchange.swift
+++ b/TalerWallet1/Model/Model+Exchange.swift
@@ -168,29 +168,20 @@ extension WalletModel {
     }
 
     /// ask wallet-core for its list of known exchanges
-    @MainActor func listExchangesM()
-      async -> [Exchange] {   // M for MainActor
-        do {
-            let request = ListExchanges()
-            let response = try await sendRequest(request, ASYNCDELAY)
-            return response.exchanges
-        } catch {
-            // TODO: Error
-            return []               // empty, but not nil
-        }
+    @MainActor func listExchangesM(devMode: Bool = false)
+      async throws -> [Exchange] {   // M for MainActor
+        let request = ListExchanges()
+        let response = try await sendRequest(request, ASYNCDELAY)
+        return response.exchanges
     }
 
     /// add a new exchange with URL to the wallet's list of known exchanges
     func getExchangeByUrl(url: String)
-      async -> Exchange? {
-        do {
-            let request = GetExchangeByUrl(exchangeBaseUrl: url)
+      async throws -> Exchange {
+        let request = GetExchangeByUrl(exchangeBaseUrl: url)
 //            logger.info("query for exchange: \(url, privacy: .public)")
-            let response = try await sendRequest(request)
-            return response
-        } catch {
-            return nil
-        }
+        let response = try await sendRequest(request)
+        return response
     }
 
     /// add a new exchange with URL to the wallet's list of known exchanges
diff --git a/TalerWallet1/Model/Model+Pending.swift 
b/TalerWallet1/Model/Model+Pending.swift
index e2dec9f..11ab98b 100644
--- a/TalerWallet1/Model/Model+Pending.swift
+++ b/TalerWallet1/Model/Model+Pending.swift
@@ -43,13 +43,9 @@ struct PendingOperation: Codable, Hashable {
 // MARK: -
 extension WalletModel {
     @MainActor func getPendingOperationsM()
-      async -> [PendingOperation] {   // M for MainActor
-        do {
-            let request = GetPendingOperations()
-            let response = try await sendRequest(request, ASYNCDELAY)
-            return response.pendingOperations
-        } catch {
-            return []
-        }
+      async throws -> [PendingOperation] {   // M for MainActor
+        let request = GetPendingOperations()
+        let response = try await sendRequest(request, ASYNCDELAY)
+        return response.pendingOperations
     }
 }
diff --git a/TalerWallet1/Model/Model+Transactions.swift 
b/TalerWallet1/Model/Model+Transactions.swift
index 26288bf..dce30e8 100644
--- a/TalerWallet1/Model/Model+Transactions.swift
+++ b/TalerWallet1/Model/Model+Transactions.swift
@@ -103,21 +103,17 @@ struct ResumeTransaction: WalletBackendFormattedRequest {
 // MARK: -
 extension WalletModel {
     /// ask wallet-core for its list of transactions filtered by searchString
-    func transactionsT(_ stack: CallStack, scopeInfo: ScopeInfo, searchString: 
String? = nil, 
+    func transactionsT(_ stack: CallStack, scopeInfo: ScopeInfo, searchString: 
String? = nil,
                        sort: String = "descending", includeRefreshes: Bool = 
false)
-    async -> [Transaction] {                                          // might 
be called from a background thread itself
-        do {
-            let request = GetTransactions(scopeInfo: scopeInfo, currency: 
scopeInfo.currency, search: searchString, sort: sort, includeRefreshes: 
includeRefreshes)
-            let response = try await sendRequest(request, ASYNCDELAY)
-            return response.transactions
-        } catch {
-            return []
-        }
+    async throws -> [Transaction] {
+        let request = GetTransactions(scopeInfo: scopeInfo, currency: 
scopeInfo.currency, search: searchString, sort: sort, includeRefreshes: 
includeRefreshes)
+        let response = try await sendRequest(request, ASYNCDELAY)
+        return response.transactions
     }
     /// fetch transactions from Wallet-Core. No networking involved
     @MainActor func transactionsMA(_ stack: CallStack, scopeInfo: ScopeInfo, 
searchString: String? = nil, sort: String = "descending")
-    async -> [Transaction] {    // M for MainActor
-        return await transactionsT(stack.push(), scopeInfo: scopeInfo, 
searchString: searchString, sort: sort)
+    async throws -> [Transaction] {    // M for MainActor
+        return try await transactionsT(stack.push(), scopeInfo: scopeInfo, 
searchString: searchString, sort: sort)
     }
 
     /// abort the specified transaction from Wallet-Core. No networking 
involved
diff --git a/TalerWallet1/Model/WalletModel.swift 
b/TalerWallet1/Model/WalletModel.swift
index 2e8d24d..ab8bb6f 100644
--- a/TalerWallet1/Model/WalletModel.swift
+++ b/TalerWallet1/Model/WalletModel.swift
@@ -27,12 +27,6 @@ class WalletModel: ObservableObject {
     let semaphore = AsyncSemaphore(value: 1)
     var cachedBalances: [Balance]? = nil
 
-#if DEBUG
-    @AppStorage("developerMode") var developerMode: Bool = true
-#else
-    @AppStorage("developerMode") var developerMode: Bool = false
-#endif
-
     @Published var showError: Bool = false
     @Published var error: ErrorData? = nil {
         didSet { showError = error != nil }
@@ -275,12 +269,7 @@ extension WalletModel {
 
 extension WalletModel {
     @MainActor
-    func showError(error: ErrorData) {
-        // Do not show dev errors to users
-        if case .developer(_) = error, !developerMode {
-            return
-        }
-
+    func showError(_ error: ErrorData) {
         self.error = error
     }
 
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift 
b/TalerWallet1/Views/Balances/BalancesListView.swift
index f12483d..8750595 100644
--- a/TalerWallet1/Views/Balances/BalancesListView.swift
+++ b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -16,6 +16,7 @@ struct BalancesListView: View {
     @Binding var shouldReloadBalances: Int
 
     @EnvironmentObject private var model: WalletModel
+    @EnvironmentObject private var controller: Controller
 
     @State private var lastReloadedBalances = 0
     @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING)   
 // Update currency when used
@@ -78,14 +79,23 @@ struct BalancesListView: View {
 
     /// runs on MainActor if called in some Task {}
     @discardableResult
-    private func reloadBalances(_ stack: CallStack, _ invalidateCache: Bool) 
async -> Int {
+    private func reloadBalances(_ stack: CallStack, _ invalidateCache: Bool) 
async -> Int? {
         if invalidateCache {
             model.cachedBalances = nil
         }
-        let reloaded = await model.balancesM(stack.push())
-        let count = reloaded.count
-        balances = reloaded         // redraw
-        return count
+
+        do {
+            let reloaded = try await model.balancesM(stack.push())
+            let count = reloaded.count
+            balances = reloaded         // redraw
+            return count
+        } catch {
+            symLog.log(error.localizedDescription)
+            model.showError(.error(error))
+            controller.playSound(0)
+        }
+
+        return nil
     }
 
     var body: some View {
@@ -126,7 +136,7 @@ extension BalancesListView {
         @Binding var amountToTransfer: Amount
         @Binding var summary: String
         @Binding var shouldReloadBalances: Int
-        var reloadBalances: (_ stack: CallStack, _ invalidateCache: Bool) 
async -> Int
+        var reloadBalances: (_ stack: CallStack, _ invalidateCache: Bool) 
async -> Int?
 
         var body: some View {
 #if PRINT_CHANGES
@@ -155,7 +165,7 @@ extension BalancesListView {
             .refreshable {  // already async
                 symLog?.log("refreshing balances")
                 let count = await reloadBalances(stack.push("refreshing 
balances"), true)
-                if count > 0 {
+                if let count, count > 0 {
                     NotificationCenter.default.post(name: .BalanceReloaded, 
object: nil)
                 }
             }
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift 
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index 0d1a80a..35a7f03 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -48,13 +48,25 @@ struct BalancesSectionView {
     @State private var shownSectionID = UUID()  // guaranteed to be different 
the first time
 
     func reloadCompleted(_ stack: CallStack) async -> () {
-        transactions = await model.transactionsT(stack.push(), scopeInfo: 
balance.scopeInfo, includeRefreshes: developerMode)
-        completedTransactions = WalletModel.completedTransactions(transactions)
+        do {
+            transactions = try await model.transactionsT(stack.push(), 
scopeInfo: balance.scopeInfo, includeRefreshes: developerMode)
+            completedTransactions = 
WalletModel.completedTransactions(transactions)
+        } catch {
+            symLog.log(error.localizedDescription)
+            model.showError(.error(error))
+            controller.playSound(0)
+        }
     }
 
     func reloadPending(_ stack: CallStack) async -> () {
-        transactions = await model.transactionsT(stack.push(), scopeInfo: 
balance.scopeInfo, includeRefreshes: developerMode)
-        pendingTransactions = WalletModel.pendingTransactions(transactions)
+        do {
+            transactions = try await model.transactionsT(stack.push(), 
scopeInfo: balance.scopeInfo, includeRefreshes: developerMode)
+            pendingTransactions = WalletModel.pendingTransactions(transactions)
+        } catch {
+            symLog.log(error.localizedDescription)
+            model.showError(.error(error))
+            controller.playSound(0)
+        }
     }
 }
 
@@ -128,11 +140,17 @@ extension BalancesSectionView: View {
 //            if shownSectionID != sectionID {
                 symLog.log(".task for BalancesSectionView - reload 
Transactions")
             // TODO: only load the MAXRECENT most recent transactions
-            let response = await model.transactionsT(stack.push(".task - 
reload Transactions"), scopeInfo: scopeInfo, includeRefreshes: developerMode)
+            do {
+                let response = try await model.transactionsT(stack.push(".task 
- reload Transactions"), scopeInfo: scopeInfo, includeRefreshes: developerMode)
                 transactions = response
                 pendingTransactions = WalletModel.pendingTransactions(response)
                 completedTransactions = 
WalletModel.completedTransactions(response)
                 shownSectionID = sectionID
+            } catch {
+                symLog.log(error.localizedDescription)
+                model.showError(.error(error))
+                controller.playSound(0)
+            }
 //            } else {
 //                symLog.log("task for BalancesSectionView \(sectionID) ❗️ 
skip reloading Transactions")
 //            }
diff --git a/TalerWallet1/Views/Banking/ExchangeListView.swift 
b/TalerWallet1/Views/Banking/ExchangeListView.swift
index 504e077..edb2cf9 100644
--- a/TalerWallet1/Views/Banking/ExchangeListView.swift
+++ b/TalerWallet1/Views/Banking/ExchangeListView.swift
@@ -16,6 +16,7 @@ struct ExchangeListView: View {
     @Binding var balances: [Balance]
     let navTitle: String
     @EnvironmentObject private var model: WalletModel
+    @EnvironmentObject private var controller: Controller
 
     @State var showAlert: Bool = false
     @State var newExchange: String = TESTEXCHANGE
@@ -27,8 +28,10 @@ struct ExchangeListView: View {
                 try await model.addExchange(url: exchange)
                 symLog.log("added: \(exchange)")
                 announce(this: "added: \(exchange)")
-            } catch {    // TODO: error handling - couldn't add exchangeURL
+            } catch {
                 symLog.log("error: \(error)")
+                model.showError(.error(error))
+                controller.playSound(0)
             }
         }
     }
@@ -74,6 +77,7 @@ struct ExchangeListCommonV {
     @Binding var balances: [Balance]
 
     @EnvironmentObject private var model: WalletModel
+    @EnvironmentObject private var controller: Controller
     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
 //    @AppStorage("depositIBAN") var depositIBAN = EMPTYSTRING
 //    @AppStorage("accountHolder") var accountHolder = EMPTYSTRING
@@ -84,7 +88,12 @@ struct ExchangeListCommonV {
     @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING)   
  // TODO: Hold different values for different currencies?
 
     func reloadExchanges() async -> Void {
-        exchanges = await model.listExchangesM()
+        do {
+            exchanges = try await model.listExchangesM()
+        } catch {
+            model.showError(.error(error))
+            controller.playSound(0)
+        }
     }
 }
 // MARK: -
diff --git a/TalerWallet1/Views/Banking/ManualWithdraw.swift 
b/TalerWallet1/Views/Banking/ManualWithdraw.swift
index 3b42f5a..0b5552a 100644
--- a/TalerWallet1/Views/Banking/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Banking/ManualWithdraw.swift
@@ -111,11 +111,15 @@ struct ManualWithdraw: View {
       }
         .task(id: amountToTransfer.value) { // re-run this whenever 
amountToTransfer changes
             if let exchangeBaseUrl = scopeInfo?.url {
+                symLog.log("getExchangeByUrl(\(exchangeBaseUrl))")
                 if exchange == nil || exchange?.tosStatus != .accepted {
-                    if let exc = await model.getExchangeByUrl(url: 
exchangeBaseUrl) {
-                        exchange = exc
-                    } else {
+                    do {
+                        exchange = try await model.getExchangeByUrl(url: 
exchangeBaseUrl)
+                    } catch {
                         // TODO: Error "Can't get Exchange / Payment Service 
Provider Info"
+                        symLog.log(error.localizedDescription)
+                        model.showError(.error(error))
+                        controller.playSound(0)
                     }
                 }
                 if !amountToTransfer.isZero {
diff --git a/TalerWallet1/Views/HelperViews/BarGraph.swift 
b/TalerWallet1/Views/HelperViews/BarGraph.swift
index 3f64829..0000fbe 100644
--- a/TalerWallet1/Views/HelperViews/BarGraph.swift
+++ b/TalerWallet1/Views/HelperViews/BarGraph.swift
@@ -15,6 +15,7 @@ struct BarGraphHeader: View {
     @Binding var shouldReloadBalances: Int
 
     @EnvironmentObject private var model: WalletModel
+    @EnvironmentObject private var controller: Controller
     @Environment(\.colorScheme) private var colorScheme
     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
     @AppStorage("minimalistic") var minimalistic: Bool = false
@@ -37,9 +38,15 @@ struct BarGraphHeader: View {
             if let scopeInfo {
                 symLog.log(".task for BarGraphHeader(\(scopeInfo.currency)) - 
reload Transactions")
                 // TODO: only load the 10 most recent transactions
-                let response = await model.transactionsT(stack.push(".task - 
reload Transactions for \(scopeInfo.currency)"),
-                                                         scopeInfo: scopeInfo)
-                completedTransactions = 
WalletModel.completedTransactions(response)
+                do {
+                    let response = try await 
model.transactionsT(stack.push(".task - reload Transactions for 
\(scopeInfo.currency)"),
+                                                                 scopeInfo: 
scopeInfo)
+                    completedTransactions = 
WalletModel.completedTransactions(response)
+                } catch {
+                    symLog.log(error.localizedDescription)
+                    model.showError(.error(error))
+                    controller.playSound(0)
+                }
             }
         }
     }
diff --git a/TalerWallet1/Views/Main/MainView.swift 
b/TalerWallet1/Views/Main/MainView.swift
index f73e7eb..f376643 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -20,6 +20,12 @@ struct MainView: View {
     let stack: CallStack
     @Binding var soundPlayed: Bool
 
+#if DEBUG
+    @AppStorage("developerMode") var developerMode: Bool = true
+#else
+    @AppStorage("developerMode") var developerMode: Bool = false
+#endif
+
     @EnvironmentObject private var controller: Controller
     @EnvironmentObject private var model: WalletModel
     @AppStorage("talerFontIndex") var talerFontIndex: Int = 0       // 
extension mustn't define this, so it must be here
@@ -71,6 +77,15 @@ struct MainView: View {
             let sheet = AnyView(URLSheet(stack: stack.push(), urlToOpen: url))
             Sheet(sheetView: sheet)
         }
+        .sheet(isPresented: $model.showError) {
+            model.cleanError()
+        } content: {
+            if let error = model.error {
+                ErrorSheet(data: error, devMode: developerMode) {
+                    model.cleanError()
+                }
+            }
+        }
     } // body
 }
 // MARK: - TabBar
@@ -256,15 +271,6 @@ extension MainView {
                     }
                 }
             }
-            .sheet(isPresented: $model.showError) {
-                model.cleanError()
-            } content: {
-                if let error = model.error {
-                    ErrorSheet(data: error, developerMode: developerMode) {
-                        model.cleanError()
-                    }
-                }
-            }
         } // body
     } // Content
 }
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift 
b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index 90a5bc8..1c10e66 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -105,10 +105,12 @@ struct RequestPayment: View {
         .task(id: amountToTransfer.value) {
             if exchange == nil {
                 if let url = scopeInfo.url {
-                    if let exc = await model.getExchangeByUrl(url: url) {
-                        exchange = exc
-                    } else {
-                        // TODO: Error "Can't get Exchange / Payment Service 
Provider Info"
+                    do {
+                        exchange = try await model.getExchangeByUrl(url: url)
+                    } catch {
+                        symLog.log(error.localizedDescription)
+                        model.showError(.error(error))
+                        controller.playSound(0)
                     }
                 }
             }
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift 
b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index ba468e8..53c4b44 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -133,10 +133,12 @@ struct SendAmount: View {
         .task(id: amountToTransfer.value) {
             if exchange == nil {
                 if let url = scopeInfo.url {
-                    if let exc = await model.getExchangeByUrl(url: url) {
-                        exchange = exc
-                    } else {
-                        // TODO: Error "Can't get Exchange / Payment Service 
Provider Info"
+                    do {
+                        exchange = try await model.getExchangeByUrl(url: url)
+                    } catch {
+                        symLog.log(error.localizedDescription)
+                        model.showError(.error(error))
+                        controller.playSound(0)
                     }
                 }
             }
diff --git a/TalerWallet1/Views/Sheets/ErrorSheet.swift 
b/TalerWallet1/Views/Sheets/ErrorSheet.swift
index fd352bd..9164d0a 100644
--- a/TalerWallet1/Views/Sheets/ErrorSheet.swift
+++ b/TalerWallet1/Views/Sheets/ErrorSheet.swift
@@ -1,17 +1,12 @@
-//
-//  ErrorSheet.swift
-//  TalerWallet
-//
-//  Created by Ivan Avalos on 08/03/24.
-//  Copyright © 2024 Taler. All rights reserved.
-//
-
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
 import SwiftUI
 
 enum ErrorData {
-case user(String)
-case developer(String)
-case taler(WalletBackendResponseError)
+case message(String)
+case error(Error)
 }
 
 struct ErrorSheet: View {
@@ -20,33 +15,50 @@ struct ErrorSheet: View {
 
     var onDismiss: () -> Void
 
-    let navTitle = String(localized: "Error")
-    let unknownError = String(localized: "Unknown error")
-
     init(message: String, copyable: Bool, onDismiss: @escaping () -> Void) {
         self.message = message
         self.copyable = copyable
         self.onDismiss = onDismiss
     }
 
-    init(error: WalletBackendResponseError, developerMode: Bool, onDismiss: 
@escaping () -> Void) {
-        if let json = error.toJSON(), developerMode {
-            self.init(message: json, copyable: true, onDismiss: onDismiss)
-        } else {
-            self.init(message: error.message, copyable: false, onDismiss: 
onDismiss)
+    init(error: Error, devMode: Bool, onDismiss: @escaping () -> Void) {
+        let walletCoreError = String(localized: "Internal core error")
+        let initializationError = String(localized: "Initialization error")
+        let serializationError = String(localized: "Serialization error")
+        let deserializationError = String(localized: "Deserialization error")
+
+        switch error {
+        case let walletError as WalletBackendError:
+            switch walletError {
+            case .walletCoreError(let error):
+                if let json = error?.toJSON(), devMode {
+                    self.init(message: json, copyable: true, onDismiss: 
onDismiss)
+                } else if let message = error?.message {
+                    self.init(message: message, copyable: false, onDismiss: 
onDismiss)
+                } else {
+                    self.init(message: walletCoreError, copyable: false, 
onDismiss: onDismiss)
+                }
+            case .initializationError:
+                self.init(message: initializationError, copyable: false, 
onDismiss: onDismiss)
+            case .serializationError:
+                self.init(message: serializationError, copyable: false, 
onDismiss: onDismiss)
+            case .deserializationError:
+                self.init(message: deserializationError, copyable: false, 
onDismiss: onDismiss)
+            }
+        default:
+            self.init(message: error.localizedDescription, copyable: false, 
onDismiss: onDismiss)
         }
     }
 
-    init(data: ErrorData, developerMode: Bool, onDismiss: @escaping () -> 
Void) {
+    init(data: ErrorData, devMode: Bool, onDismiss: @escaping () -> Void) {
+        let unknownError = String(localized: "Unknown error")
+
         switch data {
-        case .user(let message):
+        case .message(let message):
             self.init(message: message, copyable: false, onDismiss: onDismiss)
             return
-        case .developer(let message):
-            self.init(message: message, copyable: true, onDismiss: onDismiss)
-            return
-        case .taler(let error):
-            self.init(error: error, developerMode: developerMode, onDismiss: 
onDismiss)
+        case .error(let error):
+            self.init(error: error, devMode: devMode, onDismiss: onDismiss)
             return
         }
 
@@ -89,10 +101,8 @@ struct ErrorSheet: View {
                     .frame(minHeight: geometry.size.height)
                 }
             }
-            .navigationTitle(navTitle)
-            .navigationBarTitleDisplayMode(.inline)
         }.safeAreaInset(edge: .bottom) {
-            Button("Cancel", role: .cancel) {
+            Button("Close", role: .cancel) {
                 onDismiss()
             }
             .buttonStyle(TalerButtonStyle(type: .bordered))
@@ -103,13 +113,13 @@ struct ErrorSheet: View {
 }
 
 struct ErrorSheet_Previews: PreviewProvider {
-    static let error = WalletBackendResponseError(
-        talerErrorCode: 7025,
-        talerErrorHint: "A KYC step is required before withdrawal can proceed",
-        message: "A KYC step is required before withdrawal can proceed")
+    static let error = 
WalletBackendError.walletCoreError(WalletBackendResponseError(
+        code: 7025,
+        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 {
-        ErrorSheet(error: error, developerMode: true, onDismiss: {})
-        ErrorSheet(error: error, developerMode: false, onDismiss: {})
+        ErrorSheet(error: error, devMode: true, onDismiss: {})
+        ErrorSheet(error: error, devMode: false, onDismiss: {})
     }
 }
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift 
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
index ee63a98..d780e89 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
@@ -17,6 +17,7 @@ struct P2pReceiveURIView: View {
     let url: URL
     
     @EnvironmentObject private var model: WalletModel
+    @EnvironmentObject private var controller: Controller
     @Environment(\.colorScheme) private var colorScheme
     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@@ -90,10 +91,12 @@ struct P2pReceiveURIView: View {
             do { // TODO: cancelled
                 symLog.log(".task")
                 let ppResponse = try await 
model.preparePeerPushCreditM(url.absoluteString)
-                exchange = await model.getExchangeByUrl(url: 
ppResponse.exchangeBaseUrl)
+                exchange = try await model.getExchangeByUrl(url: 
ppResponse.exchangeBaseUrl)
                 peerPushCreditResponse = ppResponse
-            } catch {    // TODO: error
+            } catch {
                 symLog.log(error.localizedDescription)
+                model.showError(.error(error))
+                controller.playSound(0)
                 peerPushCreditResponse = nil
             }
         }
diff --git a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift 
b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
index 897bcae..4466510 100644
--- a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
@@ -20,6 +20,7 @@ struct PaymentView: View {
     @Binding var summary: String
 
     @EnvironmentObject private var model: WalletModel
+    @EnvironmentObject private var controller: Controller
     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
 
     @State var preparePayResult: PreparePayResult? = nil
@@ -105,8 +106,10 @@ struct PaymentView: View {
                         let result = try await 
model.preparePayForUriM(url.absoluteString)
                         preparePayResult = result
                     }
-                } catch {    // TODO: error
+                } catch {
                     symLog.log(error.localizedDescription)
+                    model.showError(.error(error))
+                    controller.playSound(0)
                 }
             }
         }
diff --git a/TalerWallet1/Views/Sheets/Sheet.swift 
b/TalerWallet1/Views/Sheets/Sheet.swift
index 7c9edb8..95da489 100644
--- a/TalerWallet1/Views/Sheets/Sheet.swift
+++ b/TalerWallet1/Views/Sheets/Sheet.swift
@@ -10,8 +10,15 @@ struct Sheet: View {
     private let symLog = SymLogV(0)
     @Environment(\.dismiss) var dismiss     // call dismiss() to get rid of 
the sheet
     @EnvironmentObject private var debugViewC: DebugViewC
+    @EnvironmentObject private var model: WalletModel
     @AppStorage("talerFontIndex") var talerFontIndex: Int = 0
 
+#if DEBUG
+    @AppStorage("developerMode") var developerMode: Bool = true
+#else
+    @AppStorage("developerMode") var developerMode: Bool = false
+#endif
+
     var sheetView: AnyView
 
     let logger = Logger(subsystem: "net.taler.gnu", category: "Sheet")
@@ -27,7 +34,15 @@ struct Sheet: View {
         let idString = debugViewC.sheetID > 0 ? String(debugViewC.sheetID)
                                               : ""      // show nothing if 0
         NavigationView {
-            sheetView
+            Group {
+                if let error = model.error {
+                    ErrorSheet(data: error, devMode: developerMode) {
+                        dismissTop()
+                    }
+                } else {
+                    sheetView
+                }
+            }
                 .navigationBarItems(leading: cancelButton)
                 .navigationBarTitleDisplayMode(.automatic)
                 
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
@@ -45,5 +60,8 @@ struct Sheet: View {
                 .accessibilityLabel(Text("Sheet.ID.", comment: 
"AccessibilityLabel"))
                 .accessibilityValue(idString)
         }
+        .onDisappear {
+            model.cleanError()
+        }
     }
 }
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
index 49d45ba..1c04d94 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
@@ -98,8 +98,8 @@ struct WithdrawURIView: View {
                 let amount = withdrawUriInfo.amount
                 let baseUrl = withdrawUriInfo.defaultExchangeBaseUrl
                            ?? 
withdrawUriInfo.possibleExchanges.first?.exchangeBaseUrl
-                if let baseUrl, let exc = await model.getExchangeByUrl(url: 
baseUrl) {
-                    exchange = exc
+                if let baseUrl {
+                    exchange = try await model.getExchangeByUrl(url: baseUrl)
                     let details = try await 
model.getWithdrawalDetailsForAmountM(baseUrl, amount: amount)
                     withdrawalAmountDetails = details
 //                    agePicker.setAges(ages: details?.ageRestrictionOptions)
@@ -107,8 +107,10 @@ struct WithdrawURIView: View {
                     symLog.log("no exchangeBaseUrl or no exchange")
                     withdrawalAmountDetails = nil
                 }
-            } catch {    // TODO: error
+            } catch {
                 symLog.log(error.localizedDescription)
+                model.showError(.error(error))
+                controller.playSound(0)
                 withdrawalAmountDetails = nil
             }
         }
diff --git a/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift 
b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
index 3c39c1d..492a26e 100644
--- a/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
@@ -25,6 +25,7 @@ struct WithdrawExchangeV: View {
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
+
         Group {
             if exchange != nil {
                 ManualWithdraw(stack: stack.push(),
@@ -44,22 +45,22 @@ struct WithdrawExchangeV: View {
                     let withdrawExchange = try await 
model.loadWithdrawalExchangeForUriM(url.absoluteString)
                     let baseUrl = withdrawExchange.exchangeBaseUrl
                     symLog.log("getExchangeByUrl(\(baseUrl))")
-                    if let exc = await model.getExchangeByUrl(url: baseUrl) {
-                        // let the controller collect CurrencyInfo from this 
formerly unknown exchange
-                        let _ = await controller.getInfo(from: baseUrl, model: 
model)
-                        if let amount = withdrawExchange.amount {
-                            amountToTransfer = amount
-                        } else {
-                            let currency = exc.scopeInfo.currency
-                            amountToTransfer.setCurrency(currency)
-                            // is already Amount.zero(currency: "")
-                        }
-                        exchange = exc
+                    let exc = try await model.getExchangeByUrl(url: baseUrl)
+                    // let the controller collect CurrencyInfo from this 
formerly unknown exchange
+                    let _ = try await controller.getInfo(from: baseUrl, model: 
model)
+                    if let amount = withdrawExchange.amount {
+                        amountToTransfer = amount
                     } else {
-                        // TODO: Error "Can't get Exchange / Payment Service 
Provider Info"
+                        let currency = exc.scopeInfo.currency
+                        amountToTransfer.setCurrency(currency)
+                        // is already Amount.zero(currency: "")
                     }
+                    exchange = exc
                 } catch {    // TODO: error
                     symLog.log(error.localizedDescription)
+                    symLog.log(error.localizedDescription)
+                    model.showError(.error(error))
+                    controller.playSound(0)
                     exchange = nil
                 }
             }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]