Почему CKModifyRecordsOperation для пакетного удаления записей в CloudKit не удаляет записи?
У меня есть код для пакетного удаления записей из частной базы данных в CloudKit, но он не работает. (Я также заметил, что сохраненные мной записи не сохранились.) Я использую настоящий iPhone 8 с фактическим идентификатором Apple ID, который я использую лично, а не учетной записью разработчика. Я получаю те же результаты с симулятором iPhone 8. У меня нет этой проблемы, когда я вхожу в iCloud с тем же Apple ID, что и моя учетная запись разработчика, с помощью iPhone 8 Simulator.
После того, как ответы на этот пост не дали мне решения, я думаю, что наиболее важным в этой проблеме является то, что она возникает, когда я использую Apple ID, который не является моей учетной записью разработчика. Я пробовал это на двух разных Apple ID, которые не являются моей учетной записью разработчика. Может быть, я что-то не замечаю?
Ни один из подобных постов в stackru не решает эту проблему.
Похоже, что некоторые записи, которые код должен удалить, на самом деле удалены, а некоторые нет. Когда я снова запускаю код, записи все еще существуют, но примерно на единицу меньше, чем раньше.
Вот мой код:
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let splitViewController = window!.rootViewController as! UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
splitViewController.delegate = self
deleteRecords()
return true
}
}
let privateDatabase = CKContainer.default().privateCloudDatabase
func deleteRecords() {
print("deleteRecords()")
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: DatabaseNameStrings.recordTypeAffirmation, predicate: predicate)
privateDatabase.perform(query, inZoneWith: nil) {
(records: [CKRecord]?, error: Error?) in
if error != nil {
print(error as Any)
} else {
if let records = records {
print("records.count=", records.count)
let recordIDsToDelete = records.map { $0.recordID }
print("recordIDsToDelete:")
print(recordIDsToDelete)
let operation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: recordIDsToDelete)
operation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
if error == nil {
print("Batch delete records!")
print("number of records deleted:", deletedRecordIDs?.count as Any)
printNumberOfRecords()
} else {
print(error as Any)
}
}
privateDatabase.add(operation)
}
}
}
}
func printNumberOfRecords() {
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: DatabaseNameStrings.recordTypeAffirmation, predicate: predicate)
privateDatabase.perform(query, inZoneWith: nil) {
(records: [CKRecord]?, error: Error?) in
if error != nil {
print(error as Any)
} else {
if let records = records {
print("Number of records in CloudKit=", records.count)
}
}
}
}
Вот результат первого запуска кода в окне отладки:
deleteRecords()
records.count= 93
recordIDsToDelete:
[<CKRecordID: 0x280bbcb00; recordName=B33A3F23-23D3-44C6-AEBC-86DD718DBB62, zoneID=...>, ... ]
Batch delete records!
number of records deleted: Optional(93)
Number of records in CloudKit= 67
Вот результат второго запуска кода в окне отладки:
deleteRecords()
records.count= 92
recordIDsToDelete:
[<CKRecordID: 0x280080d00; recordName=BBA5B236-A036-4AC9-82E1-165D3B003E23, zoneID=...>, ... ]
Batch delete records!
number of records deleted: Optional(92)
Number of records in CloudKit= 52
Когда я использую этот код вместо deleteRecords() ...
func deleteRecordsOneAtATime() {
print("deleteRecordsOneAtATime()")
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: DatabaseNameStrings.recordTypeAffirmation, predicate: predicate)
privateDatabase.perform(query, inZoneWith: nil) {
(records: [CKRecord]?, error: Error?) in
if error != nil {
print(error as Any)
} else {
if let records = records {
print("records.count=", records.count)
let recordIDsToDelete = records.map { $0.recordID }
print("recordIDsToDelete:")
print(recordIDsToDelete)
for recordID in recordIDsToDelete {
privateDatabase.delete(withRecordID: recordID) {
(localRecordID: CKRecord.ID?, error: Error?) in
if error != nil {
print("error:\n", error as Any)
} else {
if localRecordID != nil {
print("localRecordID:", localRecordID as Any)
}
}
}
printNumberOfRecords()
}
}
}
}
}
Попадаю в окно отладки:
deleteRecordsOneAtATime()
Number of records in CloudKit= 97
records.count= 97
recordIDsToDelete:
[<CKRecordID: 0x283622ec0; recordName=600B7BFE-04FE-4F63-BC4C-5AD1AE08908D, zoneID=...>, ... ]
localRecordID: Optional(<CKRecordID: 0x2821ff320; recordName=8E8CD0F0-FDF5-4CB9-B16C-5CF91C3503A2, zoneID=_defaultZone:__defaultOwner__>)
localRecordID: Optional(<CKRecordID: 0x2821ff320; recordName=8E0A0816-1B05-4707-A4E7-C40762E68663, zoneID=_defaultZone:__defaultOwner__>)
localRecordID: Optional(<CKRecordID: 0x28210b200; recordName=8E127624-F1D3-401E-ADF2-BB97354FCA98, zoneID=_defaultZone:__defaultOwner__>)
...
Number of records in CloudKit= 87
localRecordID: Optional(<CKRecordID: 0x282108660; recordName=962639D1-83E6-40D2-A57D-F70ADCEBED08, zoneID=_defaultZone:__defaultOwner__>)
localRecordID: Optional(<CKRecordID: 0x28210ff20; recordName=968D62AB-523E-464B-94B8-3C90E0382AB6, zoneID=_defaultZone:__defaultOwner__>)
localRecordID: Optional(<CKRecordID: 0x28210faa0; recordName=96C92DD2-ED27-4FED-8320-44D03981B04F, zoneID=_defaultZone:__defaultOwner__>)
localRecordID: Optional(<CKRecordID: 0x2821085a0; recordName=96A2D515-D3E7-475E-B609-8389DE4B88D1, zoneID=_defaultZone:__defaultOwner__>)
Вот мой последний код, который все еще не работает:
func removeRecords() {
print("removeRecords()")
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: DatabaseNameStrings.recordTypeAffirmation, predicate: predicate)
privateDatabase.perform(query, inZoneWith: nil) {
(records: [CKRecord]?, error: Error?) in
if error != nil {
print(error as Any)
} else {
if let records = records {
print("records.count=", records.count)
let recordIDsToDelete = records.map { $0.recordID }
print("recordIDsToDelete:")
print(recordIDsToDelete)
DispatchQueue.main.async {
if recordIDsToDelete.count > 50 {
let slice = Array(recordIDsToDelete[0 ..< 50])
let leftOver = Array(recordIDsToDelete[50 ... recordIDsToDelete.count-1])
privateDatabase.remove(recordsWith: slice) {
(result: Result<Void, Error>) in
print("result:", result)
switch result {
case .failure(let err):
print("failure")
print(err)
case .success(()):
print("success")
DispatchQueue.main.async {
privateDatabase.remove(recordsWith: leftOver) {
(result: Result<Void, Error>) in
print("result:", result)
switch result {
case .failure(let err):
print("failure")
print(err)
case .success(()):
print("success")
}
print("type of result:", type(of: result))
}
}
}
print("type of result:", type(of: result))
}
} else {
privateDatabase.remove(recordsWith: recordIDsToDelete) {
(result: Result<Void, Error>) in
print("result:", result)
switch result {
case .failure(let err):
print("failure")
print(err)
case .success(()):
print("success")
}
print("type of result:", type(of: result))
}
}
privateDatabase.remove(recordsWith: recordIDsToDelete) {
(result: Result<Void, Error>) in
print("result:", result)
switch result {
case .failure(let err):
print("failure")
print(err)
case .success(()):
print("success")
}
print("type of result:", type(of: result))
}
}
}
}
}
}
extension CKDatabase {
func remove(
recordsWith ids: [CKRecord.ID], completion: @escaping CompletionHandler<Void>) {
let operation = CKModifyRecordsOperation(recordIDsToDelete: ids)
operation.qualityOfService = .userInitiated
operation.modifyRecordsCompletionBlock = { _, _, error in
if let error = error {
print("error:")
print(error)
if let err = error as? CKError, let time = err.retryAfterSeconds {
DispatchQueue.main.asyncAfter(deadline: .now() + time) {
self.remove(recordsWith: ids, completion: completion)
}
} else {
completion(.failure(error))
print("CKDatabase.remove(_:_:) failed.")
}
} else {
completion(.success(()))
print("CKDatabase.remove(_:_:) succeeded.")
}
}
self.add(operation)
}
}
3 ответа
Я думаю, что код удалял то, что получил от метода выполнения запроса. Просто метод выполнения запроса не возвращал идентификаторы записей из всех существующих записей.
Иногда CloudKit выдает вам разные ошибки, поэтому вы должны убедиться, что вы их обработали, и снова запустить вызов, если ошибка содержит retryAfterSeconds
. Здесь используйте этот метод оболочки дляCKDatabase
с минимальными усилиями справиться с ошибками. При вызове любого API CloudKit обязательно разбивайте запросы на более мелкие блоки (например, по 100 элементов каждый).
extension CKDatabase {
func remove(
recordsWith ids: [CKRecord.ID],
completion: @escaping (Result<Void, Error>) -> Void
) {
let operation = CKModifyRecordsOperation(recordIDsToDelete: ids)
operation.qualityOfService = .userInitiated
operation.modifyRecordsCompletionBlock = { _, _, error in
if let error = error {
if let err = error as? CKError, let time = err.retryAfterSeconds {
DispatchQueue.main.asyncAfter(deadline: .now() + time) {
self.remove(recordsWith: ids, completion: completion)
}
} else {
completion(.failure(error))
}
} else {
completion(.success(()))
}
}
self.add(operation)
}
}
Попробуйте эти изменения;
после
let operation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: recordIDsToDelete)
добавлять
operation.database = privateDatabase
operation.queuePriority = .veryHigh
operation.configuration = CKOperation.Configuration()
operation.configuration.qualityOfService = .userInteractive
Затем начните операцию с;
operation.start()
вместо того;
privateDatabase.add(operation)