commit a1d90c5bd0c74a5272de57f1f3e9a3a28d220acb
parent aeab8aa5d8200e5605cb45cb8d9e7aa890ff242b
Author: Marc Stibane <marc@taler.net>
Date: Thu, 5 Jun 2025 16:16:38 +0200
Use BiometricService
Diffstat:
6 files changed, 146 insertions(+), 7 deletions(-)
diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift b/TalerWallet1/Controllers/TalerWallet1App.swift
@@ -27,6 +27,7 @@ struct TalerWallet1App: App {
private let model = WalletModel.shared
private let debugViewC = DebugViewC.shared
let logger = Logger(subsystem: "net.taler.gnu", category: "Main App")
+ private let biometricService = BiometricService.shared
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "net.taler.gnu.refresh")
@@ -47,6 +48,7 @@ struct TalerWallet1App: App {
.environmentObject(viewState2) // popToRoot
.environmentObject(controller)
.environmentObject(model)
+ .environmentObject(biometricService)
.addKeyboardVisibilityToEnvironment()
/// external events are taler:// or payto:// URLs passed to this app
/// we handle them in .onOpenURL in MainView.swift
@@ -66,6 +68,9 @@ struct TalerWallet1App: App {
logger.log("More than 5 minutes in background - tell wallet-core")
hintApplicationResumed()
}
+ if interval.seconds > 60 {
+ biometricService.isAuthenticated = false
+ }
}
backgrounded = nil
}
diff --git a/TalerWallet1/GNU_Taler InfoPlist.xcstrings b/TalerWallet1/GNU_Taler InfoPlist.xcstrings
@@ -5,6 +5,12 @@
"comment" : "Bundle display name",
"extractionState" : "extracted_with_value",
"localizations" : {
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "GNU Taler"
+ }
+ },
"en" : {
"stringUnit" : {
"state" : "translated",
@@ -23,6 +29,12 @@
"comment" : "Bundle name",
"extractionState" : "extracted_with_value",
"localizations" : {
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "GNU_Taler"
+ }
+ },
"en" : {
"stringUnit" : {
"state" : "translated",
@@ -41,6 +53,12 @@
"comment" : "Privacy - Camera Usage Description",
"extractionState" : "extracted_with_value",
"localizations" : {
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "QR Codes scannen"
+ }
+ },
"en" : {
"stringUnit" : {
"state" : "translated",
@@ -55,10 +73,40 @@
}
}
},
+ "NSFaceIDUsageDescription" : {
+ "comment" : "Privacy - Face ID Usage Description",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Schützen Sie Ihr Geld"
+ }
+ },
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Protect your money"
+ }
+ },
+ "es" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Proteja su dinero"
+ }
+ }
+ }
+ },
"NSHumanReadableCopyright" : {
"comment" : "Copyright (human-readable)",
"extractionState" : "extracted_with_value",
"localizations" : {
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "© Taler-Systems.com"
+ }
+ },
"en" : {
"stringUnit" : {
"state" : "translated",
diff --git a/TalerWallet1/Taler_Nightly InfoPlist.xcstrings b/TalerWallet1/Taler_Nightly InfoPlist.xcstrings
@@ -70,6 +70,30 @@
}
}
},
+ "NSFaceIDUsageDescription" : {
+ "comment" : "Privacy - Face ID Usage Description",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Schützen Sie Ihr Geld"
+ }
+ },
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Protect your money"
+ }
+ },
+ "es" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Proteja su dinero"
+ }
+ }
+ }
+ },
"NSHumanReadableCopyright" : {
"comment" : "Copyright (human-readable)",
"localizations" : {
diff --git a/TalerWallet1/Taler_Wallet InfoPlist.xcstrings b/TalerWallet1/Taler_Wallet InfoPlist.xcstrings
@@ -73,6 +73,30 @@
}
}
},
+ "NSFaceIDUsageDescription" : {
+ "comment" : "Privacy - Face ID Usage Description",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Schützen Sie Ihr Geld"
+ }
+ },
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Protect your money"
+ }
+ },
+ "es" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Proteja su dinero"
+ }
+ }
+ }
+ },
"NSHumanReadableCopyright" : {
"comment" : "Copyright (human-readable)",
"extractionState" : "extracted_with_value",
diff --git a/TalerWallet1/Views/Main/MainView.swift b/TalerWallet1/Views/Main/MainView.swift
@@ -26,11 +26,13 @@ struct MainView: View {
@EnvironmentObject private var controller: Controller
@EnvironmentObject private var model: WalletModel
+ @EnvironmentObject private var biometricService: BiometricService
@AppStorage("talerFontIndex") var talerFontIndex: Int = 0 // extension mustn't define this, so it must be here
@AppStorage("playSoundsI") var playSoundsI: Int = 1 // extension mustn't define this, so it must be here
@AppStorage("playSoundsB") var playSoundsB: Bool = false
+ @AppStorage("useAuthentication") var useAuthentication: Bool = false
- @StateObject var tabBarModel = TabBarModel()
+ @StateObject private var tabBarModel = TabBarModel()
@State private var selectedBalance: Balance? = nil // gets set in TransactionsListView
@State private var selectedIndex: Int? = nil // user selects savings box in OIMview
@State private var urlToOpen: URL? = nil
@@ -100,7 +102,42 @@ struct MainView: View {
// Color.clear
}
}
- }//.transition(.move(edge: .top))
+ }
+ .overlay {
+ if useAuthentication && !biometricService.isAuthenticated {
+ Color.gray.opacity(0.75)
+ .animation(.easeInOut, value: biometricService.isAuthenticated)
+ if let errorMessage = biometricService.authenticationError {
+ Text(errorMessage)
+ .talerFont(.title)
+ .foregroundColor(.red)
+ .multilineTextAlignment(.center)
+ .padding()
+ .background(WalletColors().backgroundColor)
+ .onTapGesture {
+ biometricService.authenticationError = nil
+ biometricService.authenticateUser()
+ }
+ .onAppear {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
+ biometricService.authenticationError = nil
+ }
+ }
+ } else {
+ Text("Authenticating...")
+ .talerFont(.largeTitle)
+ .foregroundStyle(WalletColors().talerColor)
+ .padding()
+ .background(WalletColors().backgroundColor)
+ .onAppear {
+ biometricService.authenticateUser()
+ }
+ .onTapGesture {
+ biometricService.authenticateUser()
+ }
+ }
+ }
+ }
let mainGroup = Group {
// show launch animation until either ready or error
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -26,6 +26,7 @@ struct SettingsView: View {
@EnvironmentObject private var controller: Controller
@EnvironmentObject private var model: WalletModel
+ @EnvironmentObject private var biometricService: BiometricService
// @Environment(\.colorSchemeContrast) private var colorSchemeContrast
#if DEBUG
@AppStorage("developerMode") var developerMode: Bool = true
@@ -43,7 +44,7 @@ struct SettingsView: View {
@AppStorage("minimalistic") var minimalistic: Bool = false
@AppStorage("localConsoleL") var localConsoleL: Bool = false // for Logs
@AppStorage("localConsoleO") var localConsoleO: Int = 0 // for Observability
- @AppStorage("oimTwoRows") var oimTwoRows: Bool = false
+ @AppStorage("useAuthentication") var useAuthentication: Bool = false
@State private var checkDisabled = false
@State private var withDrawDisabled = false
@@ -120,6 +121,10 @@ struct SettingsView: View {
SettingsItem(name: bankAccountsTitle, id1: "bankAccounts",
description: hideDescriptions ? nil : String(localized: "Your accounts for deposit...")) {}
}
+ SettingsToggle(name: String(localized: "Use FaceID / TouchID"), value: $useAuthentication, id1: "useFaceID",
+ description: minimalistic ? nil : String(localized: "Protect your money")) {
+ biometricService.isAuthenticated = false
+ }
SettingsToggle(name: String(localized: "Minimalistic"), value: $minimalistic, id1: "minimal",
description: hideDescriptions ? nil : String(localized: "Omit text where possible")) {
hideDescriptions = minimalistic //withAnimation { hideDescriptions = minimalistic }
@@ -145,10 +150,6 @@ struct SettingsView: View {
consoleManager.isVisible = localConsoleO != 0 || localConsoleL
consoleManager.clear()
}
-#if OIM
- SettingsToggle(name: String(localized: "Two rows"), value: $oimTwoRows, id1: "oimTwoRows",
- description: minimalistic ? nil : String(localized: "OIM denomination layout"))
-#endif
#if DEBUG
let showDiagnostic = diagnosticModeEnabled
#else