From 4010011b5aff8ff62f6eec5bb7037b98678ee5da Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 20 Aug 2017 22:43:46 -0700 Subject: [PATCH] Progress toward getting Database.framework to build. Mostly just commented stuff out. --- Frameworks/Data/Attachment.swift | 14 +- Frameworks/Database/AccountInfo.swift | 15 +- Frameworks/Database/ArticlesTable.swift | 140 ++++---- Frameworks/Database/AttachmentsTable.swift | 21 +- Frameworks/Database/AuthorsTable.swift | 14 +- Frameworks/Database/Database.swift | 4 +- .../Database.xcodeproj/project.pbxproj | 16 +- .../Extensions/Article+Database.swift | 14 +- .../Extensions/ArticleStatus+Database.swift | 18 +- .../Extensions/Attachment+Database.swift | 30 +- .../Database/Extensions/Author+Database.swift | 4 +- .../Database/Extensions/Feed+Database.swift | 2 +- .../Database/Extensions/String+Database.swift | 2 +- Frameworks/Database/StatusesTable.swift | 299 ++++++++++-------- .../RSDatabase/DatabaseObjectCache.swift | 4 + ToDo.ooutline | Bin 2643 -> 2640 bytes 16 files changed, 338 insertions(+), 259 deletions(-) diff --git a/Frameworks/Data/Attachment.swift b/Frameworks/Data/Attachment.swift index 40ed4cb75..61008e313 100644 --- a/Frameworks/Data/Attachment.swift +++ b/Frameworks/Data/Attachment.swift @@ -10,8 +10,7 @@ import Foundation public struct Attachment: Hashable { - public let databaseID: String // Calculated - public let articleID: String // Article.databaseID + public let attachmentID: String // Calculated public let url: String public let mimeType: String? public let title: String? @@ -19,16 +18,15 @@ public struct Attachment: Hashable { public let durationInSeconds: 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.mimeType = mimeType self.title = title self.sizeInBytes = sizeInBytes self.durationInSeconds = durationInSeconds - var s = articleID + url + var s = url s += mimeType ?? "" s += title ?? "" if let sizeInBytes = sizeInBytes { @@ -39,11 +37,11 @@ public struct Attachment: Hashable { } self.hashValue = s.hashValue - if let databaseID = databaseID { - self.databaseID = databaseID + if let attachmentID = attachmentID { + self.attachmentID = attachmentID } else { - self.databaseID = databaseIDWithString(s) + self.attachmentID = databaseIDWithString(s) } } diff --git a/Frameworks/Database/AccountInfo.swift b/Frameworks/Database/AccountInfo.swift index 985ecfd81..9dac39660 100644 --- a/Frameworks/Database/AccountInfo.swift +++ b/Frameworks/Database/AccountInfo.swift @@ -12,10 +12,11 @@ import RSDatabase // AccountInfo is a plist-compatible dictionary that’s stored as a binary plist in the database. -func accountInfoWithRow(_ row: FMResultSet) -> AccountInfo? { - - guard let rawAccountInfo = row.data(forColumn: DatabaseKey.accountInfo) else { - return nil - } - return propertyList(withData: rawAccountInfo) as? AccountInfo -} +//func accountInfoWithRow(_ row: FMResultSet) -> AccountInfo? { +// +// guard let rawAccountInfo = row.data(forColumn: DatabaseKey.accountInfo) else { +// return nil +// } +// return propertyList(withData: rawAccountInfo) as? AccountInfo +//} + diff --git a/Frameworks/Database/ArticlesTable.swift b/Frameworks/Database/ArticlesTable.swift index 78eaebbbc..cc5aa9f35 100644 --- a/Frameworks/Database/ArticlesTable.swift +++ b/Frameworks/Database/ArticlesTable.swift @@ -10,80 +10,80 @@ import Foundation import RSDatabase import Data -final class ArticlesTable: DatabaseTable { +//final class ArticlesTable: DatabaseTable { +// +// let name: String +// private let cachedArticles: NSMapTable = NSMapTable.weakToWeakObjects() +// +// init(name: String) { +// +// self.name = name +// } - let name: String - let queue: RSDatabaseQueue - private let cachedArticles: NSMapTable = NSMapTable.weakToWeakObjects() - init(name: String, queue: RSDatabaseQueue) { - self.name = name - self.queue = queue - } +// func uniquedArticles(_ fetchedArticles: Set
, statusesTable: StatusesTable) -> Set
{ +// +// var articles = Set
() +// +// 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
, statusesTable: StatusesTable) -> Set
{ - - var articles = Set
() - - 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 - } +// typealias FeedCountCallback = (Int) -> Void +// +// func numberOfArticlesWithFeedID(_ feedID: String, callback: @escaping FeedCountCallback) { +// +// queue.fetch { (database: FMDatabase!) +// +// let sql = "select count(*) from articles where feedID = ?;" +// var numberOfArticles = -1 +// +// if let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID]) { +// +// while (resultSet.next()) { +// numberOfArticles = resultSet.long(forColumnIndex: 0) +// break +// } +// } +// +// DispatchQueue.main.async() { +// callback(numberOfArticles) +// } +// } +// +// } +//} - typealias FeedCountCallback = (Int) -> Void +//private extension ArticlesTable { - func numberOfArticlesWithFeedID(_ feedID: String, callback: @escaping FeedCountCallback) { - - queue.fetch { (database: FMDatabase!) - - let sql = "select count(*) from articles where feedID = ?;" - var numberOfArticles = -1 - - if let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID]) { - - while (resultSet.next()) { - numberOfArticles = resultSet.long(forColumnIndex: 0) - break - } - } - - 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
) { - - articles.forEach { cacheArticle($0) } - } -} +// 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
) { +// +// articles.forEach { cacheArticle($0) } +// } +//} diff --git a/Frameworks/Database/AttachmentsTable.swift b/Frameworks/Database/AttachmentsTable.swift index 08149d152..fb1d0dac7 100644 --- a/Frameworks/Database/AttachmentsTable.swift +++ b/Frameworks/Database/AttachmentsTable.swift @@ -25,7 +25,14 @@ struct AttachmentsTable: DatabaseTable { 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? { - let attachmentID = row.string(forColumn: DatabaseKey.attachmentID) - if let cachedAttachment = cache(attachmentID) { + guard let attachmentID = row.string(forColumn: DatabaseKey.attachmentID) else { + return nil + } + if let cachedAttachment = cache[attachmentID] as? Attachment { 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 } } diff --git a/Frameworks/Database/AuthorsTable.swift b/Frameworks/Database/AuthorsTable.swift index ffac66c0d..e9dc6cbc6 100644 --- a/Frameworks/Database/AuthorsTable.swift +++ b/Frameworks/Database/AuthorsTable.swift @@ -31,9 +31,17 @@ struct AuthorsTable: DatabaseTable { // MARK: DatabaseTable Methods 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 { @@ -44,7 +52,7 @@ private extension AuthorsTable { return nil } - if let cachedAuthor = cache[authorID] { + if let cachedAuthor = cache[authorID] as? Author { return cachedAuthor } diff --git a/Frameworks/Database/Database.swift b/Frameworks/Database/Database.swift index a2fbf20c1..4385a3bbd 100644 --- a/Frameworks/Database/Database.swift +++ b/Frameworks/Database/Database.swift @@ -26,7 +26,7 @@ final class Database { private let queue: RSDatabaseQueue private let databaseFile: String - private let articlesTable: ArticlesTable +// private let articlesTable: ArticlesTable private let statusesTable: StatusesTable private let authorsLookupTable: DatabaseLookupTable private let attachmentsLookupTable: DatabaseLookupTable @@ -41,7 +41,7 @@ final class Database { self.databaseFile = databaseFile 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) let authorsTable = AuthorsTable(name: DatabaseTableName.authors) diff --git a/Frameworks/Database/Database.xcodeproj/project.pbxproj b/Frameworks/Database/Database.xcodeproj/project.pbxproj index c2daa7101..01535452e 100644 --- a/Frameworks/Database/Database.xcodeproj/project.pbxproj +++ b/Frameworks/Database/Database.xcodeproj/project.pbxproj @@ -467,21 +467,21 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; 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 */, - 840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */, - 84D0DEA11F4A429800073503 /* String+Database.swift in Sources */, 846FB36B1F4A937B00EAB81D /* Feed+Database.swift in Sources */, + 84D0DEA11F4A429800073503 /* String+Database.swift in Sources */, 843CB9961F34174100EE6581 /* Author+Database.swift in Sources */, 845580781F0AF678003CCFA1 /* Folder+Database.swift in Sources */, 845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */, - 845580721F0AEE49003CCFA1 /* AccountInfo.swift in Sources */, 8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */, + 8455807C1F0C0DBD003CCFA1 /* Attachment+Database.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; }; diff --git a/Frameworks/Database/Extensions/Article+Database.swift b/Frameworks/Database/Extensions/Article+Database.swift index 00c87571c..c479de172 100644 --- a/Frameworks/Database/Extensions/Article+Database.swift +++ b/Frameworks/Database/Extensions/Article+Database.swift @@ -32,17 +32,17 @@ extension Article { let bannerImageURL = row.string(forColumn: DatabaseKey.bannerImageURL) let datePublished = row.date(forColumn: DatabaseKey.datePublished) let dateModified = row.date(forColumn: DatabaseKey.dateModified) - let authors = PropertyListTransformer.authorsWithRow(row) - let tags = PropertyListTransformer.tagsWithRow(row) - let attachments = PropertyListTransformer.attachmentsWithRow(row) - let accountInfo = accountInfoWithRow(row) +// let authors = PropertyListTransformer.authorsWithRow(row) +// let tags = PropertyListTransformer.tagsWithRow(row) +// let attachments = PropertyListTransformer.attachmentsWithRow(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 { - var d = NSMutableDictionary() + let d = NSMutableDictionary() return d.copy() as! NSDictionary @@ -51,7 +51,7 @@ extension Article { extension Article: DatabaseObject { - var databaseID: String { + public var databaseID: String { get { return articleID } diff --git a/Frameworks/Database/Extensions/ArticleStatus+Database.swift b/Frameworks/Database/Extensions/ArticleStatus+Database.swift index 44235644e..24ad25b77 100644 --- a/Frameworks/Database/Extensions/ArticleStatus+Database.swift +++ b/Frameworks/Database/Extensions/ArticleStatus+Database.swift @@ -23,9 +23,9 @@ extension ArticleStatus { 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 { @@ -38,11 +38,19 @@ extension ArticleStatus { d[DatabaseKey.userDeleted] = userDeleted d[DatabaseKey.dateArrived] = dateArrived - if let accountInfo = accountInfo, let data = PropertyListTransformer.data(withPropertyList: accountInfo) { - d[DatabaseKey.accountInfo] = data - } +// if let accountInfo = accountInfo, let data = PropertyListTransformer.data(withPropertyList: accountInfo) { +// d[DatabaseKey.accountInfo] = data +// } return d.copy() as! NSDictionary } } +extension ArticleStatus: DatabaseObject { + + public var databaseID: String { + get { + return articleID + } + } +} diff --git a/Frameworks/Database/Extensions/Attachment+Database.swift b/Frameworks/Database/Extensions/Attachment+Database.swift index 5676a72cc..3ef5b0d4d 100644 --- a/Frameworks/Database/Extensions/Attachment+Database.swift +++ b/Frameworks/Database/Extensions/Attachment+Database.swift @@ -12,24 +12,36 @@ import RSDatabase 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 title = row.string(forColumn: DatabaseKey.title) let sizeInBytes = optionalIntForColumn(row, DatabaseKey.sizeInBytes) 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) - if intValue < 1 { - return nil +private func optionalIntForColumn(_ row: FMResultSet, _ columnName: String) -> Int? { + + 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 } } diff --git a/Frameworks/Database/Extensions/Author+Database.swift b/Frameworks/Database/Extensions/Author+Database.swift index 10d794800..51bc8eac9 100644 --- a/Frameworks/Database/Extensions/Author+Database.swift +++ b/Frameworks/Database/Extensions/Author+Database.swift @@ -23,9 +23,9 @@ extension Author { } } -public extension Author: DatabaseObject { +extension Author: DatabaseObject { - var databaseID: String { + public var databaseID: String { get { return authorID } diff --git a/Frameworks/Database/Extensions/Feed+Database.swift b/Frameworks/Database/Extensions/Feed+Database.swift index baf961f3b..581304278 100644 --- a/Frameworks/Database/Extensions/Feed+Database.swift +++ b/Frameworks/Database/Extensions/Feed+Database.swift @@ -13,6 +13,6 @@ extension Set where Element == Feed { func feedIDs() -> Set { - return Set(map { $0.feedID }) + return Set(map { $0.feedID }) } } diff --git a/Frameworks/Database/Extensions/String+Database.swift b/Frameworks/Database/Extensions/String+Database.swift index 13804d2ee..dcbf47493 100644 --- a/Frameworks/Database/Extensions/String+Database.swift +++ b/Frameworks/Database/Extensions/String+Database.swift @@ -14,7 +14,7 @@ import RSDatabase extension String: DatabaseObject { - var databaseID: String { + public var databaseID: String { get { return self } diff --git a/Frameworks/Database/StatusesTable.swift b/Frameworks/Database/StatusesTable.swift index 1c0aa970a..7dfbcfc82 100644 --- a/Frameworks/Database/StatusesTable.swift +++ b/Frameworks/Database/StatusesTable.swift @@ -18,6 +18,7 @@ import Data final class StatusesTable: DatabaseTable { let name: String + let databaseIDKey = DatabaseKey.articleID private let cache = DatabaseObjectCache() init(name: String) { @@ -25,40 +26,58 @@ final class StatusesTable: DatabaseTable { self.name = name } - func markArticles(_ articles: Set
, statusKey: String, flag: Bool) { - - // Main thread. - - assertNoMissingStatuses(articles) - let statuses = Set(articles.flatMap { $0.status }) - markArticleStatuses(statuses, statusKey: statusKey, flag: flag) + // Mark: DatabaseTable Methods + + func objectWithRow(_ row: FMResultSet) -> DatabaseObject? { + + if let status = statusWithRow(row) { + return status as DatabaseObject + } + return nil } - func attachStatuses(_ articles: Set
, _ 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 shouldn’t 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 save(_ objects: [DatabaseObject], in database: FMDatabase) { + + // TODO } + + + + +// func markArticles(_ articles: Set
, 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
, _ 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 shouldn’t 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) { @@ -95,128 +114,144 @@ final class StatusesTable: DatabaseTable { private extension StatusesTable { - func attachCachedStatuses(_ articles: Set
) { - - articles.forEach { (oneArticle) in - - if let cachedStatus = cache[oneArticle.databaseID] { - oneArticle.status = cachedStatus - } - } - } - - func assertNoMissingStatuses(_ articles: Set
) { - - 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
, _ database: FMDatabase) { - - fetchAndCacheStatusesForArticleIDs(articles.articleIDs(), database) - } - - func fetchAndCacheStatusesForArticleIDs(_ articleIDs: Set, _ database: FMDatabase) { - - if let statuses = fetchStatusesForArticleIDs(articleIDs, database) { - cache.addObjectsNotCached(Array(statuses)) - } - } - - func fetchStatusesForArticleIDs(_ articleIDs: Set, _ database: FMDatabase) -> Set? { - - guard let resultSet = selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else { - return nil - } - return articleStatusesWithResultSet(resultSet) - } - - func articleStatusesWithResultSet(_ resultSet: FMResultSet) -> Set { - - return resultSet.mapToSet(articleStatusWithRow) - } - - func articleStatusWithRow(_ row: FMResultSet) -> ArticleStatus? { + + func statusWithRow(_ row: FMResultSet) -> ArticleStatus? { guard let articleID = row.string(forColumn: DatabaseKey.articleID) else { return nil } - if let cachedStatus = cache[articleID] { + if let cachedStatus = cache[articleID] as? ArticleStatus { return cachedStatus } + let status = ArticleStatus(articleID: articleID, row: row) cache[articleID] = status return status } + +// func attachCachedStatuses(_ articles: Set
) { +// +// articles.forEach { (oneArticle) in +// +// if let cachedStatus = cache[oneArticle.databaseID] { +// oneArticle.status = cachedStatus +// } +// } +// } + +// func assertNoMissingStatuses(_ articles: Set
) { +// +// 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
, _ database: FMDatabase) { +// +// fetchAndCacheStatusesForArticleIDs(articles.articleIDs(), database) +// } +// +// func fetchAndCacheStatusesForArticleIDs(_ articleIDs: Set, _ database: FMDatabase) { +// +// if let statuses = fetchStatusesForArticleIDs(articleIDs, database) { +// cache.addObjectsNotCached(Array(statuses)) +// } +// } +// +// func fetchStatusesForArticleIDs(_ articleIDs: Set, _ database: FMDatabase) -> Set? { +// +// guard let resultSet = selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else { +// return nil +// } +// return articleStatusesWithResultSet(resultSet) +// } +// +// func articleStatusesWithResultSet(_ resultSet: FMResultSet) -> Set { +// +// 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 - func markArticleStatuses(_ statuses: Set, statusKey: String, flag: Bool) { +// 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) +// } +// } - // 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)) - } +// private func updateArticleStatusesInDatabase(_ articleIDs: Set, statusKey: String, flag: Bool) { +// +// updateRowsWithValue(NSNumber(value: flag), valueKey: statusKey, whereKey: DatabaseKey.articleID, matches: Array(articleIDs)) +// } // MARK: Creating - func saveStatuses(_ statuses: Set, _ database: FMDatabase) { - - let statusArray = statuses.map { $0.databaseDictionary() } - insertRows(statusArray, insertType: .orIgnore, in: database) - } - - func createAndSaveStatusesForArticles(_ articles: Set
, _ database: FMDatabase) { - - let articleIDs = Set(articles.map { $0.databaseID }) - createAndSaveStatusesForArticleIDs(articleIDs, database) - } - - func createAndSaveStatusesForArticleIDs(_ articleIDs: Set, _ database: FMDatabase) { - - let now = Date() - let statuses = articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) } - cache.addObjectsNotCached(statuses) - - saveStatuses(Set(statuses), database) - } +// func saveStatuses(_ statuses: Set, _ database: FMDatabase) { +// +// let statusArray = statuses.map { $0.databaseDictionary() } +// insertRows(statusArray, insertType: .orIgnore, in: database) +// } +// +// func createAndSaveStatusesForArticles(_ articles: Set
, _ database: FMDatabase) { +// +// let articleIDs = Set(articles.map { $0.databaseID }) +// createAndSaveStatusesForArticleIDs(articleIDs, database) +// } +// +// func createAndSaveStatusesForArticleIDs(_ articleIDs: Set, _ database: FMDatabase) { +// +// let now = Date() +// let statuses = articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) } +// cache.addObjectsNotCached(statuses) +// +// saveStatuses(Set(statuses), database) +// } // MARK: Utilities - func articleIDsMissingCachedStatuses(_ articleIDs: Set) -> Set { - - return Set(articleIDs.filter { !cache.objectWithIDIsCached($0) }) - } - - func articlesMissingStatuses(_ articles: Set
) -> Set
{ - - return articles.withNilProperty(\Article.status) - } +// func articleIDsMissingCachedStatuses(_ articleIDs: Set) -> Set { +// +// return Set(articleIDs.filter { !cache.objectWithIDIsCached($0) }) +// } +// +// func articlesMissingStatuses(_ articles: Set
) -> Set
{ +// +// return articles.withNilProperty(\Article.status) +// } } //extension ParsedItem { diff --git a/Frameworks/RSDatabase/RSDatabase/DatabaseObjectCache.swift b/Frameworks/RSDatabase/RSDatabase/DatabaseObjectCache.swift index 06f695cda..596ba1a68 100644 --- a/Frameworks/RSDatabase/RSDatabase/DatabaseObjectCache.swift +++ b/Frameworks/RSDatabase/RSDatabase/DatabaseObjectCache.swift @@ -14,6 +14,10 @@ public final class DatabaseObjectCache { private var dictionary = [String: DatabaseObject]() + public init() { + // Compiler seems to want a public init method. + } + public func addObjects(_ objects: [DatabaseObject]) { objects.forEach { add($0) } diff --git a/ToDo.ooutline b/ToDo.ooutline index 0c83d3e5a8b4b3828b45acee24abcb4b41c89485..7ecc05bb716ec1e7c098aef9074451f1775aa37a 100644 GIT binary patch delta 2013 zcmV<32O{{>6wnlZP)h>@6aWAK2moxg6iYr`&2CT!001#2000aC003ieZggdCbaO6v zZEV$8+jg5c6n*bkuy~qzz+f;qnPt0c(u|u+Tc>TVed&@xfMRip5O&9K^$Xb1IpItgJdbOeHZ}V87AdA1Y&R>dYy=PYM7`AC8&0-*rjv$^x$`~4`jU}F1xLaEoVZ44> z)*;yuj!3z5{*_P97RWxO1@}ZP*4!3Kje`XJf@`6F!6dEaE#N?H!^be-lih`?} z{j)?&qJ;nqrqLWq++K=-!#D8;;57kW(=>Uc{Wkm<5Wr@ReFb3Tg#ip#td@Nx($$ax zAY=%CSEA!z3LmOaFvU=EpbJt=l{vsEGw+on+hY}oA)i?)#%f|s3|%TDyoq5~7?Ml| zTUhGC(q@f}^({;dVS1Y;6uAgG+gSI6bqH8`1bAgTP#dN@Zsk?9ryzmKf*{J|#kWj zp~-4!K3ZCheljEPnn6UR;x%susn5`PzETvW0&A_W++MF#wMvc&$|+M3@usrGr|jsarCAo^ zNqqEK>YbJ!we78D%RI7dc61pT+S4P8GsVbg3@s=Xq*wTQfI+9Hxa8nwvDB}d4C*Zg zyKjWS?%uAn%)TuQL-(~i9=DFKHWBHFAbkRQp z_xs7wA}Cp5+Y@Iet9F62HsWRdDP#kVx&=A?ey^`vIXU^UgHiMdKU=#USoY6<&$@eV zEzjC+x2sbBW!=^~`_`V;+b_IlzU%w_LK>#AZ<+lUApxY(-4FBr@)Uk-lhCd~doN8w zudkVVf`q=cXX%0jo%ihRPC~6A$zRJL<(E8h{LAi$HTRxQ0Qq~^>H$pp0#o?g`My4K z%cqu6@6p+wybF(NH^&Xz3;|<^ z5+2<8L;^&8MdoisT;l0hp@^0)^WCxnS0S3DVQtSeMEIDX^j6p&4&Ca1b!(Kr0IjjD zg{^49h>iv}#8{dGg7`xKr%^CCMi_ExFN?lohc)siBL2WMP`Pns|5jXO3<@d*GXu57 z8`ne{gh`gfrU$-reR!03sm*TuNiOXCX?`d#VIoVyKDQdmx8u8`i;vFFLywi4gaVo- ztG;XF`={llGO^}z_B>gC_BiM97vp4c6CZ?f^{QJ!-|U!M-vjyNnIE;4bICC$?AbKB z&mJ#OkOs2xowptRc2~n*|RLw!Q5my;05a$mJO|*M$ zw42qEEzegyt!MOfQAF8?sTcJl>v^7YXMND3#5+29p)R@<+S>Fil{ zMI&oA@^IG1^VfM4BfF8xayOcpTaCB&U;NJN20sFb<4MN7sCm3YA|RX%?Bhrmz)gr&&5;wQPms@xViXOxE7Pv|f(qD>59*_WQmnk z$jd)#M<>Vgle@$4;=@0}VWz|0t2>>>4xn%hp>u_(e+Ebb9sc#KBsiZ=Q}fr==L2lj zNLUkfMuPFuVsn<4G?*l_A2-QUF?scPxRBcZ--O)~*!^RF9Fb>_g*!PgwV%h1JN7pa zr2gL!a1jy+oGJ|wcKF%z8Hw=IlVltpTpWJ>wgC~{|Ai9%PL^T2sO-W*&D$rp=IhHr zB;`)BKrVe&*Kf5X_&T}$icNO|0hq7uI=isWMEsxC@tNzN&20N<`FXj4<2$C~-BQ4Z zC=KcE-sis+*cqw3ERNp&3s6e~1QY-O00;nVwX=x_O#%*VwG>M}UCnM#2LJ#uCX?g| vEFWyO6id_bLf9k%004mm000XB0000000031|NsC0dXp~-9tOt=00000aU;Vm delta 1993 zcmV;)2R8W76w?%cP)h>@6aWAK2mrgP6icqO_>)ry004m{000aC003ieZggdCbaO6v zZEV$8TXUPb7Jl!qV0b#`0fWKdUQ5TKY_g|OrP`Yi$C>clvX zJCnS`==!a1eQPaAqraYfPgp`6qc9kY{hlHUFfc<413}3#sKv$^D z>WprwXKer*4geZIi-v) z;F_)!21K%fkG!=gq=>RMQDC6x@4Uy}>+CCAo^FtS3M*!bobS0Slo|&y`VBXKLdiOE5JBS4A;p9O0v!yzbP*qA zcKP6GEE>A58RDd`?gwWDk>iq$fOyxlkopZs{ed{qUkwKl=u0G&f{@B9Rq21IW-AJ= zYWBYcVjQkGU@#AtP+;?g2v{tLJAhX>ctugfiSlar5x{`19JvzkkQ4YYC|ND~3Z&(b z93Z5B2usniFNqD62$*|Nu%HbRk4Q`4C3O0r7}*{xK@YN(g?y~S_w=AmxP-e*?DL5@ zmB9`(RX)?Cjg<8rrfPiZV-gc&BWUe1d%$N0fTl;_m2*dOW9puvTzY#BVkoUJB2;cP zYhq}!Wd6MC^Pxgz=xZb(^>@&K)PI(k9RIu}&|>;sW=M|Xrc^>!Ux(f*d4wHj-up%L52lF}h@zDlJ@Ko$-4oA~~7q+Bh3 z`wjm1-4{Q)eXfk zC{O%jNK@~;_^5@qoNn{Nw&~GjqAA-)=4XnD))<;o%1N*A^$dg7K(g`4!)mSGHyKo0 z4EEm%gZ-m@X<2+~QnXk0pA3>ABL5o4{ zy)d|UyhZEF<>mKt0zN-BY0+D>4BiVZ{k_vr|NK(Fm|jTJuT5Hv7A^Waq2+k*7`ho= zfv4m6Y~>fM(D3xqlU2LG8J&39d~>We9@cE7cWfLfgX7$KYP)(o%%!1g$A&&U2niqwfBn#(ZZF}dHVN$-baZGE z217+Z;v@`>BSYmRsI2E`e-dg9arRmU3A^O+<6nA5teLkx0c7uCu`$56>#7kvWUx{f9GN7K|mQI zm^rt;5C>7;k=k1k7k~Pd2qJ~aELuu%6{2wx)MBO~!j}Z4w%qo->&n-Etx5I*v|(<| zZF!T2$Yf+f&l8paBlZx$Xygn|kp~&Iw|U>WMH|@@5qn@7$!u~Zek-O@204|SnUUP$ zjcFqBgE&oM^Ap#)KRt_`#H2U=I1_gEG(Q&CFct+qKbticAE&?0Za!H*PaRrrVgg7K zmworzKcCmP(#%+j>GNcN(PLdlU$u+XLv#{|)vIm+z1uOj9s}9psU5YQGs#gS%*8x> zN*^zfpZL;>DUy~+Eq%wRH7V&|nW8HyYbh;LTP=y!geX@fDrX?uh^vg`h_eTVCfcJm z+HSRE$Me-d8E6BQ7g745t9kuMd*0^USs%ISA?VLyZ9b{<;n$WHwS+W=1n}%hAsE^WRzBU`GIcJW09dHIH=&7zB&4n5I4)h9u8< zJOS3Sqv;~g!Z45krYSP3_;EI&EVp^inIu8_6Iv7uZ{ve#Hpgpu25^+2h@BQJ`fN zvhq*c(Z%`l;@4?#^YQ=OV5-A`*oDH^nU_QsAaDwyb%%(11#k>4_VuRbTAwcy{rBCM z6VIrbyTQNIT&=6Q+XZB;Z;3yP7e5~2Z9E^4V;d6F{TtJNGd{gNOhf$U{Mxetg6AGXR$Nl8=CAU7x6SYxjFsvy@Sd9KkVJV<8{zZ^ZVFavukSz z9n!@-A@<@GvdNno`KZMHx7p)2Pq#ZbRX?DS^jf&!K7CUY7q)w~FwL{|mvsjdJEy|^ zLiJ;q1Z000JF{Q(%u!svCLjI@6aWAK2mrgPvxWyv0uH;X6icqO_>)ry004m{ bli>+00=ue{4+