How can I format a SKProduct price into a Swift currency string?
Asked Answered
T

2

6

If you have code such as this, then p will be "2.99":

let price = 2.99
let p = String(format: "%.2f", price)

However, if you have code like this:

let priceNS: NSDecimalNumber = 2.99
let p2 = String(format: "%.2f", priceNS)

Then p2 is "0.00".

How can you format an NSDecimalNumber into a string like this? (NSDecimalNumber is how the price in an SKProduct is stored)

Tetradymite answered 3/10, 2017 at 22:22 Comment(6)
You should us Decimal rather than NSDecimalNumber in Swift.Woozy
@Woozy it comes from Apple StoreKit API price propertySpurlock
@LeoDabus They're bridgedWoozy
@Woozy how does that help in code such as: String(format: "%.2f", someSKProduct.price)Tetradymite
@Sausagedioxide It doesn't, but it helps in other situations by removing potentially shared mutable stateWoozy
Do not hardcode this format for currency values.Rothenberg
S
7

You should format your product price using NumberFormatter and use your product locale: https://developer.apple.com/documentation/storekit/skproduct/1506094-price

let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency 
numberFormatter.locale = someSKProduct.priceLocale
let formattedPrice = numberFormatter.string(from: someSKProduct.price) ?? ""
Spurlock answered 3/10, 2017 at 23:6 Comment(6)
SKProduct really contains a "price locale" instead of a currency code? That's pretty weird. The product shouldn't determine if e.g. I use . or , for the decimal separator.Governess
@KevinBallard I don't think thats a problem considering that the user doesn't need to input any price.Spurlock
It affects display, not input. If the user has their device set to French, and is purchasing something in USD, it should show up as e.g. 2,99 $US. But if the product.priceLocale is en_US and you set that on the formatter, they'll see $2.99 instead of 2,99 $US.Governess
It will always display using the locale from where the App is being bought. If I buy a product here in Brazil from US AppStore it will display $0.99 not R$0,99Spurlock
@KevinBallard "French" isn't a locale. Do you mean a locale of fr_FR, fr_CA, fr_US, or one of many other possibilities? The language part of the locale will have no real affect on the output of a currency value. It's the region part of the locale that matters. Either way, the priceLocale of the product should be appropriate for the user's device and the product's currency.Rothenberg
True, I used "French" as shorthand for fr_FR. In any case, it's certainly possible for priceLocale to be something like fr_FR@currency=USD, but if that's what it is, then why is it exposed as a locale at all instead of just a currency code?Governess
C
2

You can use a NumberFormatter to convert an NSNumber to a String.

let priceNS:NSDecimalNumber = 2.99
let nf = NumberFormatter()
nf.maximumFractionDigits = 2
nf.string(from: priceNS) //2.99
Coequal answered 3/10, 2017 at 22:27 Comment(3)
Using the .currency style is only appropriate if you want the currency symbol (e.g. $) to show up. If you do, leave the locale alone and set the currencyCode property instead.Governess
Depends on how the price is being shown. Maybe OP is already showing it in a context where the user knows the currency?Governess
In any case, to expand on my previous comment, the locale affects formatting, and the currencyCode affects the actual currency. Don't set the locale just to force USD, that will not work properly. Besides formatting differences, this will make it show up as e.g. $2.99 in Canada when it should be showing up as US$2.99. If you leave the locale alone and set nf.currencyCode = "USD" then you'll get the correct currency display regardless of user locale.Governess

© 2022 - 2024 — McMap. All rights reserved.