Make double 0.93 to integer 93 - swift

self.humidityLbl.text = "Humidity: \(self.currentWeather.humidity)%"
outputs Humidity: 0.93%. How can I convert double 0.93 to integer 93 in Swift?

Use a NumberFormatter with percent style to present a percentage
value to the user. This will use the proper format for the user's
locale. Examples:
let humidity = 0.93
let formatter = NumberFormatter()
formatter.numberStyle = .percent
formatter.locale = Locale(identifier: "en") // English
print(formatter.string(for: humidity)!) // 93%
formatter.locale = Locale(identifier: "de") // German
print(formatter.string(for: humidity)!) // 93 %
formatter.locale = Locale(identifier: "tr") // Turkish
print(formatter.string(for: humidity)!) // %93
formatter.locale = Locale(identifier: "ar") // Arabic
print(formatter.string(for: humidity)!) // ٩٣ ٪؜
Here I have set formatter.locale to different values for
demonstration purposes. If you don't assign a value, the user's
default settings are used.

Simple. Multiply by 100 and convert to an Int.
self.humidityLbl.text = "Humidity: \(Int(self.currentWeather.humidity * 100))%"
If you want to get fancy, create a helper extension on Double:
extension Double {
func percent() -> Int {
return Int(self * 100)
}
}
self.humidityLbl.text = "Humidity: \(self.currentWeather.humidity.percent())%"

You can cast to Int after multiplying it by 100, but you can use NSNumber instead:
let intNumber = NSNumber.init(value: 0.93 * 100).intValue
print(intNumber)
I think this is a safer way.

Related

Formatter currency value returns 'US' extra suffix

I am trying to format a currency value like this:
let price: Double = 1
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyCode = "USD"
formatter.locale = Locale(identifier: "en_US")
let priceString = formatter.string(from: NSNumber(value: Double(price / 100)))
But the result I get is "US$ 0,01" instead of "$ 0,01".How can I remove the 'US' extra suffix in front of it?
NumberFormatter is old API. Below is the modern syntax.
You need to find a locale in which "US" is implicit but where people use commas instead of periods, as we do in the US.
0.01.formatted(.currency(code: "USD"))
yields "$0.01" in Locale(identifier: "en_US").
0.01.formatted(
FloatingPointFormatStyle.Currency(code: "USD", locale: .init(identifier: "de_DE"))
)
yields "0,01 $".

Swift lose precision in decimal formatting

I have an precision issue when dealing with currency input using Decimal type. The issue is with the formatter. This is the minimum reproducible code in playground:
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.isLenient = true
formatter.maximumFractionDigits = 2
formatter.generatesDecimalNumbers = true
let text = "89806.9"
let decimal = formatter.number(from: text)?.decimalValue ?? .zero
let string = "\(decimal)"
print(string)
It prints out 89806.89999999999 instead of 89806.9. However, most other numbers are fine (e.g. 8980.9). So I don't think this is a Double vs Decimal problem.
Edit:
The reason I need to use the formatter is that sometimes I need to deal with currency format input:
let text = "$89,806.9"
let decimal = formatter.number(from: text)?.decimalValue ?? .zero
print("\(decimal)") // prints 89806.89999999999
let text2 = "$89,806.9"
let decimal2 = Decimal(string: text2)
print("\(decimal2)") // prints nil
Using the new FormatStyle seems to generate the correct result
let format = Decimal.FormatStyle
.number
.precision(.fractionLength(0...2))
let text = "89806.9"
let value = try! format.parseStrategy.parse(text)
Below is an example parsing a currency using the currency code from the locale
let currencyFormat = Decimal.FormatStyle.Currency
.currency(code: Locale.current.currencyCode!)
.precision(.fractionLength(0...2))
let amount = try! currencyFormat.parseStrategy.parse(text)
Swedish example:
let text = "89806,9 kr"
print(amount)
89806.9
Another option is to use the new init for Decimal that takes a String and a FormatStyle.Currency (or a Number or Percent)
let amount = try Decimal(text, format: currencyFormat)
and to format this value we can use formatted(_:) on Decimal
print(amount.formatted(currencyFormat))
Output (still Swedish):
89 806,9 kr
I agree that this is a surprising bug, and I would open an Apple Feedback about it, but I would also highly recommend switching to Decimal(string:locale:) rather than a formatter, which will achieve your goal (except perhaps the isLenient part).
let x = Decimal(string: text)!
print("\(x)") // 89806.9
If you want to fix fraction digits, you can apply rounding pretty easily with * 100 / 100 conversions through Int. (I'll explain if it's not obvious how to do this; it works for Decimal, though not Double.)
Following Joakim Danielson Answer see this amazing documentation on the format style
Decimal(10.01).formatted(.number.precision(.fractionLength(1))) // 10.0 Decimal(10.01).formatted(.number.precision(.fractionLength(2))) // 10.01 Decimal(10.01).formatted(.number.precision(.fractionLength(3))) // 10.010
Amazingly detailed documentation
If this is strictly a rendering issue and you're just looking to translate a currency value from raw string to formatted string then just do that.
let formatter = NumberFormatter()
formatter.numberStyle = .currency
let raw = "89806.9"
if let double = Double(raw),
let currency = formatter.string(from: NSNumber(value: double)) {
print(currency) // $89,806.90
}
If there is math involved then before you get to the use of string formatters, I would point you to
Why not use Double or Float to represent currency? and
How to round a double to an int using Banker's Rounding in C as great starting points.
I get my response with double value and remove formatter.generatesDecimalNumbers line to get work.
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.isLenient = true
formatter.maximumFractionDigits = 2
//formatter.generatesDecimalNumbers = true // I removed this line
let text = "$89806.9"
let double = formatter.number(from: text)?.doubleValue ?? .zero // converting as double or float
let string = "\(double)"
print(string) // 89806.9
let anotherText = "$0.1"
let anotherDouble = formatter.number(from: anotherText)?.doubleValue ?? .zero // converting as double or float
let anotherString = "\(anotherDouble)"
print(anotherString) // 0.1

