Make further progress on saving articles from feeds.

This commit is contained in:
Brent Simmons 2017-09-06 13:33:04 -07:00
parent d84c65c66f
commit a92492eb91
5 changed files with 71 additions and 42 deletions

View File

@ -79,10 +79,10 @@ final class ArticlesTable: DatabaseTable {
// MARK: Updating
func update(_ feed: Feed, _ parsedFeed: ParsedFeed, _ completion: @escaping RSVoidCompletionBlock) {
func update(_ feed: Feed, _ parsedFeed: ParsedFeed, _ completion: @escaping UpdateArticlesWithFeedCompletionBlock) {
if parsedFeed.items.isEmpty {
completion()
completion(nil, nil)
return
}
@ -90,40 +90,51 @@ final class ArticlesTable: DatabaseTable {
// 2. Ignore parsedItems that are userDeleted || (!starred and really old)
// 3. Fetch all articles for the feed.
// 4. Create Articles with parsedItems.
// 5.
// 5. Create array of Articles not in database and save them.
// 6. Create array of updated Articles and save whats changed.
// 7. Call back with new and updated Articles.
let feedID = feed.feedID
let parsedItemArticleIDs = Set(parsedFeed.items.map { $0.databaseIdentifierWithFeed(feed) })
let parsedItemsDictionary = parsedFeed.itemsDictionary(with: feed)
statusesTable.ensureStatusesForArticleIDs(parsedItemArticleIDs) {
statusesTable.ensureStatusesForArticleIDs(parsedItemArticleIDs) { // 1
let filteredParsedItems = self.filterParsedItems(parsedItemsDictionary)
let filteredParsedItems = self.filterParsedItems(parsedItemsDictionary) // 2
if filteredParsedItems.isEmpty {
completion()
completion(nil, nil)
return
}
queue.fetch{ (database) in
let fetchedArticles = self.fetchArticlesForFeedID(feedID, withLimits: false, database: database)
let incomingArticles = Article.articlesWithParsedItems(parsedFeed.items, accountID, feedID)
queue.update{ (database) in
let fetchedArticles = self.fetchArticlesForFeedID(feedID, withLimits: false, database: database) //3
let fetchedArticlesDictionary = fetchedArticles.dictionary()
let incomingArticles = Article.articlesWithParsedItems(filteredParsedItems, accountID, feedID) //4
let incomingArticlesDictionary = incomingArticles.dictionary()
let newArticles = Set(incomingArticles.filter { fetchedArticles[$0.articleID] == nil }) //5
if !newArticles.isEmpty {
saveNewArticles(newArticles, database)
}
let updatedArticles = incomingArticles.filter{ (incomingArticle) -> Bool in //6
if let existingArticle = fetchedArticles[incomingArticle.articleID] {
if existingArticle != incomingArticle {
return true
}
}
return false
}
if !updatedArticles.isEmpty {
saveUpdatedArticles(Set(updatedArticles), fetchedArticlesDictionary, database)
}
DispatchQueue.main.async {
completion(newArticles, updatedArticles) //7
}
}
}
// 3. For each parsedItem:
// - if userDeleted || (!starred && status.dateArrived < cutoff), then ignore
// - if matches existing article, then update database with changes between the two
// - if new, create article and save in database
fetchArticlesAsync(feed, withLimits: false) { (articles) in
self.updateArticles(articles.dictionary(), parsedFeed.itemsDictionary(with: feed), feed, completion)
}
}
@ -182,21 +193,21 @@ private extension ArticlesTable {
// MARK: Fetching
func attachRelatedObjects(_ articles: Set<Article>, _ database: FMDatabase) {
let articleArray = articles.map { $0 as DatabaseObject }
authorsLookupTable.attachRelatedObjects(to: articleArray, in: database)
attachmentsLookupTable.attachRelatedObjects(to: articleArray, in: database)
tagsLookupTable.attachRelatedObjects(to: articleArray, in: database)
// In theory, its impossible to have a fetched article without a status.
// Lets handle that impossibility anyway.
// Remember that, if nothing else, the user can edit the SQLite database,
// and thus could delete all their statuses.
statusesTable.ensureStatusesForArticles(articles, database)
}
// func attachRelatedObjects(_ articles: Set<Article>, _ database: FMDatabase) {
//
// let articleArray = articles.map { $0 as DatabaseObject }
//
// authorsLookupTable.attachRelatedObjects(to: articleArray, in: database)
// attachmentsLookupTable.attachRelatedObjects(to: articleArray, in: database)
// tagsLookupTable.attachRelatedObjects(to: articleArray, in: database)
//
// // In theory, its impossible to have a fetched article without a status.
// // Lets handle that impossibility anyway.
// // Remember that, if nothing else, the user can edit the SQLite database,
// // and thus could delete all their statuses.
//
// statusesTable.ensureStatusesForArticles(articles, database)
// }
func articleWithRow(_ row: FMResultSet) -> Article? {

View File

@ -8,7 +8,24 @@
import Foundation
public struct DatabaseTableName {
// MARK: - Notifications
public extension Notification.Name {
public static let ArticlesDidSave = Notification.Name(rawValue: "ArticlesDidSave")
}
public struct DatabaseNotificationKey {
// userInfo keys (with Set<Article> values) for ArticlesDidSave.
// One or both will be present. If present, may be empty.
static let newArticles = "newArticles"
static let updatedArticles = "updatedArticles"
}
// MARK: - Database structure
struct DatabaseTableName {
static let articles = "articles"
static let authors = "authors"
@ -19,7 +36,7 @@ public struct DatabaseTableName {
static let attachmentsLookup = "attachmentsLookup"
}
public struct DatabaseKey {
struct DatabaseKey {
// Shared
static let databaseID = "databaseID"
@ -65,7 +82,7 @@ public struct DatabaseKey {
static let emailAddress = "emailAddress"
}
public struct RelationshipName {
struct RelationshipName {
static let authors = "authors"
static let tags = "tags"

View File

@ -15,6 +15,7 @@ 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 final class Database {

View File

@ -73,7 +73,7 @@ extension Article {
static func articlesWithParsedItems(_ parsedItems: [ParsedItem], _ accountID: String, _ feedID: String) -> Set<Article> {
return parsedItems.map{ Article(parsedItem: $0, accountID: accountID, feedID: feedID) }
return Set(parsedItems.map{ Article(parsedItem: $0, accountID: accountID, feedID: feedID) })
}
// MARK: Updating with ParsedItem

Binary file not shown.