34,333
edits
Changes
Created page with "{{#pagetitle: An iOS CloudKit Sharing Example }} <seo title="An iOS CloudKit Sharing Example" titlemode="replace" keywords="ios 10, swift 3, cloudkit sharing, cloudkit sharing..."
{{#pagetitle: An iOS CloudKit Sharing Example }}
<seo title="An iOS CloudKit Sharing Example" titlemode="replace" keywords="ios 10, swift 3, cloudkit sharing, cloudkit sharing, tutorial, tutorial, example, xcode 8" description="A tutorial that demonstrates how to share cloud-based data between iOS app users using CloudKit sharing."></seo>
<table border="0" cellspacing="0" width="100%">
<tr>
<td width="20%">[[An iOS 8 CloudKit Example|Previous]]<td align="center">[[iOS 8 App Development Essentials|Table of Contents]]<td width="20%" align="right">[[An iOS CloudKit Sharing Example|Next]]</td>
<tr>
<td width="20%">An iOS 10 CloudKit Example<td align="center"><td width="20%" align="right">An iOS CloudKit Sharing Example</td>
</table>
<hr>
<htmlet>ios10</htmlet>
The chapter entitled [[An Introduction to CloudKit Sharing]] provided an overview of the way in which CloudKit sharing works and the steps involved in integrating sharing into an iOS app. The intervening chapters have focused on the creation of a project that demonstrates the integration of CloudKit data storage into iOS apps, together with the use of CloudKit subscriptions to notify users of changes to stored data records.
This chapter will extend the project created in the previous chapters to add CloudKit sharing to the CloudKitDemo app.
== Preparing the Project for CloudKit Sharing ==
Launch Xcode and open the CloudKitDemo project created in the chapter entitled An iOS 10 CloudKit Example and subsequently updated in the An iOS 10 CloudKit Subscription Example chapter of this book. In you have not completed the tasks in the previous chapters 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 at the following web page:
[http://www.ebookfrenzy.com/web/ios10 http://www.ebookfrenzy.com/web/ios10]
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 Info.plist file in the Project Navigator panel so that it loads into the property editor. Add a new entry to the file with the key field set to CKSharingSupported, the type to Boolean and the value to YES as illustrated in Figure 51 1:
[[Image:xcode_8_enable_cksharing_info_plist.png|Adding the CKSharingSupported key to the Info.plist file]]
Figure 51-1
== Adding the Share Button ==
The user interface for the app now needs to be modified to add a share button to the toolbar. Select the Main.storyboard file, locate the Bar Button Item in the Object Library panel and drag and drop an instance onto the toolbar so that it is position to the right of the existing Delete button. Once added, double-click on the button and change the text to read “Share”:
[[Image:xcode_cloudkit_sharing_share_button.png|Adding the Share button to the CloudKit Sharing demo app]]
Figure 51-2
With the Share button 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. Select the ViewController.swift file, locate the stub shareRecord method and modify it so that it reads as follows:
<pre>
@IBAction func shareRecord(_ sender: AnyObject) {
let controller = UICloudSharingController { controller,
preparationCompletionHandler in
let share = CKShare(rootRecord: self.currentRecord!)
share[CKShareTitleKey] = "An Amazing House" 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 =
sender as? UIBarButtonItem
present(controller, animated: true)
}
</pre>
The code added to this method follows the steps outlined in the chapter entitled An Introduction to 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 has the ability to create a CloudKit share, the app also needs to be modified to accept a share and display the shared record to the user. The first step in this process is to implement the userDidAcceptCloudKitShareWith method within the project app delegate class. Edit the AppDelegate.swift file and implement this method as follows:
<pre>
func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {
let acceptSharesOperation =
CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
acceptSharesOperation.perShareCompletionBlock = {
metadata, share, error in
if error != nil {
print(error?.localizedDescription)
} else {
let viewController: ViewController =
self.window?.rootViewController as! ViewController
viewController.fetchShare(cloudKitShareMetadata)
}
}
CKContainer(identifier:
cloudKitShareMetadata.containerIdentifier).add(
acceptSharesOperation)
}
</pre>
As outlined in the previous chapter, this delegate method creates a CKAcceptSharesOperation using the share metadata object passed in to the method. This operation is then performed on the CloudKit container referenced in the share metadata. The share operation has assigned to it a completion handler which, in this instance, is configured to call a method on the view controller named fetchShare. The next task is to implement this method.
== Fetching the Shared Record ==
At this point the share has been accepted and a CKShareMetadata 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:
<pre>
func fetchShare(_ metadata: CKShareMetadata) {
let operation = CKFetchRecordsOperation(
recordIDs: [metadata.rootRecordID])
operation.perRecordCompletionBlock = { record, _, error in
if error != nil {
print(error?.localizedDescription)
}
if record != nil {
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!)
}
}
}
operation.fetchRecordsCompletionBlock = { _, error in
if error != nil {
print(error?.localizedDescription)
}
}
CKContainer.default().sharedCloudDatabase.add(operation)
}
</pre>
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 is used to extract the data from the shared record and display it to the user.
== Testing the CloudKit Share Example ==
To fully test CloudKit sharing, two devices with different Apple IDs need to 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 are able to send and receive iMessage messages between each other and install and run the CloudKitDemo 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 to the Apple ID associated with the second device. When the message arrives within the Messages app on the second device, tap the share link and accept the share when prompted to do so. Once the share has been accepted, the CloudKitDemo app should launch and display the shared record.
== Summary ==
This chapter put the theory of CloudKit sharing outlined in the chapter entitled An Introduction to CloudKit Sharing into practice by enhancing the CloudKitDemo project to include the ability to share CloudKit-based records with other app users. This involved the creation and saving of a CKShare object, use of the UICloudSharingController class and the addition of code to handle the acceptance and fetching of a shared CloudKit database record.
<htmlet>ios10</htmlet>
<hr>
<table border="0" cellspacing="0" width="100%">
<tr>
<td width="20%">[[An iOS 8 CloudKit Example|Previous]]<td align="center">[[iOS 8 App Development Essentials|Table of Contents]]<td width="20%" align="right">[[An iOS CloudKit Sharing Example|Next]]</td>
<tr>
<td width="20%">An iOS 10 CloudKit Example<td align="center"><td width="20%" align="right">An iOS CloudKit Sharing Example</td>
</table>
<seo title="An iOS CloudKit Sharing Example" titlemode="replace" keywords="ios 10, swift 3, cloudkit sharing, cloudkit sharing, tutorial, tutorial, example, xcode 8" description="A tutorial that demonstrates how to share cloud-based data between iOS app users using CloudKit sharing."></seo>
<table border="0" cellspacing="0" width="100%">
<tr>
<td width="20%">[[An iOS 8 CloudKit Example|Previous]]<td align="center">[[iOS 8 App Development Essentials|Table of Contents]]<td width="20%" align="right">[[An iOS CloudKit Sharing Example|Next]]</td>
<tr>
<td width="20%">An iOS 10 CloudKit Example<td align="center"><td width="20%" align="right">An iOS CloudKit Sharing Example</td>
</table>
<hr>
<htmlet>ios10</htmlet>
The chapter entitled [[An Introduction to CloudKit Sharing]] provided an overview of the way in which CloudKit sharing works and the steps involved in integrating sharing into an iOS app. The intervening chapters have focused on the creation of a project that demonstrates the integration of CloudKit data storage into iOS apps, together with the use of CloudKit subscriptions to notify users of changes to stored data records.
This chapter will extend the project created in the previous chapters to add CloudKit sharing to the CloudKitDemo app.
== Preparing the Project for CloudKit Sharing ==
Launch Xcode and open the CloudKitDemo project created in the chapter entitled An iOS 10 CloudKit Example and subsequently updated in the An iOS 10 CloudKit Subscription Example chapter of this book. In you have not completed the tasks in the previous chapters 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 at the following web page:
[http://www.ebookfrenzy.com/web/ios10 http://www.ebookfrenzy.com/web/ios10]
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 Info.plist file in the Project Navigator panel so that it loads into the property editor. Add a new entry to the file with the key field set to CKSharingSupported, the type to Boolean and the value to YES as illustrated in Figure 51 1:
[[Image:xcode_8_enable_cksharing_info_plist.png|Adding the CKSharingSupported key to the Info.plist file]]
Figure 51-1
== Adding the Share Button ==
The user interface for the app now needs to be modified to add a share button to the toolbar. Select the Main.storyboard file, locate the Bar Button Item in the Object Library panel and drag and drop an instance onto the toolbar so that it is position to the right of the existing Delete button. Once added, double-click on the button and change the text to read “Share”:
[[Image:xcode_cloudkit_sharing_share_button.png|Adding the Share button to the CloudKit Sharing demo app]]
Figure 51-2
With the Share button 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. Select the ViewController.swift file, locate the stub shareRecord method and modify it so that it reads as follows:
<pre>
@IBAction func shareRecord(_ sender: AnyObject) {
let controller = UICloudSharingController { controller,
preparationCompletionHandler in
let share = CKShare(rootRecord: self.currentRecord!)
share[CKShareTitleKey] = "An Amazing House" 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 =
sender as? UIBarButtonItem
present(controller, animated: true)
}
</pre>
The code added to this method follows the steps outlined in the chapter entitled An Introduction to 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 has the ability to create a CloudKit share, the app also needs to be modified to accept a share and display the shared record to the user. The first step in this process is to implement the userDidAcceptCloudKitShareWith method within the project app delegate class. Edit the AppDelegate.swift file and implement this method as follows:
<pre>
func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {
let acceptSharesOperation =
CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
acceptSharesOperation.perShareCompletionBlock = {
metadata, share, error in
if error != nil {
print(error?.localizedDescription)
} else {
let viewController: ViewController =
self.window?.rootViewController as! ViewController
viewController.fetchShare(cloudKitShareMetadata)
}
}
CKContainer(identifier:
cloudKitShareMetadata.containerIdentifier).add(
acceptSharesOperation)
}
</pre>
As outlined in the previous chapter, this delegate method creates a CKAcceptSharesOperation using the share metadata object passed in to the method. This operation is then performed on the CloudKit container referenced in the share metadata. The share operation has assigned to it a completion handler which, in this instance, is configured to call a method on the view controller named fetchShare. The next task is to implement this method.
== Fetching the Shared Record ==
At this point the share has been accepted and a CKShareMetadata 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:
<pre>
func fetchShare(_ metadata: CKShareMetadata) {
let operation = CKFetchRecordsOperation(
recordIDs: [metadata.rootRecordID])
operation.perRecordCompletionBlock = { record, _, error in
if error != nil {
print(error?.localizedDescription)
}
if record != nil {
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!)
}
}
}
operation.fetchRecordsCompletionBlock = { _, error in
if error != nil {
print(error?.localizedDescription)
}
}
CKContainer.default().sharedCloudDatabase.add(operation)
}
</pre>
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 is used to extract the data from the shared record and display it to the user.
== Testing the CloudKit Share Example ==
To fully test CloudKit sharing, two devices with different Apple IDs need to 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 are able to send and receive iMessage messages between each other and install and run the CloudKitDemo 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 to the Apple ID associated with the second device. When the message arrives within the Messages app on the second device, tap the share link and accept the share when prompted to do so. Once the share has been accepted, the CloudKitDemo app should launch and display the shared record.
== Summary ==
This chapter put the theory of CloudKit sharing outlined in the chapter entitled An Introduction to CloudKit Sharing into practice by enhancing the CloudKitDemo project to include the ability to share CloudKit-based records with other app users. This involved the creation and saving of a CKShare object, use of the UICloudSharingController class and the addition of code to handle the acceptance and fetching of a shared CloudKit database record.
<htmlet>ios10</htmlet>
<hr>
<table border="0" cellspacing="0" width="100%">
<tr>
<td width="20%">[[An iOS 8 CloudKit Example|Previous]]<td align="center">[[iOS 8 App Development Essentials|Table of Contents]]<td width="20%" align="right">[[An iOS CloudKit Sharing Example|Next]]</td>
<tr>
<td width="20%">An iOS 10 CloudKit Example<td align="center"><td width="20%" align="right">An iOS CloudKit Sharing Example</td>
</table>