Streaming is available in most browsers,
and in the WWDC app.
GDPR & CloudKit
The General Data Protection Regulation (GDPR) is a European Union regulation that requires developers to give users visibility and control over the personal data you store on their behalf. Learn how to use new and existing CloudKit APIs to build privacy into your apps and make sure customers can exercise their GDPR rights.
Hello, my name is Michael Ford and I'm an engineer on the CloudKit team.
Today we're going to talk about GDPR and CloudKit.
GDPR stands for General Data Protection Regulation.
It's a European Union regulation that went into effect on May 25th, 2018.
The regulation focuses on giving users visibility into, and control over, their personal data and how companies use it.
At Apple, we value user privacy, and we want to make sure that developers have the tools they need to build great applications while protecting their user's privacy.
During this talk, we'll discuss new and existing CloudKit APIs that you can use to fulfill user requests related to GDPR or other regulations.
If you're interested in a broader privacy discussion, take a look at the "Better Apps through Better Privacy" session from WWDC 2018.
Now, you might ask, "Why are we only talking about CloudKit APIs?" Let's talk through some of the iCloud syncing services that are available for developers to use, and you will soon see why.
As a developer, you can save user data to iCloud using iCloud Backup, Key Value Store, iCloud Drive, and CloudKit.
Let's talk through their data isolation, data interaction, and API availabilities.
For Backup, Key Value, and iCloud Drive, data is always stored for a single user.
CloudKit offers developers a more flexible API, where you can share data between users, or make data available to all users.
When a user initiates an iCloud Backup restore, application data that was saved is automatically restored.
Application data saved to Key Value Store and iCloud Drive are automatically synced between all of the devices where the user is signed in to their iCloud account.
In contrast, data that your application saves to CloudKit is not automatically mirrored to another device.
You can use the Fetch Changes APIs or Query APIs to access all or part of the data stored in CloudKit.
Backup, Key Value, and Drive APIs are only available on device.
You can use Objective-C or Swift versions of the CloudKit APIs on device, or you can use the CloudKit web services to access CloudKit data from a browser.
As you can see, CloudKit is unique.
It's more flexible and there are more ways to interact with it.
When fulfilling GDPR requests for data saved to iCloud Backup, Key Value Store, or iCloud Drive, you can treat the data like other data that your application has stored locally.
However, with CloudKit, you may need to interact with the server to complete the GDPR request.
Now, as we talk about GDPR more, I want you to remember that I am an engineer, not a lawyer.
If you have questions about GDPR compliance, you should consult a lawyer.
That said, let's get into some of the user rights guaranteed by GDPR.
GDPR guarantees users the right to know what data is stored about them and how it is being used.
A user has the right to access data stored about them.
They have the right to export data.
A user can request that processing of their data be paused.
They can request that their data be deleted.
This is commonly known as the right to be forgotten.
If the data about a user is incorrect, the user should be able to update it.
Users can also object to how their data is used.
Examples include objecting to direct marketing, or objecting to processing a user's data for scientific research.
Finally, a user should have access to human intervention if automated decision making may legally or materially impact the user.
Now, that's quite a list.
Today, we're going to focus on those rights that are broadly applicable to developers using CloudKit.
This will not be a comprehensive talk about GDPR compliance.
Instead, we'll focus on how to use new and existing CloudKit APIs to fulfill user requests related to GDPR or other regulations.
Let's first review some of the concepts inside of CloudKit.
At the highest level is a CloudKit container.
You can have multiple applications interact with the same CloudKit container.
Inside of a CloudKit container, there are three types of databases.
A private database, a shared database, and a public database.
There is one public database per container.
By default, all users can read and write data in the public database.
Every user has their own private and shared database in a container.
Data in the private database belongs to that user.
If your application supports sharing, when a user accepts a sharing invitation, data becomes visible in their shared database.
This is a reference back to the owner's private database, where the actual data resides.
Inside of a given database, you can have multiple zones.
In a private database and a public database, there is always a default zone.
And in a private database, you can create multiple custom zones.
In the shared database, zones show up as you accept a sharing invitation.
Inside of a zone are records.
This is where you store the actual data.
To summarize the conceptual hierarchy of CloudKit, you first have a container.
Then, there are three different types of databases.
In a database, you can have one or more zones, depending on the type of database.
Finally, records are where the data is stored.
Now that we've reviewed those core CloudKit concepts, we're ready to focus on the individual GDPR rights.
We'll look at access and portability together.
To fulfill these rights, you'll need to fetch data from CloudKit.
For deletion, you'll need to delete data from CloudKit.
And for restriction, you will apply a restriction to an account in your CloudKit contianer.
For access and portability requests, you should provide visibility into the data that is saved inside of CloudKit for the user.
You should also provide a way to export the user's data in a standard format.
If you are exporting an image, it would make sense to use a JPEG file format.
For general structured data, use either JSON or CSV, depending on the type and amount of data.
You've just exported data.
While there's no requirement to import data, you may gain some users if you do.
If you have already synced all of the data locally, you can just fulfill the GDPR access or portability request from the data you've saved locally.
In case you haven't, you'll need to fetch the data from the CloudKit server.
So how do you fetch all of the data from CloudKit? Inside of the CloudKit hierarchy, for every container, for every database, you should fetch all zones, and then query for all records.
Let's review some of the code for how you would do that.
First, let's set up our container and database variables.
In this case, we'll focus on the private database.
Next, we'll fetch all of the zones inside of the database.
We'll iterate through all of those zones and set up a query.
You'll notice that this query is only for a single record type, so you may need to run multiple queries.
You could loop through all of the record types that you have, or enumerate them individually.
We'll pass an NSPredicate to the query that is always true, so that we get all of the records of a given record type.
Then we'll actually perform the query in the database.
Finally, once the query returns, you should display the records, or save them for export.
I just showed you code to query for all of the records in the private database.
Shared databases and public databases are more complex.
For a participant, a shared database is a partial view into the owner's private database.
Remember that the data belongs to the owner, so you should only return the data if it relates to the participant.
The same is true of the public database.
By default, any user can read or write data into the public database.
Again, you should only return data that relates to the user making the GDPR request.
Deletion is forever.
You should make sure that the user really wants to delete their data en masse.
You may want to offer a restriction as an alternative.
If the user confirms that they want to delete their data, you'll need to delete all of the user's data from CloudKit.
Let's look at the code for that now.
Once again, we'll set up the container and database variables.
We'll focus on the private database.
Again, we'll fetch all of the zones inside of the database.
Now, we're going to pull the zoneID from the individual zones and use them to create a deletion operation.
This deletion operation passes in the record zoneIDs that you want to delete.
Finally, we perform the deletion operation against the database.
We just saw code to delete all of the zones inside of the private database.
Once again, if your application supports sharing, be selective, but this time be selective in what you delete.
Deleting records in the shared database deletes them for the owner and all other participants in the share.
If there is data that should be expunged in this way, perform the record deletions before deleting the zones.
Once that's complete, delete all of the zones in the user's shared database.
In contrast to deleting individual records, this will not delete the data for the owner, or the other participants in the share.
In fact, a zone deletion in the shared database changes the user from being a participant in the share to being in an invited state.
This means that they no longer see the zones in the shared database.
Finally, in the public database, you'll need to review the data that you're storing related to a given user and determine which data is appropriate to delete.
Finally, let's talk about restriction.
In effect, this is a way to pause or un-pause processing on a user's data.
Under GDPR, you may need to pause processing when the user invokes another right.
Or in some cases, the user may request that you restrict their data rather than delete it.
We've exposed two new web service APIs that you can use to restrict or un-restrict an account.
You can invoke these web service APIs from any version of iOS, provided you have the appropriate credentials.
Let's talk through the code to restrict a user now.
First, you'll need to generate an API token in the CloudKit dashboard.
Once you have the API token, you can create a fetchAuthorization operation.
The fetchAuthorization API was first released in iOS 9.2.
With both the apiToken and the webToken, you have everything you need to make the web service restrict call.
We've wrapped that in a restrict helper function, and we'll take a look at that now.
First, you'll need to URL encode the webToken.
Create the full web service URL by combining a baseURL, apiPath, and query parameters.
Once you've done that, you should actually send the request as an HTTP post to the CloudKit server.
We won't go through the code for that now, but you can find it in our linked documentation.
Once you restrict a user, any operation against that container will result in a managedAccountRestricted error.
You will also see this error if the user has restricted their iCloud account through Apple.
Apple will stop processing the user's data and will return this error for any CloudKit request, so be sure to handle this error in your code.
To review, for access and portability, return all data inside of the user's private database.
For shared databases and the public databases, be discerning when determining which data to return to the user.
To respond to a user deletion request, you can delete all of the data inside of user's private database.
You can also delete shared zones, which removes the user as a participant.
For the public database, be discerning when determing which data to delete.
Finally, we introduced new web service APIs to restrict or un-restrict accounts.
You will see a managedAccountRestricted error if you restrict the user, or if the user has requested that Apple restrict processing across their iCloud account.
With these tools, we know that you can build great privacy into your apps.
For more information, please visit developer.apple.com, and feel free to email firstname.lastname@example.org with any questions or feedback you may have.
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.