Fix some Database.framework build errors. Add Author cache.
This commit is contained in:
parent
4d816850d6
commit
7680760537
|
@ -293,7 +293,7 @@ private extension ArticlesTable {
|
|||
|
||||
// MARK: Update Existing Articles
|
||||
|
||||
func articlesWithRelatedObjectChanges(_ comparisonKeyPath: Keypath<Article, Set<AnyHashable>>, _ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article]) -> Set<Article> {
|
||||
func articlesWithRelatedObjectChanges(_ comparisonKeyPath: KeyPath<Article, Set<AnyHashable>>, _ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article]) -> Set<Article> {
|
||||
|
||||
return updatedArticles.filter{ (updatedArticle) -> Bool in
|
||||
if let fetchedArticle = fetchedArticles[updatedArticle.articleID] {
|
||||
|
@ -304,7 +304,7 @@ private extension ArticlesTable {
|
|||
}
|
||||
}
|
||||
|
||||
func updateRelatedObjects(_ comparisonKeyPath: Keypath<Article, Set<AnyHashable>>, _ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ lookupTable: DatabaseLookupTable, _ database: FMDatabase) {
|
||||
func updateRelatedObjects(_ comparisonKeyPath: KeyPath<Article, Set<AnyHashable>>, _ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ lookupTable: DatabaseLookupTable, _ database: FMDatabase) {
|
||||
|
||||
let articlesWithChanges = articlesWithRelatedObjectChanges(comparisonKeyPath, updatedArticles, fetchedArticles)
|
||||
if !articlesWithChanges.isEmpty {
|
||||
|
@ -312,7 +312,7 @@ private extension ArticlesTable {
|
|||
}
|
||||
}
|
||||
|
||||
func saveUpdatedRelatedObjects(_ updatedArticles: Set<Article>, _fetchedArticles: [String: Article], _ database: FMDatabase) {
|
||||
func saveUpdatedRelatedObjects(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) {
|
||||
|
||||
updateRelatedObjects(\Article.tags, updatedArticles, fetchedArticles, tagsLookupTable, database)
|
||||
updateRelatedObjects(\Article.authors, updatedArticles, fetchedArticles, authorsLookupTable, database)
|
||||
|
|
|
@ -31,7 +31,7 @@ final class AuthorsTable: DatabaseRelatedObjectsTable {
|
|||
|
||||
func objectWithRow(_ row: FMResultSet) -> DatabaseObject? {
|
||||
|
||||
if let author = authorWithRow(row) {
|
||||
if let author = Author.authorWithRow(row) {
|
||||
return author as DatabaseObject
|
||||
}
|
||||
return nil
|
||||
|
@ -42,23 +42,3 @@ final class AuthorsTable: DatabaseRelatedObjectsTable {
|
|||
}
|
||||
}
|
||||
|
||||
private extension AuthorsTable {
|
||||
|
||||
func authorWithRow(_ row: FMResultSet) -> Author? {
|
||||
|
||||
guard let authorID = row.string(forColumn: DatabaseKey.authorID) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let cachedAuthor = Author.cachedAuthor[authorID] {
|
||||
return cachedAuthor
|
||||
}
|
||||
|
||||
guard let author = Author(authorID: authorID, row: row) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
cache[authorID] = author
|
||||
return author
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ extension Article {
|
|||
let dateModified = row.date(forColumn: DatabaseKey.dateModified)
|
||||
let accountInfo: [String: Any]? = nil // TODO
|
||||
|
||||
self.init(account: account, 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)
|
||||
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)
|
||||
}
|
||||
|
||||
init(parsedItem: ParsedItem, accountID: String, feedID: String) {
|
||||
|
@ -44,7 +44,7 @@ extension Article {
|
|||
let attachments = Attachment.attachmentsWithParsedAttachments(parsedItem.attachments)
|
||||
let tags = tagSetWithParsedTags(parsedItem.tags)
|
||||
|
||||
self.init(account: account, 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: 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: tags, attachments: attachments, accountInfo: nil)
|
||||
}
|
||||
|
||||
func databaseDictionary() -> NSDictionary {
|
||||
|
@ -72,7 +72,7 @@ extension Article {
|
|||
return d.copy() as! NSDictionary
|
||||
}
|
||||
|
||||
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] {
|
||||
dictionary.addOptionalStringDefaultingEmpty(self[keyPath: comparisonKeyPath], key)
|
||||
|
@ -86,8 +86,13 @@ extension Article {
|
|||
}
|
||||
|
||||
let d = NSMutableDictionary()
|
||||
if uniqueID != otherArticle.uniqueID {
|
||||
// This should be super-rare, if ever.
|
||||
if !otherArticle.uniqueID.isEmpty {
|
||||
d[DatabaseKey.uniqueID] = otherArticle.uniqueID
|
||||
}
|
||||
}
|
||||
|
||||
addPossibleStringChangeWithKeyPath(\Article.uniqueID, otherArticle, DatabaseKey.uniqueID, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.title, otherArticle, DatabaseKey.title, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.contentHTML, otherArticle, DatabaseKey.contentHTML, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.contentText, otherArticle, DatabaseKey.contentText, d)
|
||||
|
@ -100,12 +105,12 @@ extension Article {
|
|||
// If updated versions of dates are nil, and we have existing dates, keep the existing dates.
|
||||
// This is data that’s good to have, and it’s likely that a feed removing dates is doing so in error.
|
||||
|
||||
if article.datePublished != otherArticle.datePublished {
|
||||
if datePublished != otherArticle.datePublished {
|
||||
if let updatedDatePublished = otherArticle.datePublished {
|
||||
d[DatabaseKey.datePublished] = updatedDatePublished
|
||||
}
|
||||
}
|
||||
if article.dateModified != otherArticle.dateModified {
|
||||
if dateModified != otherArticle.dateModified {
|
||||
if let updatedDateModified = otherArticle.dateModified {
|
||||
d[DatabaseKey.dateModified] = updatedDateModified
|
||||
}
|
||||
|
@ -113,7 +118,7 @@ extension Article {
|
|||
|
||||
// TODO: accountInfo
|
||||
|
||||
if d.isEmpty {
|
||||
if d.count < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -143,26 +148,6 @@ extension Set where Element == Article {
|
|||
return Set<String>(map { $0.databaseID })
|
||||
}
|
||||
|
||||
func eachHasAStatus() -> Bool {
|
||||
|
||||
for article in self {
|
||||
if article.status == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func missingStatuses() -> Set<Article> {
|
||||
|
||||
return Set<Article>(self.filter { $0.status == nil })
|
||||
}
|
||||
|
||||
func statuses() -> Set<ArticleStatus> {
|
||||
|
||||
return Set<ArticleStatus>(self.flatMap { $0.status })
|
||||
}
|
||||
|
||||
func dictionary() -> [String: Article] {
|
||||
|
||||
var d = [String: Article]()
|
||||
|
|
|
@ -13,32 +13,39 @@ import RSParser
|
|||
|
||||
extension Author {
|
||||
|
||||
init?(authorID: String, row: FMResultSet) {
|
||||
|
||||
let name = row.string(forColumn: DatabaseKey.name)
|
||||
let url = row.string(forColumn: DatabaseKey.url)
|
||||
let avatarURL = row.string(forColumn: DatabaseKey.avatarURL)
|
||||
let emailAddress = row.string(forColumn: DatabaseKey.emailAddress)
|
||||
|
||||
self.init(authorID: authorID, name: name, url: url, avatarURL: avatarURL, emailAddress: emailAddress)
|
||||
}
|
||||
|
||||
init?(parsedAuthor: ParsedAuthor) {
|
||||
|
||||
self.init(authorID: nil, name: parsedAuthor.name, url: parsedAuthor.url, avatarURL: parsedAuthor.avatarURL, emailAddress: parsedAuthor.emailAddress)
|
||||
}
|
||||
|
||||
static func authorsWithParsedAuthors(_ parsedAuthors: [ParsedAuthor]?) -> Set<Author>? {
|
||||
|
||||
assert(!Thread.isMainThread)
|
||||
|
||||
guard let parsedAuthors = parsedAuthors else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let authors = Set(parsedAuthors.flatMap { Author(parsedAuthor: $0) })
|
||||
let authors = Set(parsedAuthors.flatMap { authorWithParsedAuthor($0) })
|
||||
return authors.isEmpty ? nil : authors
|
||||
}
|
||||
|
||||
static func authorWithRow(_ row: FMResultSet) -> Author? {
|
||||
|
||||
guard let authorID = row.string(forColumn: DatabaseKey.authorID) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let cachedAuthor = cachedAuthor(authorID) {
|
||||
return cachedAuthor
|
||||
}
|
||||
|
||||
guard let author = Author(authorID: authorID, row: row) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
cacheAuthor(author)
|
||||
return author
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DatabaseObject
|
||||
|
||||
extension Author: DatabaseObject {
|
||||
|
||||
public var databaseID: String {
|
||||
|
@ -47,3 +54,55 @@ extension Author: DatabaseObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private extension Author {
|
||||
|
||||
init?(authorID: String, row: FMResultSet) {
|
||||
|
||||
let name = row.string(forColumn: DatabaseKey.name)
|
||||
let url = row.string(forColumn: DatabaseKey.url)
|
||||
let avatarURL = row.string(forColumn: DatabaseKey.avatarURL)
|
||||
let emailAddress = row.string(forColumn: DatabaseKey.emailAddress)
|
||||
|
||||
self.init(authorID: authorID, name: name, url: url, avatarURL: avatarURL, emailAddress: emailAddress)
|
||||
}
|
||||
|
||||
init?(parsedAuthor: ParsedAuthor) {
|
||||
|
||||
self.init(authorID: nil, name: parsedAuthor.name, url: parsedAuthor.url, avatarURL: parsedAuthor.avatarURL, emailAddress: parsedAuthor.emailAddress)
|
||||
}
|
||||
|
||||
static func authorWithParsedAuthor(_ parsedAuthor: ParsedAuthor) -> Author? {
|
||||
|
||||
if let author = Author(parsedAuthor: parsedAuthor) {
|
||||
if let authorFromCache = cachedAuthor(author.authorID) {
|
||||
return authorFromCache
|
||||
}
|
||||
cacheAuthor(author)
|
||||
return author
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// The authorCache isn’t because we need uniquing — it’s just to cut down
|
||||
// on the number of Author instances, since they would be frequently duplicated.
|
||||
// (That is, a given feed might have 10 or 20 or whatever of the same Author.)
|
||||
|
||||
private static var authorCache = [String: Author]() //queue-only
|
||||
|
||||
static func cachedAuthor(_ authorID: String) -> Author? {
|
||||
|
||||
assert(!Thread.isMainThread)
|
||||
return authorCache[authorID]
|
||||
}
|
||||
|
||||
static func cacheAuthor(_ author: Author) {
|
||||
|
||||
assert(!Thread.isMainThread)
|
||||
authorCache[author.authorID] = author
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ private final class StatusCache {
|
|||
}
|
||||
}
|
||||
|
||||
subscript(_ articleID: String) -> ArticleStatus {
|
||||
subscript(_ articleID: String) -> ArticleStatus? {
|
||||
get {
|
||||
return self[articleID]
|
||||
}
|
||||
|
|
BIN
ToDo.ooutline
BIN
ToDo.ooutline
Binary file not shown.
Loading…
Reference in New Issue