An Introduction to CloudKit Sharing

PreviousTable of ContentsNext
An Introduction to CloudKit Data Storage on iOS 10An iOS 10 CloudKit Example


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book


Prior to the release of iOS 10, the only way to share CloudKit records between users was to store those records in a public database. With the introduction of CloudKit sharing it is now possible for individual app users to share private database records with other users.

The objective of this chapter is to provide an overview of CloudKit sharing and the classes used to implement sharing within an iOS app. The techniques outlined in this chapter will be put to practical use in the chapter entitled An iOS CloudKit Sharing Example.

Understanding CloudKit Sharing

CloudKit sharing provides a way for records contained within a private database to be shared with other app users, entirely at the discretion of the owner of that database. When a user decides to share CloudKit data, a share link in the form of a URL is sent to the person with whom the data is to be shared. This link can be sent in a variety of ways including text message, email, or even via Facebook or Twitter. When the recipient taps on the share link, the app (if installed) will be launched and provided with the shared record information ready to be displayed.

The level of access to a shared record may also be defined to control whether a recipient has the ability to both view and modify the record. It is important to be aware that when a share recipient accepts a share they are actually receiving a reference to the original record in the owner’s private database. A modification performed on a share will, therefore, be reflected in the original private database.

Preparing for CloudKit Sharing

Before an app can take advantage of CloudKit sharing, the CKSharingSupported key needs to be added to the project Info.plist file with a Boolean value of true. Also, a CloudKit record may only be shared if it is stored in a private database and is a member of a record zone other than the default zone.

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book


The CKShare Class

CloudKit sharing is made possible primarily by the CKShare class. This class is initialized with the root CKRecord instance that is to be shared with other users together with the permission setting. The CKShare object may also be configured with title and icon information to be included in the share link message. Both the CKShare and associated CKRecord object are then saved to the private database. The following code, for example, creates a CKShare object containing the record to be shared and configured for read-only access:

let share = CKShare(rootRecord: myRecord)
share[CKShareTitleKey] = "My First Share" as CKRecordValue
share.publicPermission = .readOnly

Once the share has been created, it is saved to the private database using a CKModifyRecordsOperation object. Note the recordsToSave: argument is declared as an array containing both the share and record objects:

let modifyRecordsOperation = CKModifyRecordsOperation(
		recordsToSave: [myRecord, share], 
		recordIDsToDelete: nil)

modifyRecordsOperation.timeoutIntervalForRequest = 10
modifyRecordsOperation.timeoutIntervalForResource = 10

modifyRecordsOperation.modifyRecordsCompletionBlock = 
	{ records, recordIDs, error in

    if error != nil {
        print(error?.localizedDescription)
    }
}                                  
self.privateDatabase?.add(modifyRecordsOperation)

The UICloudSharingController Class

In order to send a share link to another user, CloudKit needs to know both the identity of the recipient and the method by which the share link is to be transmitted. One option is to manually create CKShareParticipant objects for each participant and add them to the CKShare object. Alternatively, the CloudKit framework includes a view controller intended specifically for this purpose. When presented to the user (Figure 48 1), the UICloudSharingController class provides the user with a variety of options for sending the share link to another user:


The CloudKit cloud sharing container view controller

Figure 48-1


The app is responsible for creating and presenting the controller to the user, template code for which is outlined below:

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

let controller = UICloudSharingController { 
	controller, preparationCompletionHandler in

	// Code here to create the CKShare and save it to the database
}

controller.availablePermissions = [.allowPublic, .allowReadOnly]
controller.popoverPresentationController?.barButtonItem = 
			shareButton as? UIBarButtonItem

present(controller, animated: true)

Note that the above code fragment also specifies the range of permissions that are to be provided as options within the controller user interface. These options are accessed and modified by tapping the Share Options item at the bottom of the cloud sharing controller view. Figure 48 2 shows an example share options settings screen:


The CloudKit sharing share options screen

Figure 48-2


Once a method of communication has been selected by the user from the cloud sharing controller, the completion handler assigned to the controller will be called. Within this handler, the CKShare object must be created and saved as outlined in the previous section. After the share has been saved to the database, the cloud sharing controller needs to be notified that the share is ready to be sent. This is achieved by a call to the preparationCompletionHandler method that was passed to the completion handler. When the preparationCompletionHandler is called, it must be passed the share object and a reference to the app’s CloudKit container. Bringing these requirements together gives us the following code:

