Swift Number Formatting

Large decimal number formatting using NumberFormatter in Swift

You could create a Decimal explicitly to work around the mentioned bug

let formatter = NumberFormatter()
formatter.numberStyle = .decimal
if let decimalNumber = Decimal(string: "123456789123456789123"), let str = formatter.string(from:decimalNumber as NSNumber) {
print(decimalNumber)
print(str)
}

Swift. How to set custom number format? With space thouthand separator and two digits after point

let d1: Double = 20000000.0
let d2: Double = 1.2345

let formatter = NumberFormatter()
formatter.groupingSeparator = " "
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
formatter.decimalSeparator = "." // Default separator is dependent to the current local.

print(formatter.string(for: d1)) // 20 000 000.00
print(formatter.string(for: d2)) // 1.23

Convert string into currency number format, with, without or with 0 decimal places

You can simply filter non digits or periods from the original string, try to coerce the resulting string to integer. If successful set the formatter maximum fraction digits to zero otherwise set the maximum fraction digits to 2 and coerce the string to double:

extension Formatter {
static let currency: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = .init(identifier: "en_US_POSIX")
formatter.numberStyle = .currencyAccounting
formatter.currencySymbol = ""
return formatter
}()
}


extension Numeric {
var currencyUS: String {
Formatter.currency.string(for: self) ?? ""
}
}


func convertme(string: String) -> String {
let string = string.filter("0123456789.".contains)
if let integer = Int(string) {
Formatter.currency.maximumFractionDigits = 0
return Formatter.currency.string(for: integer) ?? "0"
}
Formatter.currency.maximumFractionDigits = 2
return Double(string)?.currencyUS ?? "0"
}


convertme(string: "100000")     // "100,000"
convertme(string: "100000.00") // "100,000.00"



edit/update:
"100,000." it is not a valid number format. You would need to manually insert your period at the end of the string.

func convertme(string: String) -> String {
var string = string.filter("0123456789.".contains)
// this makes sure there is only one period and keep only the last one in the string
while let firstIndex = string.firstIndex(of: "."),
let _ = string[firstIndex...].dropFirst().firstIndex(of: ".") {
string.remove(at: firstIndex)
}
// get the index of the period in your string
if let index = string.firstIndex(of: ".") {
// get the fraction digits count and set the number formatter appropriately
let fractionDigits = string[index...].dropFirst().count
Formatter.currency.minimumFractionDigits = fractionDigits
Formatter.currency.maximumFractionDigits = fractionDigits
// Number Formatter wont add a period at the end of the string if there is no fractional digits then you need to manually add it yourself
if fractionDigits == 0 {
return (Double(string)?.currencyUS ?? "0") + "."
}
} else {
// in case there is no period set the fraction digits to zero
Formatter.currency.minimumFractionDigits = 0
Formatter.currency.maximumFractionDigits = 0
}
return Double(string)?.currencyUS ?? "0"
}

Playground Testing:

convertme(string: "100000")      // "100,000"
convertme(string: "100000.") // "100,000."
convertme(string: "100000.0") // "100,000.0"
convertme(string: "100000.00") // "100,000.00"
convertme(string: "100000.000") // "100,000.000"

How to format a Double into Currency - Swift 3

You can use this string initializer if you want to force the currency to $:

String(format: "Tip Amount: $%.02f", tipAmount)

If you want it to be fully dependent on the locale settings of the device, you should use a NumberFormatter. This will take into account the number of decimal places for the currency as well as positioning the currency symbol correctly. E.g. the double value 2.4 will return "2,40 €" for the es_ES locale and "¥ 2" for the jp_JP locale.

let formatter = NumberFormatter()
formatter.locale = Locale.current // Change this to another locale if you want to force a specific locale, otherwise this is redundant as the current locale is the default already
formatter.numberStyle = .currency
if let formattedTipAmount = formatter.string(from: tipAmount as NSNumber) {
tipAmountLabel.text = "Tip Amount: \(formattedTipAmount)"
}

Swift 4: Formatting number's into friendly K's

This answer formats by truncating (versus rounding). 1,515 rounded would generate 2k whereas truncated would generate 1.5k. The function requires reducing a number's scale (removing digits to the right of the decimal) which I've just packaged as an extension so it can be used anywhere (not just in the function).

extension Double {
func reduceScale(to places: Int) -> Double {
let multiplier = pow(10, Double(places))
let newDecimal = multiplier * self // move the decimal right
let truncated = Double(Int(newDecimal)) // drop the fraction
let originalDecimal = truncated / multiplier // move the decimal back
return originalDecimal
}
}

func formatNumber(_ n: Int) -> String {
let num = abs(Double(n))
let sign = (n < 0) ? "-" : ""

switch num {
case 1_000_000_000...:
var formatted = num / 1_000_000_000
formatted = formatted.reduceScale(to: 1)
return "\(sign)\(formatted)B"

case 1_000_000...:
var formatted = num / 1_000_000
formatted = formatted.reduceScale(to: 1)
return "\(sign)\(formatted)M"

case 1_000...:
var formatted = num / 1_000
formatted = formatted.reduceScale(to: 1)
return "\(sign)\(formatted)K"

case 0...:
return "\(n)"

default:
return "\(sign)\(n)"
}
}

You can fine tune this method for specific cases, such as returning 100k instead of 100.5k or 1M instead of 1.1M. This method handles negatives as well.

print(formatNumber(1515)) // 1.5K
print(formatNumber(999999)) // 999.9K
print(formatNumber(1000999)) // 1.0M

Swift number formatting

You can construct a string with a c-like formatting using this constructor:

String(format: String, arguments:[CVarArgType])

Sample usage:

var x = 10

println(String(format: "%04d", arguments: [x])) // This will print "0010"

If you're going to use it a lot, and want a more compact form, you can implement an extension like this:

extension String {
func format(arguments: [CVarArgType]) -> String {
return String(format: self, arguments: arguments)
}
}

allowing to simplify its usage as in this example:

"%d apples cost $%03.2f".format([4, 4 * 0.33])

String formatting. Swift / iOS

You should use a NumberFormatter

import Foundation

let coinFormatter : NumberFormatter = {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.numberStyle = .decimal
formatter.locale = Locale(identifier: "en-US")
return formatter
}()

let stringFromServer = "20760.326586753041"
if let value = coinFormatter.number(from: stringFromServer),
let reformattedString = coinFormatter.string(for: value) {
print(reformattedString)
}

I create a number formatter called coinFormatter that format numbers into decimals that have at most 2 decimal places. The code below that shows how you might use such a number formatter to convert the string from the server to a number, then the number back to a string with the expected format.

It also looks like you might be trying to format the number as a currency value. There are mechanisms in NumberFormatter for properly formatting currency values that you should look into as well.

P.S. As you will see in the comments below, NumberFormatter takes into account many complexities like the Locale and common radix marks used, how you want negative numbers represented, or whether the currency symbol should be written before or after the number when representing money. Please take the time to learn more about NumberFormatter and the power it offers you.



Related Topics



Leave a reply



Submit