Article

Providing User Access to CloudKit Data

Give users access to the data your app stores on their behalf.

Overview

User data stored in CloudKit belongs to the user. For this reason, apps that integrate with CloudKit should give users a way to view and export their data.

Identify Containers and Record Types

To be sure that you're querying all the data stored by your app, cross-reference the list of containers your app has access to in Xcode, and assemble a list of those containers' identifiers. This process is described in Identifying an App's Containers.

The CloudKit Dashboard lists the different record types you've defined for each container in your app, as described in Create and Delete Record Types. Assemble a list of these types and make sure that each type has a queryable index on its recordName field.

Associate Record Types with Containers

Associate the record types with the container they appear in. The example below uses a dictionary to represent the relationship between containers and the record types they contain.

let defaultContainer = CKContainer.default()
let documents = CKContainer(identifier: "iCloud.com.example.myexampleapp.documents")
let settings = CKContainer(identifier: "iCloud.com.example.myexampleapp.settings")

let containerRecordTypes: [CKContainer: [String]] = [
    defaultContainer: ["log", "verboseLog"],
    documents: ["textDocument", "spreadsheet"],
    settings: ["preference", "profile"]
]

let containers = Array(containerRecordTypes.keys)

Find and List All Records

User data should be stored in a container's private database. Use the containers identified in the example above to find all record zones used in the private database for each container used by your app.

The example below shows how to iterate over the containers, zones, and records. It also shows how to list the fields for each record, which you use to show the data stored in those records.

// Utility function to display records.
// Customize it to display records appropriately
// according to your app's unique record types.
func printRecords(_ records: [CKRecord]) {
    for record in records {
        for key in record.allKeys() {
            let value = record[key]
            print(key + " = " + (value?.description ?? "") + ")")
        }
    }
}

for container in containers {
    // User data should be stored in the private database.
    let database = container.privateCloudDatabase
    
    database.fetchAllRecordZones { zones, error in
        guard let zones = zones, error == nil else {
            print(error!)
            return
        }
        
        // The true predicate represents a query for all records.
        let alwaysTrue = NSPredicate(value: true)
        for zone in zones {
            for recordType in containerRecordTypes[container] ?? [] {
                let query = CKQuery(recordType: recordType, predicate: alwaysTrue)
                database.perform(query, inZoneWith: zone.zoneID) { records, error in
                    guard let records = records, error == nil else {
                        print("An error occurred fetching these records.")
                        return
                    }
                    
                    printRecords(records)
                }
            }
        }
    }
}

Use each field's keys and values to give users an accessible report of the data your app stores in CloudKit.

See Also

User Data Management

Changing Access Controls on User Data

Restrict access to or lift restrictions on a user's data at their request.

Responding to Requests to Delete Data

Provide options for users to delete their CloudKit data from your app.

Identifying an App's Containers

Use Xcode's project navigator to find the identifiers of active CloudKit containers.