【问题标题】:How is Record Zone Sharing done?记录区共享是如何完成的?
【发布时间】:2021-09-02 09:53:09
【问题描述】:

我的用例如下: 我的应用程序的每个用户都可以作为所有者创建一组项目。 这些项目是私有的,直到所有者邀请其他用户作为参与者共享所有项目。 参与者可以修改共享项目和/或添加其他项目。
因此,共享不是与单个项目相关,而是与所有者的所有项目相关。

我想使用 CoreData 和 CloudKit 进行 CoreData/CloudKit 镜像,并拥有私有和共享项目的本地副本。 据我了解,CoreData 和 CloudKit 将所有镜像项目放在私有数据库中的一个特殊区域“com.apple.coredata.cloudkit.zone”中。 所以,这个区域应该是共享的,即其中的所有项目。

在 WWDC 2021 视频„Build apps that share data through CloudKit and Core Data“ 中,据说NSPersistentCloudKitContainer 使用记录区域共享,而不是使用根记录的分层记录共享。 在the docsUICloudSharingController 中给出了如何使用rootRecord 共享记录的示例。 我认为这可以修改为使用共享记录区域。
shareRecord 例如可以改为使用

let shareRecord = CKShare(rootRecord: rootRecord)

let shareRecord = CKShare.init(recordZoneID:)  

但这是使用Apple进行镜像的记录区域的ID,即“com.apple.coredata.cloudkit.zone”吗?如果是这样,如果我只有区域名称,我如何获得它的CKRecordZone.ID

【问题讨论】:

    标签: ios core-data cloudkit sharing ckrecordzone


    【解决方案1】:

    现在我想出了如何做到这一点。也许它可以帮助其他人。

    从点击分享按钮开始。
    首先,检查 iCloud 私有数据库中是否已经存在 CKShare 记录。 根据它,UICloudSharingController 被初始化以便它创建它,或者它使用它。 如果它应该创建共享,则对其进行相应配置。

    // See <https://developer.apple.com/documentation/uikit/uicloudsharingcontroller>
    @IBAction func sharingButtonTapped(_ sender: UIBarButtonItem) {
        let barButtonItem = sender
        
        /*
         If no CKShare record has been stored yet in iCloud, it will be created below using UICloudSharingController initialized with a preparation handler.
         If it exists already, UICloudSharingController is initializes with the existing CKShare record.
         */
        getShareRecord { result in
            DispatchQueue.main.async {
                let cloudSharingController: UICloudSharingController!
                switch result {
                case .success(let ckShareRecord):
                    if let shareRecord = ckShareRecord {
                        cloudSharingController = UICloudSharingController.init(share: shareRecord, container: CKContainer.default())
                    } else {
                        cloudSharingController = UICloudSharingController { [weak self] (controller, completion: @escaping (CKShare?, CKContainer?, Error?) -> Void) in
                            guard let `self` = self else { return }
                            self.share(completion: completion)
                        }
                    }
                    cloudSharingController.delegate = self
                    
                    if let popover = cloudSharingController.popoverPresentationController {
                        popover.barButtonItem = barButtonItem
                    }
                    self.present(cloudSharingController, animated: true) {}
                case .failure(let error):
                    fatalError("\(error)")
                }
            }
        }
    }
    
    // For sharing see https://developer.apple.com/documentation/cloudkit/shared_records
    private func share(completion: @escaping (CKShare?, CKContainer?, Error?) -> Void) {
        if #available(iOS 15, *) {
            // iOS 15++
            let recordZoneID = CKRecordZone.ID(zoneName: "com.apple.coredata.cloudkit.zone", ownerName: CKCurrentUserDefaultName)
            let shareRecord = CKShare(recordZoneID: recordZoneID)
            
            // Configure the share so the system displays the shopping lists name and logo 
            // when the user initiates sharing or accepts an invitation to participate.
            CloudKitHelper.fullNameOfICloudAccount { result in
                var fullName: String!
                switch result {
                case .success(let name):
                    fullName = name
                case .failure(let error):
                    print("Could not read user name: \(error)")
                    fullName = "Unknown user"
                }
                shareRecord[CKShare.SystemFieldKey.title] = String(format: NSLocalizedString("SHARING_LIST_OF_USER", comment:" "), fullName)
                let image = UIImage(named: kFileNameLogo)!.pngData()
                shareRecord[CKShare.SystemFieldKey.thumbnailImageData] = image
                // Include a custom UTI that describes the share's content.
                shareRecord[CKShare.SystemFieldKey.shareType] = "com.zeh4soft.ShopEasy.shoppingList"
                
                let recordsToSave = [shareRecord]
                let container = CKContainer.default()
                let privateDatabase = container.privateCloudDatabase
                let operation = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: nil) 
                operation.perRecordProgressBlock = { (record, progress) in
                    if progress < 1.0 {
                        print("CloudKit error: Could not save record completely")
                    }
                }
                
                operation.modifyRecordsResultBlock = { result in
                    switch result {
                    case .success:
                        completion(shareRecord, container, nil)
                    case .failure(let error):
                        completion(nil, nil, error)
                    }
                }
                
                privateDatabase.add(operation)
            }
            
        } else {
            // iOS <15
            fatalError("Sharing is only available in iOS 15")
        }
    }
    
    func getShareRecord(completion: @escaping (Result<CKShare?, Error>) -> Void) {
        let query = CKQuery(recordType: "cloudkit.share", predicate: NSPredicate(value: true))
        privateDB.fetch(withQuery: query) { result in
            switch result {
            case .success(let returned): 
                // .success((matchResults: [CKRecord.ID : Result<CKRecord, Error>], queryCursor: CKQueryOperation.Cursor?))
                let matchResults = returned.0 // [CKRecord.ID : Result<CKRecord, Error>]
                switch matchResults.count {
                case 0:
                    completion(.success(nil))
                    return
                case 1:
                    let recordResult = matchResults.values.first!
                    switch recordResult {
                    case .success(let ckRecord):
                        completion(.success(ckRecord as? CKShare))
                        return
                    case .failure(let error):
                        completion(.failure(error))
                        return
                    }
                default:
                    fatalError("More than 1 CKShare record")
                }
            case .failure(let error):
                completion(.failure(error))
                return
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-05-27
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-29
      • 2022-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多