0

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?

picciano
  • 22,341
  • 9
  • 69
  • 82
mlecoz
  • 409
  • 4
  • 17

2 Answers2

1

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!

user3069232
  • 8,587
  • 7
  • 46
  • 87
  • It's a good article, but this isn't the issue I'm looking to solve. I don't want to share data among *users* of the app; rather I want to share one CloudKit database among multiple apps not owned by the same developer. I have decided to use Firebase for this instead. – mlecoz Oct 10 '17 at 04:53
  • @mlecoz I looked into and decided to try using firebase too, but take care; read this article about the pricing structure before you get too far in. https://startupsventurecapital.com/firebase-costs-increased-by-7-000-81dc0a27271d – user3069232 Oct 10 '17 at 05:04
0

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.

picciano
  • 22,341
  • 9
  • 69
  • 82
  • Do other cloud databases like Heroku et. al. allow for something like this? Or is it pretty generally the case that one app gets one database? – mlecoz Oct 09 '17 at 22:18
  • 1
    Most other solutions (besides CloudKit) can be shared across multiple apps. Whether that's a good idea depends on your use case. – picciano Oct 09 '17 at 22:28