iosswifticloudcloudkitcloudkit-sharing

How to model my CloudKit data


In my app I decided to use CloudKit as my sync-backend. My app is not about projects, but for simplicity let's say so...

So... In my app users will have multiple projects. Each of those contains multiple entities associated with that project. For example tasks, but also reminders and so on.

All this data will be stored in the users private database. Nothing will be in the public database.

Now a user can have multiple projects.

My first question: Should each project be in it's own CKRecordZone? I didn't see a benefit of doing so?!? Can somebody explain to me what the benefit of having multiple record zones is? So currently all the projects are in ONE zone.

Next, I'd like the user to be able to share ALL his data with somebody else. The problem currently is that as the project is currently the root record in my database, I'd need to create a share for each of those projects, right?!? In my app it doesn't really make sense to invite users to each project separately, so I'd like to somehow archive that. Would it make sense, to create a new root-record that has the projects as childs and then the user would invite somebody to this new root-record?

Final question... is there something like a Sack-Team or so to ask questions about CloudKit? Would seem to be easier than starting a new question here on stackoverflow as my questions are quite specific to my app...


Solution

  • Good questions. Here's what I recommend.

    Zone

    First off, you only need one zone. But to share records from it, it must be a custom zone (you can't use the _defaultZone). Honestly, zones in CloudKit are weird and I'm not sure why they exist. Apple seems to be passing database sharding challenges on to their developers. :)

    Create a custom zone like this:

    let customZone = CKRecordZone(zoneName: "projectZone")
    
    // Save the zone in the private database
    let container = CKContainer(identifier: "...")
    let privateDB = container.privateCloudDatabase
    
    privateDB.save(customZone){ zone, error in
      if let error = error{
        print("Zone creation error: \(String(describing: error))")
      }else{
        print("Zone created: \(zone)")
      }
    }
    

    Record Types

    I would create record types like this:

    Sharing

    One of the nice things about CloudKit is that you can create relationships between records. This means you can automatically share the children of a root record without having to set up CKShares for each child individually.

    Below is an example that walks through how you would set those fields on the records.

    //Get a reference to the zone you created
    let zoneID = CKRecordZoneID(zoneName: "projectZone", ownerName: CKCurrentUserDefaultName)
    
    //Create a project record
    let projectRecord = CKRecord(recordType: "Project", zoneID: zoneID)
    projectRecord.setValue("My Cool Project", forKey: "name")
    
    //Create a task record
    let taskRecord = CKRecord(recordType: "Task", zoneID: zoneID)
    taskRecord.setValue("My Task Name", forKey: "name")
    
    //Create an association between the task and its parent project
    let parentReference = CKReference(record: projectRecord, action: .deleteSelf)
    taskRecord.setValue(parentReference, forKey: "project")
    
    //When sharing, allow this task to be automatically shared if the parent project is shared
    taskRecord.setParent(projectRecord)
    

    All of this presupposes that you create fields for your Project and Task record types of name (type: String). Then on the Task record type, you would have a project field of type Reference.

    I hope that helps and will at least get you started. I'm not aware of a CloudKit Slack channel, but if you hear about one, let me know! :)