Reputation: 6620
We use this function to convert an SKProduct into a localised price per item for our consumable bundles:
static func pricePerUnit(_ product: SKProduct, quantity: NSDecimalNumber) -> String? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.locale = product.priceLocale
let pricePerItem = product.price.dividing(by: quantity)
guard let formattedPricePerItem = numberFormatter.string(from: pricePerItem) else {
return nil
}
return "\(formattedPricePerItem) each"
}
For example, a bundle of 10 items for £9.99 becomes £0.99 for a UK user or $0.99 for a US user.
Ideally, if the amount is less than a single unit of the currency (aka one dollar), we'd like it to display with the minor currency unit (such as cents, pence, etc.).
I can't find a NumberFormatter style for this or any answer elsewhere. Can this be done using NumberFormatter?
Upvotes: 1
Views: 1034
Reputation: 2132
Formatting as cent and pence may not be directly supported by NumberFormatter
but NumberFormatter
is flexible enough so that you can configure it to your wishes. For example, one can create a helper struct that holds 2 NumberFormatter
: one to format it in dollars and pounds, and one to format it in cent and pence.
struct CurrencyFormatter {
private static let defaultFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()
private static let alternativeFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.multiplier = 100
return formatter
}()
static var alternativeCurrencySymbols = [
"USD": "c",
"GBP": "p"
]
static func string(from number: NSNumber, locale: Locale) -> String? {
var formatter = defaultFormatter
if number.isLessThan(1),
let currencyCode = locale.currencyCode,
let alternativeCurrencySymbol = alternativeCurrencySymbols[currencyCode]
{
formatter = alternativeFormatter
formatter.positiveSuffix = alternativeCurrencySymbol
formatter.negativeSuffix = alternativeCurrencySymbol
}
formatter.locale = locale
return formatter.string(from: number)
}
}
let number = NSNumber(value: 0.7)
let locale = Locale(identifier: "en_GB")
if let str = CurrencyFormatter.string(from: number, locale: locale) {
print(str) // result: 70p
}
The solution has a built-in fail safe mechanism. If you haven't defined the alternative symbol for a currency, it will fall back to the default currency format. You can test it by changing the locale to fr_FR
and the result becomes 0,70 €
.
Upvotes: 1
Reputation: 130072
Unfortunately, this cannot be done using NumberFormatter
. NumberFormatter
uses Locale
to get currency format, including the number of decimal digits, positive patterns, negative patterns, currency symbols etc.
(see Number Format Patterns in Unicode)
However, the format for minor currencies is not standardized in Unicode (although some necessary data are, e.g. multiplicators) and it's not present in iOS Locale
data.
Upvotes: 2