This is what I do now:
extension Decimal {
var formattedAmount: String {
let formatter = NumberFormatter()
formatter.generatesDecimalNumbers = true
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter.string(from: self) //mismatch type
}
}
but I cannot create NSNumber from Decimal.
This should work
extension Decimal {
var formattedAmount: String? {
let formatter = NumberFormatter()
formatter.generatesDecimalNumbers = true
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter.string(from: self as NSDecimalNumber)
}
}
This:
formatter.string(from: ...) // requires a NSNumber
You can either do this:
formatter.string(for: self) // which takes Any as argument
Or:
string(from: self as NSDecimalNumber) // cast self to NSDecimalNumber
Related
I am fairly new to Swift and having a great deal of trouble finding a way to add a space as a thousand separator.
What I am hoping to achieve is taking the result of a calculation and displaying it in a textfield so that the format is:
2 358 000
instead of
2358000
for example.
I am not sure if I should be formatting the Int value and then converting it to a String, or adding the space after the Int value is converted to a String. Any help would be greatly appreciated.
You can use NSNumberFormatter to specify a different grouping separator as follow:
update: Xcode 11.5 • Swift 5.2
extension Formatter {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = " "
return formatter
}()
}
extension Numeric {
var formattedWithSeparator: String { Formatter.withSeparator.string(for: self) ?? "" }
}
2358000.formattedWithSeparator // "2 358 000"
2358000.99.formattedWithSeparator // "2 358 000.99"
let int = 2358000
let intFormatted = int.formattedWithSeparator // "2 358 000"
let decimal: Decimal = 2358000
let decimalFormatted = decimal.formattedWithSeparator // "2 358 000"
let decimalWithFractionalDigits: Decimal = 2358000.99
let decimalWithFractionalDigitsFormatted = decimalWithFractionalDigits.formattedWithSeparator // "2 358 000.99"
If you need to display your value as currency with current locale or with a fixed locale:
extension Formatter {
static let number = NumberFormatter()
}
extension Locale {
static let englishUS: Locale = .init(identifier: "en_US")
static let frenchFR: Locale = .init(identifier: "fr_FR")
static let portugueseBR: Locale = .init(identifier: "pt_BR")
// ... and so on
}
extension Numeric {
func formatted(with groupingSeparator: String? = nil, style: NumberFormatter.Style, locale: Locale = .current) -> String {
Formatter.number.locale = locale
Formatter.number.numberStyle = style
if let groupingSeparator = groupingSeparator {
Formatter.number.groupingSeparator = groupingSeparator
}
return Formatter.number.string(for: self) ?? ""
}
// Localized
var currency: String { formatted(style: .currency) }
// Fixed locales
var currencyUS: String { formatted(style: .currency, locale: .englishUS) }
var currencyFR: String { formatted(style: .currency, locale: .frenchFR) }
var currencyBR: String { formatted(style: .currency, locale: .portugueseBR) }
// ... and so on
var calculator: String { formatted(groupingSeparator: " ", style: .decimal) }
}
Usage:
1234.99.currency // "$1,234.99"
1234.99.currencyUS // "$1,234.99"
1234.99.currencyFR // "1 234,99 €"
1234.99.currencyBR // "R$ 1.234,99"
1234.99.calculator // "1 234.99"
Note: If you would like to have a space with the same width of a period you can use "\u{2008}"
unicode spaces
formatter.groupingSeparator = "\u{2008}"
You want to use NSNumberFormatter:
let fmt = NSNumberFormatter()
fmt.numberStyle = .DecimalStyle
fmt.stringFromNumber(2358000) // with my locale, "2,358,000"
fmt.locale = NSLocale(localeIdentifier: "fr_FR")
fmt.stringFromNumber(2358000) // "2 358 000"
With Swift 5, when you need to format the display of numbers, NumberFormatter is the right tool.
NumberFormatter has a property called numberStyle. numberStyle can be set to a value of NumberFormatter.Style.decimal in order to set the formatter's style to decimal.
Therefore, in the simplest case when you want to format a number with decimal style, you can use the following Playground code:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = NumberFormatter.Style.decimal
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
According to the user's current locale, this code will print Optional("2,358,000") for en_US or Optional("2 358 000") for fr_FR.
Note that the following code snippet that uses the NumberFormatter's locale property set to Locale.current is equivalent to the previous Playground code:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = Locale.current
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
The Playground code below that uses the NumberFormatter's groupingSeparator property set to Locale.current.groupingSeparator is also equivalent to the former:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = Locale.current.groupingSeparator
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
Otherwise, if you want to set the number formatting with a specific locale formatting style, you may use the following Playground code:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = Locale(identifier: "fr_FR")
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
// prints: Optional("2 358 000")
However, if what you really want is to enforce a specific grouping separator, you may use the Playground code below:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = " "
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
// prints: Optional("2 358 000")
Leo Dabus's answer translated to Swift 3:
Into any .swift file, out of a class:
struct Number {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.groupingSeparator = " " // or possibly "." / ","
formatter.numberStyle = .decimal
return formatter
}()
}
extension Integer {
var stringWithSepator: String {
return Number.withSeparator.string(from: NSNumber(value: hashValue)) ?? ""
}
}
Usage:
let myInteger = 2358000
let myString = myInteger.stringWithSeparator // "2 358 000"
Code:
//5000000
let formatter = NumberFormatter()
formatter.groupingSeparator = " "
formatter.locale = Locale(identifier: "en_US")
formatter.numberStyle = .decimal.
Output:
5 000 000
I was looking for a currency format like $100,000.00
I accomplished it customizing the implementation Leo Dabus like this
extension Formatter {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyGroupingSeparator = ","
formatter.locale = Locale(identifier: "en_US") //for USA's currency patter
return formatter
}()
}
extension Numeric {
var formattedWithSeparator: String {
return Formatter.withSeparator.string(for: self) ?? ""
}
}
Try this
func addPoints(inputNumber: NSMutableString){
var count: Int = inputNumber.length
while count >= 4 {
count = count - 3
inputNumber.insert(" ", at: count) // you also can use ","
}
print(inputNumber)
}
The call:
addPoints(inputNumber: "123456")
The result:
123 456 (or 123,456)
I need to convert some integers to words and back again, ie: 14 to fourteen and fourteen to 14.
To convert an integer to a word, I am using the .spellout property. Is there a similar method I could apply to a string that would convert it to a numerical value? There does not seem to be an equivalent StringFormatter method.
//opposite of what I am trying to do:
public extension Int {
public var asWord: String {
let numberValue = NSNumber(value: self)
var formatter = NumberFormatter()
formatter.numberStyle = .spellOut
return formatter.string(from: numberValue)!
}
}
You can use this extension method:
public extension String {
func wordToInteger() -> Int? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .spellOut
return numberFormatter.number(from: self) as? Int
}
}
I have the following code which works fine except keeping 0 after decimal. The goal is to format the user input as user types on Textfield
MyClass {
static let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = NSLocale.current
formatter.maximumFractionDigits = 2
return formatter
}()
}
extension Double {
func formatted() -> String{
return MyClass.amountFormatter.string(from: NSNumber(value: self)) ?? "0"
}
}
This gives me the below output
1256.formatted - 1,256
1256.00.formatted - 1,256 (Need to retain 0 after decimal, desired result - 1,256.00)
1256.05.formatted - 1,256.05
1256.80.formatted - 1,256.8 (Need to retain 0 after decimal, desired result 1,256.80)
1256.5789847.formatted - 1,256.57
How to achieve the desired output
I am creating a budget app that allows the user to input their budget as well as transactions. I need to allow the user to enter both pence and pounds from separate text fields and they need to be formatted together with currency symbols. I have this working fine at the moment but would like to make it localised as currently it only works with GBP. I have been struggling to convert NSNumberFormatter examples from Objective-C to Swift.
My first issue is the fact that I need to set the placeholders for the input fields to be specific to the users location. Eg. Pounds and Pence, Dollars and Cents etc...
The second issue is that the values inputted in each of the text fields such as 10216 and 32 need to be formatted and the currency symbol specific to the users location needs to be added. So it would become £10,216.32 or $10,216.32 etc...
Also, I need to use the result of the formatted number in a calculation. So how can I do this without running into issues without running into issues with the currency symbol?
Here's an example on how to use it on Swift 3.
( Edit: Works in Swift 5 too )
let price = 123.436 as NSNumber
let formatter = NumberFormatter()
formatter.numberStyle = .currency
// formatter.locale = NSLocale.currentLocale() // This is the default
// In Swift 4, this ^ was renamed to simply NSLocale.current
formatter.string(from: price) // "$123.44"
formatter.locale = Locale(identifier: "es_CL")
formatter.string(from: price) // $123"
formatter.locale = Locale(identifier: "es_ES")
formatter.string(from: price) // "123,44 €"
Here's the old example on how to use it on Swift 2.
let price = 123.436
let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
// formatter.locale = NSLocale.currentLocale() // This is the default
formatter.stringFromNumber(price) // "$123.44"
formatter.locale = NSLocale(localeIdentifier: "es_CL")
formatter.stringFromNumber(price) // $123"
formatter.locale = NSLocale(localeIdentifier: "es_ES")
formatter.stringFromNumber(price) // "123,44 €"
Swift 3:
If you are looking for a solution that gives you:
"5" = "$5"
"5.0" = "$5"
"5.00" = "$5"
"5.5" = "$5.50"
"5.50" = "$5.50"
"5.55" = "$5.55"
"5.234234" = "5.23"
Please use the following:
func cleanDollars(_ value: String?) -> String {
guard value != nil else { return "$0.00" }
let doubleValue = Double(value!) ?? 0.0
let formatter = NumberFormatter()
formatter.currencyCode = "USD"
formatter.currencySymbol = "$"
formatter.minimumFractionDigits = (value!.contains(".00")) ? 0 : 2
formatter.maximumFractionDigits = 2
formatter.numberStyle = .currencyAccounting
return formatter.string(from: NSNumber(value: doubleValue)) ?? "$\(doubleValue)"
}
I have implemented the solution provided by #NiñoScript as an extension as well:
Extension
// Create a string with currency formatting based on the device locale
//
extension Float {
var asLocaleCurrency:String {
var formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.locale = NSLocale.currentLocale()
return formatter.stringFromNumber(self)!
}
}
Usage:
let amount = 100.07
let amountString = amount.asLocaleCurrency
print(amount.asLocaleCurrency())
// prints: "$100.07"
Swift 3
extension Float {
var asLocaleCurrency:String {
var formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = Locale.current
return formatter.string(from: self)!
}
}
Xcode 11 • Swift 5.1
extension Locale {
static let br = Locale(identifier: "pt_BR")
static let us = Locale(identifier: "en_US")
static let uk = Locale(identifier: "en_GB") // ISO Locale
}
extension NumberFormatter {
convenience init(style: Style, locale: Locale = .current) {
self.init()
self.locale = locale
numberStyle = style
}
}
extension Formatter {
static let currency = NumberFormatter(style: .currency)
static let currencyUS = NumberFormatter(style: .currency, locale: .us)
static let currencyBR = NumberFormatter(style: .currency, locale: .br)
}
extension Numeric {
var currency: String { Formatter.currency.string(for: self) ?? "" }
var currencyUS: String { Formatter.currencyUS.string(for: self) ?? "" }
var currencyBR: String { Formatter.currencyBR.string(for: self) ?? "" }
}
let price = 1.99
print(Formatter.currency.locale) // "en_US (current)\n"
print(price.currency) // "$1.99\n"
Formatter.currency.locale = .br
print(price.currency) // "R$1,99\n"
Formatter.currency.locale = .uk
print(price.currency) // "£1.99\n"
print(price.currencyBR) // "R$1,99\n"
print(price.currencyUS) // "$1.99\n"
Details
Xcode 10.2.1 (10E1001), Swift 5
Solution
import Foundation
class CurrencyFormatter {
static var outputFormatter = CurrencyFormatter.create()
class func create(locale: Locale = Locale.current,
groupingSeparator: String? = nil,
decimalSeparator: String? = nil,
style: NumberFormatter.Style = NumberFormatter.Style.currency) -> NumberFormatter {
let outputFormatter = NumberFormatter()
outputFormatter.locale = locale
outputFormatter.decimalSeparator = decimalSeparator ?? locale.decimalSeparator
outputFormatter.groupingSeparator = groupingSeparator ?? locale.groupingSeparator
outputFormatter.numberStyle = style
return outputFormatter
}
}
extension Numeric {
func toCurrency(formatter: NumberFormatter = CurrencyFormatter.outputFormatter) -> String? {
guard let num = self as? NSNumber else { return nil }
var formatedSting = formatter.string(from: num)
guard let locale = formatter.locale else { return formatedSting }
if let separator = formatter.groupingSeparator, let localeValue = locale.groupingSeparator {
formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
}
if let separator = formatter.decimalSeparator, let localeValue = locale.decimalSeparator {
formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
}
return formatedSting
}
}
Usage
let price = 12423.42
print(price.toCurrency() ?? "")
CurrencyFormatter.outputFormatter = CurrencyFormatter.create(style: .currencyISOCode)
print(price.toCurrency() ?? "nil")
CurrencyFormatter.outputFormatter = CurrencyFormatter.create(locale: Locale(identifier: "es_ES"))
print(price.toCurrency() ?? "nil")
CurrencyFormatter.outputFormatter = CurrencyFormatter.create(locale: Locale(identifier: "de_DE"), groupingSeparator: " ", style: .currencyISOCode)
print(price.toCurrency() ?? "nil")
CurrencyFormatter.outputFormatter = CurrencyFormatter.create(groupingSeparator: "_", decimalSeparator: ".", style: .currencyPlural)
print(price.toCurrency() ?? "nil")
let formatter = CurrencyFormatter.create(locale: Locale(identifier: "de_DE"), groupingSeparator: " ", decimalSeparator: ",", style: .currencyPlural)
print(price.toCurrency(formatter: formatter) ?? "nil")
Results
$12,423.42
USD12,423.42
12.423,42 €
12 423,42 EUR
12_423.42 US dollars
12 423,42 Euro
Updated for Swift 4 from #Michael Voccola's answer:
extension Double {
var asLocaleCurrency: String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = Locale.current
let formattedString = formatter.string(from: self as NSNumber)
return formattedString ?? ""
}
}
Note: no force-unwraps, force-unwraps are evil.
Swift 4 TextField Implemented
var value = 0
currencyTextField.delegate = self
func numberFormatting(money: Int) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = .current
return formatter.string(from: money as NSNumber)!
}
currencyTextField.text = formatter.string(from: 50 as NSNumber)!
func textFieldDidEndEditing(_ textField: UITextField) {
value = textField.text
textField.text = numberFormatting(money: Int(textField.text!) ?? 0 as! Int)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = value
}
extension Float {
var convertAsLocaleCurrency :String {
var formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = Locale.current
return formatter.string(from: self as NSNumber)!
}
}
This working for swift 3.1 xcode 8.2.1
Swift 4
formatter.locale = Locale.current
if you want to change locale you can do it like this
formatter.locale = Locale.init(identifier: "id-ID")
// This is locale for Indonesia locale. if you want use as per mobile phone area use it as per upper mention Locale.current
//MARK:- Complete code
let formatter = NumberFormatter()
formatter.numberStyle = .currency
if let formattedTipAmount = formatter.string(from: Int(newString)! as
NSNumber) {
yourtextfield.text = formattedTipAmount
}
add this function
func addSeparateMarkForNumber(int: Int) -> String {
var string = ""
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
if let formattedTipAmount = formatter.string(from: int as NSNumber) {
string = formattedTipAmount
}
return string
}
using:
let giaTri = value as! Int
myGuessTotalCorrect = addSeparateMarkForNumber(int: giaTri)
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