mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Rename Data.framework to Articles.framework. Rename Database.framework to ArticlesDatabase.framework.
This commit is contained in:
189
Frameworks/ArticlesDatabase/Extensions/Article+Database.swift
Normal file
189
Frameworks/ArticlesDatabase/Extensions/Article+Database.swift
Normal file
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// Article+Database.swift
|
||||
// Database
|
||||
//
|
||||
// Created by Brent Simmons on 7/3/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSDatabase
|
||||
import Articles
|
||||
import RSParser
|
||||
|
||||
extension Article {
|
||||
|
||||
init(databaseArticle: DatabaseArticle, accountID: String, authors: Set<Author>?, attachments: Set<Attachment>?) {
|
||||
|
||||
self.init(accountID: accountID, articleID: databaseArticle.articleID, feedID: databaseArticle.feedID, uniqueID: databaseArticle.uniqueID, title: databaseArticle.title, contentHTML: databaseArticle.contentHTML, contentText: databaseArticle.contentText, url: databaseArticle.url, externalURL: databaseArticle.externalURL, summary: databaseArticle.summary, imageURL: databaseArticle.imageURL, bannerImageURL: databaseArticle.bannerImageURL, datePublished: databaseArticle.datePublished, dateModified: databaseArticle.dateModified, authors: authors, attachments: attachments, status: databaseArticle.status)
|
||||
}
|
||||
|
||||
init(parsedItem: ParsedItem, accountID: String, feedID: String, status: ArticleStatus) {
|
||||
|
||||
let authors = Author.authorsWithParsedAuthors(parsedItem.authors)
|
||||
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 ?? parsedItem.dateModified, dateModified: parsedItem.dateModified, authors: authors, attachments: attachments, status: status)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func changesFrom(_ otherArticle: Article) -> NSDictionary? {
|
||||
|
||||
if self == otherArticle {
|
||||
return nil
|
||||
}
|
||||
|
||||
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.title, otherArticle, DatabaseKey.title, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.contentHTML, otherArticle, DatabaseKey.contentHTML, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.contentText, otherArticle, DatabaseKey.contentText, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.url, otherArticle, DatabaseKey.url, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.externalURL, otherArticle, DatabaseKey.externalURL, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.summary, otherArticle, DatabaseKey.summary, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.imageURL, otherArticle, DatabaseKey.imageURL, d)
|
||||
addPossibleStringChangeWithKeyPath(\Article.bannerImageURL, otherArticle, DatabaseKey.bannerImageURL, d)
|
||||
|
||||
// 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 datePublished != otherArticle.datePublished {
|
||||
if let updatedDatePublished = otherArticle.datePublished {
|
||||
d[DatabaseKey.datePublished] = updatedDatePublished
|
||||
}
|
||||
}
|
||||
if dateModified != otherArticle.dateModified {
|
||||
if let updatedDateModified = otherArticle.dateModified {
|
||||
d[DatabaseKey.dateModified] = updatedDateModified
|
||||
}
|
||||
}
|
||||
|
||||
if d.count < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
static func articlesWithParsedItems(_ parsedItems: Set<ParsedItem>, _ accountID: String, _ feedID: String, _ statusesDictionary: [String: ArticleStatus]) -> Set<Article> {
|
||||
|
||||
return Set(parsedItems.map{ Article(parsedItem: $0, accountID: accountID, feedID: feedID, status: statusesDictionary[$0.articleID]!) })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Article: DatabaseObject {
|
||||
|
||||
public func databaseDictionary() -> NSDictionary? {
|
||||
|
||||
let d = NSMutableDictionary()
|
||||
|
||||
d[DatabaseKey.articleID] = articleID
|
||||
d[DatabaseKey.feedID] = feedID
|
||||
d[DatabaseKey.uniqueID] = uniqueID
|
||||
|
||||
d.addOptionalString(title, DatabaseKey.title)
|
||||
d.addOptionalString(contentHTML, DatabaseKey.contentHTML)
|
||||
d.addOptionalString(contentText, DatabaseKey.contentText)
|
||||
d.addOptionalString(url, DatabaseKey.url)
|
||||
d.addOptionalString(externalURL, DatabaseKey.externalURL)
|
||||
d.addOptionalString(summary, DatabaseKey.summary)
|
||||
d.addOptionalString(imageURL, DatabaseKey.imageURL)
|
||||
d.addOptionalString(bannerImageURL, DatabaseKey.bannerImageURL)
|
||||
|
||||
d.addOptionalDate(datePublished, DatabaseKey.datePublished)
|
||||
d.addOptionalDate(dateModified, DatabaseKey.dateModified)
|
||||
|
||||
return (d.copy() as! NSDictionary)
|
||||
}
|
||||
|
||||
public var databaseID: String {
|
||||
return articleID
|
||||
}
|
||||
|
||||
public func relatedObjectsWithName(_ name: String) -> [DatabaseObject]? {
|
||||
|
||||
switch name {
|
||||
case RelationshipName.authors:
|
||||
return databaseObjectArray(with: authors)
|
||||
case RelationshipName.attachments:
|
||||
return databaseObjectArray(with: attachments)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func databaseObjectArray<T: DatabaseObject>(with objects: Set<T>?) -> [DatabaseObject]? {
|
||||
|
||||
guard let objects = objects else {
|
||||
return nil
|
||||
}
|
||||
return Array(objects)
|
||||
}
|
||||
}
|
||||
|
||||
extension Set where Element == Article {
|
||||
|
||||
func statuses() -> Set<ArticleStatus> {
|
||||
|
||||
return Set<ArticleStatus>(map { $0.status })
|
||||
}
|
||||
|
||||
func dictionary() -> [String: Article] {
|
||||
|
||||
var d = [String: Article]()
|
||||
for article in self {
|
||||
d[article.articleID] = article
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func databaseObjects() -> [DatabaseObject] {
|
||||
|
||||
return self.map{ $0 as DatabaseObject }
|
||||
}
|
||||
|
||||
func databaseDictionaries() -> [NSDictionary]? {
|
||||
|
||||
return self.compactMap { $0.databaseDictionary() }
|
||||
}
|
||||
}
|
||||
|
||||
private extension NSMutableDictionary {
|
||||
|
||||
func addOptionalString(_ value: String?, _ key: String) {
|
||||
|
||||
if let value = value {
|
||||
self[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
func addOptionalStringDefaultingEmpty(_ value: String?, _ key: String) {
|
||||
|
||||
if let value = value {
|
||||
self[key] = value
|
||||
}
|
||||
else {
|
||||
self[key] = ""
|
||||
}
|
||||
}
|
||||
|
||||
func addOptionalDate(_ date: Date?, _ key: String) {
|
||||
|
||||
if let date = date {
|
||||
self[key] = date as NSDate
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// ArticleStatus+Database.swift
|
||||
// Database
|
||||
//
|
||||
// Created by Brent Simmons on 7/3/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSDatabase
|
||||
import Articles
|
||||
|
||||
extension ArticleStatus {
|
||||
|
||||
convenience init(articleID: String, dateArrived: Date, row: FMResultSet) {
|
||||
|
||||
let read = row.bool(forColumn: DatabaseKey.read)
|
||||
let starred = row.bool(forColumn: DatabaseKey.starred)
|
||||
let userDeleted = row.bool(forColumn: DatabaseKey.userDeleted)
|
||||
|
||||
self.init(articleID: articleID, read: read, starred: starred, userDeleted: userDeleted, dateArrived: dateArrived)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ArticleStatus: DatabaseObject {
|
||||
|
||||
public var databaseID: String {
|
||||
return articleID
|
||||
}
|
||||
|
||||
public func databaseDictionary() -> NSDictionary? {
|
||||
|
||||
let d = NSMutableDictionary()
|
||||
|
||||
d[DatabaseKey.articleID] = articleID
|
||||
d[DatabaseKey.read] = read
|
||||
d[DatabaseKey.starred] = starred
|
||||
d[DatabaseKey.userDeleted] = userDeleted
|
||||
d[DatabaseKey.dateArrived] = dateArrived
|
||||
|
||||
return (d.copy() as! NSDictionary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Attachment+Database.swift
|
||||
// Database
|
||||
//
|
||||
// Created by Brent Simmons on 7/4/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Articles
|
||||
import RSDatabase
|
||||
import RSParser
|
||||
|
||||
extension Attachment {
|
||||
|
||||
init?(row: FMResultSet) {
|
||||
|
||||
guard let url = row.string(forColumn: DatabaseKey.url) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let attachmentID = row.string(forColumn: DatabaseKey.attachmentID)
|
||||
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(attachmentID: attachmentID, url: url, mimeType: mimeType, title: title, sizeInBytes: sizeInBytes, durationInSeconds: durationInSeconds)
|
||||
}
|
||||
|
||||
init?(parsedAttachment: ParsedAttachment) {
|
||||
|
||||
self.init(attachmentID: nil, url: parsedAttachment.url, mimeType: parsedAttachment.mimeType, title: parsedAttachment.title, sizeInBytes: parsedAttachment.sizeInBytes, durationInSeconds: parsedAttachment.durationInSeconds)
|
||||
}
|
||||
|
||||
static func attachmentsWithParsedAttachments(_ parsedAttachments: Set<ParsedAttachment>?) -> Set<Attachment>? {
|
||||
|
||||
guard let parsedAttachments = parsedAttachments else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let attachments = parsedAttachments.compactMap{ Attachment(parsedAttachment: $0) }
|
||||
return attachments.isEmpty ? nil : Set(attachments)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
return attachmentID
|
||||
}
|
||||
|
||||
public func databaseDictionary() -> NSDictionary? {
|
||||
|
||||
let d = NSMutableDictionary()
|
||||
|
||||
d[DatabaseKey.attachmentID] = attachmentID
|
||||
d[DatabaseKey.url] = url
|
||||
|
||||
if let mimeType = mimeType {
|
||||
d[DatabaseKey.mimeType] = mimeType
|
||||
}
|
||||
if let title = title {
|
||||
d[DatabaseKey.title] = title
|
||||
}
|
||||
if let sizeInBytes = sizeInBytes {
|
||||
d[DatabaseKey.sizeInBytes] = NSNumber(value: sizeInBytes)
|
||||
}
|
||||
if let durationInSeconds = durationInSeconds {
|
||||
d[DatabaseKey.durationInSeconds] = NSNumber(value: durationInSeconds)
|
||||
}
|
||||
|
||||
return (d.copy() as! NSDictionary)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Set where Element == Attachment {
|
||||
|
||||
func databaseDictionaries() -> [NSDictionary] {
|
||||
|
||||
return self.compactMap { $0.databaseDictionary() }
|
||||
}
|
||||
|
||||
func databaseObjects() -> [DatabaseObject] {
|
||||
|
||||
return self.compactMap { $0 as DatabaseObject }
|
||||
}
|
||||
}
|
||||
73
Frameworks/ArticlesDatabase/Extensions/Author+Database.swift
Normal file
73
Frameworks/ArticlesDatabase/Extensions/Author+Database.swift
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Author+Database.swift
|
||||
// Database
|
||||
//
|
||||
// Created by Brent Simmons on 7/8/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Articles
|
||||
import RSDatabase
|
||||
import RSParser
|
||||
|
||||
// MARK: - DatabaseObject
|
||||
|
||||
extension Author {
|
||||
|
||||
init?(row: FMResultSet) {
|
||||
|
||||
let authorID = row.string(forColumn: DatabaseKey.authorID)
|
||||
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)
|
||||
}
|
||||
|
||||
public static func authorsWithParsedAuthors(_ parsedAuthors: Set<ParsedAuthor>?) -> Set<Author>? {
|
||||
|
||||
guard let parsedAuthors = parsedAuthors else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let authors = Set(parsedAuthors.compactMap { Author(parsedAuthor: $0) })
|
||||
return authors.isEmpty ? nil: authors
|
||||
}
|
||||
}
|
||||
|
||||
extension Author: DatabaseObject {
|
||||
|
||||
public var databaseID: String {
|
||||
return authorID
|
||||
}
|
||||
|
||||
public func databaseDictionary() -> NSDictionary? {
|
||||
|
||||
let d = NSMutableDictionary()
|
||||
|
||||
d[DatabaseKey.authorID] = authorID
|
||||
|
||||
if let name = name {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
18
Frameworks/ArticlesDatabase/Extensions/Feed+Database.swift
Normal file
18
Frameworks/ArticlesDatabase/Extensions/Feed+Database.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Feed+Database.swift
|
||||
// Database
|
||||
//
|
||||
// Created by Brent Simmons on 8/20/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Articles
|
||||
|
||||
extension Set where Element == Feed {
|
||||
|
||||
func feedIDs() -> Set<String> {
|
||||
|
||||
return Set<String>(map { $0.feedID })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// ParsedArticle+Database.swift
|
||||
// Database
|
||||
//
|
||||
// Created by Brent Simmons on 9/18/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSParser
|
||||
import Articles
|
||||
|
||||
extension ParsedItem {
|
||||
|
||||
var articleID: String {
|
||||
if let s = syncServiceID {
|
||||
return s
|
||||
}
|
||||
// Must be same calculation as for Article.
|
||||
return Article.calculatedArticleID(feedID: feedURL, uniqueID: uniqueID)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user