I’m looking for a best practice, or standard/accepted method of handling both metric and imperial measurements in an app. I have an iOS app that is currently just in the US market, but am wanting to internationalize it for other markets. Part of this involves handling both metric and imperial measurements, and displaying the correct one based on localization or preference overrides.
As this app is designed for people traveling, it is likely that a user may want to switch between one system and the other depending on what country they are currently in, so I will need to manage both sets of data.
Since the user could enter the measurements in either system, and want to read the equivalent in the other, I need some way of maintaining them both. My first thought is to create all measurements (length, volume, mass, temp, pressure) as tuples and calculate the missing part when the data is saved. But as this is my first time dealing with two measurement systems at once, I wonder if there’s already a standard way of handling such data types.
2
Answers
I’d recommend utilizing the
Measurement
API provided by Apple in the Foundation framework. This provides a way to encapsulate a unit of measure and a double value, offering a range of methods for comparing and converting between different units.Simple example for distance measurement:
In the above code:
enum
MeasurementSystem
to represent the unit systems we will support:.metric
and.imperial
.MeasurementManager
class stores the user’s current preference for a measurement system. ThechangeSystem(to:)
function allows switching the measurement system.getUserPreferredDistance(for:)
function receives a distance in meters, creates aMeasurement
object from it, and depending on the user’s preferred system, it converts this distance to the appropriate unit and formats it for display.You can repeat similar procedures for other types of measurements (volume, mass, temperature, etc.) and store the users’ preference in persistent storage like
UserDefaults
for future sessions.With this approach, you’re leveraging Swift’s built-in functionality for managing and converting between units, rather than re-inventing the wheel.
I would suggest using the Measurement framework. Create a
Measurement
usinginit(value:unit:)
and display the results (localized for the current locale settings) in the UI usingformatted
:E.g.
Or:
If you want to allow the user to select imperial/metric within your app and want to show the results in something other than the device’s default locale, you can use
MeasurementFormatter
for fine-grained control over the localization and display of the results in your UI.Anyway,
Measurment
stores the two properties,value
andunit
, in their original values, avoiding any unnecessary rounding that can happen if you were to store the value as an arbitrarily fixed unit of measure in a singleDouble
.FWIW, you can even use this model object (the
Measurement
isCodable
) in your app. Or, if you want to avoid entangling your backend in theMeasurement
implementation details, you could always store thevalue
and the string identifier for theUnit
as two fields.