Skip to content

Instantly share code, notes, and snippets.

@devpolant
Last active July 28, 2022 20:30
Show Gist options
  • Save devpolant/8e2c0f6819341f326de9e9f53fbc547a to your computer and use it in GitHub Desktop.
Save devpolant/8e2c0f6819341f326de9e9f53fbc547a to your computer and use it in GitHub Desktop.
Custom iOS NumberFormatter Wrapper
//
// AppNumberFormatter.swift
// TrumpWall
//
// Created by Anton Poltoratskyi on 4/13/17.
// Copyright © 2017 Anton Poltoratskyi. All rights reserved.
//
import Foundation
private let currencySymbol = "$"
private let thousandSymbol = "K"
private let millionSymbol = "M"
private let billionSymbol = "B"
private let groupingFormat = "###,###,###,###.##"
private enum FormatType: Int64 {
case `default` = 1
case thousand = 1_000
case million = 1_000_000
case billion = 1_000_000_000
func convert(_ value: Double) -> Double {
return value / Double(rawValue)
}
}
class AppNumberFormatter {
static let shared = AppNumberFormatter()
private init() {}
enum Style {
case short // display as '1K, 2.5M, 10B'
case grouped // display as '10,000,000,000'
}
// MARK: - Formatters
private func baseNumberFormatter() -> NumberFormatter {
let numberFormatter = NumberFormatter()
numberFormatter.formatterBehavior = .default
numberFormatter.usesGroupingSeparator = true
numberFormatter.groupingSeparator = ","
numberFormatter.currencyGroupingSeparator = ","
numberFormatter.currencySymbol = currencySymbol
return numberFormatter
}
// MARK: Default Grouped
private lazy var currencyDefaultNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.positiveFormat = "¤\(groupingFormat)"
numberFormatter.negativeFormat = "-¤\(groupingFormat)"
return numberFormatter
}()
private lazy var defaultNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.positiveFormat = "\(groupingFormat)"
numberFormatter.negativeFormat = "-\(groupingFormat)"
return numberFormatter
}()
// MARK: Thousand
private lazy var currencyThousandNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.positiveFormat = "¤\(groupingFormat)\(thousandSymbol)"
numberFormatter.negativeFormat = "-¤\(groupingFormat)\(thousandSymbol)"
return numberFormatter
}()
private lazy var thousandNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.positiveFormat = "\(groupingFormat)\(thousandSymbol)"
numberFormatter.negativeFormat = "-\(groupingFormat)\(thousandSymbol)"
return numberFormatter
}()
// MARK: Million
private lazy var currencyMillionNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.positiveFormat = "¤\(groupingFormat)\(millionSymbol)"
numberFormatter.negativeFormat = "-¤\(groupingFormat)\(millionSymbol)"
return numberFormatter
}()
private lazy var millionNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.positiveFormat = "\(groupingFormat)\(millionSymbol)"
numberFormatter.negativeFormat = "-\(groupingFormat)\(millionSymbol)"
return numberFormatter
}()
// MARK: Billion
private lazy var currencyBillionNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.positiveFormat = "¤\(groupingFormat)\(billionSymbol)"
numberFormatter.negativeFormat = "-¤\(groupingFormat)\(billionSymbol)"
return numberFormatter
}()
private lazy var billionNumberFormatter: NumberFormatter = {
let numberFormatter = self.baseNumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.positiveFormat = "\(groupingFormat)\(billionSymbol)"
numberFormatter.negativeFormat = "-\(groupingFormat)\(billionSymbol)"
return numberFormatter
}()
private func numberFormatter(for value: Int64, style: Style, currency: Bool) -> (formatter: NumberFormatter, convertedValue: Double) {
guard style == .short else {
let formatter = currency ? currencyDefaultNumberFormatter : defaultNumberFormatter
return (formatter: formatter, convertedValue: Double(value))
}
let formatter: NumberFormatter
let convertedValue: Double
let absoluteValue = abs(value)
switch absoluteValue {
case 0..<10_000:
formatter = currency ? currencyDefaultNumberFormatter : defaultNumberFormatter
convertedValue = FormatType.default.convert(Double(value))
case 10_000..<1_000_000:
formatter = currency ? currencyThousandNumberFormatter : thousandNumberFormatter
convertedValue = FormatType.thousand.convert(Double(value))
case 1_000_000..<1_000_000_000:
formatter = currency ? currencyMillionNumberFormatter : millionNumberFormatter
convertedValue = FormatType.million.convert(Double(value))
default:
formatter = currency ? currencyBillionNumberFormatter : billionNumberFormatter
convertedValue = FormatType.billion.convert(Double(value))
}
return (formatter: formatter, convertedValue: convertedValue)
}
// MARK: - Public
func format(value: Int64, style: Style = .short, currency: Bool = true) -> String {
let (formatter, convertedValue) = self.numberFormatter(for: value, style: style, currency: currency)
let result = formatter.string(for: convertedValue)
return result ?? (value > 0 ? "\(currencySymbol)\(value)" : "-\(currencySymbol)\(value)")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment