Make ParsedFeed.items a Set<ParsedItem>. Fix some build errors in Database.framework.
This commit is contained in:
parent
7680760537
commit
7415131e8d
|
@ -8,9 +8,17 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
// This is used by an Account that needs to store extra info.
|
||||
// It’s stored as a binary plist in the database.
|
||||
|
||||
public struct AccountInfo: Equatable {
|
||||
|
||||
var dictionary: [String: AnyObject]?
|
||||
var plist: [String: AnyObject]?
|
||||
|
||||
init(plist: [String: AnyObject]) {
|
||||
|
||||
self.plist = plist
|
||||
}
|
||||
|
||||
public static func ==(lhs: AccountInfo, rhs: AccountInfo) -> Bool {
|
||||
|
||||
|
@ -18,8 +26,6 @@ public struct AccountInfo: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -18,7 +18,7 @@ public final class Feed: DisplayNameProvider, Hashable {
|
|||
public var name: String?
|
||||
public var editedName: String?
|
||||
public var articles = Set<Article>()
|
||||
public var accountInfo: [String: Any]? //If account needs to store more data
|
||||
public var accountInfo: AccountInfo? //If account needs to store more data
|
||||
public let hashValue: Int
|
||||
|
||||
public var nameForDisplay: String {
|
||||
|
|
|
@ -31,8 +31,7 @@ final class ArticlesTable: DatabaseTable {
|
|||
self.name = name
|
||||
self.accountID = accountID
|
||||
self.queue = queue
|
||||
|
||||
let statusesTable = StatusesTable(queue: queue)
|
||||
self.statusesTable = StatusesTable(queue: queue)
|
||||
|
||||
let authorsTable = AuthorsTable(name: DatabaseTableName.authors)
|
||||
self.authorsLookupTable = DatabaseLookupTable(name: DatabaseTableName.authorsLookup, objectIDKey: DatabaseKey.articleID, relatedObjectIDKey: DatabaseKey.authorID, relatedTable: authorsTable, relationshipName: RelationshipName.authors)
|
||||
|
@ -96,17 +95,16 @@ final class ArticlesTable: DatabaseTable {
|
|||
|
||||
let feedID = feed.feedID
|
||||
let parsedItemArticleIDs = Set(parsedFeed.items.map { $0.databaseIdentifierWithFeed(feed) })
|
||||
let parsedItemsDictionary = parsedFeed.itemsDictionary(with: feed)
|
||||
|
||||
statusesTable.ensureStatusesForArticleIDs(parsedItemArticleIDs) { // 1
|
||||
statusesTable.ensureStatusesForArticleIDs(parsedItemArticleIDs) { (statusesDictionary) in // 1
|
||||
|
||||
let filteredParsedItems = self.filterParsedItems(parsedItemsDictionary) // 2
|
||||
let filteredParsedItems = self.filterParsedItems(Set(parsedFeed.items), statusesDictionary) // 2
|
||||
if filteredParsedItems.isEmpty {
|
||||
completion(nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
queue.update{ (database) in
|
||||
self.queue.update{ (database) in
|
||||
|
||||
let fetchedArticles = self.fetchArticlesForFeedID(feedID, withLimits: false, database: database) //3
|
||||
let fetchedArticlesDictionary = fetchedArticles.dictionary()
|
||||
|
@ -196,10 +194,7 @@ private extension ArticlesTable {
|
|||
|
||||
func articleWithRow(_ row: FMResultSet) -> Article? {
|
||||
|
||||
guard let account = account else {
|
||||
return nil
|
||||
}
|
||||
guard let article = Article(row: row, account: account) else {
|
||||
guard let article = Article(row: row, accountID: accountID) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -360,23 +355,18 @@ private extension ArticlesTable {
|
|||
return status.dateArrived < maximumArticleCutoffDate
|
||||
}
|
||||
|
||||
func filterParsedItems(_ parsedItems: [String: ParsedItem], _ statuses: [String: ArticleStatus]) -> [String: ParsedItem] {
|
||||
func filterParsedItems(_ parsedItems: Set<ParsedItem>, _ statuses: [String: ArticleStatus]) -> Set<ParsedItem> {
|
||||
|
||||
// Drop parsedItems that we can ignore.
|
||||
|
||||
var d = [String: ParsedItem]()
|
||||
|
||||
for (articleID, parsedItem) in parsedItems {
|
||||
|
||||
return Set(parsedItems.filter{ (parsedItem) -> Bool in
|
||||
let articleID = parsedItem.articleID
|
||||
if let status = statuses[articleID] {
|
||||
if statusIndicatesArticleIsIgnorable(status) {
|
||||
continue
|
||||
return !statusIndicatesArticleIsIgnorable(status)
|
||||
}
|
||||
}
|
||||
d[articleID] = parsedItem
|
||||
}
|
||||
|
||||
return d
|
||||
assertionFailure("Expected a status for each parsedItem.")
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,23 +15,24 @@ import Data
|
|||
public typealias ArticleResultBlock = (Set<Article>) -> Void
|
||||
public typealias UnreadCountTable = [String: Int] // feedID: unreadCount
|
||||
public typealias UnreadCountCompletionBlock = (UnreadCountTable) -> Void //feedID: unreadCount
|
||||
typealias UpdateArticlesWithFeedCompletionBlock = (Set<Article>, Set<Article>) -> Void
|
||||
public typealias UpdateArticlesWithFeedCompletionBlock = (Set<Article>?, Set<Article>?) -> Void //newArticles, updateArticles
|
||||
|
||||
public final class Database {
|
||||
|
||||
private let accountID: String
|
||||
private let queue: RSDatabaseQueue
|
||||
private let databaseFile: String
|
||||
private let articlesTable: ArticlesTable
|
||||
private var articleArrivalCutoffDate = NSDate.rs_dateWithNumberOfDays(inThePast: 3 * 31)!
|
||||
private let minimumNumberOfArticles = 10
|
||||
|
||||
public init(databaseFile: String) {
|
||||
public init(databaseFile: String, accountID: String) {
|
||||
|
||||
self.account = account
|
||||
self.accountID = accountID
|
||||
self.databaseFile = databaseFile
|
||||
self.queue = RSDatabaseQueue(filepath: databaseFile, excludeFromBackup: false)
|
||||
|
||||
self.articlesTable = ArticlesTable(name: DatabaseTableName.articles, account: account, queue: queue)
|
||||
self.articlesTable = ArticlesTable(name: DatabaseTableName.articles, accountID: accountID, queue: queue)
|
||||
|
||||
let createStatementsPath = Bundle(for: type(of: self)).path(forResource: "CreateStatements", ofType: "sql")!
|
||||
let createStatements = try! NSString(contentsOfFile: createStatementsPath, encoding: String.Encoding.utf8.rawValue)
|
||||
|
@ -65,7 +66,7 @@ public final class Database {
|
|||
|
||||
// MARK: - Updating Articles
|
||||
|
||||
public func update(feed: Feed, parsedFeed: ParsedFeed, completion: @escaping RSVoidCompletionBlock) {
|
||||
public func update(feed: Feed, parsedFeed: ParsedFeed, completion: @escaping UpdateArticlesWithFeedCompletionBlock) {
|
||||
|
||||
return articlesTable.update(feed, parsedFeed, completion)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ extension Article {
|
|||
let bannerImageURL = row.string(forColumn: DatabaseKey.bannerImageURL)
|
||||
let datePublished = row.date(forColumn: DatabaseKey.datePublished)
|
||||
let dateModified = row.date(forColumn: DatabaseKey.dateModified)
|
||||
let accountInfo: [String: Any]? = nil // TODO
|
||||
let accountInfo: AccountInfo? = nil // TODO
|
||||
|
||||
self.init(accountID: accountID, articleID: articleID, 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)
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ extension Article {
|
|||
return d
|
||||
}
|
||||
|
||||
static func articlesWithParsedItems(_ parsedItems: [ParsedItem], _ accountID: String, _ feedID: String) -> Set<Article> {
|
||||
static func articlesWithParsedItems(_ parsedItems: Set<ParsedItem>, _ accountID: String, _ feedID: String) -> Set<Article> {
|
||||
|
||||
return Set(parsedItems.map{ Article(parsedItem: $0, accountID: accountID, feedID: feedID) })
|
||||
}
|
||||
|
|
|
@ -23,19 +23,6 @@ extension ParsedItem {
|
|||
}
|
||||
}
|
||||
|
||||
extension ParsedFeed {
|
||||
|
||||
func itemsDictionary(with feed: Feed) -> [String: ParsedItem] {
|
||||
|
||||
var d = [String: ParsedItem]()
|
||||
|
||||
for parsedItem in items {
|
||||
let identifier = parsedItem.databaseIdentifierWithFeed(feed)
|
||||
d[identifier] = parsedItem
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import Data
|
|||
//
|
||||
// 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);
|
||||
|
||||
typealias StatusesCompletionBlock = ([String: ArticleStatus]) -> Void // [articleID: Status]
|
||||
|
||||
final class StatusesTable: DatabaseTable {
|
||||
|
||||
let name = DatabaseTableName.statuses
|
||||
|
@ -35,7 +37,7 @@ final class StatusesTable: DatabaseTable {
|
|||
|
||||
// MARK: Creating/Updating
|
||||
|
||||
func ensureStatusesForArticleIDs(_ articleIDs: Set<String>, _ completion: @escaping RSVoidCompletionBlock) {
|
||||
func ensureStatusesForArticleIDs(_ articleIDs: Set<String>, _ completion: @escaping StatusesCompletionBlock) {
|
||||
|
||||
// Adds them to the cache if not cached.
|
||||
|
||||
|
@ -44,21 +46,21 @@ final class StatusesTable: DatabaseTable {
|
|||
// Check cache.
|
||||
let articleIDsMissingCachedStatus = articleIDsWithNoCachedStatus(articleIDs)
|
||||
if articleIDsMissingCachedStatus.isEmpty {
|
||||
completion()
|
||||
completion(statusesDictionary(articleIDs))
|
||||
return
|
||||
}
|
||||
|
||||
// Check database.
|
||||
fetchAndCacheStatusesForArticleIDs(articleIDsMissingCachedStatus) {
|
||||
|
||||
let articleIDsNeedingStatus = articleIDsWithNoCachedStatus(articleIDs)
|
||||
let articleIDsNeedingStatus = self.articleIDsWithNoCachedStatus(articleIDs)
|
||||
if articleIDsNeedingStatus.isEmpty {
|
||||
completion()
|
||||
completion(statusesDictionary(articleIDs))
|
||||
return
|
||||
}
|
||||
|
||||
// Create new statuses.
|
||||
createAndSaveStatusesForArticleIDs(articleIDsNeedingStatus, completion)
|
||||
self.createAndSaveStatusesForArticleIDs(articleIDsNeedingStatus, completion)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,16 +97,32 @@ private extension StatusesTable {
|
|||
|
||||
func articleIDsWithNoCachedStatus(_ articleIDs: Set<String>) -> Set<String> {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
return Set(articleIDs.filter { cache[$0] == nil })
|
||||
}
|
||||
|
||||
func statusesDictionary(_ articleIDs: Set<String>) -> [String: ArticleStatus] {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
|
||||
var d = [String: ArticleStatus]()
|
||||
|
||||
for articleID in articleIDs {
|
||||
if let articleStatus = cache[articleID] {
|
||||
d[articleID] = articleStatus
|
||||
}
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// MARK: Creating
|
||||
|
||||
func saveStatuses(_ statuses: Set<ArticleStatus>) {
|
||||
|
||||
queue.update { (database) in
|
||||
let statusArray = statuses.map { $0.databaseDictionary() }
|
||||
insertRows(statusArray, insertType: .orIgnore, in: database)
|
||||
self.insertRows(statusArray, insertType: .orIgnore, in: database)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,15 +144,15 @@ private extension StatusesTable {
|
|||
func fetchAndCacheStatusesForArticleIDs(_ articleIDs: Set<String>, _ completion: @escaping RSVoidCompletionBlock) {
|
||||
|
||||
queue.fetch { (database) in
|
||||
guard let resultSet = selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else {
|
||||
guard let resultSet = self.selectRowsWhere(key: DatabaseKey.articleID, inValues: Array(articleIDs), in: database) else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let statuses = resultSet.mapToSet(statusWithRow)
|
||||
let statuses = resultSet.mapToSet(self.statusWithRow)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
cache.addIfNotCached(statuses)
|
||||
self.cache.addIfNotCached(statuses)
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,11 +81,11 @@ private extension JSONFeedParser {
|
|||
return hubs.isEmpty ? nil : hubs
|
||||
}
|
||||
|
||||
static func parseItems(_ itemsArray: JSONArray, _ feedURL: String) -> [ParsedItem] {
|
||||
static func parseItems(_ itemsArray: JSONArray, _ feedURL: String) -> Set<ParsedItem> {
|
||||
|
||||
return itemsArray.flatMap { (oneItemDictionary) -> ParsedItem? in
|
||||
return Set(itemsArray.flatMap { (oneItemDictionary) -> ParsedItem? in
|
||||
return parseItem(oneItemDictionary, feedURL)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static func parseItem(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? {
|
||||
|
|
|
@ -58,12 +58,12 @@ public struct RSSInJSONParser {
|
|||
|
||||
private extension RSSInJSONParser {
|
||||
|
||||
static func parseItems(_ itemsObject: JSONArray, _ feedURL: String) -> [ParsedItem] {
|
||||
static func parseItems(_ itemsObject: JSONArray, _ feedURL: String) -> Set<ParsedItem> {
|
||||
|
||||
return itemsObject.flatMap{ (oneItemDictionary) -> ParsedItem? in
|
||||
return Set(itemsObject.flatMap{ (oneItemDictionary) -> ParsedItem? in
|
||||
|
||||
return parsedItemWithDictionary(oneItemDictionary, feedURL)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static func parsedItemWithDictionary(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? {
|
||||
|
|
|
@ -21,9 +21,9 @@ public struct ParsedFeed {
|
|||
public let authors: [ParsedAuthor]?
|
||||
public let expired: Bool
|
||||
public let hubs: [ParsedHub]?
|
||||
public let items: [ParsedItem]
|
||||
public let items: Set<ParsedItem>
|
||||
|
||||
init(type: FeedType, title: String?, homePageURL: String?, feedURL: String?, feedDescription: String?, nextURL: String?, iconURL: String?, faviconURL: String?, authors: [ParsedAuthor]?, expired: Bool, hubs: [ParsedHub]?, items:[ParsedItem]) {
|
||||
init(type: FeedType, title: String?, homePageURL: String?, feedURL: String?, feedDescription: String?, nextURL: String?, iconURL: String?, faviconURL: String?, authors: [ParsedAuthor]?, expired: Bool, hubs: [ParsedHub]?, items: Set<ParsedItem>) {
|
||||
|
||||
self.type = type
|
||||
self.title = title
|
||||
|
|
|
@ -24,15 +24,11 @@ struct RSParsedFeedTransformer {
|
|||
|
||||
private extension RSParsedFeedTransformer {
|
||||
|
||||
static func parsedItems(_ parsedArticles: Set<RSParsedArticle>) -> [ParsedItem] {
|
||||
static func parsedItems(_ parsedArticles: Set<RSParsedArticle>) -> Set<ParsedItem> {
|
||||
|
||||
// Create [ParsedItem] from set of RSParsedArticle.
|
||||
// Create Set<ParsedItem> from Set<RSParsedArticle>
|
||||
|
||||
var items = [ParsedItem]()
|
||||
for oneParsedArticle in parsedArticles {
|
||||
items += [parsedItem(oneParsedArticle)]
|
||||
}
|
||||
return items
|
||||
return Set(parsedArticles.map(parsedItem))
|
||||
}
|
||||
|
||||
static func parsedItem(_ parsedArticle: RSParsedArticle) -> ParsedItem {
|
||||
|
|
BIN
ToDo.ooutline
BIN
ToDo.ooutline
Binary file not shown.
Loading…
Reference in New Issue