CloudKit Sharing Series — Creating the CKShare

If you haven’t watched the What’s New in CloudKit session then I would suggest doing that first. It a wonderful introduction to some nice improvements to service and libraries, in addition, a thorough walk through the UICloudSharingController which should be the first thing you should go for if you are implementing sharing in your CloudKit application.

As mentioned in my previous post, I had to ditch that option because I need to limit the number of participants which isn’t currently possible.

In this post I’m going illustrate how to setup the inital share and create the share link for your participant.

IMPORTANT — ADD THIS TO YOUR APP FIRST

// Add an Info.plist key for CloudKit Sharing
<key>CKSharingSupported</key>
<true/>

The User Lookup

Before we create the share we have to find a user(s) to share with. Apple provides four ways of doing this:

The requirements for discovery are:

  1. The user must have run the app.
  2. The user must have granted the userDiscoverability permission for this container.

Discovering all the users of that app has an additional “*” with it. It will return users who have used the app, allowed discovery and are in your address book.

I’m going to assume that most people will use either email or phone for the lookup.

Look up via phone

CKContainer.default().discoverUserIdentity(withPhoneNumber: phone, completionHandler: {identity, error in

guard let userIdentity: CKUserIdentity = identity, error == nil else {

DispatchQueue.main.async(execute: {
print("fetch user by phone error " + error!.localizedDescription)
})

return
}

DispatchQueue.main.async(execute: {
print("user identity was discovered \(identity)")
})
})

You’ll need the identity object in order to add it to the participants value on the share.

Creating the CKShare

User A wants to share a Shopping List with User B.

/// Create the root record

let recordZone: CKRecordZone = CKRecordZone(zoneName: "FriendZone")
let rootRecord: CKRecord = CKRecord(recordType: "Note", zoneID: recordZone.zoneID)

// Create a CloudKit share record

let share = CKShare(rootRecord: rootRecord)

share[CKShareTitleKey] = "Shopping List” as CKRecordValue
share[CKShareThumbnailImageDataKey] = shoppingListThumbnail as CKRecordValue
share[CKShareTypeKey] = "com.yourcompany.name" as CKRecordValue

/// Setup the participants for the share (take the CKUserIdentityLookupInfo from the identity you fetched)

let fetchParticipantsOperation: CKFetchShareParticipantsOperation = CKFetchShareParticipantsOperation(userIdentityLookupInfos: [userIdentity])

fetchParticipantsOperation.fetchShareParticipantsCompletionBlock = {error in

if let error = error {
print("error for completion" + error!.localizedDescription)
}
}

fetchParticipantsOperation.shareParticipantFetchedBlock = {participant in

print("participant \(participant)")
/// 1
participant.permission = .readWrite

/// 2
share.addParticipant(participant)

let modifyOperation: CKModifyRecordsOperation = CKModifyRecordsOperation(recordsToSave: [rootRecord, share], recordIDsToDelete: nil)

modifyOperation.savePolicy = .ifServerRecordUnchanged
modifyOperation.perRecordCompletionBlock = {record, error in
print("record completion \(record) and \(error)")
}
modifyOperation.modifyRecordsCompletionBlock = {records, recordIDs, error in

guard let ckrecords: [CKRecord] = records, let record: CKRecord = ckrecords.first, error == nil else {
print("error in modifying the records " + error!.localizedDescription)
return
}

/// 3
print("share url \(url)")
}

CKContainer.default().privateDB.add(modifyOperation)
}

CKContainer.default().add(fetchParticipantsOperation)

There is a lot going on there! Let’s break some of it down.

  1. Once you have a participant you have to give them .readWrite permission on the share if you want them to be able to add additional records to the share’s parent.
  2. Add the a participant to the share!
  3. If the rootRecord and share have been succesfully saved then you’ll have access to the share’s URL.

Once you have the URL for the share you’ll then need send it to your participant using UIActivityViewController.

The url will look something like: https://www.icloud.com/share/rand0m-4ting#Share_Title_Value. In your CloudKit dashboard you can enter a fallback URL in case a user doesn’t have the latest OS that supports sharing or happens to click the link from their desktop.

Sending the Share

Once you have emailed, texted, slacked, etc. your share link the participant and they click on iOS 10 will handle prompting the user requesting their permission to join in and put them into the app. What happens next will be the focus of the next post.

Notes

  • Shares must be in a custom zone
  • Participants don’t have access to the owner’s private database. They only have a “view” into those records that are shared via the shared database.
  • The owner and the participant must keep track of the recordID for the parent record if want to support child records
Show your support

Clapping shows how much you appreciated Cory D. Wiles’s story.