如何获取已存在共享的 CKShare 对象(获取共享 URL)
How to fetch the CKShare object of an already existing share (to get share URL)
我试图让我的应用程序的用户能够检索 iCloud 共享 link 到他们已经共享的记录。通过使用 let share = CKShare(rootRecord: CKRecord)
后跟 Apple 的 UISharingController
创建共享时,我可以获得 URL。但是,每次我使用这种方法时,旧的 link 就会失效并将其他人踢出他们已经加入的共享。我如何允许用户(记录所有者)随时获取记录 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)
听起来您这里遇到了多个问题。我建议仔细阅读 UICloudSharingController documentation。
第一个问题是在您提供的代码中,您每次都创建一个新的共享,而您想要做的是创建一次,然后在您想要与之交互的任何时候显示该共享(包括获得份额 URL)。创建共享后,您可能希望以某种方式将其保留在设备上(它可以像将其存储在 UserDefaults 中一样简单)。
第二个问题是您似乎没有正确实例化 UICloudSharingController。当您最初创建 CKShare 时,您必须使用 UICloudSharingController init(preparationHandler:) 初始化程序并在其中创建共享。
根据不同初始化器的文档(强调我的):
Important
You must initialize the controller with the correct initializer
method. Do not use init(preparationHandler:) if the CKRecord is
already shared. Likewise, do not use init(share:container:) if the
CKRecord is not shared. Using the wrong initializer leads to errors
when saving the record.
然后当您想要显示 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 用于所有内容共享 - 当控制器呈现共享时,所有者可以使用 "Copy Link" 按钮(如果您允许 public 访问,可能还有用户 - 我没有看过,因为我的东西是私人的):
假设记录已经共享,您可以获得 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()
}
}
我试图让我的应用程序的用户能够检索 iCloud 共享 link 到他们已经共享的记录。通过使用 let share = CKShare(rootRecord: CKRecord)
后跟 Apple 的 UISharingController
创建共享时,我可以获得 URL。但是,每次我使用这种方法时,旧的 link 就会失效并将其他人踢出他们已经加入的共享。我如何允许用户(记录所有者)随时获取记录 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)
听起来您这里遇到了多个问题。我建议仔细阅读 UICloudSharingController documentation。
第一个问题是在您提供的代码中,您每次都创建一个新的共享,而您想要做的是创建一次,然后在您想要与之交互的任何时候显示该共享(包括获得份额 URL)。创建共享后,您可能希望以某种方式将其保留在设备上(它可以像将其存储在 UserDefaults 中一样简单)。
第二个问题是您似乎没有正确实例化 UICloudSharingController。当您最初创建 CKShare 时,您必须使用 UICloudSharingController init(preparationHandler:) 初始化程序并在其中创建共享。
根据不同初始化器的文档(强调我的):
Important
You must initialize the controller with the correct initializer method. Do not use init(preparationHandler:) if the CKRecord is already shared. Likewise, do not use init(share:container:) if the CKRecord is not shared. Using the wrong initializer leads to errors when saving the record.
然后当您想要显示 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 用于所有内容共享 - 当控制器呈现共享时,所有者可以使用 "Copy Link" 按钮(如果您允许 public 访问,可能还有用户 - 我没有看过,因为我的东西是私人的):
假设记录已经共享,您可以获得 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()
}
}