Numeric Styles
Number Style Xcode 13+
The many ways you can customize the display of numbers.
There are many ways to format Swift’s numerical types (Float, Double, Decimal, and Integer) for display to the user. Each of the following options can be used by the Percent Format Styles and the Currency Format Style.
The examples below show the individual options available to format your final string, the real power available is that you chain these options together to allow for a truly staggering amount of customization.
The easiest and best way to access this style is through the .number
extension on FormatStyle
. From there, you can use method chaining to customize the output.
Float(10).formatted(.number.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K"
You can also initialize an instance of IntegerFormatStyle<Value: BinaryInteger>
, FloatingPointFormatStyle<BinaryFloatingPoint>
or Decimal.FormatStyle
and use method chaining to customize the output.
FloatingPointFormatStyle<Double>().rounded(rule: .up, increment: 1).format(10.9) // "11"
IntegerFormatStyle<Int>().notation(.compactName).format(1_000) // "1K"
Decimal.FormatStyle().scale(10).format(1) // "10"
Property | Description |
---|---|
Rounding | Customize the rounding behaviour |
Sign | Do you want to show or hide the + or - sign? |
Decimal Separator | Do you want to show or hide the decimal separator |
Grouping | How do you want the thousands numbers to be grouped |
Precision | How many fractional or significant digits do you want to show |
Notation | Enable scientific or compact notation |
Scale | Scale the number up or down before display |
Locale | Set the Locale for one output |
Compositing | Mix and match any and all of the above |
AttributedString output | Output an AttributedString |
Rounding
At its simplest, you can call the .formatted(.number.rounded())
method on any number type (Float, Double, Decimal, or Integer) in order to get the system’s default rounding behaviour.
Double(1.9999999).formatted(.number.rounded()) // "2"
Decimal(1.9999999).formatted(.number.rounded()) // "2"
Float(1.9999999).formatted(.number.rounded()) // "2"
Int(1.9999999).formatted(.number.rounded()) // "1"
Using the full instance method, you can access more granular settings: .number.rounded(rule:increment:)
.
Rounding Rule | Description |
---|---|
.awayFromZero |
Round to the closest allowed value whose magnitude is greater than or equal to that of the source. |
.down |
Round to the closest allowed value that is less than or equal to the source. |
.toNearestOrAwayFromZero |
Round to the closest allowed value; if two values are equally close, the one with greater |
.toNearestOrEven |
Round to the closest allowed value; if two values are equally close, the even one is chosen. |
.towardZero |
Round to the closest allowed value whose magnitude is less than or equal to that of the source. |
.up |
Round to the closest allowed value that is greater than or equal to the source. |
The increment:
parameter is a Double
and tells the system under the hood what to round the value by.
Float(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Float(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Float(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Float(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Float(0.01).formatted(.number.rounded(rule: .down)) // "0.009999"
Float(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Float(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.009999"
Float(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Float(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
Double(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Double(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Double(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Double(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Double(0.01).formatted(.number.rounded(rule: .down)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Double(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
Decimal(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Decimal(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Decimal(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Decimal(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Decimal(0.01).formatted(.number.rounded(rule: .down)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Decimal(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
Sign
Controls the visibility of the negative and positive sign.
Sign Display Strategy | Description |
---|---|
.automatic |
Displays the negative sign (-) when the number is negative. Positive sign isn’t shown |
.never |
Never shows the positive (+) or negative (-) signs |
.always(includingZero:) |
Passing in true will show the positive sign on a 0 value |
Float(1.90).formatted(.number.sign(strategy: .never)) // "1.9"
Float(-1.90).formatted(.number.sign(strategy: .never)) // "1.9"
Float(1.90).formatted(.number.sign(strategy: .automatic)) // "1.9"
Float(1.90).formatted(.number.sign(strategy: .always())) // "+1.9"
Float(0).formatted(.number.sign(strategy: .always(includingZero: true))) // "+0"
Float(0).formatted(.number.sign(strategy: .always(includingZero: false))) // "0"
Decimal Separator
Controls the visibility of the decimal separator.
Decimal Separator Display Strategy | Descriotion |
---|---|
.automatic |
Only shows the decimal separator on fractional values |
.always |
Always shows the decimal separator |
Float(10).formatted(.number.decimalSeparator(strategy: .automatic)) // "10"
Float(10).formatted(.number.decimalSeparator(strategy: .always)) // "10."
Grouping
Controls if the thousands units are grouped or not.
Grouping | Descriotion |
---|---|
.never |
Never group thousands digits |
.automatic |
Group the digits automatically based on the locale |
Float(1000).formatted(.number.grouping(.automatic)) // "1,000"
Float(1000).formatted(.number.grouping(.never)) // "1000"
Precision
There are seven options to set the precision of the output.
Precision Option | Description |
---|---|
.significantDigits(Int) |
Sets a fixed number of significant digits to show |
.significantDigits(Range) |
Sets a range of significant digits to show |
.fractionLength(Int) |
Sets the number digits after the decimal separator |
.fractionLength(Range) |
Sets a range of digits to show after the decimal separator |
.integerLength(Int) |
Sets the number of digits to show before the decimal separator |
.integerLength(Range) |
Sets a range of digits to show before the decimal separator |
.integerAndFractionLength(integer:fraction:) |
Sets both the integer and fractional digits to display |
Decimal(10.1).formatted(.number.precision(.significantDigits(1))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(2))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(3))) // "10.1"
Decimal(10.1).formatted(.number.precision(.significantDigits(4))) // "10.10"
Decimal(10.1).formatted(.number.precision(.significantDigits(5))) // "10.100"
Decimal(1000000.1).formatted(.number.precision(.significantDigits(5))) // "10.100"
Decimal(1).formatted(.number.precision(.significantDigits(1 ... 3))) // "1"
Decimal(10).formatted(.number.precision(.significantDigits(1 ... 3))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(1 ... 3))) // "10.1"
Decimal(10.01).formatted(.number.precision(.significantDigits(1 ... 3))) // "10"
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
Decimal(10).formatted(.number.precision(.fractionLength(0...2))) // 10
Decimal(10.1).formatted(.number.precision(.fractionLength(0...2))) // 10.1
Decimal(10.11).formatted(.number.precision(.fractionLength(0...2))) // 10.11
Decimal(10.111).formatted(.number.precision(.fractionLength(0...2))) // 10.11
Decimal(10.111).formatted(.number.precision(.integerLength(1))) // 0.111
Decimal(10.111).formatted(.number.precision(.integerLength(2))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerLength(0...1))) // .111
Decimal(10.111).formatted(.number.precision(.integerLength(0...2))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerLength(0...3))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 1, fraction: 1))) // 0.1
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 1))) // 10.1
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 2))) // 10.11
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 3))) // 10.111
Notation
Controls the ability to use different notation styles.
Notation Setting | Description |
---|---|
.automatic |
Used primarily when compositing other styles, will choose the best notation setting for the given settings. |
.compactName |
Uses the compact notation for the thousands styles |
.scientific |
Uses scientific notation |
Float(1_000).formatted(.number.notation(.automatic)) // "1,000"
Float(1_000).formatted(.number.notation(.compactName)) // "1K"
Float(1_000).formatted(.number.notation(.scientific)) // "1E3"
Scale
Controls the scale of the number.
Float(10).formatted(.number.scale(1.0)) // "10"
Float(10).formatted(.number.scale(1.5)) // "15"
Float(10).formatted(.number.scale(2.0)) // "20"
Float(10).formatted(.number.scale(-2.0)) // "-20"
Setting the Locale
Controls the locale of the output.
Float(1_000).formatted(.number.notation(.automatic).locale(Locale(identifier: "fr_FR"))) // "1 000"
Float(1_000).formatted(.number.notation(.compactName).locale(Locale(identifier: "fr_FR"))) // "1 k"
Float(1_000).formatted(.number.notation(.scientific).locale(Locale(identifier: "fr_FR"))) // "1E3"
Float(1000).formatted(.number.grouping(.automatic).locale(Locale(identifier: "fr_FR"))) // "1 000"
Float(1000).formatted(.number.grouping(.never).locale(Locale(identifier: "fr_FR"))) // "1000"
In cases where a given Locale
has multiple number systems available, numeric format styles will default to using the number system which matches the your system’s Locale.current
value. You’re able to explicitly set the number system for the Format Style to use by initializing a new Locale
with the number system set using the BCP-47 or ICU Identifiers:
let englishArabicBCP47 = "en-u-nu-arab"
let enArabBCP47 = Locale(identifier: englishArabicBCP47)
123456.formatted(.number.locale(enArabBCP47)) // "١٢٣٬٤٥٦"
Date.now.formatted(.dateTime.year().month().day().locale(enArabBCP47)) // "Sep ٢٣, ٢٠٢٣"
let englishArabicICU = "en@numbers=arab"
let enArabICU = Locale(identifier: "en@numbers=arab")
12345.formatted(.number.locale(enArabICU)) // "١٢٬٣٤٥"
Date.now.formatted(.dateTime.year().month().day().locale(enArabICU)) // "Sep ٢٣, ٢٠٢٣"
Compositing
Any of the above styles can be combined to fully customize the output.
Float(10).formatted(.number.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K"
Attributed String Output
Outputs and AttributedString
instead of a String
.
Float(10).formatted(.number.scale(200.0).notation(.compactName).grouping(.automatic).attributed)
Each of Swift’s build-in numeric types supports the parsing of numeric string into their respective types.
// MARK: Parsing Integers
try? Int("120", format: .number) // 120
try? Int("0.25", format: .number) // 0
try? Int("1E5", format: .number.notation(.scientific)) // 100000
// MARK: Parsing Floating Point Numbers
try? Double("0.0025", format: .number) // 0.0025
try? Double("95%", format: .number) // 95
try? Double("1E5", format: .number.notation(.scientific)) // 100000
try? Float("0.0025", format: .number) // 0.0025
try? Float("95%", format: .number) // 95
try? Float("1E5", format: .number.notation(.scientific)) // 100000
// MARK: - Parsing Decimals
try? Decimal("0.0025", format: .number) // 0.0025
try? Decimal("95%", format: .number) // 95
try? Decimal("1E5", format: .number.notation(.scientific)) // 100000
Percent Style Xcode 13+
Output number as a percentage.
There are many ways to format Swift’s numerical types (Float, Double, Decimal, and Integer) for display to the user. Each of the following options can be used by the Percent Format Styles and the Currency Format Style.
The examples below show the individual options available to format your final string, the real power available is that you chain these options together to allow for a truly staggering amount of customization.
Percentages are set by a range from 0.0 to 1.0, where 0.5 being 50%. This is consistent with the rest of Cocoa.
The easiest and best way to access this style is through the .percent
extension on FormatStyle
. From there, you can use method chaining to customize the output.
0.1.formatted(.percent) // "10%"
You can also initialize an instance of IntegerFormatStyle<Value: BinaryInteger>.Percent
, FloatingPointFormatStyle<BinaryFloatingPoint>.Percent
or Decimal.FormatStyle.Percent
and use method chaining to customize the output.
FloatingPointFormatStyle<Double>.Percent().rounded(rule: .up, increment: 1).format(0.109) // "11%"
IntegerFormatStyle<Int>.Percent().notation(.compactName).format(1_000) // "1K%"
Decimal.FormatStyle.Percent().scale(12).format(0.1) // "1.2%"
Property | Description |
---|---|
Rounding | Customize the rounding behaviour |
Sign | Do you want to show or hide the + or - sign? |
Decimal Separator | Do you want to show or hide the decimal separator |
Grouping | How do you want the thousands numbers to be grouped |
Precision | How many fractional or significant digits do you want to show |
Notation | Enable scientific or compact notation |
Scale | Scale the number up or down before display |
Locale | Set the Locale for one output |
Compositing | Mix and match any and all of the above |
AttributedString output | Output an AttributedString |
Rounding
At its simplest, you can call the .formatted(.number.rounded())
method on any number type (Float, Double, Decimal, or Integer) in order to get the system’s default rounding behaviour.
Double(1.9999999).formatted(.percent.rounded()) // "199.99999%"
Decimal(1.9999999).formatted(.percent.rounded()) // "199.99999%"
Float(1.9999999).formatted(.percent.rounded()) // "199.999998%"
Int(1.9999999).formatted(.percent.rounded()) // 1%
Using the full instance method, you can access more granular settings: .number.rounded(rule:increment:)
.
Rounding Rule | Description |
---|---|
.awayFromZero |
Round to the closest allowed value whose magnitude is greater than or equal to that of the source. |
.down |
Round to the closest allowed value that is less than or equal to the source. |
.toNearestOrAwayFromZero |
Round to the closest allowed value; if two values are equally close, the one with greater |
.toNearestOrEven |
Round to the closest allowed value; if two values are equally close, the even one is chosen. |
.towardZero |
Round to the closest allowed value whose magnitude is less than or equal to that of the source. |
.up |
Round to the closest allowed value that is greater than or equal to the source. |
The increment:
parameter is a Double
and tells the system under the hood what to round the value by.
Float(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575467%"
Float(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Float(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "502%"
Float(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Float(0.01).formatted(.percent.rounded(rule: .down)) // "0.999999%"
Float(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Float(0.01).formatted(.percent.rounded(rule: .towardZero)) // "0.999999%"
Float(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Float(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "502%"
Double(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575468%"
Double(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Double(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Double(0.01).formatted(.percent.rounded(rule: .down)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .towardZero)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Double(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "501%"
Decimal(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575468%"
Decimal(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Decimal(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "501%"
Decimal(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Decimal(0.01).formatted(.percent.rounded(rule: .down)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .towardZero)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Decimal(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "500%"
Decimal(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Decimal(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "500%"
Decimal(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "501%"
Sign
Controls the visibility of the negative and positive sign.
Sign Display Strategy | Description |
---|---|
.automatic |
Displays the negative sign (-) when the number is negative. Positive sign isn’t shown |
.never |
Never shows the positive (+) or negative (-) signs |
.always(includingZero:) |
Passing in true will show the positive sign on a 0 value |
Float(1.90).formatted(.percent.sign(strategy: .never)) // "189.999998%"
Float(-1.90).formatted(.percent.sign(strategy: .never)) // "189.999998%"
Float(1.90).formatted(.percent.sign(strategy: .automatic)) // "189.999998%"
Float(1.90).formatted(.percent.sign(strategy: .always())) // "+189.999998%"
Float(0).formatted(.percent.sign(strategy: .always(includingZero: true))) // "+0%"
Float(0).formatted(.percent.sign(strategy: .always(includingZero: false))) // "0%"
Decimal Separator
Controls the visibility of the decimal separator.
Decimal Separator Display Strategy | Descriotion |
---|---|
.automatic |
Only shows the decimal separator on fractional values |
.always |
Always shows the decimal separator |
Float(10).formatted(.percent.decimalSeparator(strategy: .automatic)) // "1,000%"
Float(10).formatted(.percent.decimalSeparator(strategy: .always)) // "1,000.%"
Grouping
Controls if the thousands units are grouped or not.
Grouping | Descriotion |
---|---|
.never |
Never group thousands digits |
.always |
Always group thousands digits |
Float(1_000).formatted(.percent.grouping(.automatic)) // "100,000%"
Float(1_000).formatted(.percent.grouping(.never)) // "100000%"
Precision
There are seven options to set the precision of the output.
Precision Option | Description |
---|---|
.significantDigits(Int) |
Sets a fixed number of significant digits to show |
.significantDigits(Range) |
Sets a range of significant digits to show |
.fractionLength(Int) |
Sets the number digits after the decimal separator |
.fractionLength(Range) |
Sets a range of digits to show after the decimal separator |
.integerLength(Int) |
Sets the number of digits to show before the decimal separator |
.integerLength(Range) |
Sets a range of digits to show before the decimal separator |
.integerAndFractionLength(integer:fraction:) |
Sets both the integer and fractional digits to display |
Decimal(10.1).formatted(.percent.precision(.significantDigits(1))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(2))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(3))) // "1,010%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(4))) // "1,010%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(5))) // "1,010.0%"
Decimal(1).formatted(.percent.precision(.significantDigits(1 ... 3))) // "100%"
Decimal(10).formatted(.percent.precision(.significantDigits(1 ... 3))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(1 ... 3))) // "1,010%"
Decimal(10.01).formatted(.percent.precision(.significantDigits(1 ... 3))) // "1,000%"
Decimal(0.0001).formatted(.percent.precision(.fractionLength(1))) // 0.0%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(2))) // 0.01%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(3))) // 0.010%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...1))) // 0%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...2))) // 0.01%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...3))) // 0.01%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...4))) // 0.01%
Decimal(10.111).formatted(.percent.precision(.integerLength(1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(2))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...2))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...3))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 1, fraction: 1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 1))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 2))) // 11.10%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 3))) // 11.100%
Notation
Controls the ability to use different notation styles.
Notation Setting | Description |
---|---|
.automatic |
Used primarily when compositing other styles, will choose the best notation setting for the given settings. |
.compactName |
Uses the compact notation for the thousands styles |
.scientific |
Uses scientific notation |
Float(1_000).formatted(.percent.notation(.automatic)) // "100,000%"
Float(1_000).formatted(.percent.notation(.compactName)) // "100K%"
Float(1_000).formatted(.percent.notation(.scientific)) // "1E5%"
Scale
Controls the scale of the number.
Float(10).formatted(.percent.scale(1.0)) // "10%"
Float(10).formatted(.percent.scale(1.5)) // "15%"
Float(10).formatted(.percent.scale(2.0)) // "20%"
Float(10).formatted(.percent.scale(-2.0)) // "-20%"
Setting the Locale
Controls the locale of the output.
Float(1_000).formatted(.percent.grouping(.automatic).locale(Locale(identifier: "fr_FR"))) // "100 000 %"
Float(1_000).formatted(.percent.grouping(.never).locale(Locale(identifier: "fr_FR"))) // "100000 %"
Float(1_000).formatted(.percent.notation(.automatic).locale(Locale(identifier: "fr_FR"))) // "100 000 %"
Float(1_000).formatted(.percent.notation(.compactName).locale(Locale(identifier: "fr_FR"))) // "100 k %"
Float(1_000).formatted(.percent.notation(.scientific).locale(Locale(identifier: "fr_FR"))) // "1E5 %"
In cases where a given Locale
has multiple number systems available, numeric format styles will default to using the number system which matches the your system’s Locale.current
value. You’re able to explicitly set the number system for the Format Style to use by initializing a new Locale
with the number system set using the BCP-47 or ICU Identifiers:
let englishArabicBCP47 = "en-u-nu-arab"
let enArabBCP47 = Locale(identifier: englishArabicBCP47)
123456.formatted(.number.locale(enArabBCP47)) // "١٢٣٬٤٥٦"
Date.now.formatted(.dateTime.year().month().day().locale(enArabBCP47)) // "Sep ٢٣, ٢٠٢٣"
let englishArabicICU = "en@numbers=arab"
let enArabICU = Locale(identifier: "en@numbers=arab")
12345.formatted(.number.locale(enArabICU)) // "١٢٬٣٤٥"
Date.now.formatted(.dateTime.year().month().day().locale(enArabICU)) // "Sep ٢٣, ٢٠٢٣"
Compositing
Any of the above styles can be combined to fully customize the output.
Float(10).formatted(.percent.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K%"
Attributed String Output
Outputs and AttributedString
instead of a String
.
Float(10).formatted(.percent.scale(200.0).notation(.compactName).grouping(.automatic).attributed)
Percentages parsed as Integers will be a value from 0 - 100, while percentages parsed as floating point or decimal values will be 0.0 - 1.0.
Percentage strings can be parsed into any of Swift’s built-in numeric types.
try? Int("98%", format: .percent) // 98
try? Float("95%", format: .percent) // 0.95
try? Decimal("95%", format: .percent) // 0.95
Currency Style Xcode 13+
Output number values in the local currency.
The currency format style is very similar to the Number and Percent format styles and works with Swift’s numerical types (Float, Double, Decimal, and Integer).
The key difference is that you will need to pass in the ISO 4217 country code for the currency you would like to display.
Because accuracy can’t be guaranteed, never use floating point numbers (Float and Double) to store and do calculations on important values like money. Either store cents as Integers, or useDecimal
values.
The easiest and best way to access this style is through the .currency(code:)
extension on FormatStyle
. From there, you can use method chaining to customize the output.
10.formatted(.currency(code: "JPY")) // "10%"
You can also initialize an instance of IntegerFormatStyle<Value: BinaryInteger>.Percent
, FloatingPointFormatStyle<BinaryFloatingPoint>.Percent
or Decimal.FormatStyle.Percent
and use method chaining to customize the output.
FloatingPointFormatStyle<Double>.Currency(code: "JPY").rounded(rule: .up, increment: 1).format(10.9) // ¥11"
IntegerFormatStyle<Int>.Currency(code: "GBP").presentation(.fullName).format(42) // "42.00 British pounds"
Decimal.FormatStyle.Currency(code: "USD").scale(12).format(0.1) // "$1.20"
Property | Description |
---|---|
Rounding | Customize the rounding behaviour |
Sign | Do you want to show or hide the + or - sign? |
Decimal Separator | Do you want to show or hide the decimal separator |
Grouping | How do you want the thousands numbers to be grouped |
Precision | How many fractional or significant digits do you want to show |
Presentation | Controls the style of the displayed currency |
Scale | Scale the number up or down before display |
Locale | Set the Locale for one output |
Compositing | Mix and match any and all of the above |
AttributedString output | Output an AttributedString |
Rounding
At its simplest, you can call the .formatted(.number.rounded())
method on any number type (Float, Double, Decimal, or Integer) in order to get the system’s default rounding behaviour.
Decimal(0.59).formatted(.currency(code: "GBP").rounded()) // "£0.59"
Decimal(0.599).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Decimal(0.5999).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Using the full instance method, you can access more granular settings: .number.rounded(rule:increment:)
.
Rounding Rule | Description |
---|---|
.awayFromZero |
Round to the closest allowed value whose magnitude is greater than or equal to that of the source. |
.down |
Round to the closest allowed value that is less than or equal to the source. |
.toNearestOrAwayFromZero |
Round to the closest allowed value; if two values are equally close, the one with greater |
.toNearestOrEven |
Round to the closest allowed value; if two values are equally close, the even one is chosen. |
.towardZero |
Round to the closest allowed value whose magnitude is less than or equal to that of the source. |
.up |
Round to the closest allowed value that is greater than or equal to the source. |
The increment:
parameter is a Double
and tells the system under the hood what to round the value by.
Decimal(0.59).formatted(.currency(code: "GBP").rounded()) // "£0.59"
Decimal(0.599).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Decimal(0.5999).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Decimal(5.001).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero, increment: 1)) // "£6"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero, increment: 10)) // "£10"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .down)) // "£5.00"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .toNearestOrAwayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .towardZero)) // "£5.00"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .up)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .down, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .towardZero, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .up, increment: 1)) // "£5"
Sign
Controls the visibility of the negative and positive sign.
Sign Display Strategy | Description |
---|---|
.automatic |
Automatically desides which strategy to use |
.never |
Never shows the positive (+) or negative (-) signs |
.always() |
Always shows the positive (+) or negative (-) signs |
.always(showsZero:) |
Accepts a Bool , and controls if a 0 value gets a positive (+) sign |
.accountingAlways() |
Uses the standardized account style for numbers |
.accountingAlways(showsZero) |
Accepts a Bool , and controls if a 0 value gets a positive (+) sign |
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .automatic)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .never)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accounting)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways())) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways(showZero: true))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways(showZero: false))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always())) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always(showZero: true))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always(showZero: false))) // "+£7.00"
Decimal Separator
Controls the visibility of the decimal separator.
Decimal Separator Display Strategy | Descriotion |
---|---|
.automatic |
Only shows the decimal separator on fractional values |
.always |
Always shows the decimal separator |
Decimal(3000).formatted(.currency(code: "GBP").decimalSeparator(strategy: .automatic)) // "£3,000.00"
Decimal(3000).formatted(.currency(code: "GBP").decimalSeparator(strategy: .always)) // "£3,000.00"
Grouping
Controls if the thousands units are grouped or not.
Grouping | Descriotion |
---|---|
.never |
Never group thousands digits |
.always |
Always group thousands digits |
Int(3_000).formatted(.currency(code: "GBP").grouping(.never)) // "£3000.00"
Int(3_000).formatted(.currency(code: "GBP").grouping(.automatic)) // "£3,000.00"
Precision
There are seven options to set the precision of the output.
Precision Option | Description |
---|---|
.significantDigits(Int) |
Sets a fixed number of significant digits to show |
.significantDigits(Range) |
Sets a range of significant digits to show |
.fractionLength(Int) |
Sets the number digits after the decimal separator |
.fractionLength(Range) |
Sets a range of digits to show after the decimal separator |
.integerLength(Int) |
Sets the number of digits to show before the decimal separator |
.integerLength(Range) |
Sets a range of digits to show before the decimal separator |
.integerAndFractionLength(integer:fraction:) |
Sets both the integer and fractional digits to display |
// Please don't use Floating point numbers to store currency. Please.
Float(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(4))) // "£3,000.0029" <- This is why
Float(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(1 ... 4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(1 ... 4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(3))) // "£000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(4))) // "£3,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(5))) // "£03,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0...3))) // "£.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0...4))) // "£3,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0...5))) // "£03,000.00"
Decimal(3).formatted(.currency(code: "GBP").precision(.integerAndFractionLength(integer: 4, fraction: 4))) // "£0,003.0000"
Decimal(3).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£3.0"
Decimal(3.00004).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£3.00004"
Decimal(3.000000004).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
)
Decimal(30000.01).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£30,000.01"
Decimal(3000000.000001).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£0.0"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(1))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(2))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(3))) // "£10.1"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(4))) // "£10.10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(5))) // "£10.100"
Decimal(1).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£1"
Decimal(10).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10.1"
Decimal(10.01).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10"
Presentation
Controls how verbose the currency display is when being presented
Presentation Setting | Description |
---|---|
.fullName |
Writes out the currency value in full |
.isoCode |
Uses the ISO 4217 currency code for display |
.narrow |
Fits the string in the smallest horizontal space possible |
.standard |
The default output style |
Decimal(10).formatted(.currency(code: "GBP").presentation(.fullName)) // "10.00 British pounds"
Decimal(10).formatted(.currency(code: "GBP").presentation(.isoCode)) // "GBP 10.00"
Decimal(10).formatted(.currency(code: "GBP").presentation(.narrow)) // "£10.00"
Decimal(10).formatted(.currency(code: "GBP").presentation(.standard)) // "£10.00"
Scale
Controls the scale of the number.
Decimal(10).formatted(.currency(code: "GBP").scale(1)) // "£10.00"
Decimal(10).formatted(.currency(code: "GBP").scale(1.5)) // "£15.00"
Decimal(10).formatted(.currency(code: "GBP").scale(-1.5)) // "-£15.00"
Decimal(10).formatted(.currency(code: "GBP").scale(10)) // "£100.00"
Setting the Locale
Controls the locale of the output.
Decimal(10).formatted(.currency(code: "GBP").presentation(.fullName).locale(Locale(identifier: "fr_FR"))) // "10,00 livres sterling"
Decimal(10000000).formatted(.currency(code: "GBP").locale(Locale(identifier: "hi_IN"))) // "£1,00,00,000.00
Compositing
Any of the above styles can be combined to fully customize the output.
Decimal(10).formatted(.currency(code: "GBP").scale(200.0).sign(strategy: .always()).presentation(.fullName)) // "+2,000.00 British pounds"
Attributed String Output
Outputs and AttributedString
instead of a String
.
Decimal(10).formatted(.currency(code: "GBP").scale(200.0).sign(strategy: .always()).presentation(.fullName).attributed)
Due to rounding issues, you should never use floating point types (Double
,Float
) to store currency values. UseDecimal
instead.
try? Decimal("$100.25", format: .currency(code: "USD")) // 100.25
try? Decimal("100.25 British Points", format: .currency(code: "GBP")) // 100.25