From bdc02f0b0b7c6987ab67cdb8d412962082300b07 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 27 Apr 2020 18:09:10 -0500 Subject: [PATCH] Switch back to having separate article and status records --- .../CloudKit/CloudKitAccountDelegate.swift | 14 +-- .../CloudKit/CloudKitArticlesZone.swift | 87 +++++++++++-------- .../CloudKitArticlesZoneDelegate.swift | 14 +-- 3 files changed, 67 insertions(+), 48 deletions(-) diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index 7d35ff1ac..be3f5ccc0 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -120,13 +120,15 @@ final class CloudKitAccountDelegate: AccountDelegate { self.articlesZone.modifyArticles(statusedArticles) { result in switch result { case .success: - self.database.deleteSelectedForProcessing(syncStatuses.map({ $0.articleID }) ) - os_log(.debug, log: self.log, "Done sending article statuses.") - completion(.success(())) + self.database.deleteSelectedForProcessing(syncStatuses.map({ $0.articleID })) { _ in + os_log(.debug, log: self.log, "Done sending article statuses.") + completion(.success(())) + } case .failure(let error): - self.database.resetSelectedForProcessing(syncStatuses.map({ $0.articleID }) ) - self.processAccountError(account, error) - completion(.failure(error)) + self.database.resetSelectedForProcessing(syncStatuses.map({ $0.articleID })) { _ in + self.processAccountError(account, error) + completion(.failure(error)) + } } } diff --git a/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift b/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift index d883a9740..661ad0fbe 100644 --- a/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift +++ b/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift @@ -29,7 +29,7 @@ final class CloudKitArticlesZone: CloudKitZone { struct CloudKitArticle { static let recordType = "Article" struct Fields { - static let hollow = "hollow" + static let articleStatus = "articleStatus" static let webFeedURL = "webFeedURL" static let uniqueID = "uniqueID" static let title = "title" @@ -42,6 +42,13 @@ final class CloudKitArticlesZone: CloudKitZone { static let datePublished = "datePublished" static let dateModified = "dateModified" static let parsedAuthors = "parsedAuthors" + } + } + + struct CloudKitArticleStatus { + static let recordType = "ArticleStatus" + struct Fields { + static let webFeedExternalID = "webFeedExternalID" static let read = "read" static let starred = "starred" } @@ -109,21 +116,20 @@ final class CloudKitArticlesZone: CloudKitZone { for statusArticle in statusArticles { switch (statusArticle.status.key, statusArticle.status.flag) { case (.new, true): - // create status + newRecords.append(makeStatusRecord(statusArticle)) if let article = statusArticle.article { newRecords.append(contentsOf: makeArticleRecords(article)) } case (.starred, true), (.read, false): - // create status + modifyRecords.append(makeStatusRecord(statusArticle)) if let article = statusArticle.article { modifyRecords.append(contentsOf: makeArticleRecords(article)) } case (.deleted, true): - deleteRecordIDs.append(CKRecord.ID(recordName: statusArticle.status.articleID, zoneID: Self.zoneID)) + deleteRecordIDs.append(CKRecord.ID(recordName: statusID(statusArticle.status.articleID), zoneID: Self.zoneID)) default: - print() - // create status - // delete article record + modifyRecords.append(makeStatusRecord(statusArticle)) + deleteRecordIDs.append(CKRecord.ID(recordName: statusID(statusArticle.status.articleID), zoneID: Self.zoneID)) } } @@ -162,14 +168,49 @@ private extension CloudKitArticlesZone { completion(.failure(error)) } } + + func statusID(_ id: String) -> String { + return "s|\(id)" + } + + func articleID(_ id: String) -> String { + return "a|\(id)" + } + + func makeStatusRecord(_ statusArticle: (status: SyncStatus, article: Article?)) -> CKRecord { + let status = statusArticle.status + let recordID = CKRecord.ID(recordName: statusID(status.articleID), zoneID: Self.zoneID) + let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID) + + if let webFeedExternalID = statusArticle.article?.webFeed?.externalID { + record[CloudKitArticleStatus.Fields.webFeedExternalID] = webFeedExternalID + } + + if let article = statusArticle.article { + record[CloudKitArticleStatus.Fields.read] = article.status.read ? "1" : "0" + record[CloudKitArticleStatus.Fields.starred] = article.status.starred ? "1" : "0" + } else { + switch status.key { + case .read: + record[CloudKitArticleStatus.Fields.read] = status.flag ? "1" : "0" + case .starred: + record[CloudKitArticleStatus.Fields.starred] = status.flag ? "1" : "0" + default: + break + } + } + + return record + } func makeArticleRecords(_ article: Article) -> [CKRecord] { var records = [CKRecord]() - let recordID = CKRecord.ID(recordName: article.articleID, zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: articleID(article.articleID), zoneID: Self.zoneID) let articleRecord = CKRecord(recordType: CloudKitArticle.recordType, recordID: recordID) - articleRecord[CloudKitArticle.Fields.hollow] = "0" + let articleStatusRecordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: Self.zoneID) + articleRecord[CloudKitArticle.Fields.articleStatus] = CKRecord.Reference(recordID: articleStatusRecordID, action: .deleteSelf) articleRecord[CloudKitArticle.Fields.webFeedURL] = article.webFeed?.url articleRecord[CloudKitArticle.Fields.uniqueID] = article.uniqueID articleRecord[CloudKitArticle.Fields.title] = article.title @@ -198,37 +239,9 @@ private extension CloudKitArticlesZone { articleRecord[CloudKitArticle.Fields.parsedAuthors] = parsedAuthors } - articleRecord[CloudKitArticle.Fields.read] = article.status.read ? "1" : "0" - articleRecord[CloudKitArticle.Fields.starred] = article.status.starred ? "1" : "0" - records.append(articleRecord) return records } - func makeHollowArticleRecords(_ article: Article) -> [CKRecord] { - var records = [CKRecord]() - - let recordID = CKRecord.ID(recordName: article.articleID, zoneID: Self.zoneID) - let articleRecord = CKRecord(recordType: CloudKitArticle.recordType, recordID: recordID) - - articleRecord[CloudKitArticle.Fields.hollow] = "1" - articleRecord[CloudKitArticle.Fields.webFeedURL] = article.webFeed?.url - articleRecord[CloudKitArticle.Fields.uniqueID] = nil - articleRecord[CloudKitArticle.Fields.title] = nil - articleRecord[CloudKitArticle.Fields.contentHTML] = nil - articleRecord[CloudKitArticle.Fields.contentText] = nil - articleRecord[CloudKitArticle.Fields.url] = nil - articleRecord[CloudKitArticle.Fields.externalURL] = nil - articleRecord[CloudKitArticle.Fields.summary] = nil - articleRecord[CloudKitArticle.Fields.imageURL] = nil - articleRecord[CloudKitArticle.Fields.datePublished] = nil - articleRecord[CloudKitArticle.Fields.dateModified] = nil - articleRecord[CloudKitArticle.Fields.parsedAuthors] = nil - articleRecord[CloudKitArticle.Fields.read] = article.status.read ? "1" : "0" - articleRecord[CloudKitArticle.Fields.starred] = article.status.starred ? "1" : "0" - - records.append(articleRecord) - return records - } } diff --git a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift index ca52ec2f1..0fece208b 100644 --- a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift @@ -62,10 +62,10 @@ private extension CloudKitArticlesZoneDelegate { func process(records: [CKRecord], pendingReadStatusArticleIDs: Set, pendingStarredStatusArticleIDs: Set, completion: @escaping (Result) -> Void) { - let receivedUnreadArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticle.Fields.read] == "0" }).map({ $0.externalID })) - let receivedReadArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticle.Fields.read] == "1" }).map({ $0.externalID })) - let receivedUnstarredArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticle.Fields.starred] == "0" }).map({ $0.externalID })) - let receivedStarredArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticle.Fields.starred] == "1" }).map({ $0.externalID })) + let receivedUnreadArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.read] == "0" }).map({ stripPrefix($0.externalID) })) + let receivedReadArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.read] == "1" }).map({ stripPrefix($0.externalID) })) + let receivedUnstarredArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.starred] == "0" }).map({ stripPrefix($0.externalID) })) + let receivedStarredArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.starred] == "1" }).map({ stripPrefix($0.externalID) })) let updateableUnreadArticleIDs = receivedUnreadArticleIDs.subtracting(pendingReadStatusArticleIDs) let updateableReadArticleIDs = receivedReadArticleIDs.subtracting(pendingReadStatusArticleIDs) @@ -110,9 +110,13 @@ private extension CloudKitArticlesZoneDelegate { completion(.success(())) } } + + func stripPrefix(_ externalID: String) -> String { + return String(externalID[externalID.index(externalID.startIndex, offsetBy: 2).. ParsedItem? { - guard articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.hollow] as? String ?? "0" == "0" else { + guard articleRecord.recordType == CloudKitArticlesZone.CloudKitArticle.recordType else { return nil }