Чтобы решить эту проблему, я создал новый проект и тщательно копировал один кодовый файл за раз, чтобы изолировать, в чем проблема. Я наконец нашел его, и он приписал этой одной строке кода
CKService().share(rootController: self, result:record)
против
let ck = CKService() //this line put into the controller class's stored property
///... and then later in the controller
ckService().share(rootController: self, result:record)
В первом случае UICloudSharingController от Apple сломается. Во втором случае UICloudSharingController будет работать.
func share(rootController: UIViewController, result: CKRecord) {
DispatchQueue.main.async {
self.container.prepareSharingController(rootRecord: result,
database: self.databases[0].cloudKitDB,
zone: self.zone,
completionHandler: { controller in
guard let sharingController = controller else {
let title = "Failed to share."
let message = "Can't set up a valid share object."
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
controller?.present(alert, animated: true) { }
return
}
// Save the root record for being used in UICloudSharingControllerDelegate methods,
// and presentt the UICloudSharingController.
//
self.record = result
sharingController.delegate = self
sharingController.availablePermissions = [.allowPublic, .allowPrivate, .allowReadOnly, .allowReadWrite]
rootController.present(sharingController, animated: true) { }
}
)
}
}
И вот часть для prepareSharingController (но я не думаю, что это актуально, потому что этот код просто скопирован / вставлен из примера проекта Apple). Кроме того, код там технически не вызывается, пока пользователь не подтвердит на следующем экране)
func prepareSharingController( rootRecord: CKRecord, participantLookupInfos: [CKUserIdentity.LookupInfo]? = nil,
database: CKDatabase, zone: CKRecordZone,
completionHandler:@escaping (UICloudSharingController?) -> Void) {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
// Share setup: fetch the share if the root record has been shared, or create a new one.
//
var sharingController: UICloudSharingController? = nil
var share: CKShare! = nil
if let shareRef = rootRecord.share {
// Fetch CKShare record if the root record has alreaad shared.
//
let fetchRecordsOp = CKFetchRecordsOperation(recordIDs: [shareRef.recordID])
fetchRecordsOp.fetchRecordsCompletionBlock = {recordsByRecordID, error in
guard handleCloudKitError(error, operation: .fetchRecords, affectedObjects: [shareRef.recordID]) == nil,
let result = recordsByRecordID?[shareRef.recordID] as? CKShare else { return }
share = result
if let lookupInfos = participantLookupInfos {
self.addParticipants(to: share, lookupInfos: lookupInfos, operationQueue: operationQueue)
}
}
fetchRecordsOp.database = database
operationQueue.addOperation(fetchRecordsOp)
// Wait until all operation are finished.
// If share is still nil when all operations done, then there are errors.
//
operationQueue.waitUntilAllOperationsAreFinished()
if let share = share {
sharingController = UICloudSharingController(share: share, container: self)
}
} else {
sharingController = UICloudSharingController { (_, prepareCompletionHandler) in
let shareID = CKRecord.ID(recordName: UUID().uuidString, zoneID: zone.zoneID)
share = CKShare(rootRecord: rootRecord, shareID: shareID)
share[CKShare.SystemFieldKey.title] = rootRecord.value(forKey: "CD_name") as? CKRecordValue
//share.publicPermission = .none // default value.
// addParticipants is asynchronous, but will be executed before modifyRecordsOp because
// the operationqueue is serial.
//
if let lookupInfos = participantLookupInfos {
self.addParticipants(to: share, lookupInfos: lookupInfos, operationQueue: operationQueue)
}
// Clear the parent property because root record is now sharing independently.
// Restore it when the sharing is stoped if necessary (cloudSharingControllerDidStopSharing).
//
rootRecord.parent = nil
let modifyRecordsOp = CKModifyRecordsOperation(recordsToSave: [share, rootRecord], recordIDsToDelete: nil)
modifyRecordsOp.modifyRecordsCompletionBlock = { records, recordIDs, error in
// Use the serverRecord when a partial failure caused by .serverRecordChanged occurs.
// Let UICloudSharingController handle the other error, until failedToSaveShareWithError is called.
//
if let ckError = handleCloudKitError(error, operation: .modifyRecords, affectedObjects: [shareID]) {
if let serverVersion = ckError.serverRecord as? CKShare {
share = serverVersion
}
}
prepareCompletionHandler(share, self, error)
}
modifyRecordsOp.database = database
operationQueue.addOperation(modifyRecordsOp)
}
}
completionHandler(sharingController)
}
И, наконец, когда я говорю break , я говорю, что UICloudSharingController представленное не будет работать правильно, методы делегата не будут вызваны для UICloudSharingControllerDelegate, а также пользовательский интерфейс, показывающий имя общего ресурса, будет неправильным.
Я не могу понять, чем отличаются эти две строки. У них обоих есть полный объект CKService.