taler-ios

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

commit 027354fc9edf655f48a45dd7d599e3a88715881b
parent 5360255a8e9c51c22ba5bc7a655dc935bd4bd7f3
Author: Marc Stibane <marc@taler.net>
Date:   Mon, 18 Sep 2023 09:15:33 +0200

CallStack

Diffstat:
MTalerWallet.xcodeproj/project.pbxproj | 6++++++
MTalerWallet1/Controllers/TalerWallet1App.swift | 2+-
ATalerWallet1/Helper/CallStack.swift | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MTalerWallet1/Model/Model+Balances.swift | 2+-
MTalerWallet1/Views/Balances/BalancesListView.swift | 24+++++++++++++-----------
MTalerWallet1/Views/Balances/BalancesSectionView.swift | 1+
MTalerWallet1/Views/Main/MainView.swift | 8++++++--
MTalerWallet1/Views/Main/SideBarView.swift | 3++-
8 files changed, 119 insertions(+), 16 deletions(-)

diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj @@ -229,6 +229,8 @@ 4ECB62802A0BA6DF004ABBB7 /* Model+P2P.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */; }; 4ECB62822A0BB01D004ABBB7 /* SelectDays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */; }; 4ED2F94B2A278F5100453B40 /* ThreeAmounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmounts.swift */; }; + 4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; }; + 4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; }; 4EEC157329F8242800D46A03 /* QRGeneratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC157229F8242800D46A03 /* QRGeneratorView.swift */; }; 4EEC157629F8ECBF00D46A03 /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = 4EEC157529F8ECBF00D46A03 /* CodeScanner */; }; 4EEC157829F9032900D46A03 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC157729F9032900D46A03 /* Sheet.swift */; }; @@ -392,6 +394,7 @@ 4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Model+P2P.swift"; sourceTree = "<group>"; }; 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectDays.swift; sourceTree = "<group>"; }; 4ED2F94A2A278F5100453B40 /* ThreeAmounts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreeAmounts.swift; sourceTree = "<group>"; }; + 4EDBDCD82AB787CB00925C02 /* CallStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallStack.swift; sourceTree = "<group>"; }; 4EEC157229F8242800D46A03 /* QRGeneratorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRGeneratorView.swift; sourceTree = "<group>"; }; 4EEC157729F9032900D46A03 /* Sheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; }; 4EEC157929F9427F00D46A03 /* QRSheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRSheet.swift; sourceTree = "<group>"; }; @@ -555,6 +558,7 @@ isa = PBXGroup; children = ( 4E363CBD2A23CB2100D7E98C /* AnyTransition+backslide.swift */, + 4EDBDCD82AB787CB00925C02 /* CallStack.swift */, 4E16E12229F3BB99008B9C86 /* CurrencyFormatter.swift */, 4EAD117529F672FA008EDD0B /* KeyboardResponder.swift */, 4E363CC12A2621C200D7E98C /* LocalizedAlertError.swift */, @@ -1097,6 +1101,7 @@ 4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift in Sources */, 4E3EAE702A990778009F1BE8 /* CurrencyInputView.swift in Sources */, 4E3EAE712A990778009F1BE8 /* URL+id+iban.swift in Sources */, + 4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */, 4E3EAE722A990778009F1BE8 /* RequestPayment.swift in Sources */, 4E3EAE732A990778009F1BE8 /* SettingsItem.swift in Sources */, 4E3EAE742A990778009F1BE8 /* BalanceRowView.swift in Sources */, @@ -1198,6 +1203,7 @@ 4EB0950A2989CB7C0043A8A1 /* TalerStrings.swift in Sources */, 4EA551252A2C923600FEC9A8 /* CurrencyInputView.swift in Sources */, 4E363CBC2A237E0900D7E98C /* URL+id+iban.swift in Sources */, + 4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */, 4E9320452A1645B600A87B0E /* RequestPayment.swift in Sources */, 4EB095502989CBFE0043A8A1 /* SettingsItem.swift in Sources */, 4EB0955C2989CBFE0043A8A1 /* BalanceRowView.swift in Sources */, diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift b/TalerWallet1/Controllers/TalerWallet1App.swift @@ -37,7 +37,7 @@ struct TalerWallet1App: App { var body: some Scene { WindowGroup { - MainView(soundPlayed: $soundPlayed) + MainView(stack: CallStack("App"), soundPlayed: $soundPlayed) .environmentObject(debugViewC) // change viewID / sheetID .environmentObject(viewState) // popToRoot .environmentObject(controller) diff --git a/TalerWallet1/Helper/CallStack.swift b/TalerWallet1/Helper/CallStack.swift @@ -0,0 +1,89 @@ +// +// Copyright © 2018-2023 Marc Stibane +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +import Foundation + +struct CallStackItem { +#if DEBUG + let file: String + let function: String +#endif + let message: String +} + +#if DEBUG +extension CallStackItem: Identifiable { + var id: String { file } +} +extension CallStackItem: Equatable { + static func == (lhs: CallStackItem, rhs: CallStackItem) -> Bool { + lhs.file == rhs.file + } +} +#endif + + +struct CallStack { + private var storage = [CallStackItem]() + func peek() -> CallStackItem? { storage.first } + func push(item: CallStackItem) -> CallStack { + return CallStack(storage: [item] + storage) + } +} + +#if DEBUG +fileprivate func filePath2Name(_ file: String) -> String { + let filePath = NSString(string: file) + return filePath.lastPathComponent +} +#endif + +extension CallStack { +#if DEBUG + init(_ message: String = "", + funcName: String = #function, + filePath: String = #file, + line: UInt = #line) { + let item = CallStackItem(file: filePath2Name(filePath) + ":\(line)", function: funcName, message: message) + self.storage = [item] + } +#else + init(_ message: String = "") { + let item = CallStackItem(message: message) + self.storage = [item] + } +#endif +#if DEBUG + public func push(_ message: String = "", + funcName: String = #function, + filePath: String = #file, + line: UInt = #line) -> CallStack { + let item = CallStackItem(file: filePath2Name(filePath) + ":\(line)", function: funcName, message: message) + return push(item: item) + } +#else + public func push(_ message: String = "") -> CallStack { + let item = CallStackItem(message: message) + return push(item: item) + } +#endif +} + +//extension CallStack: Equatable { +// static func == (lhs: CallStack, rhs: CallStack) -> Bool { lhs.storage == rhs.storage } +//} diff --git a/TalerWallet1/Model/Model+Balances.swift b/TalerWallet1/Model/Model+Balances.swift @@ -43,7 +43,7 @@ fileprivate struct Balances: WalletBackendFormattedRequest { // MARK: - extension WalletModel { /// fetch Balances from Wallet-Core. No networking involved - @MainActor func balancesM() + @MainActor func balancesM(_ stack: CallStack) async -> [Balance] { // M for MainActor do { let request = Balances() diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift b/TalerWallet1/Views/Balances/BalancesListView.swift @@ -11,6 +11,7 @@ import AVFoundation struct BalancesListView: View { private let symLog = SymLogV() let navTitle: String + let stack: CallStack let hamburgerAction: () -> Void @EnvironmentObject private var model: WalletModel @@ -70,8 +71,8 @@ struct BalancesListView: View { } /// runs on MainActor if called in some Task {} - private func reloadAction() async -> Int { - let reloaded = await model.balancesM() + private func reloadAction(_ stack: CallStack) async -> Int { + let reloaded = await model.balancesM(stack.push()) let count = reloaded.count balances = reloaded return count @@ -82,7 +83,7 @@ struct BalancesListView: View { let _ = Self._printChanges() let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear #endif - Content(symLog: symLog, balances: $balances, + Content(symLog: symLog, stack: stack.push(), balances: $balances, centsToTransfer: $centsToTransfer, summary: $summary, reloadAction: reloadAction) .navigationTitle(navTitle) @@ -93,13 +94,12 @@ struct BalancesListView: View { WalletEmptyView() .refreshable { // already async symLog.log("refreshing") - let count = await reloadAction() + let count = await reloadAction(stack.push()) if count > 0 { // postNotificationM(.BalanceReloaded) NotificationCenter.default.post(name: .BalanceReloaded, object: nil) } } - } } .alert("Scanning QR-codes requires access to the camera", @@ -116,7 +116,7 @@ struct BalancesListView: View { } // sheet .task { symLog.log(".task getBalances") - _ = await reloadAction() + _ = await reloadAction(stack.push(".task getBalances")) } // task } } @@ -124,11 +124,12 @@ struct BalancesListView: View { extension BalancesListView { struct Content: View { let symLog: SymLogV? + let stack: CallStack @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic @Binding var balances: [Balance] @Binding var centsToTransfer: UInt64 @Binding var summary: String - var reloadAction: () async -> Int + var reloadAction: (_ stack: CallStack) async -> Int @State private var isActive = true @State private var shouldReload = false @@ -141,14 +142,15 @@ extension BalancesListView { Group { // necessary for .backslide transition (bug in SwiftUI) let count = balances.count List(balances, id: \.self) { balance in - BalancesSectionView(balance: balance, + BalancesSectionView(stack: stack.push(), + balance: balance, sectionCount: count, centsToTransfer: $centsToTransfer, summary: $summary) } .refreshable { // already async symLog?.log("refreshing") - let count = await reloadAction() + let count = await reloadAction(stack.push()) if count > 0 { // postNotificationM(.BalanceReloaded) NotificationCenter.default.post(name: .BalanceReloaded, object: nil) @@ -161,7 +163,7 @@ extension BalancesListView { if shouldReload { shouldReload = false symLog?.log(".onAppear ==> shouldReload was true, reloading now") - Task { await reloadAction() } // runs on MainActor + Task { await reloadAction(stack.push()) } // runs on MainActor } } .onDisappear() { @@ -172,7 +174,7 @@ extension BalancesListView { // doesn't need to be received on main thread because we just reload in a background task anyway if isActive { symLog?.log(".onNotification(.BalanceChange) ==> reload") - Task { await reloadAction() } + Task { await reloadAction(stack.push()) } } else { symLog?.log(".onNotification(.BalanceChange) ==> reload postponed, shouldReload = true") shouldReload = true diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift b/TalerWallet1/Views/Balances/BalancesSectionView.swift @@ -16,6 +16,7 @@ import SymLog struct BalancesSectionView: View { private let symLog = SymLogV() + let stack: CallStack let balance: Balance let sectionCount: Int @Binding var centsToTransfer: UInt64 diff --git a/TalerWallet1/Views/Main/MainView.swift b/TalerWallet1/Views/Main/MainView.swift @@ -15,6 +15,7 @@ struct LazyView<Content: View>: View { struct MainView: View { private let symLog = SymLogV(0) + let stack: CallStack @EnvironmentObject private var viewState: ViewState // popToRootView() @EnvironmentObject private var controller: Controller @State private var sheetPresented = false @@ -32,7 +33,7 @@ struct MainView: View { #endif Group { if controller.backendState == .ready { - Content(symLog: symLog, talerFont: $talerFont) + Content(symLog: symLog, stack: stack.push(), talerFont: $talerFont) // any change to rootViewId triggers popToRootView behaviour .id(viewState.rootViewId) .onAppear() { @@ -68,6 +69,7 @@ struct MainView: View { extension MainView { struct Content: View { let symLog: SymLogV? + let stack: CallStack @Binding var talerFont: Int @State var sidebarVisible: Bool = false func hamburgerAction() { @@ -83,6 +85,7 @@ extension MainView { SidebarItem(name: balances, sysImage: "creditcard.fill", // TODO: Wallet Icon view: AnyView(BalancesListView(navTitle: balances, + stack: stack.push(), hamburgerAction: hamburgerAction) )), SidebarItem(name: exchanges, @@ -124,7 +127,8 @@ extension MainView { .talerNavBar(talerFont: talerFont) // The side view is above (Z-Axis) the current view - SideBarView(views: views, + SideBarView(stack: stack.push(), + views: views, currentView: $currentView, sidebarVisible: sidebarVisible, hamburgerAction: hamburgerAction) diff --git a/TalerWallet1/Views/Main/SideBarView.swift b/TalerWallet1/Views/Main/SideBarView.swift @@ -15,6 +15,7 @@ struct SidebarItem { struct SideBarView: View { private let symLog = SymLogV(0) + let stack: CallStack let views: [SidebarItem] @Binding var currentView: Int let sidebarVisible: Bool @@ -85,7 +86,7 @@ fileprivate struct BindingViewContainer : View { ZStack(alignment: .leading) { views[currentView].view .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - SideBarView(views: views, currentView: $currentView, + SideBarView(stack: CallStack("Preview"), views: views, currentView: $currentView, sidebarVisible: sidebarVisible, hamburgerAction: { sidebarVisible = !sidebarVisible }) }