Created
February 25, 2020 04:57
-
-
Save iwasrobbed/f32c01c6965665f5823d0e0c683867e2 to your computer and use it in GitHub Desktop.
Variant of https://stackoverflow.com/a/55518562/308315
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
final class CurrencySymbol { | |
static let shared = CurrencySymbol() | |
/// Finds the shortest currency symbol possible and formats the amount with it | |
/// Note: this works around using `currencyCode` and how it displays `CA$1234.56` instead of `$1234.56` | |
func currencyString(for amount: Decimal, isoCurrencyCode: String?) -> String { | |
guard let isoCurrencyCode = isoCurrencyCode, | |
let currencySymbol = findSymbol(for: isoCurrencyCode) | |
else { return String(describing: amount) } | |
return formatter(for: currencySymbol).string(for: amount) ?? String(describing: amount) | |
} | |
private var symbolCache = [String: String]() | |
private var formatterCache = [String: NumberFormatter]() | |
} | |
private extension CurrencySymbol { | |
func formatter(for currencySymbol: String) -> NumberFormatter { | |
if let cachedFormatter = formatterCache[currencySymbol] { return cachedFormatter } | |
let formatter = NumberFormatter() | |
formatter.currencySymbol = currencySymbol | |
formatter.numberStyle = .currency | |
formatterCache[currencySymbol] = formatter | |
return formatter | |
} | |
func findSymbol(for currencyCode: String) -> String? { | |
if let cachedCurrencyCode = symbolCache[currencyCode] { return cachedCurrencyCode } | |
guard currencyCode.count < 4 else { return nil } | |
let symbol = findShortestSymbol(for: currencyCode) | |
symbolCache[currencyCode] = symbol | |
return symbol | |
} | |
func findShortestSymbol(for currencyCode: String) -> String? { | |
var candidates = [String]() | |
for localeId in NSLocale.availableLocaleIdentifiers { | |
guard let symbol = findSymbolBy(localeId: localeId, currencyCode: currencyCode) else { continue } | |
if symbol.count == 1 { return symbol } | |
candidates.append(symbol) | |
} | |
return candidates.sorted(by: { $0.count < $1.count }).first // find the shorted | |
} | |
func findSymbolBy(localeId: String, currencyCode: String) -> String? { | |
let locale = Locale(identifier: localeId) | |
guard let localCurrencyCode = locale.currencyCode else { return nil } | |
return currencyCode.caseInsensitiveCompare(localCurrencyCode) == .orderedSame ? locale.currencySymbol : nil | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks @iwasrobbed!. I added a little check on the
cachedFormatter
condition, just to take into account the cases when the user changes their region and thus to return the correct symbol position: