Search code examples
swiftunicodenumber-formattingdarwin

Swift: How do I include or use darwin compactdecimalformat?


I've been investigating and trying to code a compact number format otherwise known as a short scale formatter or a humanizing big numbers into smaller ones in Swift.

I suddenly thought rather than trying to recode the wheel maybe Unicode already solved this issue and saw that there is indeed some code to compact larger numbers.

In the open-source code for Apple Swift on Github there is a reference to compactdecimalformat.h at:

https://github.com/apple/swift-corelibs-foundation/blob/master/bootstrap/x86_64-apple-darwin/usr/local/include/unicode/compactdecimalformat.h

In it, it refers to numberformatter.h; which I found a reference from Unicode ICU 67.1 at:

https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/numberformatter_8h.html

However, I am not sure how to use it. I've imported Darwin, but I am not sure how to actually call compactdecimalformat or the public identifier DecimalFormat

I'd like to run some number tests against it to see if it is what I'm looking for in terms of compacting / short scaling numbers.

Thus my question is, how do you include and use the compactdecimalnumber.h file that comes as part of the Darwin import?

With thanks

edit: I think upon reading the code and comments the .h file only handles really big numbers?


Solution

  • I don't think there is such formatter in Swift but you can subclass NumberFormatter and create your own CompactNumberFormatter if needed:

    class CompactDecimalFormatter: NumberFormatter {
        override func string(for obj: Any?) -> String? {
            guard var value = (obj as? NSNumber)?.doubleValue else { return nil }
            let suffixes = ["", "k", "m", "b","t"]
            var index = suffixes.startIndex
            while index < suffixes.endIndex && value.magnitude >= 1000 {
                index = suffixes.index(after: index)
                value /= 1000
            }
            formatter.positiveSuffix = suffixes[index]
            formatter.negativeSuffix = suffixes[index]
            return formatter.string(for: value)
        }
    
        private let formatter: NumberFormatter = {
            let formatter = NumberFormatter()
            formatter.numberStyle = .decimal
            formatter.roundingMode = .down
            formatter.minimumFractionDigits = 0
            formatter.maximumFractionDigits = 1
            return formatter
        }()
    
        // You can override all the properties you thing you might need
        override var minimumIntegerDigits: Int {
            get { formatter.minimumIntegerDigits }
            set { formatter.minimumIntegerDigits = newValue }
        }
        override var maximumIntegerDigits: Int {
            get { formatter.maximumIntegerDigits }
            set { formatter.maximumIntegerDigits = newValue }
        }
    
        override var minimumFractionDigits: Int {
            get { formatter.minimumFractionDigits }
            set { formatter.minimumFractionDigits = newValue }
        }
        override var maximumFractionDigits: Int {
            get { formatter.maximumFractionDigits }
            set { formatter.maximumFractionDigits = newValue }
        }
    
        override var positivePrefix: String! {
            get { formatter.positivePrefix }
            set { formatter.positivePrefix = newValue }
        }
        override var negativePrefix: String! {
            get { formatter.negativePrefix }
            set { formatter.negativePrefix = newValue }
        }
    
        override var roundingMode: RoundingMode {
            get{ formatter.roundingMode }
            set{ formatter.roundingMode = newValue }
        }
    
        // or disable them
        override var numberStyle: Style { get { .decimal } set { } }
        override var positiveSuffix: String! { get { "" } set { } }
        override var negativeSuffix: String! { get { "" } set { } }
    }
    

    let formatter = CompactDecimalFormatter()
    formatter.string(for: 2500)  // "2.5k"