Refactored starred article passing to make it more reliable

This commit is contained in:
Maurice Parker 2020-04-03 13:26:08 -05:00
parent f143248e08
commit 10a87ccfb6
3 changed files with 70 additions and 121 deletions

View File

@ -41,19 +41,7 @@ final class CloudKitArticlesZone: CloudKitZone {
static let imageURL = "imageURL"
static let datePublished = "datePublished"
static let dateModified = "dateModified"
static let authors = "authors"
}
}
struct CloudKitAuthor {
static let recordType = "Author"
struct Fields {
static let article = "article"
static let authorID = "authorID"
static let name = "name"
static let url = "url"
static let avatarURL = "avatarURL"
static let emailAddress = "emailAddress"
static let parsedAuthors = "parsedAuthors"
}
}
@ -84,47 +72,6 @@ final class CloudKitArticlesZone: CloudKitZone {
}
}
func fetchArticle(articleID: String, completion: @escaping ((Result<(String, ParsedItem), Error>) -> Void)) {
let statusRecordID = CKRecord.ID(recordName: 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)
query(ckQuery) { result in
switch result {
case .success(let articleRecords):
if articleRecords.count == 1 {
let articleRecord = articleRecords[0]
let articleRef = CKRecord.Reference(record: articleRecord, action: .deleteSelf)
let predicate = NSPredicate(format: "article = %@", articleRef)
let ckQuery = CKQuery(recordType: CloudKitAuthor.recordType, predicate: predicate)
self.query(ckQuery) { result in
switch result {
case .success(let authorRecords):
if let webFeedID = articleRecord[CloudKitArticle.Fields.webFeedID] as? String, let parsedItem = self.makeParsedItem(articleRecord, authorRecords) {
completion(.success((webFeedID, parsedItem)))
} else {
completion(.failure(CloudKitZoneError.unknown))
}
case .failure(let error):
completion(.failure(error))
}
}
} else {
completion(.failure(CloudKitZoneError.unknown))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
private extension CloudKitArticlesZone {
@ -210,58 +157,26 @@ private extension CloudKitArticlesZone {
articleRecord[CloudKitArticle.Fields.datePublished] = article.datePublished
articleRecord[CloudKitArticle.Fields.dateModified] = article.dateModified
records.append(articleRecord)
let encoder = JSONEncoder()
var parsedAuthors = [String]()
if let authors = article.authors {
for author in authors {
let authorRecord = CKRecord(recordType: CloudKitAuthor.recordType, recordID: generateRecordID())
authorRecord[CloudKitAuthor.Fields.article] = CKRecord.Reference(record: articleRecord, action: .deleteSelf)
authorRecord[CloudKitAuthor.Fields.authorID] = author.authorID
authorRecord[CloudKitAuthor.Fields.name] = author.name
authorRecord[CloudKitAuthor.Fields.url] = author.url
authorRecord[CloudKitAuthor.Fields.avatarURL] = author.avatarURL
authorRecord[CloudKitAuthor.Fields.emailAddress] = author.emailAddress
records.append(authorRecord)
let parsedAuthor = ParsedAuthor(name: author.name,
url: author.url,
avatarURL: author.avatarURL,
emailAddress: author.emailAddress)
if let data = try? encoder.encode(parsedAuthor), let encodedParsedAuthor = String(data: data, encoding: .utf8) {
parsedAuthors.append(encodedParsedAuthor)
}
}
}
articleRecord[CloudKitArticle.Fields.parsedAuthors] = parsedAuthors
records.append(articleRecord)
return records
}
func makeParsedItem(_ articleRecord: CKRecord, _ authorRecords: [CKRecord]) -> ParsedItem? {
var parsedAuthors = Set<ParsedAuthor>()
for authorRecord in authorRecords {
let parsedAuthor = ParsedAuthor(name: authorRecord[CloudKitAuthor.Fields.name] as? String,
url: authorRecord[CloudKitAuthor.Fields.url] as? String,
avatarURL: authorRecord[CloudKitAuthor.Fields.avatarURL] as? String,
emailAddress: authorRecord[CloudKitAuthor.Fields.emailAddress] as? String)
parsedAuthors.insert(parsedAuthor)
}
guard let uniqueID = articleRecord[CloudKitArticle.Fields.uniqueID] as? String,
let feedURL = articleRecord[CloudKitArticle.Fields.webFeedID] as? String else {
return nil
}
let parsedItem = ParsedItem(syncServiceID: nil,
uniqueID: uniqueID,
feedURL: feedURL,
url: articleRecord[CloudKitArticle.Fields.url] as? String,
externalURL: articleRecord[CloudKitArticle.Fields.externalURL] as? String,
title: articleRecord[CloudKitArticle.Fields.title] as? String,
contentHTML: articleRecord[CloudKitArticle.Fields.contentHTML] as? String,
contentText: articleRecord[CloudKitArticle.Fields.contentText] as? String,
summary: articleRecord[CloudKitArticle.Fields.summary] as? String,
imageURL: articleRecord[CloudKitArticle.Fields.imageURL] as? String,
bannerImageURL: articleRecord[CloudKitArticle.Fields.imageURL] as? String,
datePublished: articleRecord[CloudKitArticle.Fields.datePublished] as? Date,
dateModified: articleRecord[CloudKitArticle.Fields.dateModified] as? Date,
authors: parsedAuthors,
tags: nil,
attachments: nil)
return parsedItem
}
}

View File

@ -8,6 +8,7 @@
import Foundation
import os.log
import RSParser
import CloudKit
import SyncDatabase
@ -26,7 +27,7 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
}
func cloudKitDidChange(record: CKRecord) {
// Process everything in the batch method
}
func cloudKitDidDelete(recordKey: CloudKitRecordKey) {
@ -65,12 +66,14 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
private extension CloudKitArticlesZoneDelegate {
func process(records: [CKRecord], pendingReadStatusArticleIDs: Set<String>, pendingStarredStatusArticleIDs: Set<String>, completion: @escaping (Result<Void, Error>) -> Void) {
let receivedUnreadArticleIDs = Set(records.filter( { $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.read] == "0" }).map({ $0.externalID }))
let receivedReadArticleIDs = Set(records.filter( { $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.read] == "1" }).map({ $0.externalID }))
let receivedUnstarredArticleIDs = Set(records.filter( { $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.starred] == "0" }).map({ $0.externalID }))
let receivedStarredArticleIDs = Set(records.filter( { $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.starred] == "1" }).map({ $0.externalID }))
let receivedUnreadArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.read] == "0" }).map({ $0.externalID }))
let receivedReadArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.read] == "1" }).map({ $0.externalID }))
let receivedUnstarredArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.starred] == "0" }).map({ $0.externalID }))
let receivedStarredArticleIDs = Set(records.filter({ $0[CloudKitArticlesZone.CloudKitArticleStatus.Fields.starred] == "1" }).map({ $0.externalID }))
let receivedStarredArticles = records.filter({ $0.recordType == CloudKitArticlesZone.CloudKitArticle.recordType })
let updateableUnreadArticleIDs = receivedUnreadArticleIDs.subtracting(pendingReadStatusArticleIDs)
let updateableReadArticleIDs = receivedReadArticleIDs.subtracting(pendingReadStatusArticleIDs)
let updateableUnstarredArticleIDs = receivedUnstarredArticleIDs.subtracting(pendingStarredStatusArticleIDs)
@ -98,23 +101,15 @@ private extension CloudKitArticlesZoneDelegate {
group.leave()
}
for updateableStarredArticleID in updateableStarredArticleIDs {
group.enter()
articlesZone?.fetchArticle(articleID: updateableStarredArticleID) { result in
switch result {
case .success(let (webFeedID, parsedItem)):
self.account?.update(webFeedID, with: Set([parsedItem])) { databaseError in
group.leave()
if let databaseError = databaseError {
os_log(.error, log: self.log, "Error occurred while storing starred items: %@", databaseError.localizedDescription)
}
}
case .failure(let error):
for receivedStarredArticle in receivedStarredArticles {
if let parsedItem = makeParsedItem(receivedStarredArticle), let statusRef = receivedStarredArticle[CloudKitArticlesZone.CloudKitArticle.Fields.articleStatus] as? CKRecord.Reference {
group.enter()
self.account?.update(statusRef.recordID.externalID, with: Set([parsedItem])) { databaseError in
group.leave()
os_log(.error, log: self.log, "Error occurred while retrieving starred items: %@", error.localizedDescription)
if let databaseError = databaseError {
os_log(.error, log: self.log, "Error occurred while storing starred items: %@", databaseError.localizedDescription)
}
}
}
}
@ -123,5 +118,44 @@ private extension CloudKitArticlesZoneDelegate {
}
}
func makeParsedItem(_ articleRecord: CKRecord) -> ParsedItem? {
var parsedAuthors = Set<ParsedAuthor>()
let decoder = JSONDecoder()
if let encodedParsedAuthors = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.parsedAuthors] as? [String] {
for encodedParsedAuthor in encodedParsedAuthors {
if let data = encodedParsedAuthor.data(using: .utf8), let parsedAuthor = try? decoder.decode(ParsedAuthor.self, from: data) {
parsedAuthors.insert(parsedAuthor)
}
}
}
guard let uniqueID = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.uniqueID] as? String,
let feedURL = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.webFeedID] as? String else {
return nil
}
let parsedItem = ParsedItem(syncServiceID: nil,
uniqueID: uniqueID,
feedURL: feedURL,
url: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.url] as? String,
externalURL: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.externalURL] as? String,
title: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.title] as? String,
contentHTML: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentHTML] as? String,
contentText: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentText] as? String,
summary: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.summary] as? String,
imageURL: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.imageURL] as? String,
bannerImageURL: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.imageURL] as? String,
datePublished: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.datePublished] as? Date,
dateModified: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.dateModified] as? Date,
authors: parsedAuthors,
tags: nil,
attachments: nil)
return parsedItem
}
}

@ -1 +1 @@
Subproject commit 47ba87875fbd026dccc2c4d4382a98cb4a1f1fbc
Subproject commit a977d8e84af8645fc8268ac843e8a79b3644b133