Units in Foundation

Assumed audience: people interested in the Swift programming language.

Starting iOS 10 and macOS 10.12, Foundation supports units and measurements out of the box, in the form of (NS)Unit and (NS)Measurement respectively. Let’s take a look at how they work.

Note: All code snippets below are written in Swift 3.0.

To get started, let’s define a measurement. To do that, you have to pass a value and a unit to the initializer:

import Foundation

let snorlaxWeight = Measurement(value: 460, unit: UnitMass.kilograms)
// -> 460.0 kg

You can then effortlessly convert the measurement to another unit:

let snorlaxWightInImperial = snorlaxWeight.converted(to: .pounds)
// -> 1014.12723328454 lb

You can also define your own unit by extending one of the default dimensions available:

extension UnitMass {
  static let snorlax = UnitMass(symbol: "snorlax", converter: UnitConverterLinear(coefficient: 460))
}

The converter is used to transform the measurement from any given unit to the base unit of the dimension; Kilograms in the case of UnitMass. You can inspect the base unit of any dimension by calling baseUnit() on it.

UnitMass.baseUnit()
// -> kg

Once defined, your custom unit can be used alongside the pre-defined ones:

let statueOfLibertyMass = Measurement(value: 225_000, unit: UnitMass.kilograms)
let statueOfLibertyMassInSnorlax = statueOfLibertyMass.converted(to: .snorlax)
// -> 489.130434782609 snorlax

You can also define a custom dimension of units by subclassing Dimension if necessary. Here is an example that defines a new UnitDataRate class for measuring data transfer rates, often used in Internet speed tests:

class UnitDataRate: Dimension {
  static let kilobitPerSecond = UnitDataRate(symbol: "kbps", converter: UnitConverterLinear(coefficient: 1))
  static let megabitPerSecond = UnitDataRate(symbol: "Mbps", converter: UnitConverterLinear(coefficient: 1_000))
  static let gigabitPerSecond = UnitDataRate(symbol: "Gbps", converter: UnitConverterLinear(coefficient: 1_000_000))

  override class func baseUnit() -> UnitDataRate {
    return .megabitPerSecond
  }
}

let speed = Measurement(value: 100, unit: UnitDataRate.megabitPerSecond)
let speedInKpbs = speed.converted(to: .kilobitPerSecond)

// -> 100000 kbps

# Learn More

Have feedback or questions? Drop me a line via the contact form, Twitter, or Mastodon.
Found any typos? Edit the post here.