let controller = UICloudSharingController { controller, 
		preparationCompletionHandler in

        let share = CKShare(rootRecord: myRecord!)
        share[CKShareTitleKey] = " My First Share" as CKRecordValue
        share.publicPermission = .readOnly

        let modifyRecordsOperation = CKModifyRecordsOperation(
			recordsToSave: [self.currentRecord!, share], 
			recordIDsToDelete: nil)

        modifyRecordsOperation.timeoutIntervalForRequest = 10
        modifyRecordsOperation.timeoutIntervalForResource = 10

        modifyRecordsOperation.modifyRecordsCompletionBlock = { records, 
			recordIDs, error in
            if error != nil {
                print(error?.localizedDescription)
            }
            preparationCompletionHandler(share, 
				CKContainer.default(), error)
        }
        self.privateDatabase?.add(modifyRecordsOperation)
    }

controller.availablePermissions = [.allowPublic, .allowReadOnly]
controller.popoverPresentationController?.barButtonItem = 
		shareButton as? UIBarButtonItem

present(controller, animated: true))

Once the preparationCompletionHandler method has been called, the app for the chosen form of communication (Messages, Mail etc) will launch preloaded with the share link. All the user needs to do at this point is enter the contact details for the intended share recipient and send the message. Figure 48 3, for example, shows a share link loaded into the Mail app ready to be sent:


Sending a CloudKit share link

Figure 48-3

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

Accepting a CloudKit Share

When a user receives a share link and selects it, a dialog will appear providing the option to accept the share and open it in the corresponding app. When the app opens, the userDidAcceptCloudKitShareWith method is called on the app delegate class:

func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {
}

When this method is called it is passed a CKShareMetadata object containing information about the share. Although the user has accepted the share, the application must also accept the share using the CKAcceptSharesOperation class as follows:

func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {

    let acceptSharesOperation = CKAcceptSharesOperation(
			shareMetadatas: [cloudKitShareMetadata])

    acceptSharesOperation.perShareCompletionBlock = { 
		metadata, share, error in
        if error != nil {
            print(error?.localizedDescription)
        }
        // Add code here to fetch shared record and display to user
    }

    CKContainer(identifier: cloudKitShareMetadata.containerIdentifier)
				.add(acceptSharesOperation)
}

Fetching a Shared Record

Once a share has been accepted by both the user and the app, the shared record needs to be fetched and presented to the user. This involves the creation of a CKFetchRecordsOperation object using the record ID contained within the CKShareMetadata object. It is important to be aware that this fetch operation must be executed on the shared cloud database instance of the app instead of the recipient’s private database. The following code, for example, fetches the record associated with a CloudKit share:

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

let operation = CKFetchRecordsOperation(
			recordIDs: [cloudKitShareMetadata.rootRecordID])

operation.perRecordCompletionBlock = { record, _, error in

    if error != nil {
        print(error?.localizedDescription)
    }

    if let shareRecord = record {
          DispatchQueue.main.async() {
              // Shared record successfully fetched. Update user 
	      // interface here to present to user.
          }
    }
}

operation.fetchRecordsCompletionBlock = { _, error in
    if error != nil {
        print(error?.localizedDescription)
    }
}

CKContainer.default().sharedCloudDatabase.add(operation)

Once the record has been fetched it can be presented to the user, taking necessary steps as above to perform any user interface updates asynchronously on the main thread.

Summary

CloudKit sharing allows records stored within a private CloudKit database to be shared with other app users at the discretion of the record owner. A user of an app could, for example, make one or more records accessible to other users so that they can view and, optionally, modify the record. When a record is shared, a share link is sent to the recipient user in the form of a URL. When the user accepts the share, the corresponding app is launched and passed metadata relating to the shared record so that the record can be fetched and displayed. CloudKit sharing involves the creation of CKShare objects initialized with the record to be shared. The UICloudSharingController class provides a pre-built view controller which handles much of the work involved in gathering the necessary information to send a share link to another user. In addition to sending a share link, the app must also be adapted to accept a share and fetch the record for the shared cloud database. This chapter has covered the basics of CloudKit sharing, a topic which will be covered further in a later chapter entitled An iOS CloudKit Sharing Example.


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book



PreviousTable of ContentsNext
An Introduction to CloudKit Data Storage on iOS 10An iOS 10 CloudKit Example