Dealing with measurement units in Swift


Greetings, traveler!

Have you ever encountered measurement units in your app? Sometimes, we don’t need to overthink this topic. But sometimes, we need to manage measurement units in more detail. For this occasion, Apple has a specific API.

  • Measurement — a numeric quantity labeled with a unit of measure, supporting unit conversion and unit-aware calculations.
  • MeasurementFormatter — a formatter that provides localized representations of units and measurements.
  • Dimension — an abstract class representing a dimensional unit of measure.

MeasurementFormatter

Alright then, let’s start with code examples. First, let’s check out MeasurementFormatter. It has the property to style its units.

let measurement = Measurement(value: 100, unit: UnitMass.kilograms)
let formatter = MeasurementFormatter()

formatter.unitStyle = .short
formatter.string(from: measurement) // 100kg

formatter.unitStyle = .medium
formatter.string(from: measurement) // 100 kg

formatter.unitStyle = .long
formatter.string(from: measurement) // 100 kilograms

And the property for handling value representation.

formatter.unitOptions = [.naturalScale] // or just .naturalScale
let measurement = Measurement(value: 100, unit: UnitMass.grams)
formatter.string(from: measurement) // 0.1 kilograms

formatter.unitOptions = .providedUnit // or [.providedUnit]
formatter.string(from: measurement) // 100 grams

The unitOptions property is also valuable when dealing with this formatter locale.

formatter.locale = Locale(identifier: "en_US")
formatter.string(from: Measurement(value: 451, unit: UnitTemperature.fahrenheit)) // 451°F

formatter.locale = Locale(identifier: "en_GB")
formatter.string(from: Measurement(value: 451, unit: UnitTemperature.fahrenheit)) // 232.778°C

Conversion

You can convert units using Unit and Measurement. Consider this example:

let squareKilometers = Measurement(value: 10, unit: UnitArea.squareKilometers) // 10.0 km²
let squareMiles = squareKilometers.converted(to: .squareMiles) // 3.8610215854244583 mi²

Operations

With Measurement type, you can use operations like this one:

let grams = Measurement(value: 100, unit: UnitMass.grams)
let kilograms = Measurement(value: 5.5, unit: UnitMass.kilograms)

grams + kilograms // 5.6 kg

Custom Dimensions

Since Dimension is an open class, you can create your own via inheritance. Just don’t forget to configure the unitOptions value.

final class UnitVegetables: Dimension {
    override static func baseUnit() -> Self {
        return UnitVegetables.potato as! Self
    }

    static let potato = UnitVegetables(
        symbol: "🥔",
        converter: UnitConverterLinear(coefficient: 1.0)
    )
}

formatter.unitOptions = .providedUnit
formatter.string(from: Measurement(value: 22, unit: UnitVegetables.potato)) // 22 🥔

Conclusion

With this API, you can reduce the headache caused by handling measurement units.