Starting with CloudKit

Jeroen Zonneveld Who am I?

Jeroen Zonneveld

The Netherlands

� iOS & tvOS Developer

� Triple, wearetriple.com Table of Contents

‣ What is CloudKit? ‣ Creating a database ‣ Connect and query to the database ‣ Store data in the database ‣ References ‣ GDPR ‣ Going to production What is CloudKit?

Who already heard of CloudKit? Who have used CloudKit before in a project? What is CloudKit?

"CloudKit is the framework that powers iCloud on all Apple platforms. It's feature-rich API to store and query your own custom data in iCloud." Examples What is CloudKit? Saving using the notes app Saving a backup of your WhatsApp history.

Check apps on your iOS device using CloudKit: Settings > Apple ID > iCloud What is CloudKit?

watchOS tvOS iOS macOS What is CloudKit?

Apple device CloudKit iCloud database What is CloudKit?

In development, when you run your app through Xcode on a simulator or a device, you need to enter iCloud credentials to read records in the Public database Shared database publicPrivate database. database In production, the default permissions allow non- authenticated users to read records in the public database but do not Focus op Public & Private allow them to write records. What is CloudKit? What is CloudKit?

Pros Cons ‣ Easy to use in development ‣ Only available for Apple devices ‣ Syncing data between Apple devices automatically ‣ No need to worry about a secure backend ‣ Sharing between Apple devices ‣ Data in a private database is not visible for the developer ‣ No need to login for the user or give permission to allow you to use CloudKit Creating a database Capabilities aanzetten iCloud + vinkje cloudkit niet vergeten. Now you can click on “CloudKit ”.

Creating a database Indien de app daar niet staat, keer de app builden. Creating a database

.developer.apple.com Creating a database Creating a database

iCloud dashboard Beheren van databases Manage database Logs bekijken om fouten in te zien ‣ van queries

Grafieken inzien van request ‣ Check logs snelheden. Inzien gebruik gemaakt wordt van ‣ View telemetry een public database API’s beheren, indien je gebruik wil ‣ Check public database usage maken van de CloudKit JS . ‣ Manage API access Klik op aapje record types om een tabel aan te maken. Voeg vervolgens de velden toe, voorzien Creating a database van een naam en een type en sla het record op. Zo gemakkelijk is het om een tabel te maken!

Vergeet daarna niet om ‘recordName’ (default property) als index actief te zetten in het indexes tabje.

Private of public maakt niet uit, dat bepaal je later in code. Creating a database

Field types you can add ‣ String, Int, Double ‣ Date / Time ‣ Location ‣ Asset ‣ Reference Creating a database Connect and query to the database Connect and query to the database

Important Objects ‣ CKContainer id firstname lastname ‣ CKDatabase ‣ CKRecord 1 Jeroen Zonneveld CKRecordZone ‣ Database ‣ CKRecordIdentifier Connect and query to the database

struct ACTalk { var title: String? var speaker: String? var date: Date? var location: String? } Connect and query to the database

Get a reference to the database

let privateDatabase = CKContainer.default().privateCloudDatabase

let publicDatabase = CKContainer.default().publicCloudDatabase

Public database Private database Connect and query to the database

Query data from a public database

let publicDatabase = CKContainer.default().publicCloudDatabase

let predicate = NSPredicate(format: "TRUEPREDICATE") let query = CKQuery(recordType: "talks", predicate: predicate)

publicDatabase.perform(query, inZoneWith: nil) { (results, error) in

}

Testen in simulator? Zorg dat iCloud account is ingesteld! Anders krijg je [CKRecord] een error Connect and query to the database

Je ontvangt data op een aparte Query data from a public database thread. Bij UI aanpassingen altijd naar de mainthread gaan! publicDatabase.perform(query, inZoneWith: nil) { [weak self] (results, error) in

guard error == nil else { // show error to the user return }

results?.forEach({ (talk) in let title = talk.object(forKey: "title") as? String let speaker = talk.object(forKey: "speaker") as? String let date = talk.object(forKey: "date") as? Date let location = talk.object(forKey: "location") as? String

let talk = ACTalk(title: title, speaker: speaker, date: date, location: location) self?.talks.append(talk) }) } Connect and query to the database Connect and query to the database

Query data from a public database

let publicDatabase = CKContainer.default().publicCloudDatabase

let predicate = NSPredicate(format: "TRUEPREDICATE") let query = CKQuery(recordType: "talks", predicate: predicate)

query.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]

