Dimensional arithmetics in Swift
This package provides an easy and natural way of dealing with physical sizes. Performing complex arithmetics or unit conversion is as easy as using floating point values. Check out the code below, to get a glimpse on what is possible with this package.
let area = 2.4[.m] * 5.86[.inch] // Area of a rectangle 2.4 m x 5.86 ″
let force = 52.36[.lb] * SI.g // Force exceeded by a mass of 52.36 ℔ under gravity
let pressure = force / area // Calculate the pressure
let min_boundary = 2 * sqrt(SI.π * area) // Calculate the minimal boundary of the area
print(pressure.convert(to: .Pa)) // Prints "651.9938206417452 Pa"
print(min_boundary.convert(to: .mm)) // Prints "2118.7329427128902 mm"
TLDR
- Install this package like any other Swift package using the Swift package manager.
 - Construct an
 SInumber usingSI(_ value: Double, _ unit: SI.Unit)or the subscript notationlet a1 = SI(2.5, .m / .s ** 2) // Creates an SI number of 2.5 m/s² let a2 = 2.5[.m / .s ** 2] // Also 2.5 m/s²- Perform any calculations you would do with a double,
 SIwill take care of the rest.- To convert an
 SInumber to a desired unit useconvert(to: SI.Unit)- Define custom units by natural calculation e.g.
 let N = .kg * .m / .s ** 2 // Creates the unit N (1 N = 1 kg·m/s²) let lb = SI.Unit("℔", 0.45359237 * kg) // Creates the unit lb with the name "℔" and 1 ℔ = 0.45359237 kg- You can use physical constants provided under
 SI
Basic usage
Every SI number consists of a value and a unit. When performing arithmetic operations, the unit of a number is considered to ensure physical accuracy and proper conversion. Adding e.g. 1 m to 500 mm will return 1.5 m, while an addition with 1 s will result in an error since the physical dimensions of the units mismatch.
Construction
To construct an SInumber you have three options:
- Using the 
SIinitializerlet myLength = SI(2.5, SI.Unit.m) // 2.5 meters let myVelocity = SI(2.5, .m / .s) // 2.5 meters per second let myScalar = SI(2.5) // 2.5 (scalar value)
 - Using the subscript shortcut. With this you can create an 
SIon the fly subscripting of aDoublelet myLength = 2.5[SI.Unit.m] // 2.5 meters let myVelocity = 2.5[.m / .s] // 2.5 meters per second let myScalar = 2.5[] // 2.5 (empty subscript for scalar value)
 
Initialization
You can use SI like a Double without the struggle of keeping track of unit conversions. Here are some examples
let l1 = 2[.m] // 2 meters
let l2 = 500[.mm] // 500 millimeters = 0.5 meters
l1 + l2 // 2.5m (2.5[.m])
l1 - l2 // 1.5m (1.5[.m])
l1 * l2 // 1m² (1[.m ** 2])
l1 / l2 // 4 (4[])
2 * l1 // 4m (4[.m])
l1 ** 2 // 4m² (4[.m ** 2])
pow(l1, 2) // 4m² (4[.m ** 2])
sqrt(l1 * l2) // 1m (1[.m])
Caution Be careful when adding two
SInumbers with mismatching physical dimensions e.g.1[.m] + 2[.s]since this will result in a precondition failure.
Conversion
To convert an SI number to a desired unit use convert() or convertToSI() to convert to an SI unit.
let myLength = 2.5[.inch].convertToSI() // 0.063 m
let myTime = 24[.hour].convert(to: .day) // 1.0 day
Caution Be careful when converting an
SInumber to a unit with mismatching physical dimensions e.g.1[.m].convert(to: .s)since this will result in a precondition failure.
To typecast an SI to an Int or Double just use the initializer.
let double = Double(2[.m] / 2[.mm]) // 1000.0
let int = Double(2[.mm] / 2[.m]) // 1000
Caution Be careful when typecasting an
SIwith a non-scalar physical dimensions e.g.Double(1.2[.m])since this will result in a precondition failure.
Custom Units
If you find your unit missing in the library of SI.Unit, you can easily create one.
Creating units on the fly
SI.Unit supports multiplication, division and exponentiation so creating a new unit in place is really easy.
let N = .kg * .m / .s ** 2 // Newton
let lb = 0.45359237 * .kg // Pound
Prefixes
To add a prefix to a unit (e.g. N → kN, m → μ), you can use the inbuilt methods.
let μm = μ(.m) // Micrometer: 1 μm = 10⁻⁶ m
let kN = k(.N) // Kilonewton: 1 kN = 10³ N
Define a named unit
To define a named SI.Unit you must use the initializer. You can create a new SI.Unit based of an existing one or one created on the fly. The name is used in the customDebugString.
Tip Add your named units as an extension of
SI.Unitto make them easily available.
extension SI.Unit {
   public static let N = Self("N", kg * m / (s ** 2)) // Force in Newton
}
let Hz = SI.Unit("Hz", .scalar / .s) // Frequency in Hertz
Unit dimension
An SI.Unit consists of a dimension and a multiplicator. The dimension is a dictionary of type [SI.Unit.Base : Int] where the key stands for the respective Base dimension and the value for its power. For example
SI.Unit("m/s", .m / .s).dimension = [SI.Unit.Base.m: 1, SI.Unit.Base.s: -1]
The multiplicator is used to convert the unit to the SI system.
All standard base units are provided in the SI package. To define a new base unit (e.g. €, happiness,...) extend of SI.Unit.Base and use the default initializer and provide the standard symbol. Then define the standard unit of the new dimension providing the new dimension, the multiplicator and a name.
extension SI.Unit.Base{
   public static let currency = Self(name: "€")
}
extension SI.Unit {
   public static let euro = Self("€", 1, [Base.currency: 1])
}
Contribution
You have found a bug 
You want to develop this package further? Just create a merge request or send me a message. Please add exhaustive test to your new functionality in order to keep this package as stable and reliable as possible. Also, please make sure to document your code to make it legible for other devs. If you have any questions just write me. Thank you very much for your contribution!