WWDC2019 Session 220

Transcript

[ Music ]
[ Cheering and Applause ]
>> Welcome.
My name is Steve Breen, and I'm
an engineer on the UIKit Team.
And today, I'm joined by my
colleagues, Troy Stevens from
the AppKit team, and Jacob
clapper from the App Store team.
And today, we're going to talk a
bit about UI data sources.
[Chuckles] All right, so today's
talk is going to be broken into
four segments.
First, I want to talk about the
current state-of-the-art.
How do we interact with data
sources today in the shipping
platforms?
And then we're going to talk
about a brand new approach we're
bring into iOS, tvOS, and the
Mac.
Then we're going to transition
to some demos, get some hands-on
time with this brand new API.
And then finally, we're going to
go on some more detailed
considerations of how to get the
best out of this new API.
All right, so let's talk a
little bit about the current
state-of-the-art.
How do we interact with UI data
sources today, in UITableView
and CollectionViews?
So here we see an example
implementation of the
UICollectionView data source.
Now, if you've worked with
UITableView or CollectionViews,
you've seen this before.
Here, we're providing three of
the required two methods in this
protocol.
And it's pretty straightforward,
right?
We have asked about the number
of sections and the number of
items in each of those sections.
And as content renders, we're
going to ask for cells.
Pretty straightforward.
And this has served us really
well for about ten years now,
right?
And it's got a couple things
going for it.
It's super simple.
You can reason about it right
away.
If you want to provide just two
methods, the number of items in
a section, if you have a
one-dimensional data source,
that's pretty straightforward.
And you can iterate on it pretty
quickly.
But it's also very flexible,
right?
Because you don't really have to
use any particular kind of data
structure to back your data
source.
It could be as simple as a
one-dimensional array.
And if you had a multi-itemed,
multi-sectioned data source, it
could be two-dimensional, right?
Very simple, very
straightforward.
Well, apps are often a little
more complicated than a, you
know, a one-dimensional or
two-dimensional array.
And apps get more and more
complex every year.
They do more things.
Our users demand more features.
And oftentimes, these data
sources are backed by complex
controllers inside of our app.
And these controllers can do a
variety of things.
They can interact with Core
Data, and they could talk to web
service.
They just do a number of
different things.
And I want to visualize this
real quick.
And let's show a conversation
between your UI layer and this
controller layer that's doing
all this heavy lifting to get
your data.
All right?
And the conversation starts out
very civil.
It's like, "Hey, give me a
number of items in a section, or
give me a cell as we render
content."
Very straightforward.
And so far, smooth sailing.
But things get more complicated
over time, right?
You get -- like let's say this
controller has a web service
request that it gets a response
from, right?
It's like, oh, I've got data for
your tweets or whatever, right?
Well, now this controller layer,
which is complex unto itself,
lets the world know, "Hey, I
changed.
Something changed."
All right, so here's where
things get a bit more complex,
right?
So now, it's up to the UI layer
to decide, "Hey, things changed.
Now, I need to -- can change
this into updates for our UI
layer.
And this involves all the
mutations that have to occur
against TableView and
CollectionView.
It can be a bit complex.
And we covered this complexity
last year in our talk Of Tour of
UICollectionView.
And how to construct those batch
updates properly, and mutate
your backing store, all those
kinds of things.
But sometimes, no matter how
hard you try--
[ Laughter ]
You know, things go wrong.
It's an imperfect world.
And judging by the laughter,
you've probably hit this before,
right?
It's not an uncommon thing.
And it's really frustrating,
right?
You hit this, and you're like,
"All right.
What did I do wrong?"
It's me. And as you dig through
your code, okay, fine.
You Google on Stack Overflow,
see what's going on.
And eventually, you might get
frustrated and just call
reloadData.
And we talked about this last
year, and that's fine.
That's correct.
Your app looks okay.
But when you call reloadData,
you get this non-animated
effect.
And it detracts from the user
experience.
So that's not great.
Okay, I want to get
philosophical just for one
slide.
I promise.
What's the problem?
Well, the problem here is
where's our truth?
You know? I mean, who got his?
And who has all the answers?
And the big issue here is that
our data controller -- or it's
acting as a data source -- has
its own version of the truth,
which changes over time.
And the UI has a version of the
truth.
And the UILayerCode is
responsible for mitigating that,
making sure that it's always in
sync.
As we saw, it's sometimes hard.
So our current approach is error
prone.
And primarily because there's
just no notion of a centralized
truth.
All right, so that's the
state-of-the-art.
That's where we are today.
But where are we going?
Well, I'm happy to announce that
for iOS, TVoS, and MacOS this
year, we're bringing a brand new
approach.
And we're calling this
DiffableDataSource.
[ Cheering and Applause ]
All right, so let's dig in.
What is this thing?
No performBatchUpdates.
Let's go on.
And along with it, all the
crashes, hassles, complexity,
all of the stuff that you don't
want to deal with, has been
jettisoned.
Instead, we have a single method
we call Apply.
What is Apply?
Apply is simple, automatic,
hassle-free diffing.
So we do this with a brand new
construct we call a Snapshot.
And it's a very simple idea.
It's effectively the truth of
the current UI state.
And instead of IndexPaths, it's
an association or a collection
of section identifiers that are
all unique and item identifiers.
And you update these not with
IndexPaths, with identifiers.
All right, let's look at
something visual to see what's
going on here.
Okay, I'm really creative guy.
So we've got these FOO, BAR, and
BIF onscreen, right.
And that's what we're
interacting with.
These are identifiers in our
app.
And let's say that our
controller changed.
And now we've got this brand new
Snapshot that we want to apply.
But this is our current
Snapshot.
How do we get from our new truth
to the current Snapshot?
Well, we can see here we've
configured a brand new Snapshot
with BAR, FOO, and BAZ.
And we have some items that are
coming along for the ride that
have just changed order, and
then a new item coming in.
So conceptually an Apply knows
about the current state and
knows about the new state, which
are going to apply to the UI
element.
And there's no step two.
That's just it.
Mm-hmm. All right, so how do we
do this?
Well, we have four classes
across all the platforms.
For iOS and TVoS, we have
UICollectionView
DiffableDataSource and
UITableView DiffableDataSource.
And then on the Mac, we have
NSCollectionView
DiffableDataSource.
And common for all the platforms
is this Snapshot class, which is
responsible for the current
UIState NSDiffableData
SourceSnapshot.
Okay, so enough background.
Let's go on, look at some code.
And for that, I'm going to bring
up my colleague, Troy Stevens.
Troy?
[ Applause ]
>> Thanks very much, Steve.
I am delighted to get to walk
you all through some examples of
using this powerful, yet
beautifully simple new API
today.
So make sure to download the
sample project for this talk if
you haven't already.
That way, you can follow along,
study it at your leisure.
And most importantly, use that
example to really grasp the
mechanics of how all of this
works.
It's not a lot of code.
It's really simple.
Now, when you look at that
example project, you're going to
notice that in addition to the
three examples of
DiffableDataSource usage that
we're going to look at today,
the same project also contains
illustrations of the powerful,
new compositional layout API
that we introduced in Session
215.
Those examples just happened to
use DiffableDataSource as a
really easy way to just populate
their CollectionViews with
sample content.
So let's go to our demo machines
and take a look.
So I've got our demo app up
here.
And as we walk through our
different examples today, we're
going to notice a repeating
pattern.
It's a simple three-step
process.
So anytime you want to put a new
set of changes, a new set of
data into a Collection View or
UITableView with the full data
source, all you do is you create
a Snapshot.
You populate that Snapshot with
the description of the items you
want to display in that update
cycle.
And then you apply the Snapshot
to automatically commit the
changes to your UI.
DiffableDataSource takes care of
all the diffing and issuing the
changes to the UI element.
So let's look at a concrete
example.
I'm going to open up Mountain
Search.
And this is a fairly typical
search UI, right?
We can all look at it and reason
about what it does.
You might see this in a Contacts
app, for example, but in this
case, we're looking at mountain
peaks around the world.
So as you can imagine, there's a
search field at the top.
As I start typing in that search
field, we would expect to see
the list automatically filtered
to just the matches.
And we'll see that if I start
typing, we can do just that.
And it all happens very
automatically and with nice
animations.
This is all incredibly easy to
do with extraordinarily little
code using DiffableDataSource.
So let's take a look at how it
works.
For this example, look at the
MountainsView ControllerSource
file.
And all the action starts when
the user types in the search
bar, right?
So we have our callback here,
searchBarTextDidChange.
It's going to be sent to our
controller.
And from there, we just call out
to our own performQuery function
passing it, the search text that
we got from that search bar.
Now performQuery itself is
remarkably simple.
All we do is call out to our
MountainsController.
That's our model layer object.
And we ask it for a filtered
sorted list of the mountains
that match our search term.
So now we've got that list of
mountains.
We go through that three-step
process that I mentioned.
We create a new NSDiffableData
SourceSnapshot.
Now this Snapshot is initially
empty.
It contains nothing.
So it's up to us to populate it
with the sections and items that
we want.
In this case, we only have a
single section to display.
So we're just going to append
one section, and we're just
going to arbitrarily call it the
main section.
Next, we append the identifiers
of the items that we want to
display in this update.
Now formally speaking, we
usually pass an array of
identifiers here.
But in Swift, you can also make
things a lot more elegant by
working with your own native
types here.
So if you have a native type,
and it can even be a value type,
such as a struct or an enum, if
you make that type hashable,
then you can just pass your own
native objects in terms of the
Swift syntax of what you're
doing.
And we'll look at the
implications of how that works
in just a little bit.
So now, we've constructed our
Snapshot.
It's ready to go.
All we have to do is call to our
DiffableDataSource, ask it to
please apply that Snapshot,
animating the differences.
DiffableDataSource goes off and
automatically figures out what's
changed between the previous
update and the next.
Notice that there's no code at
all here, where we had to stop
and figure out -- reason about
what we were displaying in the
previous update cycle before the
user type.
That one more character, it all
happens automatically.
There's nothing to worry about.
We're not dealing with
IndexPaths, which are fragile
and ephemeral, right?
They refer to a certain
particular update, and have a
different meaning and a
different update.
We're dealing with identifiers
that are robust and enduring.
So everything is just really
simple to do here.
Before we leave this bit of
code, I want you to notice
something about Snapshot, which
you may have already noticed.
It is a generic class in Swift,
so it's parameterized by the
SectionIdentifierType and the
ItemIdentifierType that we've
decided to use.
So let's look first at our
SectionIdentifierType, which is
pretty trivial.
This is a really handy technique
for the common case, where you
just have a single section,
right?
You can just declare an enum
type for this.
And one nice thing about enums
in Swift is that they're
automatically hashable.
Hashability is synthesized for
them.
So here, we just have a trivial
case of an enum with one case,
and there's nothing more to do
there.
For our mountain type, we'll
look at our Mountains
Controller, which is again, our
model layer.
And here, we see that we've
declared mountain as a Swift
struct.
And we declared that struct type
as hashable so that we can use
it with DiffableDataSource
natively rather than explicitly
have to pass an identifier.
And the important requirement
there is just that each mountain
be uniquely identifiable by its
hash value.
So we achieve this by giving
each mountain an automatically
generated unique identifier.
And here, where we implement
that hashability conformance
that we promised, we use just
that identifier to provide the
hash value.
So in this way, we're going to
have each mountain, even though
mountains, again, are value
types.
They just get passed around by
copying.
There's no pointer you can
reference.
But the identifier and the hash
value specifically of that
identifier makes them unique
enough for DiffableDataSource to
track them from what -- one
update to the next.
And as part of hashability,
we're implementing equality
tests here, too.
So we've looked at how you issue
changes to DiffableDataSource.
What about how we set one up to
use?
Let's go back to our
MountainsViewController.
And conveniently enough, we've
created a function called
ConfigureDataSource, where we
configure our data source.
And it's really just a little
bit of code here.
So in this case, we're working
with a UICollectionView.
So we instantiate a
UICollectionView
DiffableDataSource.
We parameterize it with our
section and item types.
Pass it a pointer to the
CollectionView that we want to
work with.
Now DiffableDataSource will take
that pointer and automatically
wire itself up as the data
source of that CollectionView.
So there's nothing else for us
to do.
Lastly, we have this trailing
closure parameter, the
DiffableDataSource to the
initializer.
And all this is, is the code
that you would normally have to
write if you were implementing
your own data source from
scratch, you would implement the
cellForItemAt IndexPath method.
That's data source callback
method, where you're expected to
do exactly what we do here.
We call back to our
CollectionView and ask it for a
cell of the appropriate type to
display the data we want to.
And we populate that cell with
what we want to show, and then
we return it back.
So this is just taking that
cellForItemAt IndexPath code and
conveniently transplanting it
into a nice closure
encapsulation that we pass when
we instantiate the data source.
One thing that's nice and
convenient and different here is
that in addition to being given
the IndexPath of the item that
we're being asked for, we're
also given its identifier, or in
this case, the native Swift
value type that we -- that
corresponds to that particular
item we want to show.
So we get our mountain passed
in, there's no more work to do.
We don't have to go take that
IndexPath and go look up which
of our model layer objects it
pertains to.
We just have our mountain passed
in for us.
So we just can get the name of
the mountain and set it as the
label text of that cell.
And that's all there is to it.
Everything else about how you
set up and configure your
Collection View is the same as
before.
There's no performBatchUpdates
code hiding anywhere in this
sample code.
That's all there is to it.
It's very easy.
Let's look at another example.
So here, we have a mockup of the
familiar Wi-Fi Settings UI from
iOS.
And this one's only going to be
slightly more complicated than
the last because we have two
different sections that we're
working with here.
We have what we call the Config
section at the top, where we
have the Wi-Fi enable/disable
switch.
And the display of the current
network that we're connected to.
And then below that, we have
another section that's
dynamically updating that shows
us a list of detected networks
that we could potentially
connect to.
And another thing to notice
here, if we tap the Wi-Fi
disabled switch, or toggle it
back on, we get a nice,
animated, collapse or reexpand
of our UI.
And that all comes for free with
DiffableDataSource.
So let's take a look at how this
dynamic UI is implemented.
Going to our
WiFiSettingsViewController,
we're going to look at the
updateUI function, which is just
a function we've named updateUI.
And we've taken care to make
sure that this gets called
anytime there's a change in what
we need to display.
Now most of the time, that might
be because we've detected a
different set of networks that's
now available.
But it could also be because the
user has toggled that Wi-Fi
enable/disable button.
Anything that changes our UI,
we've ensured that this will be
called.
So we still have here that same
three-step process.
After getting the data that we
want to display, we start with
just getting those config items,
because we're usually going to
want those.
We go ahead and create a
Snapshot.
And this Snapshot again is
initially empty.
So let's populate it with what
we want to show.
We append the first section, our
config section, that appears at
the top.
And we append its items.
So there's going to be one or
two items depending whether
Wi-Fi is enabled.
Now if Wi-Fi is enabled, we also
want to talk to our backend, to
our model layer.
Ask it for lists, the current
list of available networks.
And we're going to wrap that
list in some item types that
we're going to look at in just a
moment.
We append this section for that
list of networks.
And then we're going to append
the items that go in that
section.
And notice that because we're
working with two different
sections here, we can be
explicit about which section
were appending each set of items
to.
That's it.
We're ready to go.
We've described everything we
want to display.
So we just ask our
DiffableDataSource to apply
those changes, optionally
animating the differences.
Now there may be times where you
want to choose not to animate
the differences.
For example, when you're first
bringing up your UI, and you're
showing the initial set of data,
you might or might not want it
to animate.
Oftentimes, you want it to be
instantaneous.
So you'll pass false for
animating differences, as we do
in this example.
Looking at our types that we're
working with here, we've got a
section type and then item type
that parameterize the
DiffableDataSource.
Looking back at the top, we can
see, as we might imagine, that
section is still an enum type.
We just need two different
sections to work with here.
And again, section as enum type
is automatically hashable in
Swift, so we're good to go with
that.
And then here we've declared an
item type.
This is a struct type again, as
with the mountains.
And we've declared it as
hashable.
And the reason for declaring
this type is that when we look
at our list, it mostly contains
lists -- network items in it.
But in addition to that, it's
got this oddball item at the
top.
It's the Wi-Fi enable/disable
switch.
That's not a network items.
So we have a heterogeneous list
here.
And all we're doing is
encapsulating each item in this
generic wrapper type.
But that wrapper type, since
that's the type of item we're
going to hand off the
DiffableDataSource, we have to
take care to make sure that it
conforms to hashable and that
the items are uniquely
identified by their hash value.
So for the network items, we can
just get that unique identifier
from the network item and
transplant it into the capital
item.
And for the config items, we
just dynamically generate a
UUID.
Looking down here at our hash
function, again, it's just
computing a hash based on that
unique identifier value.
And that's all it takes for
DiffableDataSource to be able to
identify the items that are the
same from one update cycle to
the next.
Let's look at where we configure
our data source.
This is really very similar to
before, except this time, we're
actually working with the
UITableView.
And from the perspective of
creating and committing
Snapshots, it really doesn't
matter, the API is very similar.
But for the setup, we've got to
instantiate the right type of
DiffableDataSource.
So we got a UITableView
DiffableDataSource.
We parameterize that class name
with the section and item types
that we're going to use.
And we pass a pointer to the
table view, which again, gets
automatically wired up with this
DiffableDataSource as its data
source.
Lastly, we've got that trailing
item provider closure.
And this looks more complex at
first glance, but really, it's
only that way because we have a
variety of different types of
items.
We have those heterogeneous
items to display.
Basically three types of items,
and we're handling them
differently.
But this code, even if we
weren't using
DiffableDataSource, it would be
there.
It would just be in your cell
for ItemIndexPath method.
So again, very simple to set up
this UI.
And even configuring the
DiffableDataSource is not that
hard.
So our last example is maybe the
most fun to watch.
Here we've got a
UICollectionView again that is
displaying items that are
represented as color swatches.
And they're initially in a
randomized order, color wise.
If I tap the Sort button, we can
watch them be iteratively sorted
into spectral order.
So in addition to being really
mesmerizing and fun to watch
[chuckles] --
[ Applause ]
Credit to Steve on this one.
In addition to being really
mesmerizing, and fun to watch,
this example is just a little
bit different than the others in
the way that we construct and
commit the updates.
So if we wanted to, if we were
just -- if our goal was just to
sort everything and jump to the
end state, we could do that
here, right?
This demo is set up so that we
can watch the sort progress
iteratively through each
intermediate stage.
So in order to do that, we've
got our sword implementation
that goes through the stages and
gives them to us one at a time.
It gives us each successive new
state, and we create a Snapshot
and apply a Snapshot each time
that happens, each time we do an
update cycle.
And that gives us this nice,
fun, animated view.
So let's look at how we
implement this and how it's
different.
We're going to look at the
InsertionSortViewController
here.
And all the interesting action
happens in this PerformSortStep
function.
So as I said, we always have
that three-step cycle.
We're going to get a Snapshot,
populate it, and then apply it.
But in this case, instead of
asking for a new, empty
Snapshot, we're going to take
advantage of the ability to ask
our DiffableDataSource for its
current Snapshot.
Now this Snapshot is
prepopulated with the current
truth of what's being shown in
that UICollectionView as the
CollectionView sees it.
So we don't have to start over
from scratch.
We can start from that state and
compute the next intermediate
state.
Down here, where we populate our
Snapshot, we'll see that
familiar AppendItems function
being called.
But we also have a DeleteItems
function here.
And when you look at the
Snapshot API, you're going to
see there are whole variety of
functions for modifying an
existing Snapshot for when
you're doing this kind of usage.
You can move items from one
place to another, and so on.
But in other respects, this is
pretty much the same thing.
We're just trying to set up the
new final state of what we want
to display.
And we're just working with
identifiers and not IndexPaths,
which is really nice.
So lastly, when we're done, all
we have to do is apply that
Snapshot to our
DiffableDataSource.
And we get this nice,
progressive sorting.
So pretty cool.
[ Applause ]
Thank you.
[ Applause ]
And here's where we set up our
DiffableDataSource.
Again, it's for a
UICollectionView.
We specify the types that we're
using, the CollectionView, and
then we have our item provider
closure, which is real simple,
because we're just displaying
these color swatches.
So we've seen, through these
three examples, how easy it is,
and how little code it takes to
create these dynamic UIs that
are also really robust to
changes.
We can make changes without fear
of hitting these weird
exceptions in our code.
It's all very robust.
It's baked into the API how that
works.
Now, we did touch on some
interesting nuances, and the
importance in particular of
uniquely identifying your
objects.
And if you're working with Swift
types, what those Swift types
need to conform to and so on,
that they need to be hashable.
To take us on a deeper dive and
bring these issues into clear
focus.
I'm going to invite my
colleague, Steve, back on stage.
Thank you.
[ Applause ]
>> And so I kind of have a sense
for how this UI works walking
through all these demos.
I want to go through some more
detailed considerations of how
to get the best out of this API.
All right.
First, as we've seen all
throughout the demos, there's
basically just three steps.
You want to create a Snapshot,
configure it as you need it, and
apply it.
So you always want to call the
apply method.
Now conversely, you don't call
performBatchUpdates anymore.
That's old and dead.
Called insertItems, none of
that.
If you call these, the framework
will complain.
And, you know, you will see
that.
Okay, there are two ways to
create Snapshots.
And the most common way is to
create an empty Snapshot.
And here we see, we construct
the Snapshot with the types for
the sections and items.
And you can also create one,
like we saw in the last demo,
from the current state.
And this is very useful
sometimes when a certain action
occurs where you need to modify
just one little thing.
Now when you create this, you're
going to get a copy back.
So you can mutate that at will,
and it will not affect this --
the data source that it came
from.
Now once you have that Snapshot,
if you did ask some questions,
like, you know, "How many items
you got?
How many sections you got?
Identify the identifiers."
You can do that.
And there's a lot of API you can
check out in the SDK.
But here's a few of those.
All right, so I promise you no
more IndexPath.
So when we configured these
Snapshots, you'll never see an
IndexPath in this API, through
explicit API.
So, so far, we've seen a very
common pattern of appending
items and appending sections,
all those kinds of things.
But you can also do things like
insert and move and delete.
And all of these API's take in
other relative identifiers to
tell you where things go.
So if I want to insert 20 new
identifiers that are all unique,
before or after another
identifier, we have explicit API
for that.
So you'll say, "Insert these
identifiers before this
identifier."
Now, if you don't have anything
in that particular section,
there's no identifier to anchor
that insertion or move with,
that's why we have the append
APIs.
You can append items and
sections.
Now, in that familiar path,
where you have a number of
sections, where you're
configuring your Snapshot, you
might loop through your section
data.
And in that instance, you can
append items without specifying
the section.
There's a default parameter in
Swift that specifies to null.
In this instance, we'll just
append to the last-known
section.
So it makes that code very
pretty.
All right, so let's talk a bit
about identifiers.
These have to be unique.
And this isn't a big problem,
because most apps have some kind
of notion of identity in their
model objects.
So it's a very natural step to
just use that unique identifier.
Now in Swift, this needs to
conform to hashable.
And conveniently enough, many
things in Swift do this
automatically.
We saw auto synthesis for this
in the enum types.
And we have string, and integer,
and UUID, all these great things
that are available for use for
DiffableDataSource.
Now, we also saw that you can
bring in some model data into
these identifiers.
And this is really convenient.
Now your identity needs to come
from some unique identifier.
But you can also bring in other
attributes.
When we saw like the name and
the mountains, you can control
our example.
And this is really handy,
because when you configure your
cell, you have everything you
need right there in line.
No looking it up somewhere else.
All right, here's a little,
quick template that we see
throughout the examples that
talks about how to create a
hashable thing in Swift where
they're using a struct.
Pretty straightforward stuff.
All right, so what about
IndexPath-based APIs?
Okay, so we have CollectionView
and TableView.
They have tons of
IndexPath-based APIs.
A lot of them in the Delegate
methods.
So if a user interacts with the
content and taps on an item,
you'll get this familiar
Delegate message didSelectItemAt
IndexPath.
But we've moved into this great
new identifier-based world.
What are we going to do with
this IndexPath?
You know, that's old school.
So here we have new API's.
Let you translate between
identifiers, IndexPaths, and
then back from IndexPaths to
identifiers.
So here, we see an example.
We're taking that identifier,
that IndexPath that's past, and
converting it back to an
identifier.
And this is constant time.
This is super, super fast.
All right, so speaking of
performance.
So we've done a ton of work to
make this as fast as possible.
And there's a lot of really
great low-level stuff that just
blazes.
Now, if you've ever studied in
the computer science, the whole
notion of how diffs occur, you
know that this is a linear
operation, O of N.
And in simple terms, all it just
means is the more items you
have, the longer your diff
takes.
So during development, it's
super important to measure your
apps.
We all know this.
We want to make sure that the
main queue is always as free as
possible to be real responsive
to user events.
And we render everything really
quickly.
So as you're measuring your app
-- you all measure your apps --
during development, especially
towards the end.
I want to make sure that
everything's really free on that
main queue.
Well, if you find that you have
a large number of items in that
linear diffs taking that little
extra time, it is safe to call
the Apply method from a
background queue.
[ Applause ]
Now, what's really cool about
this is the amount of API we
have to support this.
There's no API.
[ Laughter ]
Which is the best API.
[ Cheering and Applause ]
Right?
All right, so what happens if
you call Apply from the
background queue?
Well effectively, the framework
is smart enough to know, "Hey,
I'm on the main queue."
And it says, "Let's just keep
doing this diff right here."
And once that diff is computed,
we jump back to the main queue,
apply the results from our diff,
and life goes on, like normal.
All right, so just one caveat,
and I promise, just one.
If you choose this model to call
Apply from the background queue,
be consistent.
Just always call it from the
background queue.
You never want to mix and match
calling it from a background
queue or the main queue.
Just always do it the same way.
And we're good citizens.
We'll complain about this if you
get it wrong.
All right, so at Apple
collaboration is a big part.
It's the main strength of our
organization, the way we talk to
each other and solve problems
together.
And part of this as the
frameworks authors is to make
sure that all of your clients,
or who you talk to on a regular
basis, and find out what they're
struggling with.
And this clearly is one of the
things they're struggling with.
So during this, we chatted with
the folks who are working on the
Share Sheet, this new,
redesigned Share Sheet in iOS
13, with the great airdrop
extensions.
And they found out about it kind
of late in the game when they
had this brand new design.
And they said, "Oh, this looks
great.
We need this."
And indeed they did.
So I want to bring up one of my
colleagues from the Share Sheet
team, Jacob Clapper, to walk us
through that adoption.
Jacob?
[ Applause ]
>> Hi, everyone.
I'm really excited to show you
how the Share Sheet has been
able to take advantage of the
great new CollectionView APIs in
iOS 13.
All right, so here we are in the
brand new Share Sheet.
And this Share Sheet actually
takes advantage of the new
compositional layout API's and
DiffableDataSource.
But where DiffableDataSource
really shines is in the brand
new airdrop extension.
So the airdrop extension has a
browser that's browsing for
devices.
And we actually already use
UUIDs to uniquely identify each
device that's discovered.
So as new devices are
discovered, we are able to
create an empty Snapshot, append
our sections and items, and
Apply the differences.
DiffableDataSource takes care of
the rest.
And the animations are
beautiful, no matter how many
items are removed or deleted.
DiffableDataSource has been a
game changer for us.
And we can't wait to see what
you do with it in your apps.
I'm going to hand it back over
to Troy for some final thoughts.
[ Applause ]
>> Boy, it's so exciting to me
to hear how much
DiffableDataSource is already
making a difference in the
development of our own apps.
And we couldn't be more thrilled
to make this same API that we've
been adopting internally
available to use for developers
across our platforms.
So as we've seen today,
DiffableDataSource dramatically
simplifies the work that you
have to do to get model data
into your CollectionViews and
your UITableViews.
We think it's really a game
changer.
It makes it incredibly simple
and robust.
There's no more puzzling
exceptions to debug and
hard-to-write batch update code.
You can really just focus on
what you want to do with your
app and leave the rest to the
framework.
DiffableDataSource is available
for you to use today on iOS,
TVoS, and MacOS.
In addition to providing-- to
figuring out the diffs for you,
and automatically committing
them to your UI, you get these
nice animated changes, right?
And there's no additional work
for you to do to get that nice,
pleasing user interaction
effect.
The built-in diff is fast.
It has been heavily stress
tested.
DiffableDataSource is a robust
API that is ready for you to
take and run with it.
So go out there and take it.
Adapt to your apps to use
DiffableDataSource.
We can't wait to see the burden
this is going to take off your
shoulders and the delightful
user experiences that you're
going to be able to create with
it with much less time, much
fewer headaches.
If you found this talk
interesting, and you work with
CollectionViews, we've got
another one that you're really
going to love in Advances in
Collection View Layout.
We describe an entirely new
layout system, a way to just
simply describe any custom
layout that you want to have in
your CollectionView.
And be able to see it
implemented without any sub
classing with highly performant
results.
So we think you're really going
to love this.
Make sure to catch that session.
And thank you so much for
watching today.
[ Applause ]