An iOS 17 CloudKit Sharing Tutorial

The chapter entitled An Introduction to iOS 17 CloudKit Sharing provided an overview of how CloudKit sharing works and the steps involved in integrating sharing into an iOS app. The intervening chapters have focused on creating a project that demonstrates the integration of CloudKit data storage into iOS apps. This chapter will extend the project started in the previous chapter to add CloudKit sharing to the CloudKitDemo app.

Preparing the Project for CloudKit Sharing

Launch Xcode and open the CloudKitDemo project created in this book’s chapter entitled An Introduction to iOS 17 CloudKit Sharing. If you have not completed the tasks in the previous chapter and are only interested in learning about CloudKit sharing, a snapshot of the project is included as part of the sample code archive for this book on the following web page:

https://www.ebookfrenzy.com/web/ios16/

Once the project has been loaded into Xcode, the CKSharingSupported key needs to be added to the project Info.plist file with a Boolean value of true. Select the CloudKitDemo target at the top of the Project Navigator panel, followed by the Info tab in the main panel. Next, locate the bottom entry in the Custom iOS Target Properties list, and hover the mouse pointer over the item. When the plus button appears, click it to add a new entry to the list. Complete the new property with the key field set to CKSharingSupported, the type to Boolean, and the value to YES, as illustrated in Figure 53-1:

Figure 53-1

Adding the Share Button

The user interface for the app now needs to be modified to add a share button to the toolbar. First, select the Main.storyboard file, locate the Bar Button Item in the Library panel, and drag and drop an instance onto the toolbar to position it to the right of the existing delete button.

Once added, select the button item, display the Attributes inspector, and select the square and arrow image:

Figure 53-2

Once the new button has been added, the toolbar should match Figure 53-3:

Figure 53-3

With the new share button item still selected, display the Assistant Editor panel and establish an Action connection to a method named shareRecord.

Creating the CloudKit Share

The next step is to add some code to the shareRecord action method to initialize and display the UICloudSharingController and to create and save the CKShare object. Next, select the ViewController.swift file, locate the stub shareRecord method, and modify it so that it reads as follows:

@IBAction func shareRecord(_ sender: Any) {

    let controller = UICloudSharingController { controller,
        prepareCompletionHandler in
        
        if let thisRecord = self.currentRecord {
            let share = CKShare(rootRecord: thisRecord)
            
            share[CKShare.SystemFieldKey.title] = 
                             "An Amazing House" as CKRecordValue
            share.publicPermission = .readOnly
            
            let modifyRecordsOperation = CKModifyRecordsOperation(
                recordsToSave: [thisRecord, share],
                recordIDsToDelete: nil)
            
            let configuration = CKOperation.Configuration()
            
            configuration.timeoutIntervalForResource = 10
            configuration.timeoutIntervalForRequest = 10
                         
            modifyRecordsOperation.modifyRecordsResultBlock = {
                result in
                switch result {
                case .success:
                    prepareCompletionHandler(share, CKContainer.default(), nil)
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }     
            self.privateDatabase?.add(modifyRecordsOperation)
        } else {
            print("User error: No record selected")
        }
    }
    
    controller.availablePermissions = [.allowPublic, .allowReadOnly,
            .allowReadWrite, .allowPrivate]
    controller.popoverPresentationController?.barButtonItem =
        sender as? UIBarButtonItem
    
    present(controller, animated: true)
}Code language: Swift (swift)

The code added to this method follows the steps outlined in the chapter entitled An Introduction to iOS 17 CloudKit Sharing to display the CloudKit sharing view controller, create a share object initialized with the currently selected record and save it to the user’s private database.

Accepting a CloudKit Share

Now that the user can create a CloudKit share, the app needs to be modified to accept a share and display it to the user. The first step in this process is implementing the userDidAcceptCloudKitShareWith method within the project’s scene delegate class. Edit the SceneDelegate.swift file and implement this method as follows:

.
.
import CloudKit
.
.
func windowScene(_ windowScene: UIWindowScene,
    userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
   
    acceptCloudKitShare(metadata: cloudKitShareMetadata) { [weak self] result in
        switch result {
        case .success:
            DispatchQueue.main.async {
                let viewController: ViewController = 
                     self?.window?.rootViewController as! ViewController
                viewController.fetchShare(cloudKitShareMetadata)
            }
        case .failure(let error):
            print(error.localizedDescription )
        }
    }
}
.
.Code language: Swift (swift)

When the user clicks on a CloudKit share link, for example, in an email or text message, the operating system will call the above method to notify the app that shared CloudKit data is available. The above implementation of this method calls a method named acceptCloudKitShare and passes it the CloudKitShareMetadata object it received from the operating system. If the acceptCloudKitShare method returns a successful result, the delegate method obtains a reference to the app’s root view controller and calls a method named fetchShare (which we will write in the next section) to extract the shared record from the CloudKit database and display it. Next, we need to add the acceptCloudKitShare method as follows:

Fetching the Shared Record

At this point, the share has been accepted and a CKShare.Metadata object provided, from which information about the shared record may be extracted. All that remains before the app can be tested is to implement the fetchShare method within the ViewController.swift file:

func fetchShare(_ metadata: CKShare.Metadata) {

    let operation = CKFetchRecordsOperation(recordIDs: 
                            [metadata.hierarchicalRootRecordID!])

    operation.perRecordResultBlock = { recordId, result in
        switch result {
        case .success(let record):
            DispatchQueue.main.async() {
                self.currentRecord = record
                self.addressField.text =
                    record.object(forKey: "address") as? String
                self.commentsField.text =
                    record.object(forKey: "comment") as? String
                let photo =
                    record.object(forKey: "photo") as! CKAsset
                let image = UIImage(contentsOfFile:
                                        photo.fileURL!.path)
                self.imageView.image = image
                self.photoURL = self.saveImageToFile(image!)
            }
        case .failure(let error):
            print(error.localizedDescription)
        }
    }
    
    operation.fetchRecordsResultBlock = { result in
        switch result {
        case .success:
            break
        case .failure(let error):
            print(error.localizedDescription)
        }
    }
    CKContainer.default().sharedCloudDatabase.add(operation)
}Code language: Swift (swift)

The method prepares a standard CloudKit fetch operation based on the record ID contained within the share metadata object and performs the fetch using the sharedCloudDatabase instance. On a successful fetch, the completion handler extracts the data from the shared record and displays it in the user interface.

Testing the CloudKit Share Example

To thoroughly test CloudKit sharing, two devices with different Apple IDs must be used. If you have access to two devices, create a second Apple ID for testing purposes and sign in using that ID on one of the devices. Once logged in, make sure that the devices can send and receive iMessage or email messages between each other and install and run the CloudKitDemo app on both devices. Once the testing environment is set up, launch the CloudKitDemo app on one of the devices and add a record to the private database. Once added, tap the Share button and use the share view controller interface to send a share link message to the Apple ID associated with the second device. When the message arrives on the second device, tap the share link and accept the share when prompted. Once the share has been accepted, the CloudKitDemo app should launch and display the shared record.

Summary

This chapter puts the theory of CloudKit sharing outlined in the chapter entitled An Introduction to iOS 17 CloudKit Sharing into practice by enhancing the CloudKitDemo project to include the ability to share CloudKit-based records with other app users. This involved creating and saving a CKShare object, using the UICloudSharingController class, and adding code to handle accepting and fetching a shared CloudKit database record.


Categories