Get currency symbols from currency code with swift

前端 未结 11 625
星月不相逢
星月不相逢 2020-12-24 12:21

How can I get the currency symbols for the corresponding currency code with Swift (macOS).

Example:

  • EUR = €1.00
  • USD = $1.00<
11条回答
  •  伪装坚强ぢ
    2020-12-24 13:12

    The answer may be late but hopefully this helps clarify the root cause.

    Problem

    • Currency code does not imply locale and region

    The reason why CAD becomes CA$ is probably because NSLocale looks up the first matching currency code, and for CAD, these are the matching localeIdentifiers in order of NSLocale.availableLocaleIdentifiers

    1. Optional("CA$") Optional("CA") iu_CA
    2. Optional("$") Optional("CA") fr_CA
    3. Optional("$") Optional("CA") en_CA
    

    iu_CA is Inuktitut but I'm not sure why it's listed as CA$, but I hope the point is clear.

    Similarly in CNY (Chinese Yuan):

    1. Optional("CN¥") Optional("CN") en_CN
    2. Optional("¥") Optional("CN") yue_CN
    3. Optional("¥") Optional("CN") bo_CN
    4. Optional("¥") Optional("CN") zh_CN
    5. Optional("¥") Optional("CN") ug_CN
    6. Optional("¥") Optional("CN") ii_CN
    

    The reason for showing CN¥ when en_CN is probably because JPY also uses ¥.

    In CHF (Switzerland Franc), they do not have a one-letter symbol:

    1. Optional("CHF") Optional("LI") gsw_LI
    2. Optional("CHF") Optional("CH") de_CH
    ...
    9. Optional("CHF") Optional("CH") en_CH
    10. Optional("CHF") Optional("CH") it_CH
    

    Solution

    Many apps vary, but this is the steps I took that I am happy with for my application:

    1. Find matching locale candidates using currency code lookup from all locale identifiers
    2. Pick the shortest symbol from the candidates
    3. Store the symbol somewhere so that it doesn't have to be computed each time

    Implementation

    func getSymbolForCurrencyCode(code: String) -> String {
        var candidates: [String] = []
        let locales: [String] = NSLocale.availableLocaleIdentifiers
        for localeID in locales {
            guard let symbol = findMatchingSymbol(localeID: localeID, currencyCode: code) else {
                continue
            }
            if symbol.count == 1 {
                return symbol
            }
            candidates.append(symbol)
        }
        let sorted = sortAscByLength(list: candidates)
        if sorted.count < 1 {
            return ""
        }
        return sorted[0]
    }
    
    func findMatchingSymbol(localeID: String, currencyCode: String) -> String? {
        let locale = Locale(identifier: localeID as String)
        guard let code = locale.currencyCode else {
            return nil
        }
        if code != currencyCode {
            return nil
        }
        guard let symbol = locale.currencySymbol else {
            return nil
        }
        return symbol
    }
    
    func sortAscByLength(list: [String]) -> [String] {
        return list.sorted(by: { $0.count < $1.count })
    }
    

    Usage

    let usd = getSymbolForCurrencyCode(code: "USD")
    let jpy = getSymbolForCurrencyCode(code: "JPY")
    let cny = getSymbolForCurrencyCode(code: "CNY")
    let cad = getSymbolForCurrencyCode(code: "CAD")
    let uah = getSymbolForCurrencyCode(code: "UAH")
    let krw = getSymbolForCurrencyCode(code: "KRW")
    let zar = getSymbolForCurrencyCode(code: "ZAR")
    let chf = getSymbolForCurrencyCode(code: "CHF")
    let all = [usd, jpy, cny, cad, uah, krw, zar, chf]
    
    (lldb) po all
    ▿ 8 elements
      - 0 : "$"
      - 1 : "¥"
      - 2 : "¥"
      - 3 : "$"
      - 4 : "₴"
      - 5 : "₩"
      - 6 : "R"
      - 7 : "CHF"
    

    Problems

    1. Instinctively, I see that the one letter symbol approach can show an incorrect symbol if there are more than one distinct symbols for currency code, but I haven't seen such case.
    2. Computing this each time is heavy lifting so when a user sets their currency setting, it's wise to store the computed result and use that result upon each lookup

提交回复
热议问题