Skip to content

Instantly share code, notes, and snippets.

@iwasrobbed
Created February 25, 2020 04:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iwasrobbed/f32c01c6965665f5823d0e0c683867e2 to your computer and use it in GitHub Desktop.
Save iwasrobbed/f32c01c6965665f5823d0e0c683867e2 to your computer and use it in GitHub Desktop.
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
}
}
@albertoramonj
Copy link

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:

if let cachedFormatter = formatterCache[currencySymbol],
   cachedFormatter.locale == Locale.current {
    return cachedFormatter
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment