NSDecimal number multiplication - ios

I am trying to do some calculations of text fields I got it to work using Doubles:
let firstValue = Double(miles.text!)
let secondValue = Double(payPerMile.text!)
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
formatter.locale = Locale.current
formatter.usesGroupingSeparator = true
let result = Double(firstValue! * secondValue!) as NSNumber
grossPay.text = "\(formatter.string(from: result)!)"
While doing more research on the subject I was informed that using Doubles is not the best route to go so I am trying to use NSDecimalNumber instead.
My problem is I can't figure out how to get it to work when switching over.
#IBAction func miles(_ sender: Any) {
let firstValue = NSDecimalNumber(string: miles.text)
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.usesGroupingSeparator = true
miles.text = "\(formatter.string(from: firstValue as NSNumber)!)"
}
#IBAction func payPerMile(_ sender: Any) {
let firstValue = NSDecimalNumber(string: payPerMile.text)
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
formatter.locale = Locale.current
formatter.usesGroupingSeparator = true
payPerMile.text = "\(formatter.string(from: firstValue as NSNumber)!)"
}
#IBAction func grossPay(_ sender: Any) {
let firstValue = NSDecimalNumber(string: miles.text!)
let secondValue = NSDecimalNumber(string: payPerMile.text!)
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
formatter.locale = Locale.current
formatter.usesGroupingSeparator = true
let result = (firstValue as Decimal) * (secondValue as Decimal) as NSDecimalNumber
grossPay.text = "\(formatter.string(from: result)!)"
just gives me the result of NaN
miles.text is formatted to .decimal
payPerMile.text is formatted to .currency
I am not sure if that could be part of my problem or not.

If payPerMile is formatted as .currency, then it probably contains a currency-character. You cannot simply create an NSDecimalNumber from such a string.
Your second line gives you a NaN for secondValue.
What you can do, is use an NumberFormatter to get the NSNumber from the string.
let formatter = NumberFormatter()
formatter.numberStyle = .currency
let payPerMileNumber = formatter.number(from: "$34.75") // NSNumber with 34.75

Related

Localized currency symbol from NumberFormatter

how to set NumberFormatter to return currency symbol not the iso one but local which is visible in iOS locale settings pane. Like for Polish currency I need to have “zł” symbol not the “PLN”.
I cannot find any way to get it from system as this cannot be hardcoded. IAP localized price also uses “zł” not “PLN”
I tried this way:
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = Locale.current.currencySymbol
formatter.maximumFractionDigits = 2
let price = formatter.string(from: (offeringPrice / 12) as NSNumber) ?? ""
but whatever I try to use as currency symbol I always get in return "PLN"
If you set formatter.locale to pl_PL it works:
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 2
formatter.currencyCode = "PLN"
let polandLocale = Locale(identifier: "pl_PL")
let usLocale = Locale(identifier: "en_US")
let offeringPrice = 50.0
let price = (offeringPrice / 12) as NSNumber
formatter.locale = usLocale
formatter.string(from: price) // "PLN 4.17"
formatter.locale = polandLocale
formatter.string(from: price) // "4,17 zł"
I've found this solution
func getCurrencySymbol(from currencyCode: String) -> String? {
let locale = NSLocale(localeIdentifier: currencyCode)
if locale.displayName(forKey: .currencySymbol, value: currencyCode) == currencyCode {
let newlocale = NSLocale(localeIdentifier: currencyCode.dropLast() + "_en")
return newlocale.displayName(forKey: .currencySymbol, value: currencyCode)
}
return locale.displayName(forKey: .currencySymbol, value: currencyCode)
}
original answer here: https://stackoverflow.com/a/49146857/1285959

How to make NumberFormatter() result readable?

