NetNewsWire/Frameworks/Database/StatusesTable.swift

171 lines
4.7 KiB
Swift
Raw Normal View History

//
// StatusesTable.swift
// Evergreen
//
// Created by Brent Simmons on 5/8/16.
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSCore
import RSDatabase
import Data
2017-08-06 21:37:47 +02:00
// Article->ArticleStatus is a to-one relationship.
//
// CREATE TABLE if not EXISTS statuses (articleID TEXT NOT NULL PRIMARY KEY, read BOOL NOT NULL DEFAULT 0, starred BOOL NOT NULL DEFAULT 0, userDeleted BOOL NOT NULL DEFAULT 0, dateArrived DATE NOT NULL DEFAULT 0, accountInfo BLOB);
final class StatusesTable: DatabaseTable {
2017-07-30 20:22:21 +02:00
let name = DatabaseTableName.statuses
private let cache = DatabaseObjectCache()
2017-07-29 21:13:38 +02:00
2017-08-27 00:37:15 +02:00
// MARK: Fetching
func statusWithRow(_ row: FMResultSet) -> ArticleStatus? {
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
return nil
}
if let cachedStatus = cache[articleID] as? ArticleStatus {
return cachedStatus
}
guard let dateArrived = row.date(forColumn: DatabaseKey.dateArrived) else {
return nil
}
let articleStatus = ArticleStatus(articleID: articleID, dateArrived: dateArrived, row: row)
cache[articleID] = articleStatus
return articleStatus
}
// MARK: Creating/Updating
2017-08-27 00:37:15 +02:00
func ensureStatusesForArticles(_ articles: Set<Article>, _ database: FMDatabase) {
let articlesNeedingStatuses = articles.missingStatuses()
if articlesNeedingStatuses.isEmpty {
return
}
let articleIDs = articlesNeedingStatuses.articleIDs()
ensureStatusesForArticleIDs(articleIDs, database)
2017-08-04 06:10:01 +02:00
2017-08-27 00:37:15 +02:00
attachCachedStatuses(articlesNeedingStatuses)
assert(articles.eachHasAStatus())
}
func ensureStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
// Check cache.
let articleIDsMissingCachedStatus = articleIDsWithNoCachedStatus(articleIDs)
if articleIDsMissingCachedStatus.isEmpty {
return
}
// Check database.
fetchAndCacheStatusesForArticleIDs(articleIDsMissingCachedStatus, database)
let articleIDsNeedingStatus = articleIDsWithNoCachedStatus(articleIDs)
if articleIDsNeedingStatus.isEmpty {
return
}
// Create new statuses.
createAndSaveStatusesForArticleIDs(articleIDsNeedingStatus, database)
}
func fetchStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) -> [String: ArticleStatus] {
// Does not create statuses. Checks cache first, then database only if needed.
var d = [String: ArticleStatus]()
var articleIDsMissingCachedStatus = Set<String>()
for articleID in articleIDs {
if let cachedStatus = cache[articleID] as? ArticleStatus {
d[articleID] = cachedStatus
}
else {
articleIDsMissingCachedStatus.insert(articleID)
}
}
if articleIDsMissingCachedStatus.isEmpty {
return d
}
fetchAndCacheStatusesForArticleIDs(articleIDsMissingCachedStatus, database)
for articleID in articleIDsMissingCachedStatus {
if let cachedStatus = cache[articleID] as? ArticleStatus {
d[articleID] = cachedStatus
}
}
return d
}
// MARK: Marking
func markArticleIDs(_ articleIDs: Set<String>, _ statusKey: String, _ flag: Bool, _ database: FMDatabase) {
updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey, whereKey: DatabaseKey.articleID, matches: Array(articleIDs), database: database)
}
2017-08-05 20:12:45 +02:00
}
2017-07-30 20:36:27 +02:00
2017-08-05 20:12:45 +02:00
private extension StatusesTable {
2017-08-06 21:37:47 +02:00
2017-08-27 00:37:15 +02:00
func attachCachedStatuses(_ articles: Set<Article>) {
for article in articles {
if let cachedStatus = cache[article.articleID] as? ArticleStatus {
article.status = cachedStatus
}
2017-08-06 21:37:47 +02:00
}
}
func articleIDsWithNoCachedStatus(_ articleIDs: Set<String>) -> Set<String> {
return Set(articleIDs.filter { cache[$0] == nil })
}
// MARK: Creating
2017-08-27 00:37:15 +02:00
func saveStatuses(_ statuses: Set<ArticleStatus>, _ database: FMDatabase) {
let statusArray = statuses.map { $0.databaseDictionary() }
insertRows(statusArray, insertType: .orIgnore, in: database)
}
func cacheStatuses(_ statuses: Set<ArticleStatus>) {
let databaseObjects = statuses.map { $0 as DatabaseObject }
cache.addObjectsNotCached(databaseObjects)
}
2017-08-27 00:37:15 +02:00
func createAndSaveStatusesForArticles(_ articles: Set<Article>, _ database: FMDatabase) {
let articleIDs = Set(articles.map { $0.articleID })
createAndSaveStatusesForArticleIDs(articleIDs, database)
}
func createAndSaveStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
let now = Date()
let statuses = Set(articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) })
2017-08-27 00:37:15 +02:00
cacheStatuses(statuses)
saveStatuses(statuses, database)
2017-08-27 00:37:15 +02:00
}
func fetchAndCacheStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
guard let resultSet = selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else {
return
}
let statuses = resultSet.mapToSet(statusWithRow)
cacheStatuses(statuses)
}
}