The Foundation framework, which includes the root object class, classes representing basic data types such as strings and byte arrays, and collection classes for storing other objects, has added new support for specifying Units and Measurements. Understand how to model your measurements and convert within dimensions. Learn from the experts how to use and surface Units and Measurements in your interfaces.
I'm a software engineer on the Foundation Team.
Welcome to my talk.
Thanks to those of you for sticking around.
I know it's kind of late in the day.
So, I wanted to start by thinking about apps
that we commonly associate with measurements.
The first thing that comes to mind for me, I don't know
about you, are converter apps.
Makes sense, convert one unit to another.
But I wonder though, if there are other apps
that also use measurements but maybe in a less obvious way.
I don't know, but when I think about, you know, measurements,
it's interesting, right, because we use them all the time.
We just don't normally think about them as explicitly,
and they can pop up in really surprising ways,
one of those ways being when they're not in terms of units
that are common for whatever our current context is.
So, let's say I'm in France, and I'm using an app
that is calculating road distance,
and I expect the measurement to show up in terms of kilometers,
but they show up in terms of miles.
It's like, hmm.
It makes for kind of a jarring, not so great user experience.
And so in this talk, we're going to introduce to you a new suite
of APIs that helps you ensure
that this never happens in your app.
So, if we think again about this question of, like,
other apps that we associate with measurements,
I had mentioned converter apps, which is still true.
But another example could be games, right?
So, today we're talking about this game that I'm working on.
It's called Jammin' in the Streetz with a z
because z's are cool, and I want my game to be cool.
So, the premise of the game is that we have to get our player
to dance from level to level.
I have some pretty set goals for this game.
One, I need this game to be super fun.
Like, the most fun a game has ever been, ever.
Two, tons of emoji, because I love emoji.
Three, it needs to be available worldwide.
I want it everywhere.
So, when we think about some of things
that this game would include, right,
every round would be called a jam session,
and within this jam session, it's tracking the total amount
of time that it takes our player to get from level to level,
the distance that they've traveled,
the number of dance movements they've performed,
and maybe even, like, the rate at which they're traveling.
But let's think about the things that I had just mentioned.
They're all measureable.
They're quantifiable objects, right?
And so, if we were stop this presentation right now,
whip out a computer, and try to code this up,
how would we represent this?
Well, we could represent them all as doubles.
Easy. The problem with that though is we're actually missing
some context when we do that.
And so let's look at an example.
So we have our little player.
I told you, lots of emoji, I'm a big fan.
And our little player has moonwalked, because why not,
and they've moonwalked a certain distance.
Now, if we stored this distance as 5, it's like, hmm, okay,
but what does five actually mean?
It brings up this question of five what?
But if we say five feet, it's like oh, okay.
This actually makes sense.
It now has context in a physical space.
And so now, let's talk about the way that,
or the API that Foundation is introducing
to accurately represent this entire context.
So, you have a new struct.
It's called Measurement, and it's generic on the UnitType,
and we'll get into what a UnitType is in a little bit.
Contains a unit as well as a value, and an initializer.
So, what's cool about this is that now you're actually able
to represent a measurement fully with full context.
And so if we go back to our little player,
let's say in the game we are not only keeping track
of distance traveled, but we're also keeping track
of the distance that the player has left to go.
How would we do this with this new API?
Well, now we can represent distance traveled
as a measurement of five feet.
We could also represent distance to go
as a measurement of, let's say, six feet.
And so now, you can actually do some really cool things
You can add them together to get a total distance,
which is a measurement that represents 11 feet.
You can multiply them and get, you know,
the value that you expect.
You could also divide them,
and get a similar value, which is awesome.
So if we go back to thinking about a unit,
what are some things that we tend to associate with a unit?
Well, first off, every unit has a symbol, hands down.
Units can have a dimension, and so an example of this would be
for the dimension length.
It could use, like, feet is a unit of length, right?
Units could also be equivalent to each other,
so one foot could be equivalent to approximately 0.3 meters.
And so now we have APIs
to actually represent a unit object.
Now, if you're paying close attention, before I had said
that every unit has a symbol,
and so in this API we're representing that here.
We don't necessarily represent the dimension in here
or the equivalents because not every unit can be equivalent
to another one, and not every unit is dimensional.
But at the very least, every unit has a symbol,
so we're representing that here.
But if we go back to this concept
of a dimension, what is a dimension?
What does that actually mean?
Well, dimensions are categories of units that can be expressed
with different kinds of units.
So, when we think of length, as an example,
length can be represented as kilometers,
feet, miles, what have you.
Dimensions also always have a base unit, so going back
to the length example, the base unit of length is meter.
You can also perform conversions within dimensions,
within a single dimension.
So you can convert kilometers to feet, vice versa,
meters to miles, et cetera.
So now we have API to represent a dimension, and so you see here
that dimension subclass is Unit,
which means that it inherently acquires the symbol property.
We have a converter property,
and I'll go into what a unit converter is in a little bit,
but essentially it defines the conversion of the unit to
and from its base unit.
We have an initializer, which takes the symbol
and a converter instance, and as I've mentioned,
every dimension has a base unit.
So now we have a class property
that will return that unit for you.
The important thing to remember here, though,
is that every instance of a dimension is
in and of itself a unit.
Now, with this new API that we're introducing,
the cool thing is that now we are providing
to you some 170-plus units that you can just use out of the box.
You don't even have to define them yourself.
You can just use them, like, after this presentation.
And most of these units are in accordance
with the International System of Units, so they're units
that you're already used to seeing, which is really cool.
Now let's look at an example.
We have the dimension of length, and it has a whole suite
of units that are class properties representing
And so, if we go back to this, you know,
instances of a dimension are, in fact, units,
what happens is any time you call these properties you're
getting back an instance of unit length.
The difference between each of these instances, that one,
their symbols are different, and two, the converters
that define their definition are different.
So, just an overview of all of the classes that we're providing
to you, all the different unit types.
Remember I told you there's like 170-plus of them, so, you know,
play with them to your hearts' content.
But we have some pretty cool ones here, some common ones,
like area, mass, temperature, length.
But also, some kind of uncommon ones like illuminance
and electric current, but sounds really fun if play with them.
So, let's go back to our distance traveled
and our distance to go measurements.
So, this is code we've already seen.
Now, we're going to comment the last two lines
out because we're going to redefine them.
So, we're leaving distanceTraveled alone,
but now we're defining distanceToGo with the same value
of six, but instead of passing in feet as the unit,
we're passing in kilometers.
Now, this is where things start to get a little hairy,
because we're now adding distanceTraveled
and distanceToGo together, but one's in feet
and one's in kilometers.
What does this mean for totalDistance?
Is it in terms of feet?
It is in terms of kilometers?
Will this stage just blow up in five seconds?
I don't know.
But, phew, no, it won't.
Actually what happened is that the result is in terms
of the base unit, which is meters.
And so what's cool about this is that Measurement is handling all
of this implicitly for you.
You don't have to worry about it at all.
You could just use the operators as you would
with regular scalar values
and you get the results that you expect.
So, now let's say, and again, I wanted to be able
to track the player at certain points and, for the sake
of the demo we're just printing out strings, but maybe I want
to do something funkier,
add more functionality at some point.
But the key here is that I want to be able
to compare these measurements, and now you can.
So Measurement has support for comparison operators for you
to be able to do this.
And so if we were to friend distanceMarker,
we would get what we expect, which is barely started,
because Measurement was properly able to deduce
that distanceTraveled is less than distanceToGo.
So, let's talk more about defining a unit
because we haven't really delved into that yet.
As previously mentioned, units are always defined
in terms of their base unit.
There are methods that describe this conversion to
and from the base unit side, but we'll talk
about that a little bit more when we go into UnitConverter.
The important thing, though, to remember with this is
that conversion can only happen within a single dimension.
So, when you think of length, for example,
you can convert kilometers to feet, but would you try
to convert kilometers to seconds?
Conceptually that doesn't actually make a lot of sense.
And so, in this case, with Measurement, if you tried
to do that, it would throw in Objective-C
or it would be a fatal error in Swift.
So, remember I had mentioned that we provided
like 170 plus units for you out of the box.
Chances are, you won't need to define your own custom units,
but if you do, that's the only time you have to think
about defining them, which is pretty cool.
You can actually just use them,
the units that are already provided for you out of the box,
but if you are creating a custom unit, then you can think
about how the converter would be set up.
And so, the cool thing is
that Measurement can handle the conversion
for you implicitly even with your custom unit.
It just auto-magically works.
So, let's talk about some custom units.
We're making some custom units for the game.
We have a jamz unit here, because I want to be able
to calculate total time in a jam session in terms
of this unit called jamz.
Seconds is a little boring for me.
But if you see here, we have this UnitConverterLinear object.
What is that?
We haven't really talked about that yet.
So let's go back to talking
about conversions a little bit more.
So again, to and from the base unit.
UnitConverter is a root class that defines two methods
that describe this conversion.
So baseUnitValue(fromValue), and value(fromBaseUnitValue).
UnitConverterLinear overrides those two methods
and defines them linearly.
So, for all the math people out there, it's in terms
of A X plus B, where A is the coefficient
and B is the constant.
So, if we go back to this jamz unit,
we see here that this coefficient is 30,
a scalar value of 30, so we're saying
that one jamz unit is equivalent to 30 seconds and so now,
in our linear function, we have 30 times whatever the jamz value
So, if a jam session was four total jamz, four jamz,
then if we wanted to convert it to seconds,
it would be four times 30 to give us 120 seconds,
and you could do the reverse.
120 divided by 30 to get the jamz value.
Yeah. So let's say I wanted to define other custom units,
for length, for example, because I don't want
to calculate distance traveled in terms of feet or meters,
I wanted to do something a little bit more interesting,
So, here, same concept.
One hopz is equivalent to 0.75 meters
and so we're defining the formulas for that here,
and same with some other custom units
that I would create for this.
So, you start to get the idea.
So, if you recall correctly, when I was talking
about the jam session, one of the measurements
that I had named was the number of dance movements performed.
Unfortunately, the international system
of units does not have a dimension
that recognizes dance movements.
I'm not entirely sure why, but it's a thing
that we are defining here today.
And so, we have UnitDanceMove
with a base unit of wackyArmMovements.
And so you see here that one wackyArmMovement is equivalent
to another wackyArmMovement, so what this means,
another way to think about how you define your units is how
many of the base unit make up this particular unit.
So, here's one-to-ones so our coefficient is one.
Let's say we wanted to define a robot movement.
It's like, approximately
like four wackyArmMovements, I think [laughter].
cabbagePatch is like three wackyArmMovements.
Sure. And of course, no dance movement dimension is complete
without jazzHands, which is about two wackyArmMovements.
I think it's pretty accurate, we'll stick with that for now.
And so now, let's go back and actually create this jam session
that I had outlined earlier.
So distance traveled, this is in terms of steps taken,
which will be in terms of this unit hopz that we created.
jamTime will be in jamz, naturally,
and dance moves will be in terms of the robot.
So our player will be roboting the entire time, of course.
And so the dance rate will actually be terms
of meters per second, but if you recall,
none of the other measurements were in meters or in seconds,
so how are we going to derive that value?
Well, we can take stepsTaken and convert it to meters.
We can also take the jamTime and convert them to seconds,
and now we can actually return a measurement that's
in terms of meters per second.
And so the cool thing here is that in very few lines
of code we were actually able
to completely define our jamSession.
So, now we know how to actually represent measurements and units
as model objects, which is pretty cool.
But I told you, though,
that I want this game available everywhere.
Now to do that we actually have to format these measurements.
That's where things get a little tricky.
So, if we have our player, and let's say instead of dancing
for only five feet, they danced the robot for five kilometers.
If we wanted to represent this
across the world, how would this look?
Well, in Canada, we could actually just write it
as it was previously written, which would be five kilometers.
If we were to try to represent this in Chinese, however,
we'd actually have to translate the unit.
In Arabic, we'd have to translate the unit
and change the number representation and make sure
that our right-to-left ordering is correct.
So, all of this is more logic that I'd have
to add manually to my app.
And then finally, in the U.S., we're like, "Kilometers?
What are those?
What are those things?"
So, then, not only do I have to handle conversion, just in terms
of my calculations, but I also have to handle conversions just
for formatting, which is additional logic I have
to add to my app.
So, what's the solution here?
You let Foundation do all the work for you.
We have a new formatter called MeasurementFormatter.
It formatters both measurements and units, and its locale-aware,
so you don't have to worry about any of this.
So let's take a look at it.
Its subclass is Formatter.
If you're familiar with any
of our other formatters, same concept.
It has the unitOptions property, and we'll talk a little bit more
about unitOptions in a second.
It also has unitStyle.
So, again, if you're familiar
with the other formatter, short, medium, long.
It has a locale that's setable.
Now, chances are, you're just going to default
to the current locale of the user,
which is what this locale will always default to,
but if you needed to set it explicitly, you can.
It also takes a custom numberFormatter.
So, let's say you wanted your value in your measurement
to be represented in terms of scientific notation,
you can provide a custom numberFormatter
to do that for you.
It also has methods that take
in both a Measurement object and a Unit object.
So let's talk a little bit more about the unit options.
The cool thing is that out-of-the-box,
by default the formatter formats according to the preferred unit
of your user's locale.
So, you don't even have to think about it.
It also takes into account things like purpose.
So, you know, if you're calculating a length in terms
of road distance verses in terms of person height, you're going
to want to use different units depending on the context.
So I'll take a look at some of the options
that MeasurementFormatter provides.
One is provided unit, and so, let's say we have a case
where we want to pass in a measurement of five kilometers,
but our locale is the U.S. Now normally in the U.S., we would,
for road distance for example, change it to miles
because that's what's common for us here.
But if you set provided unit, it'll ensure
that whatever unit you pass in is the unit
that actually gets formatted.
There's also an action called natural scale.
So, this is great in particular for, like, UI stuff.
So, if your, you know, your app is running on the watch,
and you're really concerned about screen real estate,
then instead of putting in a thousand meters,
which would take up a bulk of your screen,
you can actually have it formatted to one kilometer.
Another is temperature without unit.
So let's say you have a measurement
that represents 90 degrees Fahrenheit,
but you don't want the Fahrenheit unit
to actually show.
You can set this to get the result that you were expecting.
So, let's play around with some examples.
We have our formatter here,
and we have our original distance measurement that's
in five kilometers, because that's how far our
And now we want to get a resulting string from that.
You'll see that the result is in miles and the coolest thing
about this is that in three lines of code,
not only did we get the result that we expected,
but we didn't have to do anything to the formatter,
just out-of-the-box without setting anything,
it knew exactly what to do.
Now let's say that we give it our custom unit, the hopz unit.
And Measurement, MeasurementFormatter
that has no conception, no idea that hopz is actually a unit,
but we create this hopz distance, and we pass it
to the formatter, and it's actually still able
to do the conversion implicitly on our behalf.
We don't have to do anything.
Now let's say though, you know, we have these custom units
and we actually want people to see them,
so we'll set provided unit.
We'll give it a measurement that has our hopz unit again.
And now the result will be in terms of that unit.
Now this case is a particularly interesting case
because not only are we providing a custom unit,
but we're also providing a custom unit that's
within a custom dimension, right?
And so at this point, it's like, not really sure,
what will MeasurementFormatter do?
Oh, well, it'll do exactly what we expect,
which is pretty awesome.
So, now, I'm going to hand it over to another member
of the Foundation team, and he's going
to show how measurements are used and can be used
and formatted in the High Scores feature of this game.
Thank you, Daphne.
So, my name is Peter Hosey.
I am also an engineer on the Foundation team,
and like Daphne said, this is our High Score list,
where we're showing a list of the levels in the game.
As you tap on each one, you see some basic facts
about the level, the name, a picture of it.
You see some important information
about playing the level, things you need to know.
And you even see your statistics
of how well you've done in the game.
You see things like your high score,
how many wacky arm movements you've done.
You see how far you've danced.
You see how fast you've danced.
But these values are all just numbers.
They lack dimension.
So we don't know, like, how far is 6811?
Now you could implement an entire unit system yourself.
You could start small by just tacking a unit onto the end
of each of these numbers.
You could maybe build out a little more, and like,
have a unit conversion system
that will understand different locales
and translate the unit names automatically,
and that's a lot of work, isn't it?
Like Daphne said, let Foundation do the work do the work for you.
We now have Measurement and Unit,
and MeasurementFormatter types in Foundation,
so let's use them in our game.
So we're going to start by creating the custom units
that Daphne showed you, some of them anyway.
We've got a couple of length units, we've got our speed unit,
and we have our four custom dance movements
that do not come with Foundation.
Now that we've got our custom units, we can bring these
into our model, which is this levels struct,
which has our basic facts about the level
and includes the player's statistics,
which are just numbers.
So let's change them to measurements.
So, now we have a measurement of dance moves, a measurement
of length, and a measurement of speed,
and as we change the properties,
so much we change the initializer.
So, now it's possible to create a level with measurements,
and we want to do that in the List ViewController,
which is a controller for this view here, this is the list.
Our List ViewController queries are synchronization API,
which returns a JSON list of dictionaries, one per level,
containing all of this information.
And particularly includes the player's statistics
as just numbers.
So, we want to create measurements
around these numbers,
so now this a number of wackyArmMovements.
This is a number of Hopz.
This is a number of hopzPerJamz.
Now that this information is in our model and we've created it
as measurements in our List ViewController,
and that's the only change that you had to make
in the List ViewController.
Now we can go over to the Detail ViewController,
which is what shows this view here, and shows this
for every one of these levels,
and we can use our new measurements here.
So, we already have one formatter in place.
This is a NumberFormatter, and this is what does this padding
to six digits with zeros, and we want to keep that,
but we want to build on top of it.
We want to make this show the units.
So we're going to add two more formatters,
and I'll explain why to in a moment.
Now that we've created them,
we go to the same place we're already configuring the number
formatter, which is in our viewedDidLoad method.
This is inherited from UIViewController,
and here we're overwriting it.
We configure the highScore NumberFormatter
with our minimum integer digits, and now we're going
to configure our MeasurementFormatter
to use our provided unit, which is wackyArmMovements,
which you can't tell here, can you?
And use our NumberFormatter
so that we continue to pad to six digits.
And I mentioned we created two new MeasurementFormatters,
so let's configure the other one.
This one we want to use the StandardNumberFormatting,
so we're not going to set this NumberFormatter,
but we're still going to set it to use the provided unit.
And now if I run that, oops, oh.
So, I've configured the formatters.
Now I need to actually use the strings that they'll give me.
So we're already talking to one formatter,
and we say sting from this value.
Well that worked fine when this was a number,
but now it's a measurement.
So now we need to talk to the MeasurementFormatter.
Now, it's a simple one-word change.
Otherwise, it's exactly the same.
Works the same for any formatter.
The other two we're creating string directly to the number,
and you can imagine how this is not very good
for your localization effort.
Here too we want to use a formatter.
So, we'll use our other formatter, and it's,
again, the same thing.
We do string from, in this case a string from Measurement,
and this returns a string, and we pass that to our label.
So now we can run the app.
And now we see our custom units show up.
So this is a start, but,
we're still not really providing real world context,
which is our goal to start with.
We need to show these in real world units.
So we could do some conversion logic of our own,
but MeasurementFormatter can do that for us.
So, we're going to create one more MeasurementFormatter,
and as we have the custom units, MeasurementFormatter,
we're also going to have the Locale-Aware
As we create it, same as we configure it,
except we don't' actually need to do anything,
because MeasurementFormatter out-of-the-box converts
automatically to the unit
that the player expects for their locale.
Now, this is where it's going to be a little bit tricky,
so bear with me a moment.
We're currently talking to one formatter,
asking it for one string,
and passing it directly to the label.
So what we're going to do instead, is we're going to talk
to the customUnitsMeasurement Formatter first,
get its string for this distance.
Then we talk to the localeAwareMeasurement Formatter
and get its string for this distance.
And then we'll use a Swift string interpolation
to put these two things together and generate one string
that we will pass to the label, and we did this for the distance
and we do it also for the dance rate.
And that's all we have to do.
In order to show both our custom units and the units
that the player expects for their locale,
their real world distance.
But we don't need to stop there,
because remember the goal here is to have this game all
over the world in every country.
So miles is great in the United States,
we're in the United States.
We see miles.
But we want to make sure this works in every country.
So I'm going to make use of an Xcode feature that's part
of your scheme, so I'm going to edit my scheme here.
I'm going to duplicate the scheme,
and when it duplicates the scheme.
It's going to ask me for a name for it, so I'm going
to give my scheme a very distinctive name.
And now, having named my scheme,
I'm going to make one simple change, under the run verb,
Options tab, Application Region.
I'm going to our testing into an exotic locale, like, say Canada.
And now, I'm going to run with this scheme,
and with no code change to the app,
with no configuration changes in the simulator,
just changing the scheme, we can now see that in Canada,
this shows kilometers.
That's all you have to do to make this work
with our new Measurement and Unit
and MeasurementFormatter types in Foundation.
Thank you [applause].
Thank you, Daphne.
Thanks so much, Peter.
So, obviously this game is going to be a huge hit.
I'm, like, super pumped about it.
Let's wrap up real quick though.
So, we just saw throughout this whole talk
and in the demo how Measurements and Units are now model objects
that we can use in our apps, which is really awesome.
We also saw that it's super easy to format them
and they require very little work on our part, which is cool.
And the best part is that we get all
of this very powerful localization for free,
without having to specify any objects, or any options,
just right out of the box.
So, now you don't actually have
to suddenly become a polyglot or, you know,
change all the logic and coding in your app
to be able to support this.
You could just use it as-is.
If you'd like more information, you should check out the link.
These sessions are actually already passed,
but if you're super interested in it,
I would recommend checking out the videos,
and thank you so much.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.