CloudKit is the framework that powers iCloud services on all of Apple's platforms. Learn about the new features that have been added over the past year, and dive deep into the new sharing APIs that lets you share private data between iCloud users.
[ Music ]
Good afternoon and welcome to session 226,
What's New in CloudKit.
My name is Paul Seligman.
I'm an engineer on the CloudKit client team and I'm very excited
to be with you here today to talk about some updates
and new features in the CloudKit ecosystem.
So, what are we going to talk about today?
We're going to start off today
with a quick overview of what is CloudKit.
We're then going to switch gears and talk about Telemetry,
a new feature which gives you the ability
to visualize how your CloudKit-backed applications
We're going to talk about some improvements to our APIs
and their availabilities.
And we're going to talk about sharing, a new feature
which gives your users the ability
to share their data while maintaining full control
over who has access to it.
So, what is CloudKit?
CloudKit is a technology that gives you the ability
to have your application data
and your user data available wherever you need it.
CloudKit is a framework which gives you access
to a database stored on iCloud.
We use CloudKit extensively inside of Apple.
This gives you the confidence to know that we are committed to it
and it gives us the confidence to know that we can scale
to hundreds of millions of users.
CloudKit is available on all of Apple's platforms.
Now, I'm going to quickly summarize an introduction
to CloudKit, covering topics that we did a few years ago
in introduction to CloudKit.
I recommend you go check out that session after this one
if you'd like a broader introduction to the ecosystem.
I'd also like to mention these talks which go into more detail
about specific aspects of CloudKit.
You can use these to find ways that you
and your application can use CloudKit to your advantage.
Now, all the sessions are online and are linked
Here we see the list of objects
that every developer using CloudKit needs
to be familiar with.
Let's step through them starting with Containers.
A Container is the mechanism by which we silo data up on iCloud.
So, notes uses a Container.
Photos uses a Container and your Applicationm when built on top
of CloudKit, will also have access to its own Container.
If we look inside of a Container,
we see that Containers contains databases.
And until last week this is our data model.
A Container had two databases, the public and the private.
With the introduction of Sharing,
we introduced a third database type, the Shared database.
More about that in a little bit.
The basic unit of storage inside of CloudKit is a record.
A record is a group of key value pairs and typically it maps
to an object model, an object in your data model.
Now, we don't store records loosely inside of databases.
Rather, we encapsulate records inside of Record Zones.
Many records can exist inside of a record zone
and many Record Zones can exist inside of a database.
Different databases support different types of Record Zones.
The public and private databases have a default record zone.
This is where all your records are going to end
up unless you specify otherwise.
Your private database can also contain custom Record Zones
which are zones that your application creates
and uploads into the database.
And lastly, the new shared database consists
of shared Record Zones.
With the introduction of sharing,
we're going to add one new core concept to this list.
The concept of a Share.
A Share is a subclass of a Record and as such,
lives alongside Records inside of Record Zones.
You can think of a Record as being the thing that you want
to share and Share representing how you're going to share it,
things like participants and permissions.
Again, we'll get more into that in a little bit.
Just know that it exists.
Now, I mentioned that we use CloudKit extensively inside
of Apple and I wanted to take a moment
to highlight some of our clients.
In the public database are a couple applications
that you've probably used, the WWDC App and the News App.
The News App in particular stores article content, images,
etc., in the public database.
And it's a great use of the public database.
Storing content that you want generally accessible
to all of your users.
And we can contrast this with the private database.
The private database is where you're going
to store the user's private data.
We have several clients of this inside
of Apple including iCloud Backup, iCloud Drive,
iCloud Photo Library and Notes.
And I'm happy to report that two new features,
Notes Collaboration and Activity Sharing are both built
on top of CloudKit Sharing.
So, as such, the Notes
and Activity Applications are clients of the shared database.
Two years ago we introduced CloudKit
by providing two native frameworks,
one on iOS and one on macOS.
Last year we extended that family, adding a data framework
for tvOS and two web frameworks, CloudKit JS
and CloudKit Web Services.
The web frameworks give your users access to CloudKit data,
whether they're on the web or on a platform
that doesn't have a native framework alternative.
And this year we're going to go ahead and complete the circle
by adding a native framework on watchOS.
With this, we now have a native CloudKit framework available
across all of Apple's platforms.
Let's take a moment and step
through some notable platform-specific changes
Starting with macOS.
The big news that we want to share with you is
that you no longer need
to distribute your application via the Mac App Store in order
to take advantage of CloudKit.
Using the new iCloud for Developer ID feature,
you can directly entitle your application to use CloudKit
and other iCloud services via your permissioning profiles.
Next I want to talk about server to server.
This is a feature that's been out in the wild
for a few months now and it's the, gives you the ability
to have your servers directly talk to CloudKit servers
as administrative users.
Your servers can authenticate themselves
to CloudKit using a public/private key pair you've
previously established on the CloudKit Dashboard.
And you can set your servers to have full rewrite access
to the public database.
This is a great way for you to import your data
from your servers into CloudKit or to export data
from CloudKit to your servers.
Or to keep two sets of data up-to-date
between your servers and CloudKit.
With the introduction of CloudKit as a native framework
on watchOS, you now have another mechanism
to keep your watch Apps and your iOS Apps up-to-date.
In that way, you can think of CloudKit as an alternative
to the watch connectivity framework.
And CloudKit comes with one notable advantage.
That is standalone functionality.
CloudKit uses NSURL session and as a result,
we're going to send our network
over the best available interface.
If your watch is connected to an iOS device,
we'll send traffic over that iOS device.
But the watch is also capable of talking directly
to CloudKit servers when it's on Wi-Fi.
Now, we are presenting a full-ish version
of the CloudKit API.
With the introduction of CloudKit as the native framework
on watchOS, you now have the ability
to write similar application code that uses CloudKit
across all of Apple's platforms.
The Activity App for example has used this
to write similar CloudKit code on iOS and on watchOS
to provide the activity sharing future.
Now, notice I said similar code and not identical code.
As you write code and deploy it to the variety
of Apple's platforms, you need to keep in mind
that the strengths and realities of each platform.
In other words, you're going
to hit limited resources in some cases.
You need to keep in mind the CPU characteristics,
the storage capacities and the network characteristics
such as latency and throughput.
You can use this in determining how often you want to talk
to the servers and how much data you're willing
to send over the wire.
As always, testing is the best way
to tune your App appropriately for the platform and ensure
that your users are going
to have the best possible experience.
Now I'd like to change gears and talk about Telemetry.
Telemetry is a new feature which allows you
to visualize the behavior
of your CloudKit-backed applications.
We surface Telemetry as a series of charts which are available
on the CloudKit Dashboard.
You can use these charts to visualize your behavior
in the public database or an aggregate of your behavior
across all users' private databases.
You can scope these charts so that you're viewing data
on an hourly, daily, weekly or monthly basis.
And you can choose to view the entirety of your application
or scope these charts down to a specific operation type.
So, let's go and see what this looks like.
Here we have the CloudKit Dashboard
which you're probably familiar with.
And I want to call your attention to this new UI element
in the lower left, the Performance tab.
When you select the Performance tab, you now have access
to a series of charts which gives you information
about how your clients are behaving.
They fall into two categories.
The first is performance charts and here we surface information
such as the number of operations per second
and the average size of your requests.
And again, you can scope this so that you're visualizing data
in the public or private database along a variety
of timescales and potentially on a per-operation type basis.
The other type of chart that we surface is what we call our
And the one I want to call attention to is client errors.
This tells you what percentage of requests that you have issued
that have resulted in a client error.
Now, a client error is a subset of the errors
that you might receive from a CKOperation.
And it is that subset which we think your application should be
able to resolve and take action on.
So for example, maybe you tried to save a record
and there was a conflicting record change upon the server.
Or perhaps you attempted to fetch changes from a Record Zone
that the server doesn't know about.
Both of these would be considered client errors
and would be surfaced in this chart.
By being able to visualize your error trends,
we hope that you can take advantages to find
when your client's, situations
when your clients are seeing abnormally frequent number
Now, we've said in the past that error handling is essential
for a CloudKit-backed Application.
The difference between an Application
that handles errors well and an application
that handles errors poorly is the difference
between a functioning App and a nonfunctioning App.
It's that serious.
It's an integral part in writing a CloudKit-based Application.
So, we hope that you can use these charts to figure
out situations in which you need
to go examine how your clients are handling their errors.
For more information on how to handle errors well,
I want to invite you to tomorrow's talk,
CloudKit Best Practices.
We'll spend some time diving into proper error handling.
Next, I'd like to talk to you about some improvements
to our APIs, all of which are new since the last WWDC.
And really, there's four that I want to call your attention to.
Starting with Long-Lived Operations.
Long-Lived Operations give you a mechanism
by which you don't have to repeat work
that you've already done gets the server.
So, as it stands now, when your application goes away, it exits.
Any operations that were outstanding
on behalf of it are torn down.
Even if that operation was moments away from completing.
By making your operations long-lived,
your operations can outlive the lifetime of your Application.
They will continue running and CloudKit will continue
to cache responses from the server in a local cache.
When your Application is next launched,
you've resumed the operation.
And we're just going to go ahead and feed you those caches
out of our local cache.
In many cases, this can completely eliminate the need
for another network round trip.
We're going to talk about Long-Lived Operations
in more detail at tomorrow's talk, Best Practices, 9 am,
I hope you can join us.
Next I want to touch on a topic that we've heard
of from our developers and it has to do
with CKOperation behavior on bad network.
And the picture I want to paint
for you here is we've got a device.
The device has network that says it's available
but we're not getting any traffic going
over in either direction.
And as a side note, you can actually go ahead
and replicate the scenario yourself using the network link
conditioner, a great developer tool
for replicating behavior such as this.
Now, a CKOperation is a subclass of an NS operation.
And as such, as a QualityOfService property.
If your operation is marked as user interactive
or user initiated, then on a bad network, we're going to tear it
down after 1 minute and give you a network timeout error.
If your operation has any of these other QualityOfServices,
we're going to go ahead
and continue attempting it for up to seven days.
It might not be what you expected.
What's more, if you choose not
to set an explicit QualityOfService
on your CKOperation, we will choose one for you
and we choose utility.
So, if you add all this up, we get a lot
of developer reports saying, you know, it's been 5 days,
why is my operation still outstanding.
So, we want to address this and we're going
to address this with two new APIs.
The first covers network inactivity and we expose it
as the timeout interval
for request property on a CKOperation.
It defaults to 1 minute and it's the amount of time
that we're willing to wait for a packet to go over the wire.
If we don't hear any traffic received or sent in that amount
of time, we're going to tear down your operation to tell you
that the network timed out.
We're also going to expose an end to end timeout,
and we expose this as the timeout interval
for resource property on a CKOperation.
This defaults to seven days and it governs the amount of time
that we're willing to wait for an entire network round trip
from your device to the service server
and its completion back to the device.
Now, I want to make note
that a CKOperation may issue multiple network requests
as it's going about its job.
So, a CKOperation may take more time than you expect so long
as you are making progress in the wire.
Next, I want to talk about how do we efficiently fetch a series
of record changes when there are many Record Zones
up on the server.
As we'll learn when we get into sharing,
your client may see more Record Zones then you've seen
in the past.
So, our answer to this used to be that you need
to fetch the entire list of Record Zones
from a database using a CKFetchRecordZonesOperation.
There's a couple problems with this.
We don't want you to poll and we don't want to have
to fetch the entire list of Record Zones down from server.
So, we're no longer going to recommend this for this approach
and we're going to replace it with two new concepts.
The first, CKDatabaseSubscription.
This is a new subscription type
that will fire whenever any change happens inside
of a database.
Even in a Record Zone that you haven't learned about yet.
And we're going to couple
that with a CKFetchDatabaseChanges
This is an operation that allows you to ask the server for a list
of Record Zones that have pending changes
since some point in time in the past.
Okay, so now you have a list of Record Zones that you want
to go fetch changes for.
How are you going to do that?
Well, the old way was that you would issue a
You'd pass in a single Record Zone and get the changes
for that single Record Zone.
We don't want you to have to enumerate, you know,
sequentially through all these Record Zones so we've gone ahead
and deprecated this operation outright.
And we've replaced it with a brand-new operation
with a very similar sounding name,
the CKFetchRecordZone changes operation.
This is essentially a batch interface over the old operation
and it gives you the ability to fetch record changes
across multiple Record Zones in a single network round trip.
So, let's go ahead and visualize this.
Here we have a database, several Record Zones,
each Record Zone has a series of records.
And the client, which is up-to-date
with all of these changes.
Now, along come a couple of new records.
Your client, by virtue of having previously saved a
CKDatabaseSubscription, will cause a push to be generated
on the server and sent to the client.
Next, using a CKFetchDatabaseChanges
operation, you can ask the server for a list
of Record Zones that have pending changes.
In this case, the first and the third.
Now, armed with that list of Record Zones,
you can issue a CKFetchRecordZoneChanges
operation requesting all those records and all
of the change Record Zones in a single network round trip.
And lastly, I'd like to talk
about how do you efficiently fetch changes
when there are many records sitting
in a Record Zone up on the server.
If you've used CloudKit to do this in the past,
then you're familiar with the moreComing flag, which was set
on a CKFetchRecordChanges operation to inform you
that not only have we given you some changes but there are more
up on the server that you should go fetch
with a subsequent CKFetchRecordChanges operation.
Now, there's a couple problems with this approach.
The first is that we've distributed the logic
of check the flag and issue another operation
to all of our clients.
It's another potential point of failure.
And secondly, while you're determining that you need
to fetch and cue a new operation and doing that in cueing.
CloudKit is sitting around idle.
We want to address both of those so we took advantage of the fact
that we made a brand-new operation,
CKFetchRecordChanges operation, to change this model.
Instead of us telling you
when there are more changes available,
you tell us what your intention is via the new
When this is set to true, then CloudKit will fetch a batch
of changes from the server, hand them to your client,
and then immediately go back to the server
for the next batch of changes.
This allows us to keep the pipeline full,
pulling network data over the network while you're processing.
Now, we think that this is going to be such a common behavior
that we've gone ahead and we've made this the default behavior
for this new class.
So, new CKFetchRecordZoneChanges operations
by default will fetch the entirety of records
down from a particular Record Zone.
As you might imagine, if you've got a large Record Zone, say,
your user's iCloud Photo Library up on the server,
this means that the subsequent operation to fetch all records
in the Record Zone is going
to take a very long time to complete.
We want to make sure that you are resilient in the face
of operations that fail part way through.
We don't want to have to go ahead and re-download batches
that we've already fetched from the server.
So, we've added a new callback on this new class.
And after we hand you a batch of changes, we're going to go ahead
and tell you about an updated server change token.
And your code is going to be responsible
for doing two different things.
First, you're going to go ahead
and commit all the per record changes
that you've received from the server.
And secondly, you're going to go ahead and you're going to cache
that server change token.
If the operation fails at some point in the future,
you can issue a brand-new CKFetchRecordZoneChanges
operation, pass in this locally cached server change token
and essentially pick back up where you left off.
No need to re-download the batches of changes
that you've already downloaded from the server.
And so these are just 4 of the API improvements that we hope
that you can take advantage of as you write applications backed
by iCloud, backed by CloudKit.
And with that, I'd like to go ahead and switch gears
and invite up Jacob Farkas to walk us through the sharing UI.
My name is Jacob Farkas and I'm an engineer
on the CloudKit team.
And today I'm going to talk to you
about how you can add CloudKit sharing UI to your application
by only writing a couple of lines of code.
We've introduced a new class in CloudKit called CKShare.
It's a subclass of CKRecord and it's responsible
for storing two important pieces of information.
One, what is shared, and two,
what that record is being shared with.
So, let's look at an example of this.
We've got our private database here and we have a note
in the private database that we'd like to share.
To do that, we're going to create the CKShare
and initialize it using that record as the record.
You always need to create a Share with a root record
so there's always something in the Share.
Next, we're going to save that Share and the root record
to the server at the same time.
You want to do that because there's a new property
on CKRecord that's a reference
to the Share that we're creating.
By saving the root record and the Share at the same time,
that reference will be set to the share you just created.
So, now we've defined what we want to share but we need
to define who we want to share that with.
To do that, we've created a new lookup service in CloudKit.
This lookup service takes a email address and it turns it
into a CKShare participant.
You can set the Share participant on the Share,
save that Share to the server and now
that person's iCloud account has access to the Share.
We also support looking up users via phone numbers
or CloudKit user record IDs.
Now, we want to let users have control over what appears
in their shared database so we don't want to these records
to just instantly appear.
The user should have control so they should be able to accept
that Share and join it.
But that means we need a way of telling that other user
that we've made a share for them and invited them
and that they need to join it.
And we do that via URLs.
Every share has a URL which uniquely identifies it.
If the user taps on this URL in iOS or clicks on it in macOS,
we're going to show the accept UI.
We're going to ask them if they want to join the Share.
And if they do, they'll be taken to the App
and shown the items in that Share.
The great thing about a URL is that if this user is
on an older platform or on a platform
that doesn't support sharing,
this will take them to iCloud.com.
And we can show them information about the Share
and tell them how they can accept it and join the Share.
So, let's put this URL into an email and send it off
to the other participant we invited.
They're going to receive the email, click on it and now
in their Shared Database they see the Share and the Note
that we created and shared with them.
The great thing here is that the Share Database is actually just
a view into the owner's private database.
So, if this other user has the right access to the Share,
if they update that Note, we're going to see
that same change happen in our private database.
So, let's take a look at what this looks like in the UI.
All right, we've got Notes here and we've added sharing
to Notes in macOS X Sierra.
By using the same CloudKit sharing APIs
that we're making available to all of you today.
So, you'll see that there's a new Share Add Person button
And if we tap on that, we get a new sheet
that lets us choose how we want to share that URL.
When we hit Share, the system UI is calling into Notes
and telling Notes that it needs to save that Share
and the root record to the server.
Once it's done that, the system UI shows a Mail Compose window.
We can invite the other user.
We hit Send.
And the system UI is actually saving that Share to the server,
looking up the participants
and sending the email off to the other user.
So, if we switch over to our iPad here with the other user,
we see the email we just sent.
We can tap on that URL.
And we'll be asked if we want to join the Share.
When we do that, we're launched right into Notes.
The Share shows up, the Note downloads and now we're sharing
that Note with the other user.
If I make changes on the Note from the originator,
let's say I check off avocados on the list and I add limes
as something else to pick up.
We'll see those happen in the note that's being shared to us.
So, let's look at the code behind that.
You're probably all familiar
with the CloudKit framework already which is where CKRecord
and the new CKShare object live.
If you want to use this new system sharing UI, you're going
to find that on macOS in AppKit and in iOS on UIKit.
We'll start by looking at the iOS sharing API.
Before we create a Share, before we bring up the UI,
we need to create a Share of course.
So, we'll create a Share here with our record.
We will set a couple properties to let the UI show that Share,
title and a thumbnail.
And then we move on to creating a cloud,
a UI cloud-sharing controller.
We initialize that with the Share we just made
and we pass it a preparation handler.
This preparation handler is going to be called
when it's time to save that share
in the record to the server.
So, our handler here will create a CKModifyRecords operation.
Save the record and Share to the server and when it's done,
it will call the completion handler.
Next, we might want to set some properties
on this UI cloud sharing controller.
One of the properties we can set it is the available permissions.
We can say whether we want that Share to be publicly shared only
or maybe we only want to give the participants
We also want to set the present,
presentation controller source view so that the pop-up appears
in the same place as the button that we tapped to add people.
We'll want to set ourself as a delegate
so that we get callbacks about what's happening in the UI.
And finally we call Present.
And when we do that, we're going to get a pop-up
that looks something like this.
Now, if you've already saved the Share of the server,
you can call UI cloud sharing control with just the Share.
And it will present a list of invited users
and let them manage the users on the Share
and stop sharing if they'd like.
Everything is taken care of for you by the system UI.
The macOS sharing API is really similar so we're just going
to go very quickly and highlight the differences here.
First off, you create an NSItem provider
and you register your CloudKit Share with that.
This handler looks the same as what we saw before.
You save the Share in the record to the server
and when you're done, you call the completion handler.
Next, you're going to create an NSSharingService.
That sharing service is going to have a delegate
that you set yourself and you call perform
with the NSItem provider that you created earlier.
Finally, NSSharingService is callback based.
So, if you want to set options on what the share can do,
you'll do that with callback like options for Share.
On macOS, the Share create UI will look like this.
And if you want to modify the participants
on a Share, it'll look like this.
Next, if a user accepts a Share for your Application,
your Application is going to get launched
and it'll receive this callback Application user Accepted
That callback will contain Share metadata that'll tell you
about the Share in the root record
that the user just accepted.
It looks really similar on iOS with the exception
of using UIApplication instead of NSApplication.
And finally, you need to tell the system
that your Application supports CloudKit sharing.
And you do this via the CKSharingSupported key
in your info P list.
We're also happy to announce
that we've added full sharing support
you can create Shares, accept them and we've given you some UI
that you can use to manage the Share.
You can try this all out right now in the CloudKit catalog.
So, I'm going to hand things off now to my colleague, Vanessa,
who is going to tell you a little bit more
about sharing in depth.
Thank you, Jacob.
Hi. And good afternoon.
My name is Vanessa Hong and I'm an engineer
on the CloudKit server team.
So, today we will deep dive into sharing by looking
at some common use cases.
We'll start with the data that's being shared
and then we'll go step-by-step all the way
down into the internals of the CKShare object.
Then I'll talk about how you can call our sharing APIs
if you want to create your own custom UI.
And then finally we'll close it out with some special notes.
So, let's get started.
Jacob showed how to Share a single record.
But the item the owner wants
to share may not be a single record.
It may consist of many records.
Possibly already linked via CKReferences.
And your application may want a participant
to see only a subset of these records.
This is why we introduced a new field
on the CKRecord called the Parent Reference.
Set the Parent Reference on any records that you wish
to be included in the shared hierarchy.
And you can set this up even before the user decides
When the user does share,
you will create the CKShare only with the root record.
Then, all of the all the descendent records
that are linked to the root record via the Parent Reference
are automatically included in the shared hierarchy.
So, let's see what this looks like in the shared database.
A shared database is only a view
into the owner's private database.
So, it doesn't contain any physical records.
When a participant accepts a Share,
they only see what is shared to them.
So, they see the shared hierarchy.
This means there's no two copies of these records.
There's only one copy and that copy lives
in the owner's private DB.
So, this means the owner and all the participants are interacting
with the same set of records.
This kind of contention may end up causing conflicts.
To learn how to deal with conflicts, I'd like to refer you
to a past WWDC talk called Advanced CloudKit from 2014.
Now, a read/write participant can modify,
remove and add records.
But we don't want them to be able
to add just anything they want into somebody else's DB.
For instance, they cannot add a random root record.
They also cannot add a record without a Parent Reference,
even if it's somehow linked to the shared hierarchy.
So, the correct way to add a new record via the shared database
is to set a Parent Reference and link it to the shared hierarchy.
So, even though you're adding a new record
for the participant via the shared database,
that new record lives in the owner's private DB.
So, what this means is all records that are added
by the participant are counted against the owner's quota.
So, the producement's quota is not affected
and your developer quota is not affected.
The owner's private database is the only place
that we store these records so we can count them only
against the owner's quota.
And that's how you share multiple records.
Let's take a closer look at the shared database.
So, here we have two Shares from two different owners
but the Shares have the same name,
so how do you tell the difference?
Well, we glossed over a very important detail which is
that all records in CloudKit live in Zones.
And a Zone is identified by that CKRecord Zone ID.
The Zone name is the name of the custom Zone that you created
in the owner's private DB.
An owner name is the owner's user record name.
So, in our example, the two Zones have the same name
but different owners.
So, let's say the first owner shares something else
but in a different Zone.
So when you call the FetchDatabaseChanges API,
you will see this new zone appear.
And then when you call FetchRecordZoneChanges,
you'll see the new record and the Share.
Now, let's say the second owner shares something else
but in the existing Zone.
Well, this Zone already exists so we won't create a new one.
We'll just reuse it.
When you call the FetchChanges APIs, you will see
that this Zone has changed and that there are new records.
And that is our shared database.
So, let's take this one level down and look
at the CKShare object.
So, before the owner can create a Share,
they must do something to Share.
So, the records describe what to Share.
And the CKShare describes how those records should be shared.
So, we're going to be looking at the how.
So, as Jacob mentioned, every CKShare is a CKRecord
but it has some additional properties.
And we've been looking at how these properties apply
to the lifecycle of a Share.
So, we're going to start from the beginning
and the owner will create a Share.
And the owner has to decide what is the public permission
for the Share.
So, in this case the owner says it should be none,
because he wants to invite participants.
And let's say he invites two participants.
Their status is automatically invited.
And then the owner decides what permission
to give to each participant.
Then, the owner saves the Share
and then he gets a URL for the Share.
So, there are two things happening here.
One is that the Share has a state.
And the State says only these two participants can accept
The owner is the one with the URL and it is his responsibility
to tell people about this URL.
So, even if he tells 100 people about this URL,
only these two participants can accept the Share.
So, when a participant accepts the Share,
they accept via the URL.
And after that accept,
their acceptance status becomes accepted.
And then the permission
in the Share is exactly what the owner gave them.
So, now let's say the owner wants
to create a more open share.
So, let's start over.
The owner sets up a Share and then he decides
that the public permission should be readOnly
He doesn't add any participants.
He just saves the Share.
And then he gets a URL for the share.
So, there's still two things happening.
One is that the Share has a state
and it says anyone can join.
And the owner has a URL.
And it's his responsibility to tell people about it.
So, if he tells 100 people, then all 100 people can join.
So, when they join, they would have to join via the URL
and then that participant appears
in the Share and accepted state.
Their permission is inherited
from the Share's public permission field.
And that's how you set up the Share and accept the Share.
So, the next phase of the Share's lifecycle is
when a participant leaves.
And a participant can leave a Share
by deleting the CKShare object from their shared DB.
This will also remove the shared records from their shared DB.
So, to be clear, the CKShare still exists.
It exists in the owner's private DB.
It's just that this participant no longer is
in the Share in accepted state.
And the owner has full power over his Share
so he can remove anybody he wants.
Let's say he wants to remove everybody.
He would do that by deleting the CKShared object
from his private database.
This will also remove the pointer
from the root record to the Share.
And now the owner is back in the initial state of being unshared.
So, let's move on and talk about the CKShareParticipant object.
So, if you've seen this object before in the lifecycle,
you saw the acceptance status and the permission.
But now let's look at the user identity field.
This has a look up info.
And the look up info is how this participant was invited
to the share.
So, it will have their email, phone or user record ID.
And the name components are the first and last name
and this is populated with when the participant accepts
Every CKShareParticipant is mapped to an iCloud account.
So, let's say the owner invites 4 participants and we were able
to find iCloud accounts for the first two but we couldn't
for participant 3 and 4.
This is perfectly okay.
CloudKit will create a temporary placeholder
for participant 3 and 4.
And the only people who can accept as participants 3
and 4 are the ones who can prove
that they owned the email address or phone number
that the owner invited them with.
This is called the verification flow.
This will link the email or phone to their account
so that they never have to go
through the verification flow again.
And that's all the objects that we have in sharing.
So, now let's move on and talk about sharing APIs.
So, if you want to create your own custom UI,
you can call our APIs.
And there are two things that you can do.
So, on the behalf of the owner,
you can help them set up the Share.
On behalf of the participant,
you can help them accept the Share.
On watchOS and tvOS, there's no built-in system UI.
So, you can ask your user to go to a different platform to set
up a Share and accept the Share.
And then the Share data is available across all platforms.
Alternatively, you can just call our sharing APIs.
And this is how you do it.
On behalf of the owner, you can help them add participants.
You would have to look up by email, phone or user record ID
and then translate that to a CKShareParticipant object.
Once you have the CKShareParticipant object,
add those to the share.
And then call CKModifyRecords operation to save the Share.
Now your application has a URL for the share.
And it is up to you, the application or the owner,
to tell people about the URL.
When a participant accepts a Share,
we always start with the URL.
You first have to convert the URL to CKShareMetadata object
and then pass that metadata object
to the CKAcceptShares operation.
Now, the participant will show up in a Share in accepted state.
Now, there are some limitations to the accepted API.
For privacy reasons, we cannot return
to you their name components.
And the verification flow is not available.
So, if you get this error
or if it has iCloud account Boolean is false,
then you can ask your user to open up the URL themselves.
This will trigger the system or the web to take them
through the verification flow.
And that's our sharing APIs.
So, now let's talk about your users.
A user of your application can invite anyone they want via any
email or any phone number.
Now, what this means is the potential audience
for your application is much larger
than your current user base.
So, these MIT's may not have installed the latest
They might not even own an Apple product.
So, when they click on the URL, we take them to the web.
And in the example for Notes, this is what they'll see.
They will be asked to join a Share,
after which they'll see the shared Note.
And they can interact with this Note just
like they would on a device.
But this is the Notes Web Application
that lives on iCloud.com.
What about your Application?
Well, by default, your users will see something like this.
It has your App icon and it asks your user
to go on the latest device.
Which is not the ideal user experience.
So, I do have some good news for you.
You can go to your CloudKit Dashboard
and configure a fallback URL.
So, when an invitee clicks on a URL that is shared to them,
we redirect them to your fallback URL.
We'll append the token that is from the unique URL
for the Share so that you can immediately take them
to accept the Share and then show them the shared data.
Now, I hope you're excited to get started on sharing.
There is just one last thing that you need to know.
A CKShare is of this new record type
and this record type behaves just
like any other record type in CloudKit.
You can create custom fields on it.
You can run queries.
You also have the first created in the development environment.
And the easiest way to create it is just log in as a user
in your dev environment and share something
from your private database.
This will trigger the creation of the record type.
And then go to your CloudKit Dashboard
and deploy your scheme into production.
If you don't do this, then users
in the production environment may get errors
when they create the Share
because the record type doesn't exist yet.
And that wraps it up.
So, you learned today that CloudKit is available on all
of our platforms including watchOS and is available
on the web via CloudKit JS.
Telemetry is available on our CloudKit Dashboard.
It's a great way to visualize your application's behavior
including error trends.
There are many API improvements including Long-Lived Operations,
[inaudible] and the new fetch changes APIs.
And now you know all about our new feature, sharing.
You've seen this system UI and you know how
to create your own custom UI by calling our sharing APIs.
And you've seen all the objects that we used
in sharing including the sharers lifecycle.
And I bet you will go back and configure those fallback URLs.
So, I want to thank you for sharing this experience with us.
I want to draw your attention to CloudKit Best Practices.
It's tomorrow at 9 AM.
It's a great session on how to use CloudKit more effectively.
Thank you and enjoy the rest of your WWDC conference.
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.