commit 28e5f780070ef8f734c9cd989de0dcb34cc63de7
parent adfc9193b9feb82ef37ff0ca8c1dfc1c5b31a9c9
Author: Marc Stibane <marc@taler.net>
Date: Fri, 20 Oct 2023 08:17:42 +0200
CurrencySpecification
Diffstat:
6 files changed, 128 insertions(+), 76 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj
@@ -7,7 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
- 4E16E12329F3BB99008B9C86 /* CurrencyFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* CurrencyFormatter.swift */; };
+ 4E16E12329F3BB99008B9C86 /* CurrencySpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */; };
4E2254972A822B8100E41D29 /* payment_received.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 4E2254952A822B8100E41D29 /* payment_received.m4a */; };
4E2254982A822B8100E41D29 /* payment_sent.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 4E2254962A822B8100E41D29 /* payment_sent.m4a */; };
4E3327BA2AD1635100BF5AD6 /* AsyncSemaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E3327B92AD1635100BF5AD6 /* AsyncSemaphore.swift */; };
@@ -74,7 +74,7 @@
4E3EAE502A990778009F1BE8 /* Model+Transactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095322989CBFE0043A8A1 /* Model+Transactions.swift */; };
4E3EAE512A990778009F1BE8 /* Controller+playSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E578E912A481D8600F21F1C /* Controller+playSound.swift */; };
4E3EAE522A990778009F1BE8 /* WalletEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095392989CBFE0043A8A1 /* WalletEmptyView.swift */; };
- 4E3EAE532A990778009F1BE8 /* CurrencyFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* CurrencyFormatter.swift */; };
+ 4E3EAE532A990778009F1BE8 /* CurrencySpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */; };
4E3EAE542A990778009F1BE8 /* TalerDater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB095062989CB7C0043A8A1 /* TalerDater.swift */; };
4E3EAE552A990778009F1BE8 /* Model+Balances.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E3B4BC42A428AF700CC88B8 /* Model+Balances.swift */; };
4E3EAE562A990778009F1BE8 /* LocalizedAlertError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E363CC12A2621C200D7E98C /* LocalizedAlertError.swift */; };
@@ -287,7 +287,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 4E16E12229F3BB99008B9C86 /* CurrencyFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencyFormatter.swift; sourceTree = "<group>"; };
+ 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySpecification.swift; sourceTree = "<group>"; };
4E2254952A822B8100E41D29 /* payment_received.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = payment_received.m4a; sourceTree = "<group>"; };
4E2254962A822B8100E41D29 /* payment_sent.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = payment_sent.m4a; sourceTree = "<group>"; };
4E3327B92AD1635100BF5AD6 /* AsyncSemaphore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncSemaphore.swift; sourceTree = "<group>"; };
@@ -569,7 +569,7 @@
4E363CBD2A23CB2100D7E98C /* AnyTransition+backslide.swift */,
4E3327B92AD1635100BF5AD6 /* AsyncSemaphore.swift */,
4EDBDCD82AB787CB00925C02 /* CallStack.swift */,
- 4E16E12229F3BB99008B9C86 /* CurrencyFormatter.swift */,
+ 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */,
4EAD117529F672FA008EDD0B /* KeyboardResponder.swift */,
4E363CC12A2621C200D7E98C /* LocalizedAlertError.swift */,
4E578E912A481D8600F21F1C /* Controller+playSound.swift */,
@@ -1084,7 +1084,7 @@
4E3EAE502A990778009F1BE8 /* Model+Transactions.swift in Sources */,
4E3EAE512A990778009F1BE8 /* Controller+playSound.swift in Sources */,
4E3EAE522A990778009F1BE8 /* WalletEmptyView.swift in Sources */,
- 4E3EAE532A990778009F1BE8 /* CurrencyFormatter.swift in Sources */,
+ 4E3EAE532A990778009F1BE8 /* CurrencySpecification.swift in Sources */,
4E3EAE542A990778009F1BE8 /* TalerDater.swift in Sources */,
4E3EAE552A990778009F1BE8 /* Model+Balances.swift in Sources */,
4E3EAE562A990778009F1BE8 /* LocalizedAlertError.swift in Sources */,
@@ -1189,7 +1189,7 @@
4EB095592989CBFE0043A8A1 /* Model+Transactions.swift in Sources */,
4E578E922A481D8600F21F1C /* Controller+playSound.swift in Sources */,
4EB0955F2989CBFE0043A8A1 /* WalletEmptyView.swift in Sources */,
- 4E16E12329F3BB99008B9C86 /* CurrencyFormatter.swift in Sources */,
+ 4E16E12329F3BB99008B9C86 /* CurrencySpecification.swift in Sources */,
4EB095092989CB7C0043A8A1 /* TalerDater.swift in Sources */,
4E3B4BC52A428AF700CC88B8 /* Model+Balances.swift in Sources */,
4E363CC22A2621C200D7E98C /* LocalizedAlertError.swift in Sources */,
diff --git a/TalerWallet1/Helper/CurrencyFormatter.swift b/TalerWallet1/Helper/CurrencyFormatter.swift
@@ -1,30 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import Foundation
-import taler_swift
-
-public class CurrencyFormatter: NumberFormatter {
- public static let shared = CurrencyFormatter()
-
- public override init() {
- super.init()
- self.locale = Locale.current
- self.numberStyle = .currency // currencyISOCode, currencyPlural, (currencyAccounting)
- self.numberStyle = .currencyISOCode
-// self.numberStyle = .spellOut
-
-// self.currencyCode = code // EUR, USD, JPY, GBP
-// self.minimumFractionDigits = fractionDigits
-// self.maximumFractionDigits = fractionDigits
-// self.groupingSize = 3 // thousands
-// self.groupingSeparator = ","
-// self.usesGroupingSeparator = true
-// self.decimalSeparator = "."
- }
-
- required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-}
diff --git a/TalerWallet1/Helper/CurrencySpecification.swift b/TalerWallet1/Helper/CurrencySpecification.swift
@@ -0,0 +1,109 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import Foundation
+import taler_swift
+
+public struct CurrencyInfo {
+ let scope: ScopeInfo
+ let specs: CurrencySpecification
+ let formatter: CurrencyFormatter
+}
+
+public struct CurrencySpecification2: Codable, Sendable {
+ let currencySpecification: CurrencySpecification
+}
+
+public struct CurrencySpecification: Codable, Sendable {
+ enum CodingKeys: String, CodingKey {
+ case name = "name"
+ case decimalSeparator = "decimal_separator"
+ case groupSeparator = "group_separator"
+ case fractionalInputDigits = "num_fractional_input_digits"
+ case fractionalNormalDigits = "num_fractional_normal_digits"
+ case fractionalTrailingZeroDigits = "num_fractional_trailing_zero_digits"
+ case isCurrencyNameLeading = "is_currency_name_leading"
+ case altUnitNames = "alt_unit_names"
+ }
+ /// some name for this CurrencySpecification
+ let name: String
+ /// e.g. “.” for $, and “,” for €
+ let decimalSeparator: String
+ /// e.g. “,” for $, and “.” or “ ” for € (France uses a narrow space character)
+ let groupSeparator: String?
+ /// how much digits the user may enter after the decimal separator
+ let fractionalInputDigits: Int
+ /// €,$,£: 2; some arabic currencies: 3, ¥: 0
+ let fractionalNormalDigits: Int
+ /// usually same as numFractionalNormalDigits, but e.g. might be 2 for ¥
+ let fractionalTrailingZeroDigits: Int
+ /// true for “$ 3.50”; false for “3,50 €”
+ let isCurrencyNameLeading: Bool
+ /// map of powers of 10 to alternative currency names / symbols
+ /// must always have an entry under "0" that defines the base name
+ /// e.g. "0 => €" or "3 => k€". For BTC, would be "0 => BTC, -3 => mBTC".
+ /// This way, we can also communicate the currency symbol to be used.
+ let altUnitNames: [Int : String]?
+}
+
+
+public class CurrencyFormatter: NumberFormatter {
+
+ var hasAltUnitName0: Bool // specs.altUnitNames[0] should have the Symbol ($,€,¥)
+ /// factory
+ static func formatter(scope: ScopeInfo, specs: CurrencySpecification) -> CurrencyFormatter {
+ let formatter = CurrencyFormatter()
+ formatter.setCode(to: scope.currency)
+ formatter.setMinimumFractionDigits(specs.fractionalTrailingZeroDigits)
+ if let symbol = specs.altUnitNames?[0] {
+ formatter.setSymbol(to: symbol)
+ formatter.hasAltUnitName0 = true
+ }
+ return formatter
+ }
+
+ public override init() {
+ self.hasAltUnitName0 = false
+ super.init()
+ self.locale = Locale.current
+ self.usesGroupingSeparator = true
+ self.numberStyle = .currencyISOCode // .currency
+ self.maximumFractionDigits = 8 // ensure that formatter will not round
+
+// self.currencyCode = code // EUR, USD, JPY, GBP
+// self.currencySymbol = symbol
+// self.internationalCurrencySymbol =
+// self.minimumFractionDigits = fractionDigits
+// self.maximumFractionDigits = fractionDigits
+// self.groupingSize = 3 // thousands
+// self.groupingSeparator = ","
+// self.decimalSeparator = "."
+ }
+
+ func setUseSymbol(_ useSymbol: Bool) {
+ numberStyle = useSymbol ? .currency : .currencyISOCode
+ }
+
+ func setCode(to code:String) {
+ currencyCode = code
+ }
+
+ func setSymbol(to symbol:String) {
+ currencySymbol = symbol
+ }
+
+ func setMinimumFractionDigits(_ digits: Int) {
+ minimumFractionDigits = digits
+ }
+
+ func setLocale(to newLocale: String) {
+ locale = Locale(identifier: newLocale)
+ maximumFractionDigits = 8 // ensure that formatter will not round
+// NumberFormatter.RoundingMode
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+}
diff --git a/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift b/TalerWallet1/Views/Peer2peer/PaymentPurpose.swift
@@ -19,12 +19,14 @@ struct PaymentPurpose: View {
@FocusState private var isFocused: Bool
- let formatter = CurrencyFormatter.shared // TODO: based on currency
+// let formatter = CurrencyFormatter.shared // TODO: based on currency
// let buttonFont: Font = .talerTitle2
private var label: String {
- let mag = pow(10, formatter.maximumFractionDigits)
- return formatter.string(for: Decimal(centsToTransfer) / mag) ?? ""
+// let mag = pow(10, formatter.maximumFractionDigits)
+// return formatter.string(for: Decimal(centsToTransfer) / mag) ?? ""
+
+ return String(centsToTransfer / 100)
}
var body: some View {
diff --git a/TalerWallet1/Views/Peer2peer/SendPurpose.swift b/TalerWallet1/Views/Peer2peer/SendPurpose.swift
@@ -18,11 +18,13 @@ struct SendPurpose: View {
@Binding var expireDays: UInt
// var deactivateAction: () -> Void
- let formatter = CurrencyFormatter.shared // TODO: based on currency
+// let formatter = CurrencyFormatter.shared // TODO: based on currency
private var value: String {
- let mag = pow(10, formatter.maximumFractionDigits)
- return formatter.string(for: Decimal(centsToTransfer) / mag) ?? ""
+// let mag = pow(10, formatter.maximumFractionDigits)
+// return formatter.string(for: Decimal(centsToTransfer) / mag) ?? ""
+
+ return String(centsToTransfer / 100)
}
var body: some View {
diff --git a/taler-swift/Sources/taler-swift/Amount.swift b/taler-swift/Sources/taler-swift/Amount.swift
@@ -38,34 +38,6 @@ enum AmountError: Error {
case divideByZero
}
-public struct CurrencySpecification: Codable, Sendable {
- enum CodingKeys: String, CodingKey {
- case decimalSeparator = "decimal_separator"
- case name = "name"
- case fractionalInputDigits = "num_fractional_input_digits"
- case fractionalNormalDigits = "num_fractional_normal_digits"
- case fractionalTrailingZeroDigits = "num_fractional_trailing_zero_digits"
- case isCurrencyNameLeading = "is_currency_name_leading"
- case altUnitNames = "alt_unit_names"
- }
- /// e.g. “.” for $ and ¥; “,” for €
- let decimalSeparator: String
- /// some name for this CurrencySpecification
- let name: String
- /// how much digits the user may enter after the decimal separator
- let fractionalInputDigits: Int
- /// €,$,£: 2; some arabic currencies: 3, ¥: 0
- let fractionalNormalDigits: Int
- /// usually same as numFractionalNormalDigits, but e.g. might be 2 for ¥
- let fractionalTrailingZeroDigits: Int
- /// true for “$ 3.50”; false for “3,50 €”
- let isCurrencyNameLeading: Bool
- /// map of powers of 10 to alternative currency names / symbols
- /// must always have an entry under "0" that defines the base name
- /// e.g. "0 => €" or "3 => k€". For BTC, would be "0 => BTC, -3 => mBTC".
- /// This way, we can also communicate the currency symbol to be used.
- let altUnitNames: [Int : String]
-}
/// A value of some currency.
public final class Amount: Codable, Hashable, @unchecked Sendable, CustomStringConvertible { // TODO: @unchecked
@@ -90,9 +62,6 @@ public final class Amount: Codable, Hashable, @unchecked Sendable, CustomStringC
/// The fractional value of the amount (number to the right of the decimal point).
var fraction: UInt32
- /// Additional info for formatting currency strings
- var currencySpecification: CurrencySpecification?
-
public func hash(into hasher: inout Hasher) {
hasher.combine(currency)
if let normalized = try? normalizedCopy() {
@@ -118,9 +87,9 @@ public final class Amount: Codable, Hashable, @unchecked Sendable, CustomStringC
/// The string representation of the value, formatted as "`integer`.`fraction`".
public var valueStr: String {
var decimalSeparator = "."
- if let currencySpecification { // TODO: use locale
- decimalSeparator = currencySpecification.decimalSeparator
- }
+// if let currencySpecification { // TODO: use locale
+// decimalSeparator = currencySpecification.decimalSeparator
+// }
if fraction == 0 {
return "\(integer)"
} else {