Progress toward getting Database.framework to build. Mostly just commented stuff out.

This commit is contained in:
Brent Simmons 2017-08-20 22:43:46 -07:00
parent 9ddaaf5f5d
commit 4010011b5a
16 changed files with 338 additions and 259 deletions

View File

@ -10,8 +10,7 @@ import Foundation
public struct Attachment: Hashable { public struct Attachment: Hashable {
public let databaseID: String // Calculated public let attachmentID: String // Calculated
public let articleID: String // Article.databaseID
public let url: String public let url: String
public let mimeType: String? public let mimeType: String?
public let title: String? public let title: String?
@ -19,16 +18,15 @@ public struct Attachment: Hashable {
public let durationInSeconds: Int? public let durationInSeconds: Int?
public let hashValue: Int public let hashValue: Int
public init(databaseID: String?, articleID: String, url: String, mimeType: String?, title: String?, sizeInBytes: Int?, durationInSeconds: Int?) { public init(attachmentID: String?, url: String, mimeType: String?, title: String?, sizeInBytes: Int?, durationInSeconds: Int?) {
self.articleID = articleID
self.url = url self.url = url
self.mimeType = mimeType self.mimeType = mimeType
self.title = title self.title = title
self.sizeInBytes = sizeInBytes self.sizeInBytes = sizeInBytes
self.durationInSeconds = durationInSeconds self.durationInSeconds = durationInSeconds
var s = articleID + url var s = url
s += mimeType ?? "" s += mimeType ?? ""
s += title ?? "" s += title ?? ""
if let sizeInBytes = sizeInBytes { if let sizeInBytes = sizeInBytes {
@ -39,11 +37,11 @@ public struct Attachment: Hashable {
} }
self.hashValue = s.hashValue self.hashValue = s.hashValue
if let databaseID = databaseID { if let attachmentID = attachmentID {
self.databaseID = databaseID self.attachmentID = attachmentID
} }
else { else {
self.databaseID = databaseIDWithString(s) self.attachmentID = databaseIDWithString(s)
} }
} }

View File

@ -12,10 +12,11 @@ import RSDatabase
// AccountInfo is a plist-compatible dictionary thats stored as a binary plist in the database. // AccountInfo is a plist-compatible dictionary thats stored as a binary plist in the database.
func accountInfoWithRow(_ row: FMResultSet) -> AccountInfo? { //func accountInfoWithRow(_ row: FMResultSet) -> AccountInfo? {
//
guard let rawAccountInfo = row.data(forColumn: DatabaseKey.accountInfo) else { // guard let rawAccountInfo = row.data(forColumn: DatabaseKey.accountInfo) else {
return nil // return nil
} // }
return propertyList(withData: rawAccountInfo) as? AccountInfo // return propertyList(withData: rawAccountInfo) as? AccountInfo
} //}

View File

@ -10,80 +10,80 @@ import Foundation
import RSDatabase import RSDatabase
import Data import Data
final class ArticlesTable: DatabaseTable { //final class ArticlesTable: DatabaseTable {
//
// let name: String
// private let cachedArticles: NSMapTable<NSString, Article> = NSMapTable.weakToWeakObjects()
//
// init(name: String) {
//
// self.name = name
// }
let name: String
let queue: RSDatabaseQueue
private let cachedArticles: NSMapTable<NSString, Article> = NSMapTable.weakToWeakObjects()
init(name: String, queue: RSDatabaseQueue) {
self.name = name // func uniquedArticles(_ fetchedArticles: Set<Article>, statusesTable: StatusesTable) -> Set<Article> {
self.queue = queue //
} // var articles = Set<Article>()
//
// for oneArticle in fetchedArticles {
//
// assert(oneArticle.status != nil)
//
// if let existingArticle = cachedArticle(oneArticle.databaseID) {
// articles.insert(existingArticle)
// }
// else {
// cacheArticle(oneArticle)
// articles.insert(oneArticle)
// }
// }
//
// statusesTable.attachCachedStatuses(articles)
//
// return articles
// }
func uniquedArticles(_ fetchedArticles: Set<Article>, statusesTable: StatusesTable) -> Set<Article> { // typealias FeedCountCallback = (Int) -> Void
//
var articles = Set<Article>() // func numberOfArticlesWithFeedID(_ feedID: String, callback: @escaping FeedCountCallback) {
//
for oneArticle in fetchedArticles { // queue.fetch { (database: FMDatabase!)
//
assert(oneArticle.status != nil) // let sql = "select count(*) from articles where feedID = ?;"
// var numberOfArticles = -1
if let existingArticle = cachedArticle(oneArticle.databaseID) { //
articles.insert(existingArticle) // if let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID]) {
} //
else { // while (resultSet.next()) {
cacheArticle(oneArticle) // numberOfArticles = resultSet.long(forColumnIndex: 0)
articles.insert(oneArticle) // break
} // }
} // }
//
statusesTable.attachCachedStatuses(articles) // DispatchQueue.main.async() {
// callback(numberOfArticles)
return articles // }
} // }
//
// }
//}
typealias FeedCountCallback = (Int) -> Void //private extension ArticlesTable {
func numberOfArticlesWithFeedID(_ feedID: String, callback: @escaping FeedCountCallback) { // func cachedArticle(_ articleID: String) -> Article? {
//
queue.fetch { (database: FMDatabase!) // return cachedArticles.object(forKey: articleID as NSString)
// }
let sql = "select count(*) from articles where feedID = ?;" //
var numberOfArticles = -1 // func cacheArticle(_ article: Article) {
//
if let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID]) { // cachedArticles.setObject(article, forKey: article.databaseID as NSString)
// }
while (resultSet.next()) { //
numberOfArticles = resultSet.long(forColumnIndex: 0) // func cacheArticles(_ articles: Set<Article>) {
break //
} // articles.forEach { cacheArticle($0) }
} // }
//}
DispatchQueue.main.async() {
callback(numberOfArticles)
}
}
}
}
private extension ArticlesTable {
func cachedArticle(_ articleID: String) -> Article? {
return cachedArticles.object(forKey: articleID as NSString)
}
func cacheArticle(_ article: Article) {
cachedArticles.setObject(article, forKey: article.databaseID as NSString)
}
func cacheArticles(_ articles: Set<Article>) {
articles.forEach { cacheArticle($0) }
}
}

View File

@ -25,7 +25,14 @@ struct AttachmentsTable: DatabaseTable {
func objectWithRow(_ row: FMResultSet) -> DatabaseObject? { func objectWithRow(_ row: FMResultSet) -> DatabaseObject? {
return attachmentWithRow(row) as DatabaseObject if let attachment = attachmentWithRow(row) {
return attachment as DatabaseObject
}
return nil
}
func save(_ objects: [DatabaseObject], in database: FMDatabase) {
// TODO
} }
} }
@ -33,11 +40,17 @@ private extension AttachmentsTable {
func attachmentWithRow(_ row: FMResultSet) -> Attachment? { func attachmentWithRow(_ row: FMResultSet) -> Attachment? {
let attachmentID = row.string(forColumn: DatabaseKey.attachmentID) guard let attachmentID = row.string(forColumn: DatabaseKey.attachmentID) else {
if let cachedAttachment = cache(attachmentID) { return nil
}
if let cachedAttachment = cache[attachmentID] as? Attachment {
return cachedAttachment return cachedAttachment
} }
return Attachment(attachmentID: attachmentID, row: row) guard let attachment = Attachment(attachmentID: attachmentID, row: row) else {
return nil
}
cache[attachmentID] = attachment as DatabaseObject
return attachment
} }
} }

