diff options
author | Iván Ávalos <avalos@disroot.org> | 2024-03-08 13:27:44 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2024-04-11 10:26:04 -0600 |
commit | db06fc9130deb1dfebeef69e33deb0299e73418d (patch) | |
tree | 361a5d416f96ea886c6db62de724f8f7f970bfb2 | |
parent | c38068b2d616bcdde39d0845f5be136fd1f1591d (diff) | |
download | taler-ios-db06fc9130deb1dfebeef69e33deb0299e73418d.tar.gz taler-ios-db06fc9130deb1dfebeef69e33deb0299e73418d.tar.bz2 taler-ios-db06fc9130deb1dfebeef69e33deb0299e73418d.zip |
Laying the foundations of improved error handling
-rw-r--r-- | TalerWallet.xcodeproj/project.pbxproj | 12 | ||||
-rw-r--r-- | TalerWallet1/Controllers/Controller.swift | 20 | ||||
-rw-r--r-- | TalerWallet1/Helper/Encodable+toJSON.swift | 22 | ||||
-rw-r--r-- | TalerWallet1/Views/Main/MainView.swift | 15 | ||||
-rw-r--r-- | TalerWallet1/Views/Sheets/ErrorSheet.swift | 88 |
5 files changed, 157 insertions, 0 deletions
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj index 9e801c2..c2be79e 100644 --- a/TalerWallet.xcodeproj/project.pbxproj +++ b/TalerWallet.xcodeproj/project.pbxproj @@ -278,6 +278,10 @@ E37AA62B2AF197E5003850CF /* Model+Refund.swift in Sources */ = {isa = PBXBuildFile; fileRef = E37AA6292AF197E5003850CF /* Model+Refund.swift */; }; E37AA62E2AF19BE0003850CF /* RefundURIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E37AA62D2AF19BE0003850CF /* RefundURIView.swift */; }; E37AA62F2AF19BE0003850CF /* RefundURIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E37AA62D2AF19BE0003850CF /* RefundURIView.swift */; }; + E3E48FB22B9B7B5400898A0F /* ErrorSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E48FB12B9B7B5400898A0F /* ErrorSheet.swift */; }; + E3E48FB32B9B7B5400898A0F /* ErrorSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E48FB12B9B7B5400898A0F /* ErrorSheet.swift */; }; + E3E48FB52B9B7D5000898A0F /* Encodable+toJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E48FB42B9B7D5000898A0F /* Encodable+toJSON.swift */; }; + E3E48FB62B9B7D5000898A0F /* Encodable+toJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E48FB42B9B7D5000898A0F /* Encodable+toJSON.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -458,6 +462,8 @@ D14AFD3E24D232B500C51073 /* TalerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TalerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E37AA6292AF197E5003850CF /* Model+Refund.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Model+Refund.swift"; sourceTree = "<group>"; }; E37AA62D2AF19BE0003850CF /* RefundURIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefundURIView.swift; sourceTree = "<group>"; }; + E3E48FB12B9B7B5400898A0F /* ErrorSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorSheet.swift; sourceTree = "<group>"; }; + E3E48FB42B9B7D5000898A0F /* Encodable+toJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Encodable+toJSON.swift"; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -640,6 +646,7 @@ 4E8E25322A1CD39700A27BFA /* EqualIconWidthDomain.swift */, 4EBA563E2A7FD9390084948B /* SuperScriptDigits.swift */, 4EC4008B2AE5664100DF72C7 /* CharacterSet+contains.swift */, + E3E48FB42B9B7D5000898A0F /* Encodable+toJSON.swift */, ); path = Helper; sourceTree = "<group>"; @@ -830,6 +837,7 @@ 4EB0952A2989CBFE0043A8A1 /* Payment */, E37AA62C2AF19BA6003850CF /* Refund */, 4E3B4BBF2A41E64000CC88B8 /* P2P_Sheets */, + E3E48FB12B9B7B5400898A0F /* ErrorSheet.swift */, ); path = Sheets; sourceTree = "<group>"; @@ -1130,6 +1138,7 @@ 4E3EAE312A990778009F1BE8 /* SendAmount.swift in Sources */, 4E3EAE332A990778009F1BE8 /* EqualIconWidthDomain.swift in Sources */, 4EEC11932B83FB7A00146CFF /* SubjectInputV.swift in Sources */, + E3E48FB52B9B7D5000898A0F /* Encodable+toJSON.swift in Sources */, 4E3EAE342A990778009F1BE8 /* SuperScriptDigits.swift in Sources */, 4E3EAE352A990778009F1BE8 /* P2pPayURIView.swift in Sources */, 4E3EAE362A990778009F1BE8 /* Model+Payment.swift in Sources */, @@ -1151,6 +1160,7 @@ 4E3EAE452A990778009F1BE8 /* P2PReadyV.swift in Sources */, 4E3EAE462A990778009F1BE8 /* TextFieldAlert.swift in Sources */, 4E3EAE472A990778009F1BE8 /* QuiteSomeCoins.swift in Sources */, + E3E48FB22B9B7B5400898A0F /* ErrorSheet.swift in Sources */, 4E2D8DD52B45822A00234039 /* AmountV.swift in Sources */, 4E3EAE492A990778009F1BE8 /* ManualWithdrawDone.swift in Sources */, 4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in Sources */, @@ -1247,6 +1257,7 @@ 4E40E0BE29F25ABB00B85369 /* SendAmount.swift in Sources */, 4E8E25332A1CD39700A27BFA /* EqualIconWidthDomain.swift in Sources */, 4EEC11942B83FB7A00146CFF /* SubjectInputV.swift in Sources */, + E3E48FB62B9B7D5000898A0F /* Encodable+toJSON.swift in Sources */, 4EBA563F2A7FD9390084948B /* SuperScriptDigits.swift in Sources */, 4E578E942A4822D500F21F1C /* P2pPayURIView.swift in Sources */, 4EB095542989CBFE0043A8A1 /* Model+Payment.swift in Sources */, @@ -1268,6 +1279,7 @@ 4EB3136129FEE79B007D68BC /* P2PReadyV.swift in Sources */, 4EB0956B2989CBFE0043A8A1 /* TextFieldAlert.swift in Sources */, 4EBA82AD2A3F580500E5F39A /* QuiteSomeCoins.swift in Sources */, + E3E48FB32B9B7B5400898A0F /* ErrorSheet.swift in Sources */, 4E2D8DD62B45822A00234039 /* AmountV.swift in Sources */, 4EB431672A1E55C700C5690E /* ManualWithdrawDone.swift in Sources */, 4E753A082A0B6A5F002D9328 /* ShareSheet.swift in Sources */, diff --git a/TalerWallet1/Controllers/Controller.swift b/TalerWallet1/Controllers/Controller.swift index 03c6997..d0941f8 100644 --- a/TalerWallet1/Controllers/Controller.swift +++ b/TalerWallet1/Controllers/Controller.swift @@ -48,6 +48,13 @@ class Controller: ObservableObject { var messageForSheet: String? = nil + @Published var showError: Bool = false + @Published var error: WalletBackendResponseError? = nil { + didSet { + self.showError = error != nil + } + } + init() { // for family in UIFont.familyNames { // print(family) @@ -218,3 +225,16 @@ extension Controller { return .unknown } } + +// MARK: - +extension Controller { + @MainActor + func showError(error: WalletBackendResponseError) { + self.error = error + } + + @MainActor + func cleanError() { + self.error = nil + } +} diff --git a/TalerWallet1/Helper/Encodable+toJSON.swift b/TalerWallet1/Helper/Encodable+toJSON.swift new file mode 100644 index 0000000..265148d --- /dev/null +++ b/TalerWallet1/Helper/Encodable+toJSON.swift @@ -0,0 +1,22 @@ +// +// Codable+toJson.swift +// TalerWallet +// +// Created by Ivan Avalos on 08/03/24. +// Copyright © 2024 Taler. All rights reserved. +// + +import Foundation + +extension Encodable { + func toJSON(_ encoder: JSONEncoder = JSONEncoder()) -> String? { + let e = encoder + e.outputFormatting = .prettyPrinted + if let data = try? e.encode(self) { + let result = String(decoding: data, as: UTF8.self) + return String(result) + } + + return nil + } +} diff --git a/TalerWallet1/Views/Main/MainView.swift b/TalerWallet1/Views/Main/MainView.swift index 6563daa..6c9100c 100644 --- a/TalerWallet1/Views/Main/MainView.swift +++ b/TalerWallet1/Views/Main/MainView.swift @@ -101,6 +101,12 @@ extension MainView { @State private var showKycAlert: Bool = false @State private var kycURI: URL? +#if DEBUG + @AppStorage("developerMode") var developerMode: Bool = true +#else + @AppStorage("developerMode") var developerMode: Bool = false +#endif + private var openKycButton: some View { Button("KYC") { showKycAlert = false @@ -249,6 +255,15 @@ extension MainView { } } } + .sheet(isPresented: $controller.showError) { + controller.cleanError() + } content: { + if let error = controller.error { + ErrorSheet(error: error, developerMode: developerMode) { + controller.cleanError() + } + } + } } // body } // Content } diff --git a/TalerWallet1/Views/Sheets/ErrorSheet.swift b/TalerWallet1/Views/Sheets/ErrorSheet.swift new file mode 100644 index 0000000..5b64cf3 --- /dev/null +++ b/TalerWallet1/Views/Sheets/ErrorSheet.swift @@ -0,0 +1,88 @@ +// +// ErrorSheet.swift +// TalerWallet +// +// Created by Ivan Avalos on 08/03/24. +// Copyright © 2024 Taler. All rights reserved. +// + +import SwiftUI + +struct ErrorSheet: View { + var message: String + var copyable: Bool + + var onDismiss: () -> Void + + let navTitle = String(localized: "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) + } + } + + var body: some View { + NavigationView { + VStack { + Image(systemName: "exclamationmark.circle.fill") + .resizable() + .frame(width: 100, height: 100) + .aspectRatio(contentMode: .fit) + .foregroundStyle(.red) + .padding(.bottom) + + if copyable { + if #available(iOS 16.4, *) { + Text(message) + .foregroundStyle(.red) + .monospaced() + } else { + Text(message) + .foregroundStyle(.red) + .font(.system(.body, design: .monospaced)) + } + + CopyButton(textToCopy: message, vertical: false) + .accessibilityLabel("Copy the error JSON") + .padding(.top) + } else { + Text(message) + .multilineTextAlignment(.center) + } + } + .navigationTitle(navTitle) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem { + Button { + onDismiss() + } label: { + Label("Close", systemImage: "xmark.circle") + } + } + } + .padding() + } + } +} + +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 var previews: some View { + ErrorSheet(error: error, developerMode: true, onDismiss: {}) + ErrorSheet(error: error, developerMode: false, onDismiss: {}) + } +} |