From 9eef9ea8dcc99f663462484492b96e497eab5622 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 26 Apr 2020 03:19:20 -0500 Subject: [PATCH] Delete or send article records based on article status --- .../CloudKit/CloudKitAccountDelegate.swift | 16 ++-- .../CloudKitAccountZoneDelegate.swift | 8 +- .../CloudKit/CloudKitArticlesZone.swift | 87 +++++++------------ 3 files changed, 44 insertions(+), 67 deletions(-) diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index b9155a566..bf9850a91 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -112,7 +112,7 @@ final class CloudKitAccountDelegate: AccountDelegate { func processWithArticles(_ articles: Set
) { - self.articlesZone.sendArticleStatus(syncStatuses, articles: articles) { result in + self.articlesZone.modifyArticlesAndStatuses(syncStatuses, articles: articles) { result in switch result { case .success: self.database.deleteSelectedForProcessing(syncStatuses.map({ $0.articleID }) ) @@ -151,7 +151,7 @@ final class CloudKitAccountDelegate: AccountDelegate { func refreshArticleStatus(for account: Account, completion: @escaping ((Result) -> Void)) { os_log(.debug, log: log, "Refreshing article statuses...") - articlesZone.refreshArticleStatus() { result in + articlesZone.refreshArticlesAndStatuses() { result in os_log(.debug, log: self.log, "Done refreshing article statuses.") switch result { case .success: @@ -616,9 +616,9 @@ private extension CloudKitAccountDelegate { group.notify(queue: DispatchQueue.main) { - self.articlesZone.deleteArticles(deletedArticles) { _ in + self.articlesZone.deleteArticlesAndStatuses(deletedArticles) { _ in self.refreshProgress.completeTask() - self.articlesZone.sendNewArticles(newArticles) { _ in + self.articlesZone.saveNewArticlesAndStatuses(newArticles) { _ in self.refreshProgress.completeTask() completion() } @@ -665,9 +665,9 @@ private extension CloudKitAccountDelegate { let newArticles = articleChanges.newArticles ?? Set
() let deletedArticles = articleChanges.deletedArticles ?? Set
() - self.articlesZone.deleteArticles(deletedArticles) { _ in + self.articlesZone.deleteArticlesAndStatuses(deletedArticles) { _ in self.refreshProgress.completeTask() - self.articlesZone.sendNewArticles(newArticles) { _ in + self.articlesZone.saveNewArticlesAndStatuses(newArticles) { _ in self.refreshProgress.clear() completion(.success(feed)) } @@ -744,9 +744,9 @@ private extension CloudKitAccountDelegate { let newArticles = articleChanges.newArticles ?? Set
() let deletedArticles = articleChanges.deletedArticles ?? Set
() - self.articlesZone.deleteArticles(deletedArticles) { _ in + self.articlesZone.deleteArticlesAndStatuses(deletedArticles) { _ in self.refreshProgress.completeTask() - self.articlesZone.sendNewArticles(newArticles) { _ in + self.articlesZone.saveNewArticlesAndStatuses(newArticles) { _ in self.refreshProgress.clear() completion(.success(feed)) } diff --git a/Frameworks/Account/CloudKit/CloudKitAccountZoneDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountZoneDelegate.swift index a7d1dd8b8..606df77eb 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountZoneDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountZoneDelegate.swift @@ -214,9 +214,9 @@ private extension CloudKitAcountZoneDelegate { switch result { case .success(let articleChanges): - self.articlesZone?.deleteArticles(articleChanges.deletedArticles ?? Set
()) { _ in + self.articlesZone?.deleteArticlesAndStatuses(articleChanges.deletedArticles ?? Set
()) { _ in self.refreshProgress?.completeTask() - self.articlesZone?.sendNewArticles(articleChanges.newArticles ?? Set
()) { _ in + self.articlesZone?.saveNewArticlesAndStatuses(articleChanges.newArticles ?? Set
()) { _ in self.refreshProgress?.completeTask() completion(webFeed) } @@ -251,9 +251,9 @@ private extension CloudKitAcountZoneDelegate { BatchUpdate.shared.end() switch result { case .success(let articleChanges): - self.articlesZone?.deleteArticles(articleChanges.deletedArticles ?? Set
()) { _ in + self.articlesZone?.deleteArticlesAndStatuses(articleChanges.deletedArticles ?? Set
()) { _ in self.refreshProgress?.completeTask() - self.articlesZone?.sendNewArticles(articleChanges.newArticles ?? Set
()) { _ in + self.articlesZone?.saveNewArticlesAndStatuses(articleChanges.newArticles ?? Set
()) { _ in self.refreshProgress?.completeTask() completion(webFeed) } diff --git a/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift b/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift index 6c658569a..89bf64d96 100644 --- a/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift +++ b/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift @@ -59,7 +59,7 @@ final class CloudKitArticlesZone: CloudKitZone { self.database = container.privateCloudDatabase } - func refreshArticleStatus(completion: @escaping ((Result) -> Void)) { + func refreshArticlesAndStatuses(completion: @escaping ((Result) -> Void)) { fetchChangesInZone() { result in switch result { case .success: @@ -69,7 +69,7 @@ final class CloudKitArticlesZone: CloudKitZone { self.createZoneRecord() { result in switch result { case .success: - self.refreshArticleStatus(completion: completion) + self.refreshArticlesAndStatuses(completion: completion) case .failure(let error): completion(.failure(error)) } @@ -81,7 +81,7 @@ final class CloudKitArticlesZone: CloudKitZone { } } - func sendNewArticles(_ articles: Set
, completion: @escaping ((Result) -> Void)) { + func saveNewArticlesAndStatuses(_ articles: Set
, completion: @escaping ((Result) -> Void)) { guard !articles.isEmpty else { completion(.success(())) return @@ -95,37 +95,38 @@ final class CloudKitArticlesZone: CloudKitZone { saveIfNew(records, completion: completion) } - func deleteArticles(_ articles: Set
, completion: @escaping ((Result) -> Void)) { + func deleteArticlesAndStatuses(_ articles: Set
, completion: @escaping ((Result) -> Void)) { guard !articles.isEmpty else { completion(.success(())) return } - let recordIDs = articles.map { CKRecord.ID(recordName: $0.articleID, zoneID: Self.zoneID) } + let recordIDs = articles.map { CKRecord.ID(recordName: statusID($0.articleID), zoneID: Self.zoneID) } delete(recordIDs: recordIDs, completion: completion) } - func sendArticleStatus(_ syncStatuses: [SyncStatus], articles: Set
, completion: @escaping ((Result) -> Void)) { + func modifyArticlesAndStatuses(_ syncStatuses: [SyncStatus], articles: Set
, completion: @escaping ((Result) -> Void)) { var records = makeStatusRecords(syncStatuses, articles) - let starredArticles = articles.filter({ $0.status.starred == true }) - makeArticleRecordsIfNecessary(starredArticles) { result in + let saveArticles = articles.filter { $0.status.read == false || $0.status.starred == true } + for saveArticle in saveArticles { + records.append(contentsOf: makeArticleRecords(saveArticle)) + } + + let deleteArticleIDs = articles.subtracting(saveArticles).map { + return CKRecord.ID(recordName: articleID($0.articleID), zoneID: Self.zoneID) + } + + self.modify(recordsToSave: records, recordIDsToDelete: deleteArticleIDs) { result in switch result { - case .success(let articleRecords): - records.append(contentsOf: articleRecords) - self.modify(recordsToSave: records, recordIDsToDelete: []) { result in - switch result { - case .success: - completion(.success(())) - case .failure(let error): - self.handleSendArticleStatusError(error, syncStatuses: syncStatuses, starredArticles: articles, completion: completion) - } - } + case .success: + completion(.success(())) case .failure(let error): self.handleSendArticleStatusError(error, syncStatuses: syncStatuses, starredArticles: articles, completion: completion) } } + } func handleSendArticleStatusError(_ error: Error, syncStatuses: [SyncStatus], starredArticles: Set
, completion: @escaping ((Result) -> Void)) { @@ -133,7 +134,7 @@ final class CloudKitArticlesZone: CloudKitZone { self.createZoneRecord() { result in switch result { case .success: - self.sendArticleStatus(syncStatuses, articles: starredArticles, completion: completion) + self.modifyArticlesAndStatuses(syncStatuses, articles: starredArticles, completion: completion) case .failure(let error): completion(.failure(error)) } @@ -147,12 +148,19 @@ final class CloudKitArticlesZone: CloudKitZone { private extension CloudKitArticlesZone { + func statusID(_ id: String) -> String { + return "s|\(id)" + } + + func articleID(_ id: String) -> String { + return "a|\(id)" + } + func makeNewStatusRecords(_ articles: Set
) -> [CKRecord] { - var records = [CKRecord]() for article in articles { - let recordID = CKRecord.ID(recordName: article.articleID, zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: Self.zoneID) let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID) if let webFeedExternalID = article.webFeed?.externalID { record[CloudKitArticleStatus.Fields.webFeedExternalID] = webFeedExternalID @@ -165,7 +173,6 @@ private extension CloudKitArticlesZone { } func makeStatusRecords(_ syncStatuses: [SyncStatus], _ articles: Set
) -> [CKRecord] { - var articleDict = [String: Article]() for article in articles { articleDict[article.articleID] = article @@ -177,7 +184,7 @@ private extension CloudKitArticlesZone { var record = records[status.articleID] if record == nil { - let recordID = CKRecord.ID(recordName: status.articleID, zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: statusID(status.articleID), zoneID: Self.zoneID) record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID) records[status.articleID] = record } @@ -196,42 +203,12 @@ private extension CloudKitArticlesZone { return Array(records.values) } - - func makeArticleRecordsIfNecessary(_ articles: Set
, completion: @escaping ((Result<[CKRecord], Error>) -> Void)) { - let group = DispatchGroup() - var records = [CKRecord]() - - for article in articles { - - let statusRecordID = CKRecord.ID(recordName: article.articleID, zoneID: Self.zoneID) - let statusRecordRef = CKRecord.Reference(recordID: statusRecordID, action: .deleteSelf) - let predicate = NSPredicate(format: "articleStatus = %@", statusRecordRef) - let ckQuery = CKQuery(recordType: CloudKitArticle.recordType, predicate: predicate) - - group.enter() - exists(ckQuery) { result in - switch result { - case .success(let recordFound): - if !recordFound { - records.append(contentsOf: self.makeArticleRecords(article)) - } - case .failure: - records.append(contentsOf: self.makeArticleRecords(article)) - } - group.leave() - } - - } - - group.notify(queue: DispatchQueue.main) { - completion(.success(records)) - } - } func makeArticleRecords(_ article: Article) -> [CKRecord] { var records = [CKRecord]() - let articleRecord = CKRecord(recordType: CloudKitArticle.recordType, recordID: generateRecordID()) + let recordID = CKRecord.ID(recordName: articleID(article.articleID), zoneID: Self.zoneID) + let articleRecord = CKRecord(recordType: CloudKitArticle.recordType, recordID: recordID) let articleStatusRecordID = CKRecord.ID(recordName: article.articleID, zoneID: Self.zoneID) articleRecord[CloudKitArticle.Fields.articleStatus] = CKRecord.Reference(recordID: articleStatusRecordID, action: .deleteSelf)