【问题标题】:How to fetch the CKShare object of an already existing share (to get share URL)如何获取已存在共享的 CKShare 对象(获取共享 URL)
【发布时间】:2019-04-08 17:06:07
【问题描述】:

我正在尝试让我的应用程序的用户能够检索到他们已经共享的记录的 iCloud 共享链接。创建共享时,我可以使用let share = CKShare(rootRecord: CKRecord) 后跟Apple 的UISharingController 来获取URL。但是,每次我使用这种方法时,旧链接都会失效,并将其他人踢出他们已经加入的共享。如何允许用户(记录所有者)随时获取记录 CKShare 对象的可共享 URL?

//Initially shares the record
let share = CKShare(rootRecord: newRecord)
share[CKShare.SystemFieldKey.title] = "Group" as CKRecordValue?        
let modifyRecordsOperation = CKModifyRecordsOperation( recordsToSave: [newRecord, share], recordIDsToDelete: nil)

//Activates the UISharingController (code not shown)

【问题讨论】:

    标签: ios swift cloudkit cloudkit-sharing


    【解决方案1】:

    假设记录已被分享,您可以通过以下方式获取邀请其他参与者的 URL:

    if let shareReference = record.share {
        database.fetch(withRecordID: shareReference.recordID) { (share, error) in
            let shareURL = (share as? CKShare)?.url
        }
    }
    

    如果您想为现有共享再次显示 UICloudSharingController(不生成新 URL),您可以这样做:

    let sharingController = UICloudSharingController { (_, prepareCompletionHandler) in
    
        func createNewShare() {
            let operationQueue = OperationQueue()
            operationQueue.maxConcurrentOperationCount = 1
    
            let share = CKShare(rootRecord: record)
            share[CKShare.SystemFieldKey.title] = "Title" as CKRecordValue
            share.publicPermission = .readWrite
    
            let modifyRecordsOp = CKModifyRecordsOperation(recordsToSave: [share, record], recordIDsToDelete: nil)
            modifyRecordsOp.modifyRecordsCompletionBlock = { records, recordIDs, error in
                prepareCompletionHandler(share, self, error)
            }
            modifyRecordsOp.database = database
            operationQueue.addOperation(modifyRecordsOp)
        }
    
        if let shareReference = record.share {
            database.fetch(withRecordID: shareReference.recordID) { (share, error) in
                guard let share = share as? CKShare else {
                    createNewShare()
                    return
                }
                prepareCompletionHandler(share, self, nil)
            }
        } else {
            createNewShare()
        }
    }
    

    【讨论】:

      【解决方案2】:

      听起来您在这里遇到了多个问题。我建议仔细检查 UICloudSharingController documentation

      第一个问题是在您提供的代码中,您每次都在创建一个新的共享,而您要做的是创建一次,然后在您想要与之交互的任何时候显示该共享(包括获取共享 URL)。创建共享后,您可能希望以某种方式将其保存在设备上(可以像将其存储在 UserDefaults 中一样简单)。

      第二个问题是听起来您没有正确实例化 UICloudSharingController。当您最初创建 CKShare 时,您必须使用 UICloudSharingController init(preparationHandler:) 初始化程序并在其中创建共享。

      根据不同初始化程序的文档(强调我的):

      重要

      您必须使用正确的初始化程序初始化控制器 方法。如果 CKRecord 是,不要使用 init(preparationHandler:) 已经分享了。同样,不要使用 init(share:container:) 如果 CKRecord 不共享。使用错误的初始化程序会导致错误 保存记录时。

      然后,当您想要显示 shareSheet 时,您使用 UICloudSharingController init(share:container:) 初始化程序,并将您持久保存到设备的共享提供给它。

      这是 UICloudSharingController 文档中提供的用于创建 CKShare 的代码:

      func presentCloudSharingController(_ sender: Any) {  guard
          let barButtonItem = sender as? UIBarButtonItem,
          let rootRecord = self.recordToShare else {
          return
        }
      
        let cloudSharingController = UICloudSharingController { [weak self] (controller, completion: @escaping (CKShare?, CKContainer?, Error?) -> Void) in
          guard let `self` = self else {
            return
          }
          self.share(rootRecord: rootRecord, completion: completion)
        }
      
        if let popover = cloudSharingController.popoverPresentationController {
          popover.barButtonItem = barButtonItem
        }
        self.present(cloudSharingController, animated: true) {}
      }
      
      func share(rootRecord: CKRecord, completion: @escaping (CKShare?, CKContainer?, Error?) -> Void) {
        let shareRecord = CKShare(rootRecord: rootRecord)
        let recordsToSave = [rootRecord, shareRecord];
        let container = CKContainer.default()
        let privateDatabase = container.privateCloudDatabase
        let operation = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: []) 
        operation.perRecordCompletionBlock = { (record, error) in
          if let error = error {
            print("CloudKit error: \(error.localizedDescription)")
          }
        }
      
        operation.modifyRecordsCompletionBlock = { (savedRecords, deletedRecordIDs, error) in
          if let error = error {
            completion(nil, nil, error)
          } else {
            completion(shareRecord, container, nil)
          }
        }
      
        privateDatabase.add(operation)
      }
      

      下次你想展示它时,它会更加严格:

       let share = // yourCKShare
       let container = // yourCKContainer
       let viewController = // yourViewController
       let shareController = UICloudSharingController(share: share, container: container)
       viewController.present(shareController, animated: true)
      

      最后回答您问题的 URL 部分,UICloudSharingController 用于所有内容共享 - 当控制器呈现共享时,所有者(可能还有用户)可以使用“复制链接”按钮,如果你允许公共访问 - 我没有看过,因为我的东西是私人的):

      【讨论】:

      • 太棒了,非常感谢!总的来说,我对 swift 和编码词汇还比较陌生(swift 是我的第一语言,我还在上高中,还没有任何专业指导)。我仍然无法弄清楚的问题是如何准确地从 iCloud 服务器获取现有的 CKShare 对象,以便我可以将其放入 UISharingController。我知道如何使用便利 api 和 CKoperations 获取正常的 CK 记录,但它们没有附加 CKShare 对象。如何获取 CKShare 对象?
      • 不幸的是,这可能有点超出评论的范围 - 我建议做更多的研究和/或开始一个不同的问题。关于 CloudKit 的 WWDC 视频非常适合一些基本的高级概念内容(WWDC 2016 的视频在我看来很突出)。我发现 CloudKit 背后的概念也很难理解!
      • 嗯实际上我猜这在问题标题中是正确的,所以也许不要开始一个新问题。您是否尝试过使用 CKQueryOperation 或 CKFetchRecordZoneChangesOperation 来获取共享?
      • 如果一个 CKRecord 已经被共享,难道不应该简单地向下转换,像这样:let share = rootRecord as? CKShare
      • rootRecord 不是 CKShare 记录——它们是两个不同的记录。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-06
      • 1970-01-01
      • 2022-11-17
      • 2021-08-22
      • 2021-07-28
      • 2012-11-01
      相关资源
      最近更新 更多