diff --git a/Frameworks/Data/ArticleStatus.swift b/Frameworks/Data/ArticleStatus.swift index da77bdd1b..0637258a0 100644 --- a/Frameworks/Data/ArticleStatus.swift +++ b/Frameworks/Data/ArticleStatus.swift @@ -35,7 +35,12 @@ public final class ArticleStatus: Hashable { self.accountInfo = accountInfo self.hashValue = articleID.hashValue } - + + public convenience init(articleID: String, dateArrived: Date) { + + init(articleID: articleID, read: false, starred: false, userDeleted: false, dateArrived: dateArrived, accountInfo: nil) + } + public func boolStatus(forKey key: String) -> Bool { if let articleStatusKey = ArticleStatusKey(rawValue: key) { diff --git a/Frameworks/Database/StatusesTable.swift b/Frameworks/Database/StatusesTable.swift index 65bf1b64a..2cf9782c8 100644 --- a/Frameworks/Database/StatusesTable.swift +++ b/Frameworks/Database/StatusesTable.swift @@ -45,7 +45,11 @@ final class StatusesTable: DatabaseTable { } func ensureStatusesForParsedArticles(_ parsedArticles: [ParsedItem], _ callback: @escaping RSVoidCompletionBlock) { - + + // 1. Check cache for statuses + // 2. Fetch statuses not found in cache + // 3. Create, save, and cache statuses not found in database + var articleIDs = Set(parsedArticles.map { $0.articleID }) articleIDs = articleIDsMissingStatuses(articleIDs) if articleIDs.isEmpty { @@ -62,18 +66,11 @@ final class StatusesTable: DatabaseTable { self.cache.addObjectsNotCached(Array(statuses)) let newArticleIDs = self.articleIDsMissingStatuses(articleIDs) - self.createStatusForNewArticleIDs(newArticleIDs) - callback() - } - } - } + if !newArticleIDs.isEmpty { + self.createAndSaveStatusesForArticleIDs(newArticleIDs) + } - func assertNoMissingStatuses(_ articles: Set
) { - - for oneArticle in articles { - if oneArticle.status == nil { - assertionFailure("All articles must have a status at this point.") - return + callback() } } } @@ -81,25 +78,14 @@ final class StatusesTable: DatabaseTable { private extension StatusesTable { - // MARK: Marking - - func markArticleStatuses(_ statuses: Set, statusKey: String, flag: Bool) { - - // Ignore the statuses where status.[statusKey] == flag. Update the remainder and save in database. - - var articleIDs = Set() - - statuses.forEach { (oneStatus) in - - if oneStatus.boolStatus(forKey: statusKey) != flag { - oneStatus.setBoolStatus(flag, forKey: statusKey) - articleIDs.insert(oneStatus.articleID) + func assertNoMissingStatuses(_ articles: Set
) { + + for oneArticle in articles { + if oneArticle.status == nil { + assertionFailure("All articles must have a status at this point.") + return } } - - if !articleIDs.isEmpty { - updateArticleStatusesInDatabase(articleIDs, statusKey: statusKey, flag: flag) - } } // MARK: Fetching @@ -126,45 +112,56 @@ private extension StatusesTable { return statuses } - // MARK: Saving + // MARK: Updating - func saveStatuses(_ statuses: Set) { - - let statusArray = statuses.map { $0.databaseDictionary() } - insertRows(statusArray, insertType: .orIgnore) + func markArticleStatuses(_ statuses: Set, statusKey: String, flag: Bool) { + + // Ignore the statuses where status.[statusKey] == flag. Update the remainder and save in database. + + var articleIDsToUpdate = Set() + + statuses.forEach { (oneStatus) in + + if oneStatus.boolStatus(forKey: statusKey) == flag { + return + } + + oneStatus.setBoolStatus(flag, forKey: statusKey) + articleIDsToUpdate.insert(oneStatus.articleID) + } + + if !articleIDsToUpdate.isEmpty { + updateArticleStatusesInDatabase(articleIDsToUpdate, statusKey: statusKey, flag: flag) + } } - + private func updateArticleStatusesInDatabase(_ articleIDs: Set, statusKey: String, flag: Bool) { updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey, whereKey: DatabaseKey.articleID, matches: Array(articleIDs)) } // MARK: Creating - - func createStatusForNewArticleIDs(_ articleIDs: Set) { + + func saveStatuses(_ statuses: Set) { + + let statusArray = statuses.map { $0.databaseDictionary() } + insertRows(statusArray, insertType: .orIgnore) + } + + func createAndSaveStatusesForArticleIDs(_ articleIDs: Set) { let now = Date() - let statuses = articleIDs.map { (oneArticleID) -> ArticleStatus in - return ArticleStatus(articleID: oneArticleID, read: false, starred: false, userDeleted: false, dateArrived: now) - } + let statuses = articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) } cache.addObjectsNotCached(statuses) - queue.update { (database: FMDatabase!) -> Void in - - let falseValue = NSNumber(value: false) - - articleIDs.forEach { (oneArticleID) in - - let _ = database.executeUpdate("insert or ignore into statuses (read, articleID, starred, userDeleted, dateArrived) values (?, ?, ?, ?, ?)", withArgumentsIn:[falseValue, oneArticleID as NSString, falseValue, falseValue, now]) - } - } + saveStatuses(Set(statuses)) } // MARK: Utilities func articleIDsMissingStatuses(_ articleIDs: Set) -> Set { - return Set(articleIDs.filter { cache[$0] == nil }) + return Set(articleIDs.filter { !objectWithIDIsCached[$0] }) } } diff --git a/Frameworks/RSDatabase/RSDatabase/ObjectCache.swift b/Frameworks/RSDatabase/RSDatabase/ObjectCache.swift index c4fbd4bce..fdf4565a4 100644 --- a/Frameworks/RSDatabase/RSDatabase/ObjectCache.swift +++ b/Frameworks/RSDatabase/RSDatabase/ObjectCache.swift @@ -8,6 +8,8 @@ import Foundation +// Not thread-safe. + public final class ObjectCache { private let keyPathForID: KeyPath @@ -60,7 +62,7 @@ public final class ObjectCache { // When an object is not already cached, cache it, // then consider that version the unique version. - return objects.map({ (object) -> T in + return objects.map { (object) -> T in let identifier = identifierForObject(object) if let cachedObject = self[identifier] { @@ -68,7 +70,15 @@ public final class ObjectCache { } add(object) return object - }) + } + } + + public func objectWithIDIsCached(_ identifier: String) -> Bool { + + if let _ = self[identifier] { + return true + } + return false } public subscript(_ identifier: String) -> T? {