taler-ios

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

commit 7efecc640d3418f65aea51c8b93135283e10c7fd
parent e937aac39f15f6453563b3109613540e0d7a4977
Author: Marc Stibane <marc@taler.net>
Date:   Tue, 24 Oct 2023 22:37:28 +0200

DD51 - CurrencySpecification

Diffstat:
MTalerWallet1/Helper/CurrencySpecification.swift | 119++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
1 file changed, 72 insertions(+), 47 deletions(-)

diff --git a/TalerWallet1/Helper/CurrencySpecification.swift b/TalerWallet1/Helper/CurrencySpecification.swift @@ -10,53 +10,78 @@ public struct CurrencyInfo { let specs: CurrencySpecification let formatter: CurrencyFormatter - func string(for value: Double, useSymbol: Bool = true) -> String { + /// returns all characters left from the decimalSeparator + func integerPartStr(_ integerStr: String, decimalSeparator: String) -> String { + if let integerIndex = integerStr.endIndex(of: decimalSeparator) { + // decimalSeparator was found ==> return all characters left of it + return String(integerStr[..<integerIndex]) + } + guard let firstChar = integerStr.first else { return "" } // TODO: should NEVER happen! Show error + let digitSet = CharacterSet.decimalDigits + if digitSet.contains(firstChar) { + // Currency Symbol is after the amount ==> return only the digits + return String(integerStr.unicodeScalars.filter { digitSet.contains($0) }) + } else { + // Currency Symbol is in front of the amount ==> return everything + return integerStr + } + } + + func string(for valueTuple: (Double, Double), useSymbol: Bool = true) -> String { formatter.setUseSymbol(useSymbol) - if let valueStr = formatter.string(for: value) { - let decimalSeparator = formatter.decimalSeparator ?? specs.decimalSeparator - if let decimalIndex = valueStr.endIndex(of: decimalSeparator) { - var fraction = 1 - var integerStr = String(valueStr[..<decimalIndex]) - let fractionStr = valueStr[decimalIndex...] - for character in fractionStr { - let charStr = String(character) - if let digit = Int(charStr) { - let digitStr = fraction > specs.fractionalNormalDigits ? - SuperScriptDigits(charStr) : charStr - integerStr += digitStr - } else { integerStr += charStr } - fraction += 1 + let (integer, fraction) = valueTuple + if let integerStr = formatter.string(for: integer) { + if fraction == 0 { return integerStr } // TODO: add trailing zeroes + if let fractionStr = formatter.string(for: fraction) { + if let decimalSeparator = formatter.currencyDecimalSeparator { + if let fractionIndex = fractionStr.endIndex(of: decimalSeparator) { + var fractionPartStr = String(fractionStr[fractionIndex...]) + var resultStr = integerPartStr(integerStr, decimalSeparator: decimalSeparator) + if !resultStr.contains(decimalSeparator) { + resultStr += decimalSeparator + } +// print(resultStr, fractionPartStr) + var fractionCnt = 1 + for character in fractionPartStr { + let isSuper = fractionCnt > specs.fractionalNormalDigits + let charStr = String(character) + if let digit = Int(charStr) { + let digitStr = isSuper ? SuperScriptDigits(charStr) : charStr + resultStr += digitStr + if (fractionCnt > 0) { fractionCnt += 1 } + } else { + // probably the Currency Code or Symbol. Just pass it on... + resultStr += charStr + // make sure any following digits (part of the currency name) are not converted to SuperScript + fractionCnt = 0 + } + } +// print(resultStr) + return resultStr + } + // if we arrive here then fractionStr doesn't have a decimal separator. Yikes! } - return integerStr + // if we arrive here then the formatter doesn't have a currencyDecimalSeparator } - return valueStr - } else { // formatter doesn't work - we need to format ourselves - var madeUpStr = "" - var currencyName = scope.currency - var hasSymbol = false - if useSymbol && formatter.hasAltUnitName0 { + // if we arrive here then we do not get a formatted string for fractionStr. Yikes! + } + // if we arrive here then we do not get a formatted string for integerStr. Yikes! + // TODO: log.error(formatter doesn't work) + // we need to format ourselves + var currencyName = scope.currency + if useSymbol { + if formatter.hasAltUnitName0 { if let symbol = specs.altUnitNames?[0] { currencyName = symbol - hasSymbol = true } } - if specs.isCurrencyNameLeading { - madeUpStr = currencyName - if !hasSymbol { - madeUpStr += " " - } - } - let integerPart = Int(value) - madeUpStr += String(integerPart) - madeUpStr += specs.decimalSeparator - if !specs.isCurrencyNameLeading { - if !hasSymbol { - madeUpStr += " " - } - madeUpStr += currencyName - } - return madeUpStr - } // DIY + } + var madeUpStr = currencyName + " " + String(integer) +// let homeCurrency = Locale.current.currency //'currency' is only available in iOS 16 or newer + madeUpStr += Locale.current.decimalSeparator ?? "." // currencyDecimalSeparator + madeUpStr += String(String(fraction).dropFirst()) // remove the leading 0 + // TODO: fractionalNormalDigits, fractionalTrailingZeroDigits + return madeUpStr } } @@ -67,20 +92,20 @@ public struct CurrencySpecification2: Codable, Sendable { public struct CurrencySpecification: Codable, Sendable { enum CodingKeys: String, CodingKey { case name = "name" - case decimalSeparator = "decimal_separator" - case groupSeparator = "group_separator" +// 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 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? +// let decimalSeparator: String taken from Locale.current + /// e.g. “,” for $, and “.” or “ ” for € (France uses a narrow space character, Hungaria a normal one) +// let groupSeparator: String? taken from Locale.current /// how much digits the user may enter after the decimal separator let fractionalInputDigits: Int /// €,$,£: 2; some arabic currencies: 3, ¥: 0 @@ -88,7 +113,7 @@ public struct CurrencySpecification: Codable, Sendable { /// 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 +// 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".