Create Articles with attached objects.

This commit is contained in:
Brent Simmons 2017-09-13 21:41:01 -07:00
parent 57cf5a25d7
commit 7563906f9b
10 changed files with 172 additions and 73 deletions

View File

@ -175,14 +175,56 @@ private extension ArticlesTable {
// Note: the row is a result of a JOIN query with the statuses table, // Note: the row is a result of a JOIN query with the statuses table,
// so we can get the status at the same time and avoid additional database lookups. // so we can get the status at the same time and avoid additional database lookups.
article.status = statusesTable.statusWithRow(row) // article.status = statusesTable.statusWithRow(row)
return article return article
} }
func articlesWithResultSet(_ resultSet: FMResultSet, _ database: FMDatabase) -> Set<Article> { func articlesWithResultSet(_ resultSet: FMResultSet, _ database: FMDatabase) -> Set<Article> {
let articles = resultSet.mapToSet(articleWithRow) // Create set of stub Articles without related objects.
attachRelatedObjects(articles, database) // Then fetch the related objects, given the set of articleIDs.
// Then create set of Articles *with* related objects and return it.
let stubArticles = resultSet.mapToSet(articleWithRow)
if stubArticles.isEmpty {
return stubArticles
}
// Fetch related objects.
let articleIDs = stubArticles.articleIDs()
let authorsMap = authorsLookupTable.fetchRelatedObjects(for: articleIDs, in: database)
let attachmentsMap = attachmentsLookupTable.fetchRelatedObjects(for: articleIDs, in: database)
let tagsMap = tagsLookupTable.fetchRelatedObjects(for: articleIDs, in: database)
if authorsMap == nil && attachmentsMap == nil && tagsMap == nil {
return stubArticles
}
// Create articles with related objects.
var articles = Set<Article>()
for stubArticle in articles {
var authors: Set<Author>? = nil
var attachments: Set<Attachment>? = nil
var tags: Set<String>? = nil
let articleID = stubArticle.articleID
if let authorsMap = authorsMap {
authors = authorsMap.authors(for: articleID)
}
if let attachmentsMap = attachmentsMap {
attachments = attachmentsMap.attachments(for: articleID)
}
if let tagsMap = tagsMap {
tags = tagsMap.tags(for: articleID)
}
let realArticle = stubArticle.articleByAttaching(authors, attachments, tags)
articles.insert(realArticle)
}
return articles return articles
} }
@ -378,13 +420,13 @@ private extension ArticlesTable {
// Only update exactly what has changed in the Article (if anything). // Only update exactly what has changed in the Article (if anything).
// Untested theory: this gets us better performance and less database fragmentation. // Untested theory: this gets us better performance and less database fragmentation.
guard let fetchedArticle = fetchedArticle[updatedArticle.articleID] else { guard let fetchedArticle = fetchedArticles[updatedArticle.articleID] else {
assertionFailure("Expected to find matching fetched article."); assertionFailure("Expected to find matching fetched article.");
saveNewArticles(Set([updatedArticle]), database) saveNewArticles(Set([updatedArticle]), database)
return return
} }
guard let changesDictionary = updatedArticle.changesFrom(fetchedArticle), !changesDictionary.isEmpty else { guard let changesDictionary = updatedArticle.changesFrom(fetchedArticle), changesDictionary.count > 0 else {
// Not unexpected. There may be no changes. // Not unexpected. There may be no changes.
return return
} }

View File

@ -8,12 +8,13 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840405CE1F1A963700DF0296 /* AttachmentsTable.swift */; }; 840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840405CE1F1A963700DF0296 /* AttachmentsTable.swift */; };
84288A001F6A3C4400395871 /* DatabaseObject+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842889FF1F6A3C4400395871 /* DatabaseObject+Database.swift */; };
84288A021F6A3D8000395871 /* RelatedObjectsMap+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84288A011F6A3D8000395871 /* RelatedObjectsMap+Database.swift */; };
843CB9961F34174100EE6581 /* Author+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F20F901F1810DD00D8E682 /* Author+Database.swift */; }; 843CB9961F34174100EE6581 /* Author+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F20F901F1810DD00D8E682 /* Author+Database.swift */; };
844BEE411F0AB3AB004AB7CD /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 844BEE371F0AB3AA004AB7CD /* Database.framework */; }; 844BEE411F0AB3AB004AB7CD /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 844BEE371F0AB3AA004AB7CD /* Database.framework */; };
844BEE461F0AB3AB004AB7CD /* DatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE451F0AB3AB004AB7CD /* DatabaseTests.swift */; }; 844BEE461F0AB3AB004AB7CD /* DatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE451F0AB3AB004AB7CD /* DatabaseTests.swift */; };
845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580661F0AEBCD003CCFA1 /* Constants.swift */; }; 845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580661F0AEBCD003CCFA1 /* Constants.swift */; };
845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580751F0AF670003CCFA1 /* Article+Database.swift */; }; 845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580751F0AF670003CCFA1 /* Article+Database.swift */; };
845580781F0AF678003CCFA1 /* Folder+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580771F0AF678003CCFA1 /* Folder+Database.swift */; };
8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */; }; 8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */; };
8455807C1F0C0DBD003CCFA1 /* Attachment+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */; }; 8455807C1F0C0DBD003CCFA1 /* Attachment+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */; };
846146271F0ABC7B00870CB3 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846146241F0ABC7400870CB3 /* RSParser.framework */; }; 846146271F0ABC7B00870CB3 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846146241F0ABC7400870CB3 /* RSParser.framework */; };
@ -113,13 +114,14 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
840405CE1F1A963700DF0296 /* AttachmentsTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentsTable.swift; sourceTree = "<group>"; }; 840405CE1F1A963700DF0296 /* AttachmentsTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentsTable.swift; sourceTree = "<group>"; };
842889FF1F6A3C4400395871 /* DatabaseObject+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DatabaseObject+Database.swift"; sourceTree = "<group>"; };
84288A011F6A3D8000395871 /* RelatedObjectsMap+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RelatedObjectsMap+Database.swift"; sourceTree = "<group>"; };
844BEE371F0AB3AA004AB7CD /* Database.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Database.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 844BEE371F0AB3AA004AB7CD /* Database.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Database.framework; sourceTree = BUILT_PRODUCTS_DIR; };
844BEE401F0AB3AB004AB7CD /* DatabaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DatabaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 844BEE401F0AB3AB004AB7CD /* DatabaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DatabaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
844BEE451F0AB3AB004AB7CD /* DatabaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseTests.swift; sourceTree = "<group>"; }; 844BEE451F0AB3AB004AB7CD /* DatabaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseTests.swift; sourceTree = "<group>"; };
844BEE471F0AB3AB004AB7CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 844BEE471F0AB3AB004AB7CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
845580661F0AEBCD003CCFA1 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; }; 845580661F0AEBCD003CCFA1 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
845580751F0AF670003CCFA1 /* Article+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Article+Database.swift"; path = "Extensions/Article+Database.swift"; sourceTree = "<group>"; }; 845580751F0AF670003CCFA1 /* Article+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Article+Database.swift"; path = "Extensions/Article+Database.swift"; sourceTree = "<group>"; };
845580771F0AF678003CCFA1 /* Folder+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Folder+Database.swift"; path = "Extensions/Folder+Database.swift"; sourceTree = "<group>"; };
845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "ArticleStatus+Database.swift"; path = "Extensions/ArticleStatus+Database.swift"; sourceTree = "<group>"; }; 845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "ArticleStatus+Database.swift"; path = "Extensions/ArticleStatus+Database.swift"; sourceTree = "<group>"; };
8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Attachment+Database.swift"; path = "Extensions/Attachment+Database.swift"; sourceTree = "<group>"; }; 8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Attachment+Database.swift"; path = "Extensions/Attachment+Database.swift"; sourceTree = "<group>"; };
8461461E1F0ABC7300870CB3 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = "<group>"; }; 8461461E1F0ABC7300870CB3 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = "<group>"; };
@ -212,13 +214,14 @@
8461462A1F0AC44100870CB3 /* Extensions */ = { 8461462A1F0AC44100870CB3 /* Extensions */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
845580771F0AF678003CCFA1 /* Folder+Database.swift */,
846FB36A1F4A937B00EAB81D /* Feed+Database.swift */, 846FB36A1F4A937B00EAB81D /* Feed+Database.swift */,
845580751F0AF670003CCFA1 /* Article+Database.swift */, 845580751F0AF670003CCFA1 /* Article+Database.swift */,
845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */, 845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */,
84F20F901F1810DD00D8E682 /* Author+Database.swift */, 84F20F901F1810DD00D8E682 /* Author+Database.swift */,
8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */, 8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */,
84D0DEA01F4A429800073503 /* String+Database.swift */, 84D0DEA01F4A429800073503 /* String+Database.swift */,
842889FF1F6A3C4400395871 /* DatabaseObject+Database.swift */,
84288A011F6A3D8000395871 /* RelatedObjectsMap+Database.swift */,
); );
name = Extensions; name = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -472,13 +475,14 @@
84D0DEA11F4A429800073503 /* String+Database.swift in Sources */, 84D0DEA11F4A429800073503 /* String+Database.swift in Sources */,
843CB9961F34174100EE6581 /* Author+Database.swift in Sources */, 843CB9961F34174100EE6581 /* Author+Database.swift in Sources */,
848AD2961F58A91E004FB0EC /* UnreadCountDictionary.swift in Sources */, 848AD2961F58A91E004FB0EC /* UnreadCountDictionary.swift in Sources */,
845580781F0AF678003CCFA1 /* Folder+Database.swift in Sources */,
845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */, 845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */,
8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */, 8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */,
8455807C1F0C0DBD003CCFA1 /* Attachment+Database.swift in Sources */, 8455807C1F0C0DBD003CCFA1 /* Attachment+Database.swift in Sources */,
84288A021F6A3D8000395871 /* RelatedObjectsMap+Database.swift in Sources */,
84BB4BA91F11A32800858766 /* TagsTable.swift in Sources */, 84BB4BA91F11A32800858766 /* TagsTable.swift in Sources */,
840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */, 840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */,
84F20F8F1F180D8700D8E682 /* AuthorsTable.swift in Sources */, 84F20F8F1F180D8700D8E682 /* AuthorsTable.swift in Sources */,
84288A001F6A3C4400395871 /* DatabaseObject+Database.swift in Sources */,
84E156EC1F0AB80E00F8CC05 /* ArticlesTable.swift in Sources */, 84E156EC1F0AB80E00F8CC05 /* ArticlesTable.swift in Sources */,
84E156EE1F0AB81400F8CC05 /* StatusesTable.swift in Sources */, 84E156EE1F0AB81400F8CC05 /* StatusesTable.swift in Sources */,
84E156EA1F0AB80500F8CC05 /* Database.swift in Sources */, 84E156EA1F0AB80500F8CC05 /* Database.swift in Sources */,

