Search code examples
swiftformattingformat

How to use .formatted(.percent) with a specified precision? (FormatStyle protocol)


I want to convert a progress view's Double value ranging from 0 to 1 into a formatted String with a selected amount of fraction digits, e.g. 0.789123456 to "79,1 %".

Added challenge: I want to use Foundation's new FormatStyle protocol method .formatted().

To my surprise, I can't get it to work. I can't find a way to specify the precision.
Here are some of my tries:

var progress: Double = 0.789123456
progress.formatted(.percent) // "78,9123456 %" it starts so easy
progress.formatted(.number.precision(.fractionLength(1))) // "0,79"
progress.formatted(.number.precision(.fractionLength(0...1))) // "0,79"
progress.formatted(.number.precision(.fractionLength(0...1))).formatted(.percent) // does not compile
// "Instance method 'formatted' requires the types 'FloatingPointFormatStyle<Double>.FormatOutput' (aka 'String') and 'FloatingPointFormatStyle<Double>.Percent.FormatInput' (aka 'Double') be equivalent"

Double(progress.formatted(.number.precision(.fractionLength(1)))) // nil
Double("0.79")?.formatted(.percent) // "79 %", gotcha, I have german Locale!

Locale.current // "en_DE (current)"
Locale.current.decimalSeparator // ","

Double(progress.formatted(.number.precision(.fractionLength(1))).replacingOccurrences(of: Locale.current.decimalSeparator!, with: ".")) // "0,8"
Double(progress.formatted(.number.precision(.fractionLength(1))).replacingOccurrences(of: Locale.current.decimalSeparator!, with: "."))?.formatted(.percent) // "80 %"

Is there a way to do this with FormatStyle?
Or do I have to go with the old NumberFormatter and its maximumFractionDigits?


Solution

  • When using .formatted(...) you first choose what format style to use, in this case FormatStyle.Percent

    progress.formatted(.percent)
    

    and then you can configure this format style to your liking

    progress.formatted(.percent.precision(.fractionLength(1)))
    

    Another example with a rounding rule

    progress.formatted(.percent.precision(.fractionLength(1)).rounded(rule: .down))