iOS CloudKit

iOS CloudKit

Building a backend server, managing user authentication, and paying for database hosting can be overwhelming for indie developers.

Apple solves this with CloudKit, a powerful backend-as-a-service framework built directly into the Apple ecosystem.

CloudKit provides cloud storage, user authentication, and seamless data syncing across all of a user's Apple devices.


The Three Databases

CloudKit organizes your app's data into three distinct databases:

  1. Public Database: Accessible by all users of your app. Perfect for global leaderboards, news feeds, or shared public assets.
  2. Private Database: Highly secure and tied directly to the user's iCloud account. Only the user can see this data. Great for personal notes, private photos, or custom settings.
  3. Shared Database: Used when a user explicitly shares specific records from their private database with other specific iCloud users.

Frictionless Authentication

One of the greatest benefits of CloudKit is that it eliminates the need for login screens.

If a user is signed into their iPhone with their Apple ID, they are automatically authenticated with your CloudKit backend!

You do not need to manage passwords, handle forgotten email loops, or implement complex OAuth flows.


Creating and Saving Records

Data in CloudKit is structured using CKRecord objects. Think of a CKRecord like a dictionary or a JSON object.

You define a record type (like a database table), assign key-value pairs, and save it to the desired database.

Saving a CloudKit Record:

import CloudKit

func saveNoteToCloud() async { // Create a new record of type "Note" let newNote = CKRecord(recordType: "Note") newNote["title"] = "My First Cloud Note" as CKRecordValue newNote["content"] = "This is syncing magically!" as CKRecordValue // Get the user's private database let privateDatabase = CKContainer.default().privateCloudDatabase do { // Save the record to the cloud asynchronously let savedRecord = try await privateDatabase.save(newNote) print("Successfully saved record ID: \(savedRecord.recordID)") } catch { print("Failed to save to iCloud: \(error.localizedDescription)") } }


Querying Records

To fetch data back from CloudKit, you construct a CKQuery using an NSPredicate (which acts like a SQL WHERE clause).

If you want all records of a certain type, you just pass an empty predicate.

Fetching Records:

import CloudKit

func fetchNotes() async { let privateDatabase = CKContainer.default().privateCloudDatabase // Predicate(value: true) means "Get everything" let query = CKQuery(recordType: "Note", predicate: NSPredicate(value: true)) do { let (matchResults, _) = try await privateDatabase.records(matching: query) print("Found \(matchResults.count) notes!") } catch { print("Error fetching records: \(error)") } }


Exercise

Which CloudKit database should you use to store a user's personal, highly sensitive diary entries?