View File

@ -0,0 +1,32 @@
//
// DatabaseObject+Database.swift
// Database
//
// Created by Brent Simmons on 9/13/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import RSDatabase
import Data
extension Array where Element == DatabaseObject {
func asTags() -> Set<String>? {
let tags = Set(self.map { $0 as! String })
return tags.isEmpty ? nil : tags
}
func asAuthors() -> Set<Author>? {
let authors = Set(self.map { $0 as! Author })
return authors.isEmpty ? nil : authors
}
func asAttachments() -> Set<Attachment>? {
let attachments = Set(self.map { $0 as! Attachment })
return attachments.isEmpty ? nil : attachments
}
}

View File

@ -13,7 +13,7 @@ import RSParser
extension Article { extension Article {
init?(row: FMResultSet, authors: Set<Author>, attachments: Set<Attachment>, tags: Set<String>, accountID: String) { init?(row: FMResultSet, accountID: String, authors: Set<Author>? = nil, attachments: Set<Attachment>? = nil, tags: Set<String>? = nil) {
guard let feedID = row.string(forColumn: DatabaseKey.feedID) else { guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
return nil return nil
@ -40,12 +40,21 @@ extension Article {
init(parsedItem: ParsedItem, accountID: String, feedID: String) { init(parsedItem: ParsedItem, accountID: String, feedID: String) {
let authors = parsedItem.authors?.authors() let authors = Author.authorsWithParsedAuthors(parsedItem.authors)
let attachments = Attachment.attachmentsWithParsedAttachments(parsedItem.attachments) let attachments = Attachment.attachmentsWithParsedAttachments(parsedItem.attachments)
self.init(accountID: accountID, articleID: parsedItem.syncServiceID, feedID: feedID, uniqueID: parsedItem.uniqueID, title: parsedItem.title, contentHTML: parsedItem.contentHTML, contentText: parsedItem.contentText, url: parsedItem.url, externalURL: parsedItem.externalURL, summary: parsedItem.summary, imageURL: parsedItem.imageURL, bannerImageURL: parsedItem.bannerImageURL, datePublished: parsedItem.datePublished, dateModified: parsedItem.dateModified, authors: authors, tags: parsedItem.tags, attachments: attachments, accountInfo: nil) self.init(accountID: accountID, articleID: parsedItem.syncServiceID, feedID: feedID, uniqueID: parsedItem.uniqueID, title: parsedItem.title, contentHTML: parsedItem.contentHTML, contentText: parsedItem.contentText, url: parsedItem.url, externalURL: parsedItem.externalURL, summary: parsedItem.summary, imageURL: parsedItem.imageURL, bannerImageURL: parsedItem.bannerImageURL, datePublished: parsedItem.datePublished, dateModified: parsedItem.dateModified, authors: authors, tags: parsedItem.tags, attachments: attachments, accountInfo: nil)
} }
func articleByAttaching(_ authors: Set<Author>?, _ attachments: Set<Attachment>?, _ tags: Set<String>?) -> Article {
if authors == nil && attachments == nil && tags == nil {
return self
}
return Article(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)
}
private func addPossibleStringChangeWithKeyPath(_ comparisonKeyPath: KeyPath<Article,String?>, _ otherArticle: Article, _ key: String, _ dictionary: NSMutableDictionary) { private func addPossibleStringChangeWithKeyPath(_ comparisonKeyPath: KeyPath<Article,String?>, _ otherArticle: Article, _ key: String, _ dictionary: NSMutableDictionary) {
if self[keyPath: comparisonKeyPath] != otherArticle[keyPath: comparisonKeyPath] { if self[keyPath: comparisonKeyPath] != otherArticle[keyPath: comparisonKeyPath] {

View File

@ -30,6 +30,16 @@ extension Author {
self.init(authorID: nil, name: parsedAuthor.name, url: parsedAuthor.url, avatarURL: parsedAuthor.avatarURL, emailAddress: parsedAuthor.emailAddress) self.init(authorID: nil, name: parsedAuthor.name, url: parsedAuthor.url, avatarURL: parsedAuthor.avatarURL, emailAddress: parsedAuthor.emailAddress)
} }
static func authorsWithParsedAuthors(_ parsedAuthors: Set<ParsedAuthor>?) -> Set<Author>? {
guard let parsedAuthors = parsedAuthors else {
return nil
}
let authors = Set(parsedAuthors.flatMap { Author(parsedAuthor: $0) })
return authors.isEmpty ? nil: authors
}
} }
extension Author: DatabaseObject { extension Author: DatabaseObject {
@ -42,25 +52,24 @@ extension Author: DatabaseObject {
public func databaseDictionary() -> NSDictionary? { public func databaseDictionary() -> NSDictionary? {
var d = NSMutableDictionary() let d = NSMutableDictionary()
// TODO d[DatabaseKey.authorID] = authorID
if d.count < 1 { if let name = name {
return nil d[DatabaseKey.name] = name
} }
if let url = url {
d[DatabaseKey.url] = url
}
if let avatarURL = avatarURL {
d[DatabaseKey.avatarURL] = avatarURL
}
if let emailAddress = emailAddress {
d[DatabaseKey.emailAddress] = emailAddress
}
return (d.copy() as! NSDictionary) return (d.copy() as! NSDictionary)
} }
} }
// MARK: Author creation from Set<ParsedAuthor>
extension Set where Element == ParsedAuthor {
func authors() -> Set<Author>? {
let createdAuthors = Set(self.flatMap { Author(parsedAuthor: $0) })
return createdAuthors.isEmpty ? nil: createdAuthors
}
}

View File

@ -1,18 +0,0 @@
//
// Folder+Database.swift
// Database
//
// Created by Brent Simmons on 7/3/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import Data
extension Folder {
func flattenedFeedIDs() -> [String] {
return flattenedFeeds().map { $0.feedID }
}
}

View File

@ -17,10 +17,8 @@ extension String: DatabaseObject {
public func databaseDictionary() -> NSDictionary? { public func databaseDictionary() -> NSDictionary? {
preconditionFailure("databaseDictionary() called for a tag: this should never happen.") preconditionFailure("databaseDictionary() called for a tag: this should never happen.")
return nil // unused
} }
public var databaseID: String { public var databaseID: String {
get { get {
return self return self

View File

@ -0,0 +1,38 @@
//
// RelatedObjectsMap+Database.swift
// Database
//
// Created by Brent Simmons on 9/13/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import RSDatabase
import Data
extension RelatedObjectsMap {
func attachments(for articleID: String) -> Set<Attachment>? {
if let objects = self[articleID] {
return objects.asAttachments()
}
return nil
}
func authors(for articleID: String) -> Set<Author>? {
if let objects = self[articleID] {
return objects.asAuthors()
}
return nil
}
func tags(for articleID: String) -> Set<String>? {
if let objects = self[articleID] {
return objects.asTags()
}
return nil
}
}

View File

@ -54,13 +54,12 @@ final class StatusesTable: DatabaseTable {
fetchAndCacheStatusesForArticleIDs(articleIDsMissingCachedStatus) { fetchAndCacheStatusesForArticleIDs(articleIDsMissingCachedStatus) {
let articleIDsNeedingStatus = self.articleIDsWithNoCachedStatus(articleIDs) let articleIDsNeedingStatus = self.articleIDsWithNoCachedStatus(articleIDs)
if articleIDsNeedingStatus.isEmpty { if !articleIDsNeedingStatus.isEmpty {
completion(self.statusesDictionary(articleIDs)) // Create new statuses.
return self.createAndSaveStatusesForArticleIDs(articleIDsNeedingStatus)
} }
// Create new statuses. completion(self.statusesDictionary(articleIDs))
self.createAndSaveStatusesForArticleIDs(articleIDsNeedingStatus, completion)
} }
} }
@ -121,12 +120,12 @@ private extension StatusesTable {
func saveStatuses(_ statuses: Set<ArticleStatus>) { func saveStatuses(_ statuses: Set<ArticleStatus>) {
queue.update { (database) in queue.update { (database) in
let statusArray = statuses.map { $0.databaseDictionary() } let statusArray = statuses.map { $0.databaseDictionary()! }
self.insertRows(statusArray, insertType: .orIgnore, in: database) self.insertRows(statusArray, insertType: .orIgnore, in: database)
} }
} }
func createAndSaveStatusesForArticleIDs(_ articleIDs: Set<String>, _ completion: @escaping RSVoidCompletionBlock) { func createAndSaveStatusesForArticleIDs(_ articleIDs: Set<String>) {
assert(Thread.isMainThread) assert(Thread.isMainThread)
@ -134,10 +133,6 @@ private extension StatusesTable {
let statuses = Set(articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) }) let statuses = Set(articleIDs.map { ArticleStatus(articleID: $0, dateArrived: now) })
cache.addIfNotCached(statuses) cache.addIfNotCached(statuses)
// No need to wait for database to return before calling completion,
// since the new statuses have been cached at this point.
completion()
saveStatuses(statuses) saveStatuses(statuses)
} }

View File

@ -6,12 +6,12 @@
</editor> --> </editor> -->
<title>ToDo</title> <title>ToDo</title>
<dateCreated>Tue, 12 Sep 2017 20:15:17 GMT</dateCreated> <dateCreated>Tue, 12 Sep 2017 20:15:17 GMT</dateCreated>
<expansionState>11,16,17,20,24,29,31,34,37,39,41,43,47,52,55,57,59,61,63,72,77,79,85,90</expansionState> <expansionState>11,16,17,20,24,29,31,34,37,39,40,44,49,52,54,56,58,67,72,78,83</expansionState>
<vertScrollState>0</vertScrollState> <vertScrollState>59</vertScrollState>
<windowTop>209</windowTop> <windowTop>248</windowTop>
<windowLeft>30</windowLeft> <windowLeft>30</windowLeft>
<windowRight>762</windowRight> <windowRight>762</windowRight>
<windowBottom>968</windowBottom> <windowBottom>1007</windowBottom>
</head> </head>
<body> <body>
<outline text="App"> <outline text="App">
@ -63,20 +63,16 @@
<outline text="Frameworks"> <outline text="Frameworks">
<outline text="Account"/> <outline text="Account"/>
<outline text="Database"> <outline text="Database">
<outline text="Get it building again"/>
<outline text="Authors">
<outline text="databaseDictionary()"/>
</outline>
<outline text="Database.swift"> <outline text="Database.swift">
<outline text="updateFeedWithParsedFeed"/> <outline text="updateFeedWithParsedFeed"/>
<outline text="Handle accountInfo in statuses"/> <outline text="Handle accountInfo in statuses"/>
<outline text="Send notification on saving/updating"/> <outline text="Send notification on saving/updating"/>
</outline> </outline>
<outline text="ArticlesTable"> <outline text="ArticlesTable">
<outline text="Fetch related objects for articles in time for Article.init, so Article can be immutable"/>
<outline text="mark articles"/> <outline text="mark articles"/>
<outline text="Delete old articles"/> <outline text="Delete old articles"/>
<outline text="Update cutoff date periodically"/> <outline text="Update cutoff date periodically"/>
<outline text="Do something with statuses on fetching articles"/>
</outline> </outline>
<outline text="StatusesTable"> <outline text="StatusesTable">
<outline text="Update cached statuses on marking"/> <outline text="Update cached statuses on marking"/>
@ -91,9 +87,6 @@
<outline text="Tags table"> <outline text="Tags table">
<outline text="Search on tags using COLLATE NOCASE"/> <outline text="Search on tags using COLLATE NOCASE"/>
</outline> </outline>
<outline text="DatabaseLookupTable">
<outline text="Fetch relationships using unique IDs instead of objects"/>
</outline>
<outline text="Unit tests"> <outline text="Unit tests">
<outline text="fetching articles"/> <outline text="fetching articles"/>
<outline text="fetching unread articles"/> <outline text="fetching unread articles"/>
@ -111,9 +104,6 @@
</outline> </outline>
</outline> </outline>
</outline> </outline>
<outline text="RSDatabase">
<outline text="DatabaseRelatedObjectsTable.save implementation should use cache"/>
</outline>
<outline text="Data"> <outline text="Data">
<outline text="Make model classes quicklookable"/> <outline text="Make model classes quicklookable"/>
</outline> </outline>