I have function with number formatter.
When user enter a number in textField it's formatting and shows with additional zeros.
Example: I entered "15.45". My result "15.4500000000".
How could I make work formatter to hide 0s when the number haven't any signs after?
Example: I entered "15.45". My result is "15.45 x 0.085" = "1.31325" (Not "1.313250000000").
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
formatter.minimumSignificantDigits = 10
if let text = textField.text, let number = formatter.number(from: text) {
year = number.doubleValue
yearLabel.text = formatter.string(from: NSDecimalNumber(value: year).multiplying(by: 1))
}
Try not setting minimumSignificantDigits to 10, just delete that line.
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 10
if let number = formatter.number(from: "1.123456789") {
formatter.string(from: number)
}
Just change
formatter.minimumSignificantDigits = 10
to
formatter.maximumFractionDigits = 10

How to use currencyFormatter with a decimal

I'm trying to take a decimal I'm storing in CoreData and run it through the currency formatter in Swift 3. Here is what I'm trying to use:
var currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = NumberFormatter.Style.currency
// localize to your grouping and decimal separator
currencyFormatter.locale = NSLocale.current
var priceString = currencyFormatter.stringFromNumber(NSNumber(totalAmount))
Where totalAmount is the decimal I'm using for CoreData.
But . I get this error when trying to convert my decimal to a NSNumber()
Argument labels '(_:)' do not match any available overloads
stringFromNumber got renamed to string(from:), e.g.
var priceString = currencyFormatter.string(from: NSNumber(totalAmount))
but you don't have to convert to NSNumber
var priceString = currencyFormatter.string(for: totalAmount)
You can have something like:
class YourClass: UIViewController {
static let priceFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.formatterBehavior = .behavior10_4
formatter.numberStyle = .currency
return formatter
}()
}
Usage:
yourLabel.text = YourClass.priceFormatter.string(from: totalAmount)

Changing grouping separator, currency symbol and position of currency symbol for multiple currencies in Swift

