Transcript
[ Music ]
[Applause]
>> Hello, everyone.
My name's Tina.
I'm an engineer on the
Foundation team.
I'm going over the highlights of
the new APIs we added to
Foundation.
Now, let's get started with the
API highlights.
These are a variety of things
we're going to talk about today.
Let's get started with Ordered
Collection Diffing.
It's an API that allows you to
compute and code and apply the
diff between collections.
To illustrate this, I'm going to
tell a story.
There is a bear string that
really wants to become a bird.
Let's figure out how to do this.
We notice that the bear has an E
and A that the bird doesn't
have.
And it also needs an I and a D
from the bird.
So let's go ahead and remove the
E and A and insert an I in the
middle and a D in the end.
So that took two removals and
two insertions to transform a
bear to the bird.
You can do this very easily with
Ordered Collection Diffing API.
The diff here is a collection
difference type.
It's a collection of insertions
and removals that describe the
element that is inserted or
removed and the offset of the
element in the collection.
Here it contains two insertions
and two removals as we just saw.
We can apply the diff on the
bear to get a new bird string.
This is a very powerful API and
not only works with strings, but
any collection types.
That was Ordered Collection
Diffing.
Now, let's talk about data and
contiguity.
Your app may be building
pictures or other types of files
from the disk.
Usually, these forms of data can
easily be represented by a
contiguous area in memory.
On the other hand, your app may
be downloading data from the
internet using dispatch data or
URL session, for example.
This produces bytes that arrive
in multiple chunks in different
time, and occupy these
contiguous areas in memory.
Prior to Swift 5, struct Data
represented both contiguous and
discontiguous regions.
This unified interface was very
simple to use, but it also meant
that to provide whole buffer
access to the underlying raw
bytes, we needed to copy the
regions into a contiguous area.
This meant that the performance
may sometimes be unpredictable.
In fact, we have look at
real-world usage of data, and
every discontiguous data gets
flattened [assumed spelling]
sometime during its lifecycle.
So from Swift 5 and onwards, we
promise that struct Data is a
contiguous buffer type.
And to present this promise in
syntax, we introduce the
ContiguousBytes protocol.
Conforming to this protocol in
the case that the type offers
direct access to the underlying
raw bytes in a contiguous
manner.
So you don't need to worry about
accidentally flattening your
data anymore.
Now, how are we going to work
with other buffer types that
don't promise contiguity?
We introduce two new protocols
by taking struct Data's
interface that's independent to
contiguity and generalize it
across various buffer types, and
we arrive at DataProtocol.
That is a collection of bytes
and MutableDataProtocol to offer
additional mutability
guarantees.
Buffer types offered by
Foundation the Swift Standard
Library and the dispatch
framework have adopted these
protocols.
You probably have worked with
some of the types, including
Data, array of UInt8 and
DispatchData.
We encourage you to adopt them
on your types or methods too as
a generic constraint.
Now, let's talk about
compression.
Oftentimes, you want to make
your data as small as possible.
It could be that your
application is running on
devices with limited disk space
or you need to transmit
resources from or to the
internet.
This is a very common task.
So we added the compression API
to data in Swift.
It is now as simple.
Thank you.
[ Applause ]
It's as simple as this one line
of code.
The API also supports four
compression algorithms to offer
you different balances between
speed, memory and compression
ratio.
I believe you can find one that
best suits your needs.
Now, let's move on to units.
Foundation already supports 21
classes to represent common
physical units such as length,
speed and duration.
We extended the unit duration
class with subsecond precision
units up until picoseconds.
That is a trillionth of a
second.
The unit frequency class uses
hertz as a base unit currently,
and this year it gained
framesPerSecond.
It's functionally equivalent to
hertz but its cemented meaning
makes it ideal to represent
on-device FPS measurement.
The UnitInformationStorage class
is new.
It can be used to represent the
amount of digital information.
The basic units are bits, bytes
and nibbles.
And the commonly used SI prefix
and base-2 prefix units from
kilo and kibi up until yotta and
yobi are also supported.
You can use it along with
MeasurementFormatter to format,
say, data size, or you can use
it with ByteCountFormatter if
you want more exact control.
And we have more formatters for
you.
You probably have seen strings
like these somewhere every day.
Like, the status of a message
you sent is read 1 hour ago, or
there is a payment due tomorrow.
That is a date or time displayed
as a relative point to the
present.
It's not very trivial to make it
correct for all locales, so we
added the
RelativeDateTimeFormatter to
help you with this.
[ Cheering ]
Thank you.
And just like other formatters,
the return string is locale
friendly, and there are multiple
styles you can use.
Another thing you probably want
to format is a list, and we also
added ListFormatter to make this
super easy for you [laughs].
[ Applause ]
It formats a list into a string
that uses the correct separator
and conjunctions for all
locales.
Here is another example.
Say there is an event that's
going to occur at three
different dates.
So you probably want to show a
string like the top or the one
on the bottom that you spell out
the name of the month on your
Event page.
And, of course, you want to
display those using the correct
format and localized name for
different locales.
This is important, because, for
example, in Europe, they usually
counting before month in
writing.
You can also do this very easily
with ListFormatter.
It comes with a property called
itemFormatter whose role is to
format each item in a list.
In this case, we want to format
a list of dates, and we know
that Foundation supports date
formatting using DateFormatter.
So let's use it as our date
itemFormatter.
And this is all we need.
We're all set.
This gives your localized
strings as the examples on the
bottom.
You can easily change the date
style by setting the property on
DateFormatter.
ListFormatter works with all
formatters, so please take
advantage of it and make string
localized greatly in your
application.
So that was Formatter.
Now let's move on to Operation
Queue.
Imagine in your app there are a
bunch of background tasks
running concurrently.
At this moment, the user wants
to save the state of this
running app, so how would you do
this?
Well, you want to make sure all
the running tasks are finished
before you can save.
So it might be tempting to do
something like this.
That is you check the number of
running operations in the
concurrent queue, and if there
isn't any, it must mean that
everything we scheduled must
have finished.
So we're ready to save, right?
Nope, this is wrong.
Please don't do this.
It's possible that some other
tasks are being scheduled at the
same time in all their threads,
like right after the check
passes and before the save
happens.
What you actually want here is a
barrier to guard the safe
operation and to make sure that
no other tasks can run while the
app is being saved.
Something like this.
So we're very excited to let you
know that Operation Queue now
supports barrier.
You should add the BarrierBlock
and put your saving operation
inside the barrier.
This guarantees that the save
operation is the only task
running at that particular
period of time, and it only
executes when everything
previously scheduled is finished
and that no other new jobs will
run before it's finished.
[ Applause ]
Another update we have for
Operation Queue is progress
reporting.
Sometimes you may want to track
the overall progress of your
concurrent jobs and maybe
display a progress bar in your
UI.
You can now do this by setting
the totalUnitCount on the
progress property of the
Operation Queue.
Each operation you added to the
Operation Queue counterbuilds
[assumed spelling] one unit of
completion to the overall
progress once operation is
finished.
Next up. Filesystem.
iOS now supports USB and SMB
volumes, which is a network
filesystem.
This means that you should be
prepared to handle files that
may exist on other volumes.
So make sure you use
FileManager's
itemReplacementDirectory when
choosing locations to write new
file contents to when doing an
Atomic Safe-Save operation.
Next, be prepared that volumes
may disappear completely if the
user pulls out the USB drive or
you lose network connectivity to
an SMB server.
So if you choose the memory
mapped files when reading data
objects, make sure you use
mappedIfSafe as your reading
option.
This will allow the system to
map the file into virtual memory
but only if the file is located
on nonremovable volumes.
Accessing files from USB or SMB
drive may be tangibly slower
than when you access files on
the internal drive.
So if you're not doing this
already, please defer file
system access to non-main thread
to make sure your app remains
responsive.
You may need to test file system
capabilities before you use
them.
For instance, the APFS features
that you're familiar with like
cloning may not be available
when you access external volumes
via USB or SMB.
You can test for these
capabilities in events with
various URL resource keys, or
you can make sure you're
prepared to handle and receive
errors.
You can find more information
about this in the What's New in
File Management and QuickLook
talk.
Next up. Swift update.
Some Swift APIs were designed
originally with Objective-C in
mind, and we continued to
improve them over the years.
First is scanner.
This is the API in Swift 4.
It used to use NSString and
return objects by reference.
In Swift 5.1, we simplify this
interface to this one line of
code.
What's more, the new API uses
the string type of Swift so that
strings are collections of
graphing instead of code points.
This means that you can now work
on complex sequences with the
new scanner API perfectly, such
as emojis.
Another improvement is
FileHandle.
It used to throw exceptions when
errors occur on the underlying
file descriptor.
This year, we introduced
error-based API, so you can now
handle the error right away at
call sites.
The API for writing data now
also works with data protocol.
It's also optimized to work with
noncontiguous data.
So these were the highlights of
the new APIs.
We encourage you to grab the new
Xcode and try these exciting
features out.
When writing new methods to work
with binary data, consider
making them bound on the data
protocol type instead of
requiring particular collection,
like array of UInt8.
If you're displaying dates or
lists in your UI with strings,
use RelativeDateTimeFormatter or
a ListFormatter to makes sure
the string work in every locale.
We urge you to use Operation
Queue's barrier to avoid race
condition.
And if you're using some sort of
progress-reporting mechanism,
switch to use the one provided
by Operation Queue.
Thank you.
[ Applause ]