Recently I came across a quite curious behavior regarding units and measurements in Swift which I can't understand. After creating measurements with units created with a static UnitMass accessor I can properly format them and receive sensible outputs:
let measurement = Measurement(value: 42.0, unit: UnitMass.kilograms)
measurement.formatted() // -> "42 kg" correct
When creating a measurement using a UnitMass created using init with its symbol I get wrong outputs
let anotherMass = UnitMass(symbol: UnitMass.kilograms.symbol)
let anotherMeasurement = Measurement(value: 42.0, unit: anotherMass)
anotherMeasurement.formatted() // -> "0 μg" wrong
The same happens when using a MeasurementFormatter instead of accessing formatted on the measurement. What is interesting: When debugging this in a playground and inspecting the outputs anotherMass and anotherMeasurement seem to be valid and produce sensible values:

Complete Playground
import UIKit
let mass = UnitMass.kilograms
let anotherMass = UnitMass(symbol: mass.symbol)
let measurement = Measurement(value: 42.0, unit: mass)
let anotherMeasurement = Measurement(value: 42.0, unit: anotherMass)
let formatted = measurement.formatted()
let anotherFormatted = anotherMeasurement.formatted()
Unfortunately I think I can't just use the static init because I want to store a unit in a SwiftData model and because UnitMass is not Codable I can only store the symbol representation. Therefor I need to work with init(symbol:) when converting back to a Unit.
The
initthat you are using is declared inUnit. It won't know anything aboutUnitMass, and won't see that your symbol to be the same symbol asUnitMass.kilograms, and returnUnitMass.kilograms. After all, if it worked this way, you wouldn't be able to create new units with the same symbol as existing units.Unit.initjust creates a brand new unit with that symbol, unrelated to any other units.Also note how
Dimension.init(symbol:converter)is used to create your custom unit. See the docsThis presumably also applies to
Unit.init(symbol:).If you want to store a unit in SwiftData, a workaround would be to store the
Datarepresentation of theUnit(which you can get fromNSKeyedArchiversinceUnitconforms toNSSecureCoding). You can also try storing it as a@Attribute(.transformable)usingNSSecureUnarchiveFromDataTransformerlike this post shows.Alternatively, you can also store a conversion coefficient. You can get this by getting
(unit.converter as? UnitConverterLinear).coefficint. Then useDimension.init(symbol:converter)to recreate your unit, as a custom unit with the same conversion factor. However, you still lose localisation in the formatting this way.