Search code examples
iosswiftseparator

Adding Thousand Separator to Int in Swift


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.


Solution

  • edit/update: Using the new generic formatted method (iOS15+/Xcode 13.0+):

    current locale

    2358000.formatted()      // "2,358,000"
    

    fixed locales

    2358000.formatted(.number.locale(.init(identifier: "fr_FR")))  // "2 358 000"
    2358000.formatted(.number.locale(.init(identifier: "pt_BR")))  // "2.358.000"
    

    original post

    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}"