Display data in the right way in Swift. List, Date, Number, and Measurement formatters

Display data in the right way in Swift. List, Date, Number, and Measurement formatters

The main goal of this article is to share my experience in formatting data. Most of the apps have to show date, time, list of data, or transform and display mass, angle, duration, area, speed, volume, energy, length, temperature, pressure, and other kinds of measurements. It is very important to display data in the right way with proper format, units, imperial or metric measurements, localized.

1. DateFormater

DateFormater – formatter class that converts between dates and their textual representations. DateFormater has a lot of properties, but the most useful of them are dateFormat, timeStyle, dateStyle, amSymbol, pmSymbol, and timeZone.

dateFormatString property for custom date and time format. Very useful if you need to show time in some non-default format.

let dateFormater = DateFormatter()
dateFormater.dateFormat = "dd MMM, yyyy - hh:mm:ss"
let now = dateFormater.string(from: Date())

//CONSOLE: now
05 May, 2020 - 03:37:12

dateStyle, timeStyleDateFormatter.Style property for showing preselected formats of date and time. DateFormatter.Style has none, short, medium, long, full cases. Formats will be shown below.

dateStyle = .none // - nil
dateStyle = .short // - 6/5/20
dateStyle = .medium // - Jun 5, 2020
dateStyle = .long // - June 5, 2020
dateStyle = .full // - Friday, June 5, 2020

timeStyle = .none // - nil
timeStyle = .short // - 3:47 PM
timeStyle = .medium // - 3:47:56 PM
timeStyle = .long // - 3:47:56 PM GMT+3
timeStyle = .full // - 3:47:56 PM Eastern European Summer Time

amSymbol, pmSymbol – custom symbols for AM and PM parts in date and time.

timeZone – for which timezone to convert date and time. List of timezones

Examples:

  • For example, we have a timestamp and need to show the date and time:
let date = Date(timeIntervalSince1970: 1591361276)
let dateFormater = DateFormatter()
dateFormater.timeStyle = .short
dateFormater.dateStyle = .long
let dateString = dateFormater.string(from: date)

//CONSOLE: dateString
June 5, 2020 at 3:47 PM
  • For example, we have a timestamp and need to show the time in specific timezone and custom AM and PM parts:
let date = Date(timeIntervalSince1970: 1591361276)
let dateFormater = DateFormatter()
dateFormater.timeStyle = .short
dateFormater.dateStyle = .none
dateFormater.amSymbol = "Ante meridiem"
dateFormater.pmSymbol = "Post meridiem"
dateFormater.timeZone = TimeZone(identifier: "Antarctica/Rothera")
let dateString = dateFormater.string(from: date)

//CONSOLE: dateString
9:47 Ante meridiem

2. ListFormatter

ListFormatter – formatted class for array representation. it is a cool way to show a list of data in a human-readable format.

let animals = ["Cat", "Dog", "Bird"]
let formater = ListFormatter()
formater.locale = Locale(identifier: "en_US")
let displayList = formater.string(from: animals)

//CONSOLE: displayList
"Cat, Dog, and Bird"

It is easy to set any locale to ListFormatter, and the list will be like below. List of all Locales.

let animals = ["Cat", "Dog", "Bird"]
let formater = ListFormatter()
formater.locale = Locale(identifier: "fr_FR")
let displayList = formater.string(from: animals)

//CONSOLE: displayList
"Cat, Dog et Bird"

Of course, if you want to show list only with commas, you can use the old way:

let animals = ["Cat", "Dog", "Bird"]
let displayList = animals.joined(separator: ", ")

//CONSOLE: displayList
"Cat, Dog, Bird"

3. NumberFormatter

NumberFormatter – super useful formatter for formating prices, percents. You can see a default usage of NumberFormtter.

let priceFormatter = NumberFormatter()
priceFormatter.locale = Locale(identifier: "en_FR")
priceFormatter.numberStyle = .currency
let displayPrice = priceFormatter.string(from: 20.5)

//CONSOLE: displayPrice
€20,5

numberStyle – is an enum like in DateFormatter and ListFormatter like above. numberStyle has next cases: none, decimal, currency, percent, scientific, spellOut, ordinal, currencyISOCode, currencyPlural, currencyAccounting. This a screen from official documentation when you can see all differences between numberStyle types and different locales.

https://developer.apple.com/documentation/foundation/numberformatter/style

Ordinal numberStyle in NumberFormatter

I want to pay your attention on ordinal numberStyle. It is useful when you want to show index numbers like 1st, 2nd.

let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .ordinal
let firstPlace = numberFormatter.string(from: 1)
let secondPlace = numberFormatter.string(from: 2)
let anotherPlace = numberFormatter.string(from: 33)