My app uses multiple currencies, and these currencies uses different formats, for example:
Price for Ruble shows as: 1,101 Руб.
Same amount for US Dollar shows as: US $1 101
How would I change the grouping separator, currency symbol and position of currency symbol, by defining a set of different formats for different currencies.
This is how my short code stands
var formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.locale = NSLocale.currentLocale()
formatter.stringFromNumber(4500000)
//Output : $4,500,000.00
//Expected : 4,500,000 Руб.
Swift 4 or later
extension Formatter {
static let belarusianRuble: NumberFormatter = {
let formatter = NumberFormatter()
// set the numberStyle to .CurrencyStyle
formatter.numberStyle = .currency
// set the desired negative and positive formats grouping, and currency symbol position
formatter.positiveFormat = "#,##0 ¤"
formatter.negativeFormat = "-#,##0 ¤"
// set your custom currency symbol
formatter.currencySymbol = "Руб"
return formatter
}()
}
let stringToDisplay = Formatter.belarusianRuble.string(for: 4500000) // "4,500,000 Руб"
extension Formatter {
static let currencyBYR: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.positiveFormat = "#,##0 ¤"
formatter.negativeFormat = "-#,##0 ¤"
formatter.currencySymbol = "Руб"
return formatter
}()
static let currencyEUR: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "pt_PT")
formatter.numberStyle = .currency
return formatter
}()
static let currencyUSD: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_US")
formatter.numberStyle = .currency
return formatter
}()
static let currencyBRL: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "pt_BR")
formatter.numberStyle = .currency
return formatter
}()
static let currencyRUB: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 0
return formatter
}()
static let currencyLocale: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = .current
formatter.numberStyle = .currency
return formatter
}()
}
extension Numeric {
var currencyLocale: String { return Formatter.currencyLocale.string(for: self) ?? "" }
var currencyUSD: String { return Formatter.currencyUSD.string(for: self) ?? "" }
var currencyEUR: String { return Formatter.currencyEUR.string(for: self) ?? "" }
var currencyBYR: String { return Formatter.currencyBYR.string(for: self) ?? "" }
var currencyBRL: String { return Formatter.currencyBRL.string(for: self) ?? "" }
var currencyRUB: String { return Formatter.currencyRUB.string(for: self) ?? "" }
}
Usage
let amount = 4500000.0
let stringLocale = amount.currencyLocale // "$4,500,000.00"
let stringUSD = amount.currencyUSD // "$4,500,000.00"
let stringEUR = amount.currencyEUR // "4 500 000,00 €"
let stringBRL = amount.currencyBRL // "R$ 4.500.000,00"
let stringBYR = amount.currencyBYR // "4,500,000 Руб"
let stringRUB = amount.currencyRUB // "4 500 000 ₽"
I ended up with Currency class that uses current locale decimal/thousands/grouping separators (thanks to #jcaron recommendation in comments)
The class offers few customization, which fits my task:
Currency symbol
Minimum/maximum fraction digits
Positioning symbol either left/right
class Currency {
var formatter = NumberFormatter()
var symbol: String
var isRTL: Bool
init(_ currencySymbol: String, minFractionDigits: Int, maxFractionDigits: Int, isRTL: Bool) {
self.formatter.currencySymbol = ""
self.formatter.minimumFractionDigits = minFractionDigits
self.formatter.maximumFractionDigits = maxFractionDigits
self.formatter.numberStyle = .currency
self.symbol = currencySymbol
self.isRTL = isRTL
}
func beautify(_ price: Double) -> String {
let str = self.formatter.string(from: NSNumber(value: price))!
if self.isRTL {
return str + self.symbol
}
return self.symbol + str
}
}
Initialize required formatters
struct CurrencyFormatter {
static let byr = Currency(" Руб.", minFractionDigits: 2, maxFractionDigits: 2, isRTL: true)
static let usd = Currency("US $", minFractionDigits: 2, maxFractionDigits: 2, isRTL: false)
static let rub = Currency("\u{20BD} ", minFractionDigits: 0, maxFractionDigits: 1, isRTL: false)
}
Usage
CurrencyFormatter.byr.beautify(12345.67) // Output: 12 345,67 Руб.
CurrencyFormatter.usd.beautify(12345.67) // Output: US $12 345,67
CurrencyFormatter.rub.beautify(12345.67) // Output: ₽ 12 345,7

Format NSString using NSFormatter

I am getting price of an object in string and I want to formate it as currency.
I am using the following code:
var priceString : NSString = urlDict.objectForKey("price") as NSString
var formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.locale = NSLocale.currentLocale()
let priceNumber = formatter.numberFromString(priceString)
I am getting the data fine in priceString but priceNumber is nil.
And if I try this:
var priceNumber : NSNumber = urlDict.objectForKey("price") as NSNumber
then also the priceNumber is nil.
What approach should I take to achieve a formatted price from the string?
Thanks all for the suggestions:
I have combined above solutions to form a function:
func formatAsPrice(priceString: NSString) -> NSString {
let rsSymbol = "\u{20B9}" // The currency symbol for India
var priceStrTemp = priceString
priceStrTemp = rsSymbol + priceStrTemp
var formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.locale = NSLocale.currentLocale()
formatter.locale = NSLocale(localeIdentifier: "en_IN") //Explicit
formatter.secondaryGroupingSize = 2 // grouping as Indian currency style
let priceNumber = formatter.numberFromString(priceStrTemp)!
var finalPrice: NSString! = (rsSymbol + " \(priceNumber)") as NSString!
return finalPrice
}
The Formatter code is fine.
You have to pass correct Currency string with currency symbol
func format(amount: String) -> NSNumber? {
let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.locale = NSLocale.currentLocale()
return formatter.numberFromString(amount)
}
format("NOK 12,00") // returns 12.00
format("NOK 12") // Also fine
format("$ 12") // Error, my local currency is NOK (Norwegian krone)
format("12,00") // Error, needs currency symbol in the string.
If you want to parse trying without currency sublime like this "12,00" that you should set currencySymbol to empty string. Like this
func format(amount: String) -> NSNumber? {
let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.currencySymbol = ""
...
}
format("12,00") // Works fine now

Resources