Change to save new records only if there isn't already a server side record

This commit is contained in:
Maurice Parker 2020-05-09 19:36:03 -05:00
parent aa3b1771e7
commit 6c6f401e3e
3 changed files with 92 additions and 2 deletions

View File

@ -14,6 +14,7 @@ struct CloudKitArticleStatusUpdate {
enum Record {
case all
case new
case statusOnly
case delete
}
@ -27,6 +28,10 @@ struct CloudKitArticleStatusUpdate {
return .delete
}
if statuses.count == 1, statuses.first!.key == .new {
return .new
}
if let article = article {
if article.status.read == false || article.status.starred == true {
return .all

View File

@ -111,6 +111,7 @@ final class CloudKitArticlesZone: CloudKitZone {
}
var modifyRecords = [CKRecord]()
var newRecords = [CKRecord]()
var deleteRecordIDs = [CKRecord.ID]()
for statusUpdate in statusUpdates {
@ -118,6 +119,9 @@ final class CloudKitArticlesZone: CloudKitZone {
case .all:
modifyRecords.append(makeStatusRecord(statusUpdate))
modifyRecords.append(makeArticleRecord(statusUpdate.article!))
case .new:
newRecords.append(makeStatusRecord(statusUpdate))
newRecords.append(makeArticleRecord(statusUpdate.article!))
case .delete:
deleteRecordIDs.append(CKRecord.ID(recordName: statusID(statusUpdate.articleID), zoneID: Self.zoneID))
case .statusOnly:
@ -126,10 +130,17 @@ final class CloudKitArticlesZone: CloudKitZone {
}
}
self.modify(recordsToSave: modifyRecords, recordIDsToDelete: deleteRecordIDs) { result in
modify(recordsToSave: modifyRecords, recordIDsToDelete: deleteRecordIDs) { result in
switch result {
case .success:
self.saveIfNew(newRecords) { result in
switch result {
case .success:
completion(.success(()))
case .failure(let error):
completion(.failure(error))
}
}
case .failure(let error):
self.handleModifyArticlesError(error, statusUpdates: statusUpdates, completion: completion)
}

View File

@ -234,6 +234,80 @@ extension CloudKitZone {
modify(recordsToSave: records, recordIDsToDelete: [], completion: completion)
}
/// Saves or modifies the records as long as they are unchanged relative to the local version
func saveIfNew(_ records: [CKRecord], completion: @escaping (Result<Void, Error>) -> Void) {
let op = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: [CKRecord.ID]())
op.savePolicy = .ifServerRecordUnchanged
op.isAtomic = false
op.modifyRecordsCompletionBlock = { [weak self] (_, _, error) in
guard let self = self else { return }
switch CloudKitZoneResult.resolve(error) {
case .success, .partialFailure:
DispatchQueue.main.async {
completion(.success(()))
}
case .zoneNotFound:
self.createZoneRecord() { result in
switch result {
case .success:
self.saveIfNew(records, completion: completion)
case .failure(let error):
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
case .userDeletedZone:
DispatchQueue.main.async {
completion(.failure(CloudKitZoneError.userDeletedZone))
}
case .retry(let timeToWait):
self.retryIfPossible(after: timeToWait) {
self.saveIfNew(records, completion: completion)
}
case .limitExceeded:
let chunkedRecords = records.chunked(into: 300)
let group = DispatchGroup()
var errorOccurred = false
for chunk in chunkedRecords {
group.enter()
self.saveIfNew(chunk) { result in
if case .failure(let error) = result {
os_log(.error, log: self.log, "%@ zone modify records error: %@", Self.zoneID.zoneName, error.localizedDescription)
errorOccurred = true
}
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
if errorOccurred {
completion(.failure(CloudKitZoneError.unknown))
} else {
completion(.success(()))
}
}
default:
DispatchQueue.main.async {
completion(.failure(CloudKitError(error!)))
}
}
}
database?.add(op)
}
/// Save the CKSubscription
func save(_ subscription: CKSubscription, completion: @escaping (Result<CKSubscription, Error>) -> Void) {
database?.save(subscription) { [weak self] savedSubscription, error in