Advances in Foundation

Session 723 WWDC 2019

The Foundation framework provides a base layer of functionality for apps and frameworks that's used throughout the macOS, iOS, watchOS, and tvOS SDKs. Hear about valuable enhancements to Foundation collections, performance, internationalization features, and Swift integration.

[ 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 ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US