// CONSOLE: firstPlace, secondPlace, anotherPlace
"1st", "2nd", "33rd"

SpellOut numberStyle in NumberFormatter

let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .spellOut
let result = numberFormatter.string(from: 234.5)

// CONSOLE: firstPlace, secondPlace, anotherPlace
"two hundred thirty-four point five"

4. MeasurementFormatter

MeasurementFormatter – A formatter that provides localized representations of units and measurements. It is a less used formatter but is insanely useful when you need to display different data in different units. MeasurementFormatter can work with a ton of preset units. It is a list of provided units:

UnitAcceleration, UnitAngle, UnitArea, UnitConcentrationMass, UnitDispersion, UnitDuration, UnitElectricCharge, UnitElectricCurrent, UnitElectricPotentialDifference, UnitElectricResistance, UnitEnergy, UnitFrequency, UnitFuelEfficiency, UnitInformationStorage, UnitLength, UnitIlluminance, UnitMass, UnitPower, UnitPressure, UnitSpeed, UnitTemperature, UnitVolume.

Measurements of the same unit can be added or subtracted, multiplied, or divided.

I will show you a few examples with different Units, and after that, you can easily use other of them which not covered in my examples.

MeasurementFormatter has unitOptions and unitStyle properties. it is easy to set up the needed format of presentation.

unitOptions has 3 cases – providedUnit, naturalScale, temperatureWithoutUnit.

  • providedUnit – displays the unit of measure without any converting. Locale won’t affect if unitOptions set to providedUnit
  • naturalScale – displays the unit depend on the current locale of the formatter. Formatter can decide to convert the value from the given unit to a specific another for a better and complex presentation view. (you can set 0.1 kilometers, and formatter will show you 100m instead 0.1km.
  • temperatureWithoutUnit – displays temperature without any unit.

unitStyle has 3 cases – short, medium, long formats. For example, formatted 1 kilometer will be – 1km, 1 km, 1 kilometers respectively

How to show UnitSpeed in the specific unit without converting:

let measurementFormatter = MeasurementFormatter()
var speedMeasurement = Measurement(value: 0.000001, unit: UnitSpeed.kilometersPerHour)
measurementFormatter.unitOptions = .providedUnit
measurementFormatter.unitStyle = .short
let result = measurementFormatter.string(from: speedMeasurement)

// CONSOLE: result
50km/h

unitOptions must be providedUnit because we don’t convert or formate speed to another unit.

How to convert UnitLength to another unit:

let measurementFormatter = MeasurementFormatter()
measurementFormatter.unitOptions = .providedUnit
measurementFormatter.unitStyle = .long

var miles = Measurement(value: 2, unit: UnitLength.miles)
miles.convert(to: .centimeters)
let displayString = measurementFormatter.string(from: miles)

//CONSOLE: displayString
321,868.8 centimeters

Make sure you need to set unitOptions to providedUnit, because naturalScale will convert centimeters back to miles (because it is the main option of naturalScale mode).

How to present a localized UnitMass:

let measurementFormatter = MeasurementFormatter()
measurementFormatter.unitOptions = .naturalScale
measurementFormatter.unitStyle = .medium
measurementFormatter.locale = Locale(identifier: "en_US")

var massMeasurement = Measurement(value: 10, unit: UnitMass.kilograms)
let displayString = measurementFormatter.string(from: massMeasurement)

//CONSOLE: displayString
22.046 lb

Make sure you need to set unitOptions to naturalScale because providedUnit will prevent the localized converting that we actually need.

How to add, subtract, multiply, or divide Measurements?

let measurementFormatter = MeasurementFormatter()
measurementFormatter.unitOptions = .providedUnit
measurementFormatter.unitStyle = .medium

let firstMeasure = Measurement(value: 10, unit: UnitAngle.degrees)
let secondMeasure = Measurement(value: 10, unit: UnitAngle.radians)
var sum = firstMeasure + secondMeasure
sum.convert(to: .radians)
let displayString = measurementFormatter.string(from: sum)

//CONSOLE: displayString
10.175 rad

Conclusions:

In my opinion, even theses 4 formatters can do your code more flexible, readable, and maintainable. Formating and converting is a huge part of each app. You always need to prepare your data from the server to human-readable convenient form. Of course, you can write all these formatters and convertors by yourself, but it is silly to reinvent the wheel each time in each app. That’s why it is crucial to understand and use these formatters for a better user experience.

Additional Resources:

Timezones List

Locales List

MeasurementFormatter

Photo by Safar Safarov on Unsplash