View File

@ -31,9 +31,17 @@ struct AuthorsTable: DatabaseTable {
// MARK: DatabaseTable Methods // MARK: DatabaseTable Methods
func objectWithRow(_ row: FMResultSet) -> DatabaseObject? { func objectWithRow(_ row: FMResultSet) -> DatabaseObject? {
return authorWithRow(row) as DatabaseObject if let author = authorWithRow(row) {
return author as DatabaseObject
}
return nil
} }
func save(_ objects: [DatabaseObject], in database: FMDatabase) {
// TODO
}
} }
private extension AuthorsTable { private extension AuthorsTable {
@ -44,7 +52,7 @@ private extension AuthorsTable {
return nil return nil
} }
if let cachedAuthor = cache[authorID] { if let cachedAuthor = cache[authorID] as? Author {
return cachedAuthor return cachedAuthor
} }

View File

@ -26,7 +26,7 @@ final class Database {
private let queue: RSDatabaseQueue private let queue: RSDatabaseQueue
private let databaseFile: String private let databaseFile: String
private let articlesTable: ArticlesTable // private let articlesTable: ArticlesTable
private let statusesTable: StatusesTable private let statusesTable: StatusesTable
private let authorsLookupTable: DatabaseLookupTable private let authorsLookupTable: DatabaseLookupTable
private let attachmentsLookupTable: DatabaseLookupTable private let attachmentsLookupTable: DatabaseLookupTable
@ -41,7 +41,7 @@ final class Database {
self.databaseFile = databaseFile self.databaseFile = databaseFile
self.queue = RSDatabaseQueue(filepath: databaseFile, excludeFromBackup: false) self.queue = RSDatabaseQueue(filepath: databaseFile, excludeFromBackup: false)
self.articlesTable = ArticlesTable(name: DatabaseTableName.articles, queue: queue) // self.articlesTable = ArticlesTable(name: DatabaseTableName.articles, queue: queue)
self.statusesTable = StatusesTable(name: DatabaseTableName.statuses) self.statusesTable = StatusesTable(name: DatabaseTableName.statuses)
let authorsTable = AuthorsTable(name: DatabaseTableName.authors) let authorsTable = AuthorsTable(name: DatabaseTableName.authors)

View File

@ -467,21 +467,21 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
84E156EA1F0AB80500F8CC05 /* Database.swift in Sources */,
84E156EC1F0AB80E00F8CC05 /* ArticlesTable.swift in Sources */,
84E156EE1F0AB81400F8CC05 /* StatusesTable.swift in Sources */,
8455807C1F0C0DBD003CCFA1 /* Attachment+Database.swift in Sources */,
84F20F8F1F180D8700D8E682 /* AuthorsTable.swift in Sources */,
845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */, 845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */,
840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */,
84D0DEA11F4A429800073503 /* String+Database.swift in Sources */,
846FB36B1F4A937B00EAB81D /* Feed+Database.swift in Sources */, 846FB36B1F4A937B00EAB81D /* Feed+Database.swift in Sources */,
84D0DEA11F4A429800073503 /* String+Database.swift in Sources */,
843CB9961F34174100EE6581 /* Author+Database.swift in Sources */, 843CB9961F34174100EE6581 /* Author+Database.swift in Sources */,
845580781F0AF678003CCFA1 /* Folder+Database.swift in Sources */, 845580781F0AF678003CCFA1 /* Folder+Database.swift in Sources */,
845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */, 845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */,
845580721F0AEE49003CCFA1 /* AccountInfo.swift in Sources */,
8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */, 8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */,
8455807C1F0C0DBD003CCFA1 /* Attachment+Database.swift in Sources */,
84BB4BA91F11A32800858766 /* TagsTable.swift in Sources */, 84BB4BA91F11A32800858766 /* TagsTable.swift in Sources */,
840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */,
84F20F8F1F180D8700D8E682 /* AuthorsTable.swift in Sources */,
84E156EC1F0AB80E00F8CC05 /* ArticlesTable.swift in Sources */,
84E156EE1F0AB81400F8CC05 /* StatusesTable.swift in Sources */,
845580721F0AEE49003CCFA1 /* AccountInfo.swift in Sources */,
84E156EA1F0AB80500F8CC05 /* Database.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -32,17 +32,17 @@ extension Article {
let bannerImageURL = row.string(forColumn: DatabaseKey.bannerImageURL) let bannerImageURL = row.string(forColumn: DatabaseKey.bannerImageURL)
let datePublished = row.date(forColumn: DatabaseKey.datePublished) let datePublished = row.date(forColumn: DatabaseKey.datePublished)
let dateModified = row.date(forColumn: DatabaseKey.dateModified) let dateModified = row.date(forColumn: DatabaseKey.dateModified)
let authors = PropertyListTransformer.authorsWithRow(row) // let authors = PropertyListTransformer.authorsWithRow(row)
let tags = PropertyListTransformer.tagsWithRow(row) // let tags = PropertyListTransformer.tagsWithRow(row)
let attachments = PropertyListTransformer.attachmentsWithRow(row) // let attachments = PropertyListTransformer.attachmentsWithRow(row)
let accountInfo = accountInfoWithRow(row) // let accountInfo = accountInfoWithRow(row)
self.init(account: account, feedID: feed, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: accountInfo) self.init(account: account, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: accountInfo)
} }
func databaseDictionary() -> NSDictionary { func databaseDictionary() -> NSDictionary {
var d = NSMutableDictionary() let d = NSMutableDictionary()
return d.copy() as! NSDictionary return d.copy() as! NSDictionary
@ -51,7 +51,7 @@ extension Article {
extension Article: DatabaseObject { extension Article: DatabaseObject {
var databaseID: String { public var databaseID: String {
get { get {
return articleID return articleID
} }

View File

@ -23,9 +23,9 @@ extension ArticleStatus {
dateArrived = NSDate.distantPast dateArrived = NSDate.distantPast
} }
let accountInfoPlist = accountInfoWithRow(row) // let accountInfoPlist = accountInfoWithRow(row)
self.init(articleID: articleID, read: read, starred: starred, userDeleted: userDeleted, dateArrived: dateArrived!, accountInfo: accountInfoPlist) self.init(articleID: articleID, read: read, starred: starred, userDeleted: userDeleted, dateArrived: dateArrived!, accountInfo: nil)
} }
func databaseDictionary() -> NSDictionary { func databaseDictionary() -> NSDictionary {
@ -38,11 +38,19 @@ extension ArticleStatus {
d[DatabaseKey.userDeleted] = userDeleted d[DatabaseKey.userDeleted] = userDeleted
d[DatabaseKey.dateArrived] = dateArrived d[DatabaseKey.dateArrived] = dateArrived
if let accountInfo = accountInfo, let data = PropertyListTransformer.data(withPropertyList: accountInfo) { // if let accountInfo = accountInfo, let data = PropertyListTransformer.data(withPropertyList: accountInfo) {
d[DatabaseKey.accountInfo] = data // d[DatabaseKey.accountInfo] = data
} // }
return d.copy() as! NSDictionary return d.copy() as! NSDictionary
} }
} }
extension ArticleStatus: DatabaseObject {
public var databaseID: String {
get {
return articleID
}
}
}

View File

@ -12,24 +12,36 @@ import RSDatabase
extension Attachment { extension Attachment {
init?(databaseID: String, row: FMResultSet) { init?(attachmentID: String, row: FMResultSet) {
guard let url = row.string(forColumn: DatabaseKey.url) else {
return nil
}
let articleID = row.string(forColumn: DatabaseKey.articleID)
let url = row.string(forColumn: DatabaseKey.url)
let mimeType = row.string(forColumn: DatabaseKey.mimeType) let mimeType = row.string(forColumn: DatabaseKey.mimeType)
let title = row.string(forColumn: DatabaseKey.title) let title = row.string(forColumn: DatabaseKey.title)
let sizeInBytes = optionalIntForColumn(row, DatabaseKey.sizeInBytes) let sizeInBytes = optionalIntForColumn(row, DatabaseKey.sizeInBytes)
let durationInSeconds = optionalIntForColumn(row, DatabaseKey.durationInSeconds) let durationInSeconds = optionalIntForColumn(row, DatabaseKey.durationInSeconds)
self.init(databaseID: databaseID, articleID: articleID, url: url, mimeType: mimeType, title: title, sizeInBytes: sizeInBytes, durationInSeconds: durationInSeconds) self.init(attachmentID: attachmentID, url: url, mimeType: mimeType, title: title, sizeInBytes: sizeInBytes, durationInSeconds: durationInSeconds)
} }
private func optionalIntForColumn(_ row: FMResultSet, _ columnName: String) -> Int? { }
let intValue = row.long(forColumn: columnName) private func optionalIntForColumn(_ row: FMResultSet, _ columnName: String) -> Int? {
if intValue < 1 {
return nil let intValue = row.long(forColumn: columnName)
if intValue < 1 {
return nil
}
return intValue
}
extension Attachment: DatabaseObject {
public var databaseID: String {
get {
return attachmentID
} }
return intValue
} }
} }

View File

@ -23,9 +23,9 @@ extension Author {
} }
} }
public extension Author: DatabaseObject { extension Author: DatabaseObject {
var databaseID: String { public var databaseID: String {
get { get {
return authorID return authorID
} }

View File

@ -13,6 +13,6 @@ extension Set where Element == Feed {
func feedIDs() -> Set<String> { func feedIDs() -> Set<String> {
return Set(map { $0.feedID }) return Set<String>(map { $0.feedID })
} }
} }

View File

@ -14,7 +14,7 @@ import RSDatabase
extension String: DatabaseObject { extension String: DatabaseObject {
var databaseID: String { public var databaseID: String {
get { get {
return self return self
} }

View File

@ -18,6 +18,7 @@ import Data
final class StatusesTable: DatabaseTable { final class StatusesTable: DatabaseTable {
let name: String let name: String
let databaseIDKey = DatabaseKey.articleID
private let cache = DatabaseObjectCache() private let cache = DatabaseObjectCache()
init(name: String) { init(name: String) {
@ -25,40 +26,58 @@ final class StatusesTable: DatabaseTable {
self.name = name self.name = name
} }
func markArticles(_ articles: Set<Article>, statusKey: String, flag: Bool) { // Mark: DatabaseTable Methods
// Main thread. func objectWithRow(_ row: FMResultSet) -> DatabaseObject? {
assertNoMissingStatuses(articles) if let status = statusWithRow(row) {
let statuses = Set(articles.flatMap { $0.status }) return status as DatabaseObject
markArticleStatuses(statuses, statusKey: statusKey, flag: flag) }
return nil
} }
func attachStatuses(_ articles: Set<Article>, _ database: FMDatabase) { func save(_ objects: [DatabaseObject], in database: FMDatabase) {
// Look in cache first. // TODO
attachCachedStatuses(articles)
let articlesNeedingStatuses = articlesMissingStatuses(articles)
if articlesNeedingStatuses.isEmpty {
return
}
// Fetch from database.
fetchAndCacheStatusesForArticles(articlesNeedingStatuses, database)
attachCachedStatuses(articlesNeedingStatuses)
// Create new statuses, and cache and save them in the database.
// It shouldnt happen that an Article in the database has no corresponding ArticleStatus,
// but the case should be handled anyway.
let articlesNeedingStatusesCreated = articlesMissingStatuses(articlesNeedingStatuses)
if articlesNeedingStatusesCreated.isEmpty {
return
}
createAndSaveStatusesForArticles(articlesNeedingStatusesCreated, database)
assertNoMissingStatuses(articles)
} }
// func markArticles(_ articles: Set<Article>, statusKey: String, flag: Bool) {
//
// // Main thread.
//
// assertNoMissingStatuses(articles)
// let statuses = Set(articles.flatMap { $0.status })
// markArticleStatuses(statuses, statusKey: statusKey, flag: flag)
// }
// func attachStatuses(_ articles: Set<Article>, _ database: FMDatabase) {
//
// // Look in cache first.
// attachCachedStatuses(articles)
// let articlesNeedingStatuses = articlesMissingStatuses(articles)
// if articlesNeedingStatuses.isEmpty {
// return
// }
//
// // Fetch from database.
// fetchAndCacheStatusesForArticles(articlesNeedingStatuses, database)
// attachCachedStatuses(articlesNeedingStatuses)
//
// // Create new statuses, and cache and save them in the database.
// // It shouldnt happen that an Article in the database has no corresponding ArticleStatus,
// // but the case should be handled anyway.
//
// let articlesNeedingStatusesCreated = articlesMissingStatuses(articlesNeedingStatuses)
// if articlesNeedingStatusesCreated.isEmpty {
// return
// }
// createAndSaveStatusesForArticles(articlesNeedingStatusesCreated, database)
//
// assertNoMissingStatuses(articles)
// }
// func ensureStatusesForParsedArticles(_ parsedArticles: [ParsedItem], _ callback: @escaping RSVoidCompletionBlock) { // func ensureStatusesForParsedArticles(_ parsedArticles: [ParsedItem], _ callback: @escaping RSVoidCompletionBlock) {
@ -95,128 +114,144 @@ final class StatusesTable: DatabaseTable {
private extension StatusesTable { private extension StatusesTable {
func attachCachedStatuses(_ articles: Set<Article>) {
articles.forEach { (oneArticle) in
if let cachedStatus = cache[oneArticle.databaseID] {
oneArticle.status = cachedStatus
}
}
}
func assertNoMissingStatuses(_ articles: Set<Article>) {
for oneArticle in articles {
if oneArticle.status == nil {
assertionFailure("All articles must have a status at this point.")
return
}
}
}
// MARK: Fetching // MARK: Fetching
func fetchAndCacheStatusesForArticles(_ articles: Set<Article>, _ database: FMDatabase) { func statusWithRow(_ row: FMResultSet) -> ArticleStatus? {
fetchAndCacheStatusesForArticleIDs(articles.articleIDs(), database)
}
func fetchAndCacheStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
if let statuses = fetchStatusesForArticleIDs(articleIDs, database) {
cache.addObjectsNotCached(Array(statuses))
}
}
func fetchStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) -> Set<ArticleStatus>? {
guard let resultSet = selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else {
return nil
}
return articleStatusesWithResultSet(resultSet)
}
func articleStatusesWithResultSet(_ resultSet: FMResultSet) -> Set<ArticleStatus> {
return resultSet.mapToSet(articleStatusWithRow)
}
func articleStatusWithRow(_ row: FMResultSet) -> ArticleStatus? {
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else { guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
return nil return nil
} }
if let cachedStatus = cache[articleID] { if let cachedStatus = cache[articleID] as? ArticleStatus {
return cachedStatus return cachedStatus
} }
let status = ArticleStatus(articleID: articleID, row: row) let status = ArticleStatus(articleID: articleID, row: row)
cache[articleID] = status cache[articleID] = status
return status return status
} }
// func attachCachedStatuses(_ articles: Set<Article>) {
//
// articles.forEach { (oneArticle) in
//
// if let cachedStatus = cache[oneArticle.databaseID] {
// oneArticle.status = cachedStatus
// }
// }
// }
// func assertNoMissingStatuses(_ articles: Set<Article>) {
//
// for oneArticle in articles {
// if oneArticle.status == nil {
// assertionFailure("All articles must have a status at this point.")
// return
// }
// }
// }
// MARK: Fetching
// func fetchAndCacheStatusesForArticles(_ articles: Set<Article>, _ database: FMDatabase) {
//
// fetchAndCacheStatusesForArticleIDs(articles.articleIDs(), database)
// }
//
// func fetchAndCacheStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
//
// if let statuses = fetchStatusesForArticleIDs(articleIDs, database) {
// cache.addObjectsNotCached(Array(statuses))
// }
// }
//
// func fetchStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) -> Set<ArticleStatus>? {
//
// guard let resultSet = selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else {
// return nil
// }
// return articleStatusesWithResultSet(resultSet)
// }
//
// func articleStatusesWithResultSet(_ resultSet: FMResultSet) -> Set<ArticleStatus> {
//
// return resultSet.mapToSet(articleStatusWithRow)
// }
//
// func articleStatusWithRow(_ row: FMResultSet) -> ArticleStatus? {
//
// guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
// return nil
// }
// if let cachedStatus = cache[articleID] {
// return cachedStatus
// }
// let status = ArticleStatus(articleID: articleID, row: row)
// cache[articleID] = status
// return status
// }
// MARK: Updating // MARK: Updating
func markArticleStatuses(_ statuses: Set<ArticleStatus>, statusKey: String, flag: Bool) { // func markArticleStatuses(_ statuses: Set<ArticleStatus>, statusKey: String, flag: Bool) {
//
// // Ignore the statuses where status.[statusKey] == flag. Update the remainder and save in database.
//
// var articleIDsToUpdate = Set<String>()
//
// 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)
// }
// }
// Ignore the statuses where status.[statusKey] == flag. Update the remainder and save in database. // private func updateArticleStatusesInDatabase(_ articleIDs: Set<String>, statusKey: String, flag: Bool) {
//
var articleIDsToUpdate = Set<String>() // updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey, whereKey: DatabaseKey.articleID, matches: Array(articleIDs))
// }
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<String>, statusKey: String, flag: Bool) {
updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey, whereKey: DatabaseKey.articleID, matches: Array(articleIDs))
}
// MARK: Creating // MARK: Creating
func saveStatuses(_ statuses: Set<ArticleStatus>, _ database: FMDatabase) { // func saveStatuses(_ statuses: Set<ArticleStatus>, _ database: FMDatabase) {
//
let statusArray = statuses.map { $0.databaseDictionary() } // let statusArray = statuses.map { $0.databaseDictionary() }
insertRows(statusArray, insertType: .orIgnore, in: database) // insertRows(statusArray, insertType: .orIgnore, in: database)
} // }
//
func createAndSaveStatusesForArticles(_ articles: Set<Article>, _ database: FMDatabase) { // func createAndSaveStatusesForArticles(_ articles: Set<Article>, _ database: FMDatabase) {
//
let articleIDs = Set(articles.map { $0.databaseID }) // let articleIDs = Set(articles.map { $0.databaseID })
createAndSaveStatusesForArticleIDs(articleIDs, database) // createAndSaveStatusesForArticleIDs(articleIDs, database)
} // }
//
func createAndSaveStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) { // func createAndSaveStatusesForArticleIDs(_ articleIDs: Set<String>, _ database: FMDatabase) {
//
let now = Date() // let now = Date()
let statuses = articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) } // let statuses = articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) }
cache.addObjectsNotCached(statuses) // cache.addObjectsNotCached(statuses)
//
saveStatuses(Set(statuses), database) // saveStatuses(Set(statuses), database)
} // }
// MARK: Utilities // MARK: Utilities
func articleIDsMissingCachedStatuses(_ articleIDs: Set<String>) -> Set<String> { // func articleIDsMissingCachedStatuses(_ articleIDs: Set<String>) -> Set<String> {
//
return Set(articleIDs.filter { !cache.objectWithIDIsCached($0) }) // return Set(articleIDs.filter { !cache.objectWithIDIsCached($0) })
} // }
//
func articlesMissingStatuses(_ articles: Set<Article>) -> Set<Article> { // func articlesMissingStatuses(_ articles: Set<Article>) -> Set<Article> {
//
return articles.withNilProperty(\Article.status) // return articles.withNilProperty(\Article.status)
} // }
} }
//extension ParsedItem { //extension ParsedItem {

View File

@ -14,6 +14,10 @@ public final class DatabaseObjectCache {
private var dictionary = [String: DatabaseObject]() private var dictionary = [String: DatabaseObject]()
public init() {
// Compiler seems to want a public init method.
}
public func addObjects(_ objects: [DatabaseObject]) { public func addObjects(_ objects: [DatabaseObject]) {
objects.forEach { add($0) } objects.forEach { add($0) }

Binary file not shown.