publicDatabase.perform(query, inZoneWith: nil) { (results, error) in

} Connect and query to the database

Query data from a public database Connect and query to the database Connect and query to the database

struct ACTalk { var title: String? var speaker: String? var date: Date? var location: String? var speakerImage: UIImage? } Connect and query to the database

Query data from a public database

publicDatabase.perform(query, inZoneWith: nil) { [weak self] (results, error) in

results?.forEach({ (talk) in

var speakerImage: UIImage? if let speakerAsset = talk.object(forKey: "speakerImage") as? CKAsset, let data = try? Data(contentsOf: speakerAsset.fileURL) { speakerImage = UIImage(data: data) }

}) } Connect and query to the database

Query data from a public database

publicDatabase.perform(query, inZoneWith: nil) { [weak self] (results, error) in

results?.forEach({ (talk) in let speakerAsset = talk.object(forKey: "speakerImage") as? CKAsset let speakerImage = speakerAsset?.image }) }

extension CKAsset { var image: UIImage? { guard let data = try? Data(contentsOf: fileURL) else { return nil } guard let image = UIImage(data: data) else { return nil } return image } } Connect and query to the database Store data in the database Store data in the database Store data in the database

Store a talk in the database

func addTalkToDatabase(title: NSString, speaker: NSString, location: NSString, date: NSDate, image: UIImage) { let talkRecord = CKRecord(recordType: "talks")

talkRecord["title"] = title talkRecord["speaker"] = speaker talkRecord["location"] = location talkRecord["date"] = date

let asset = try? CKAsset(image: image) talkRecord["speakerImage"] = asset

let publicDatabase = CKContainer.default().publicCloudDatabase

publicDatabase.save(talkRecord) { record, error in // check error // do complete action } } References References References References CKReference

Talk

title speaker Favorite

talk

Talk

title speaker References

struct ACTalk { var record: CKRecord? } References

Store a talk reference in the favorites database guard let record = talk.record else { return } let privateDatabase = CKContainer.default().privateCloudDatabase let favoritesRecord = CKRecord(recordType: "favorites") let reference = CKReference(record: record, action: CKReferenceAction.deleteSelf) favoritesRecord["talk"] = reference privateDatabase.save(favoritesRecord) { [weak self] (record, error) in guard error == nil, let record = record else { return } self?.favorites?.append(record) } GDPR

General Data Protection Regulation GDPR

‣ Since May 25, 2018, only if you target people in the EU ‣ Create a privacy statement which data you are collecting ‣ Add an option to requests to Delete Data GDPR

Delete all data from the user

CKContainer.default().privateCloudDatabase.fetchAllRecordZones { zones, error in guard let zones = zones, error == nil else { print("Error fetching zones.") return }

let zoneIDs = zones.map { $0.zoneID } let deletionOperation = CKModifyRecordZonesOperation(recordZonesToSave: nil, recordZoneIDsToDelete: zoneIDs)

deletionOperation.modifyRecordZonesCompletionBlock = { _, _, error in guard error == nil else { print("Error deleting records.") return }

print("Records successfully deleted in this zone.") }

CKContainer.default().privateCloudDatabase.add(deletionOperation) } Going to production Going to production Going to production Going to production

Production talks to production CloudKit, Only for tvOS you are able to select that the build should be connect to development Summary Summary

‣ Create a database online using the developer portal ‣ It’s easy to query or insert data using the CloudKit framework ‣ Use references to create links between tables ‣ Add a ‘remove my data’ function to your privacy statement to confirm to GDPR rules More about CloudKit… Sharing API

https://developer.apple.com/documentation/uikit/uicloudsharingcontroller CloudKit JS

https://developer.apple.com/documentation/cloudkitjs Apple sessions

Introducing CloudKit - Session 208, WWDC 2014 Advanced CloudKit - Session 231, WWDC 2014 CloudKit JS and Web Services - Session 710, WWDC 2015 CloudKit Tips and Tricks - Session 715, WWDC 2015 What's New with CloudKit - Session 704, WWDC 2015 What's New with CloudKit - Session 226, WWDC 2016 CloudKit Best Practices - Session 231, WWDC 2016 Build Better Apps with CloudKit Dashboard - Session 226, WWDC 2017

developer.apple.com/icloud/cloudkit Examples on GitHub

‣ Altconf schedule example ‣ GDPR delete all data playground

https://github.com/jeroenzonneveld/cloudkit-examples Thank you!

Jeroen Zonneveld