From 44393a0c4a33b09d9566f948dd46cb748704f584 Mon Sep 17 00:00:00 2001 From: Heejin Jung <54030948+kes02@users.noreply.github.com> Date: Wed, 5 Aug 2026 19:45:16 +0900 Subject: [PATCH] feat: add KRW to the preferred currency picker KRW was the one major Asian currency missing from the picker added in #2490, so won-billed users converted USD estimates by hand. No new exchange-rate logic is needed: fetchLatestRatesIfNeeded already merges every rate in the open.er-api.com payload, which carries KRW. Listing the code in supportedCurrencies is what opens the requiresLiveRates gate, so selecting KRW triggers the same live-fetch and 24h-cache path as the existing currencies. The hardcoded fallback rate is only read before the first successful fetch. KRW is zero-decimal and currencyString pins en_US, so ICU resolves the fraction digits and won renders without a fractional separator on any host. Refs #2449 Co-Authored-By: Claude Opus 5 (1M context) --- Sources/CodexBar/PreferencesGeneralPane.swift | 2 ++ Sources/CodexBarCore/CurrencyExchange.swift | 3 ++- Tests/CodexBarTests/UsageFormatterTests.swift | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/CodexBar/PreferencesGeneralPane.swift b/Sources/CodexBar/PreferencesGeneralPane.swift index db9f290da2..d8750a5c1f 100644 --- a/Sources/CodexBar/PreferencesGeneralPane.swift +++ b/Sources/CodexBar/PreferencesGeneralPane.swift @@ -82,6 +82,7 @@ enum PreferredCurrencyOption: String, CaseIterable, Identifiable { case eur = "EUR" case cny = "CNY" case jpy = "JPY" + case krw = "KRW" case cad = "CAD" case aud = "AUD" case hkd = "HKD" @@ -101,6 +102,7 @@ enum PreferredCurrencyOption: String, CaseIterable, Identifiable { case .eur: "EUR (€)" case .cny: "CNY (¥)" case .jpy: "JPY (¥)" + case .krw: "KRW (₩)" case .cad: "CAD ($)" case .aud: "AUD ($)" case .hkd: "HKD ($)" diff --git a/Sources/CodexBarCore/CurrencyExchange.swift b/Sources/CodexBarCore/CurrencyExchange.swift index b6e47a5301..bf8b96d9a2 100644 --- a/Sources/CodexBarCore/CurrencyExchange.swift +++ b/Sources/CodexBarCore/CurrencyExchange.swift @@ -15,7 +15,7 @@ public final class CurrencyExchange: @unchecked Sendable { /// All currency codes supported by the converter. public static let supportedCurrencies: [String] = [ - "USD", "GBP", "EUR", "CNY", "JPY", "CAD", "AUD", "HKD", "TWD", "SGD", "INR", + "USD", "GBP", "EUR", "CNY", "JPY", "KRW", "CAD", "AUD", "HKD", "TWD", "SGD", "INR", ] private let lock = NSLock() @@ -27,6 +27,7 @@ public final class CurrencyExchange: @unchecked Sendable { "EUR": 0.92, "CNY": 7.27, "JPY": 154.0, + "KRW": 1428.90, "CAD": 1.38, "AUD": 1.55, "HKD": 7.80, diff --git a/Tests/CodexBarTests/UsageFormatterTests.swift b/Tests/CodexBarTests/UsageFormatterTests.swift index bc03694e77..b68b64ac57 100644 --- a/Tests/CodexBarTests/UsageFormatterTests.swift +++ b/Tests/CodexBarTests/UsageFormatterTests.swift @@ -511,6 +511,12 @@ struct UsageFormatterTests { let explicitCNY = UsageFormatter.convertedCostString(10.0, preferredCurrency: "CNY", providerCurrency: "USD") #expect(explicitCNY.contains("¥")) + let krwRate = exchange.rate(for: "KRW") ?? 1428.90 + #expect(abs((exchange.convert(usdAmount: 10.0, to: "KRW") ?? 0) - 10.0 * krwRate) < epsilon) + let explicitKRW = UsageFormatter.convertedCostString(10.0, preferredCurrency: "KRW", providerCurrency: "USD") + #expect(explicitKRW.contains("₩")) + #expect(!explicitKRW.contains(".")) + #expect(exchange.convert(amount: 10.0, from: "CHF", to: "USD") == nil) let unavailable = UsageFormatter.convertedCostString( 10.0, @@ -528,6 +534,7 @@ struct UsageFormatterTests { #expect(!CurrencyExchange.requiresLiveRates(preferredCurrencyCode: "CHF")) #expect(CurrencyExchange.requiresLiveRates(preferredCurrencyCode: "GBP")) #expect(CurrencyExchange.requiresLiveRates(preferredCurrencyCode: " eur ")) + #expect(CurrencyExchange.requiresLiveRates(preferredCurrencyCode: "KRW")) } @Test