What is the best way to get a Swift string into a swift-currency/USD format?

How do I get a string into a 'currency' format USD(xxx)?
I am trying to use the following library:
https://github.com/peek-travel/swift-currency
I want to be able to take a string:
var testStr1 = "$30.01"
var testStr2 = "$ 30.01"
and convert this into a currency as I have read from several posts using a double or float is bad, but if I start with a String, what else can I convert it to?
I thought I could use the "import Currency" library to do this, but this is not working.
let updatedString = testStr1.replacingOccurrences(of: "$", with: "")
let formatter = NumberFormatter()
formatter.locale = Locale.current // USA: Locale(identifier: "en_US")
formatter.numberStyle = .decimal
let number = formatter.number(from: test)
var dollars = USD(updatedString)
How do I get a string into a 'currency' format USD(xxx)? If there is a better way to accomplish this?
The basic concept presented by Himanshu works fine, but your problem isn't necessarily making use of an appropriate formatter, but how to fix your input, as the formatter expects a NSNumber and not a String.
So a quick internet check had me looking at Remove all non-numeric characters from a string in swift
So I could take a String, filter out all the "non numerical" junk and then make a Double out of it.
let input = Double(value.filter("0123456789.".contains))
from there I was able to borrow the concept from Himanshu and make a simple format function
func format(_ value: String, locale: Locale = Locale.current) -> String? {
guard let input = Double(value.filter("0123456789.".contains)) else { return nil }
//value.trimmingCharacters(in: .whitespacesAndNewlines)
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = .currency
currencyFormatter.locale = locale
return currencyFormatter.string(from: NSNumber(value: input))
}
I then made use of a Playground to test it using
var testStr1 = "$30.01"
var testStr2 = "$ 30.01"
format(testStr1, locale: Locale(identifier: "en_US")) // $30.01
format(testStr2, locale: Locale(identifier: "en_US")) // $30.01
format(testStr1, locale: Locale(identifier: "fr_FR")) // 30,01 €
format(testStr2, locale: Locale(identifier: "fr_FR")) // 30,01 €
format(testStr1, locale: Locale(identifier: "de_DE")) // 30,01 €
format(testStr2, locale: Locale(identifier: "de_DE")) // 30,01 €
Now, if you specifically want to use USD(xxx) as the format, then you could simply use a basic NumberFormatter and generate your own String from the resulting conversion of the input to a Double
I have read from several posts using a double or float is bad
So, yes, maintaining a currency value as a Double or Float is generally a bad idea, currency values are typically maintained as a Int or Long, but this is due to how Double and Float representation works in computers, for the, general, presentation, you should be fine, but each use case needs be assessed.
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = .currency
// localize to your grouping and decimal separator
currencyFormatter.locale = Locale.current
// We'll force unwrap with the !, if you've got defined data you may need more
error checking
let priceString = currencyFormatter.string(from: 9999.99)!
print(priceString) // Displays $9,999.99 in the US locale
**Forcing a Custom Locale**
You can override the users locale to display specific currency formats by changing the Locale using the identifier.
currencyFormatter.locale = Locale(identifier: "fr_FR")
if let priceString = currencyFormatter.string(from: 9999.99) {
print(priceString) // Displays 9 999,99 € in the French locale
}
currencyFormatter.locale = Locale(identifier: "de_DE")
if let priceString = currencyFormatter.string(from: 9999.99) {
print(priceString) // Displays 9.999,99 € in the German locale
}

Avoiding non breaking space using NumberFormatter

I have a NumberFormatter setup like the following:
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.groupingSeparator = "."
currencyFormatter.numberStyle = .currency
currencyFormatter.locale = Locale(identifier: "it_IT")
currencyFormatter.currencySymbol = ""
I need to retrieve a value based on a string value as input.
currencyFormatter.number(from: "1.000,00") // nil
currencyFormatter.number(from: "1.000,00\u{00a0}") // 1000
In the first sample, the formatter returns nil, while in the second I obtain the correct value (\u{00a0} is the non breaking space symbol).
Is there a way to make the first sample working without adding the space symbol?
If there is no currency symbol, this number formatter's style needs to be .decimal, not .currency. This is sufficient:
let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .decimal
currencyFormatter.locale = Locale(identifier: "it_IT")

How to format negative 0 with currency NumberFormatter

I noticed my app is displaying "-$0.00" for a value of -0. The simple form of the math is let balance: Double = -1 * value. I want my negative 0 values to just display as "$0.00". Is there a way to have the NumberFormatter properly handle this?
let formatter = NumberFormatter()
formatter.numberStyle = .currency
let d: Double = -1 * 0
formatter.string(from: d as NSNumber)! // -$0.00
If you are working with money, currency etc it is much better to use Decimal than Double and it will also solve your problem here, change the declaration of d as below
let d: Decimal = -1 * 0
and the formatter will produce "$0.00"
My current approach is:
struct Formatter {
static func currency(_ value: Double) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
let value = (value == 0) ? 0 : value // Convert -0 to 0
return formatter.string(from: value as NSNumber) ?? ""
}
}
Formatter.currency(-1 * 0) // $0.00