[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.
- [taler-taler-ios] branch master updated (c38068b -> 6b94fe0), gnunet, 2024/04/11
- [taler-taler-ios] 02/17: Bump version to 0.9.6 (1), gnunet, 2024/04/11
- [taler-taler-ios] 03/17: updateExchangeEntry (temporarily) back to exchangeBaseUrl (instead of scopeInfo), gnunet, 2024/04/11
- [taler-taler-ios] 06/17: clientCancellationId, gnunet, 2024/04/11
- [taler-taler-ios] 07/17: Laying the foundations of improved error handling, gnunet, 2024/04/11
- [taler-taler-ios] 04/17: emitObservabilityEvents, gnunet, 2024/04/11
- [taler-taler-ios] 05/17: symLog after quickjs.sendMessage returned, gnunet, 2024/04/11
- [taler-taler-ios] 01/17: rename setConfig, gnunet, 2024/04/11
- [taler-taler-ios] 11/17: Prepare notification error handling, gnunet, 2024/04/11
- [taler-taler-ios] 13/17: Fixes for error handling rebase, gnunet, 2024/04/11
- [taler-taler-ios] 09/17: Implement error handling all around (+refactoring),
gnunet <=
- [taler-taler-ios] 08/17: Improve errors and move them to WalletModel, gnunet, 2024/04/11
- [taler-taler-ios] 15/17: cleanup, gnunet, 2024/04/11
- [taler-taler-ios] 14/17: catch WalletBackendError.walletCoreError, gnunet, 2024/04/11
- [taler-taler-ios] 16/17: error handling, logging, gnunet, 2024/04/11
- [taler-taler-ios] 17/17: Bump version to 0.9.7 (1), gnunet, 2024/04/11
- [taler-taler-ios] 12/17: Centralize all error handling in the model, gnunet, 2024/04/11
- [taler-taler-ios] 10/17: Improvements to error dialog, gnunet, 2024/04/11