mlecoz
mlecoz

Reputation: 449

Accessing another app's CloudKit database?

Suppose John developed App A and Heather developed App B. They each have different Apple Developer's accounts and they are not on the same team or associated in any way. App B is backed by a public CloudKit database. Is there any way for App A to write to App B's public CloudKit database? Namely, can App A do this:

let DB = CKContainer(identifier: "iCloud.com.Heather.AppB").publicCloudDatabase

and then write to or read from this DB?

I'm guessing that this is not allowed out of the box, but is there a way to set up authentication so that this is possible?

Upvotes: 0

Views: 812

Answers (2)

user3069232
user3069232

Reputation: 8995

This looks/sounds like the solution you seek.

CloudKit share Data between different iCloud accounts but not with everyone as outlined by https://stackoverflow.com/users/1878264/edwin-vermeer an iCloud specialist on SO.

There is third party explaination on this link too. https://medium.com/@kwylez/cloudkit-sharing-series-intro-4fc82dad7a9

Key steps shamelessly cut'n'pasted ... make sure you read and credit Cory on medium.com!

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

More code...

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)")
        })
    })

/// Create a shred 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)

And on the other side of the fence.

let acceptShareOperation: CKAcceptSharesOperation =     CKAcceptSharesOperation(shareMetadatas: [shareMeta])
    acceptShareOperation.qualityOfService = .userInteractive
    acceptShareOperation.perShareCompletionBlock = {meta, share, error in
        Log.print("meta \(meta) share \(share) error \(error)")
    }
    acceptShareOperation.acceptSharesCompletionBlock = {error in
        Log.print("error in accept share completion \(error)")
        /// Send your user to wear that need to go in your app
    }

    CKContainer.default().container.add(acceptShareOperation)

Really I cannot hope to do the article justice, go read it... its in three parts!

Upvotes: 1

picciano
picciano

Reputation: 22701

If the apps were in the same organization, there is a way to set up shared access. But as you described the situation, it is not possible.

Upvotes: 0

Related Questions