Make progress on getting Database.framework to build.

This commit is contained in:
Brent Simmons 2017-07-12 13:25:10 -07:00
parent f46a3ece48
commit e3b8e6833b
14 changed files with 94 additions and 128 deletions

View File

@ -16,7 +16,7 @@ public struct Attachment: Equatable {
public let sizeInBytes: Int?
public let durationInSeconds: Int?
init(url: String, mimeType: String?, title: String?, sizeInBytes: Int?, durationInSeconds: Int?) {
public init(url: String, mimeType: String?, title: String?, sizeInBytes: Int?, durationInSeconds: Int?) {
self.url = url
self.mimeType = mimeType

View File

@ -0,0 +1,21 @@
//
// AccountInfo.swift
// Database
//
// Created by Brent Simmons on 7/3/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import RSCore
import RSDatabase
// AccountInfo is a plist-compatible dictionary thats stored as a binary plist in the database.
func accountInfoWithRow(_ row: FMResultSet) -> AccountInfo? {
guard let rawAccountInfo = row.data(forColumn: DatabaseKey.accountInfo) else {
return nil
}
return propertyList(withData: rawAccountInfo) as? AccountInfo
}

View File

@ -51,5 +51,10 @@ public struct DatabaseKey {
// Tag
static let tagName = "tagName"
// Author
static let name = "name"
static let avatarURL = "avatarURL"
static let emailAddress = "emailAddress"
}

View File

@ -9,7 +9,7 @@ CREATE TABLE if not EXISTS tags(tagName TEXT NOT NULL, articleID TEXT NOT NULL,
CREATE TABLE if not EXISTS attachments(articleID TEXT NOT NULL, url TEXT NOT NULL, mimeType TEXT, title TEXT, sizeInBytes INTEGER, durationInSeconds INTEGER, PRIMARY KEY(articleID, url));
CREATE INDEX if not EXISTS feedIndex on articles (feedID);
CREATE INDEX if not EXISTS articles_feedID_index on articles (feedID);
CREATE INDEX if not EXISTS tags_tagName_index on tags(tagName COLLATE NOCASE);

View File

@ -222,16 +222,16 @@ private extension Database {
let oneArticleDictionary = oneDictionary.mutableCopy() as! NSMutableDictionary
let articleID = oneArticleDictionary[DatabaseKey.articleID]!
oneArticleDictionary.removeObject(forKey: articleIDKey)
oneArticleDictionary.removeObject(forKey: DatabaseKey.articleID)
let _ = database.rs_updateRows(with: oneArticleDictionary as [NSObject: AnyObject], whereKey: articleIDKey, equalsValue: articleID, tableName: articlesTableName)
let _ = database.rs_updateRows(with: oneArticleDictionary as [NSObject: AnyObject], whereKey: DatabaseKey.articleID, equalsValue: articleID, tableName: DatabaseTableName.articles)
}
}
if !newArticleDictionaries.isEmpty {
for oneNewArticleDictionary in newArticleDictionaries {
let _ = database.rs_insertRow(with: oneNewArticleDictionary as [NSObject: AnyObject], insertType: RSDatabaseInsertOrReplace, tableName: articlesTableName)
let _ = database.rs_insertRow(with: oneNewArticleDictionary as [NSObject: AnyObject], insertType: RSDatabaseInsertOrReplace, tableName: DatabaseTableName.articles)
}
}
}
@ -256,7 +256,7 @@ private extension Database {
var d = [String: AnyObject]()
for oneArticle in articles {
let oneArticleID = (oneArticle as AnyObject).value(forKey: articleIDKey) as! String
let oneArticleID = (oneArticle as AnyObject).value(forKey: DatabaseKey.articleID) as! String
d[oneArticleID] = oneArticle as AnyObject
}
return d

View File

@ -10,7 +10,7 @@
844BEE411F0AB3AB004AB7CD /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 844BEE371F0AB3AA004AB7CD /* Database.framework */; };
844BEE461F0AB3AB004AB7CD /* DatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE451F0AB3AB004AB7CD /* DatabaseTests.swift */; };
845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580661F0AEBCD003CCFA1 /* Constants.swift */; };
845580721F0AEE49003CCFA1 /* PropertyListTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580711F0AEE49003CCFA1 /* PropertyListTransformer.swift */; };
845580721F0AEE49003CCFA1 /* AccountInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580711F0AEE49003CCFA1 /* AccountInfo.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 */; };
@ -113,7 +113,7 @@
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>"; };
845580661F0AEBCD003CCFA1 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
845580711F0AEE49003CCFA1 /* PropertyListTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyListTransformer.swift; sourceTree = "<group>"; };
845580711F0AEE49003CCFA1 /* AccountInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInfo.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>"; };
@ -206,7 +206,7 @@
845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */,
8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */,
84BB4BA31F119D4A00858766 /* Author+Database.swift */,
845580711F0AEE49003CCFA1 /* PropertyListTransformer.swift */,
845580711F0AEE49003CCFA1 /* AccountInfo.swift */,
);
name = Extensions;
sourceTree = "<group>";
@ -462,7 +462,7 @@
845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */,
845580781F0AF678003CCFA1 /* Folder+Database.swift in Sources */,
845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */,
845580721F0AEE49003CCFA1 /* PropertyListTransformer.swift in Sources */,
845580721F0AEE49003CCFA1 /* AccountInfo.swift in Sources */,
8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */,
84BB4BA41F119D4A00858766 /* Author+Database.swift in Sources */,
84BB4BA91F11A32800858766 /* TagsManager.swift in Sources */,

View File

@ -35,9 +35,9 @@ extension Article {
let authors = PropertyListTransformer.authorsWithRow(row)
let tags = PropertyListTransformer.tagsWithRow(row)
let attachments = PropertyListTransformer.attachmentsWithRow(row)
let accountInfo = PropertyListTransformer.accountInfoWithRow(row)
let accountInfo = accountInfoWithRow(row)
self.init(account: account, feedID: feed, 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(account: account, feedID: feed, 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)
}
func databaseDictionary() -> NSDictionary {

View File

@ -27,7 +27,7 @@ extension ArticleStatus {
dateArrived = NSDate.distantPast
}
let accountInfoPlist = PropertyListTransformer.accountInfoWithRow(row)
let accountInfoPlist = accountInfoWithRow(row)
self.init(articleID: articleID!, read: read, starred: starred, userDeleted: userDeleted, dateArrived: dateArrived!, accountInfo: accountInfoPlist)
}

View File

@ -7,22 +7,24 @@
//
import Foundation
import Data
extension Attachment {
convenience init?(databaseDictionary d: [String: Any]) {
init?(databaseDictionary d: [String: Any]) {
guard let url = d[DatabaseKey.url] as? String else {
return nil
}
let mimeType = d[DatabaseKey.mimeType] as? String
let title = d[DatabaseKey.title] as? String
let sizeInBytes = d[DatabaseKey.sizeInBytes] as? Int
let durationInSeconds = d[DatabaseKey.durationInSeconds] as? Int
self.init(url: url, mimeType: mimeType, title: title, durationInSeconds: durationInSeconds)
self.init(url: url, mimeType: mimeType, title: title, sizeInBytes: sizeInBytes, durationInSeconds: durationInSeconds)
}
class func attachments(with plist: [Any]) -> [Attachment]? {
static func attachments(with plist: [Any]) -> [Attachment]? {
return plist.flatMap{ (oneDictionary) -> Attachment? in
if let d = oneDictionary as? [String: Any] {

View File

@ -7,33 +7,18 @@
//
import Foundation
import Data
import RSDatabase
extension Author {
private static let
convenience init?(databaseDictionary d: [String: Any]) {
guard let url = d[DatabaseKey.url] as? String else {
return nil
}
let mimeType = d[DatabaseKey.mimeType] as? String
let title = d[DatabaseKey.title] as? String
let durationInSeconds = d[DatabaseKey.durationInSeconds] as? Int
self.init(url: url, mimeType: mimeType, title: title, durationInSeconds: durationInSeconds)
init?(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(name: name, url: url, avatarURL: avatarURL, emailAddress: emailAddress)
}
class func attachments(with plist: [Any]) -> [Attachment]? {
return plist.flatMap{ (oneDictionary) -> Attachment? in
if let d = oneDictionary as? [String: Any] {
return Attachment(databaseDictionary: d)
}
return nil
}
}
}

View File

@ -1,84 +0,0 @@
//
// AccountInfo.swift
// Database
//
// Created by Brent Simmons on 7/3/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import RSDatabase
import Data
// This allows for serializing structures such as Author, Attachment, and AccountInfo
// without having to create separate tables and lookup tables.
// While there are good strong arguments for using separate tables,
// we decided that the relative simplicity this allows is worth it.
struct PropertyListTransformer {
static func accountInfoWithRow(_ row: FMResultSet) -> AccountInfo? {
guard let rawAccountInfo = row.data(forColumn: DatabaseKey.accountInfo) else {
return nil
}
return propertyList(withData: rawAccountInfo) as? AccountInfo
}
static func tagsWithRow(_ row: FMResultSet) -> [String]? {
guard let d = row.data(forColumn: DatabaseKey.tags) else {
return nil
}
return propertyList(withData: d) as? [String]
}
static func attachmentsWithRow(_ row: FMResultSet) -> [Attachment]? {
guard let d = row.data(forColumn: DatabaseKey.attachments) else {
return nil
}
guard let plist = propertyList(withData: d) as? [Any] else {
return nil
}
return Attachment.attachments(with: plist)
}
static func authorsWithRow(_ row: FMResultSet) -> [Author]? {
guard let d = row.data(forColumn: DatabaseKey.authors) else {
return nil
}
guard let plist = propertyList(withData: d) as? [Any] else {
return nil
}
return Author.authors(with: plist)
}
static func propertyListWithRow(_ row: FMResultSet, column: String) -> Any? {
guard let rawData = row.data(forColumn: column) else {
return nil
}
return propertyList(withData: rawData)
}
static func propertyList(withData data: Data) -> Any? {
do {
return try PropertyListSerialization.propertyList(fromData: rawAccountInfo, options: [], format: nil)
} catch {
return nil
}
}
static func data(withPropertyList plist: Any) -> Data? {
do {
return try PropertyListSerialization.data(from: plist, format: .binary, options: [])
}
catch {
return nil
}
}
}

View File

@ -8,15 +8,18 @@
import Foundation
import RSDatabase
import Data
// Tags  and the non-existence of tags are cached, once fetched, for the lifetime of the run.
// This uses some extra memory but cuts way down on the amount of database time spent
// maintaining the tags table.
typealias TagNameSet = Set<String>
final class TagsManager {
private var articleIDCache = [String: <String>]() // articleID: tag
private var articleIDsWithNoTags = Set<String>()
private var articleIDCache = [String: TagNameSet]() // articleID: tags
private var articleIDsWithNoTags = TagNameSet()
private let queue: RSDatabaseQueue
@ -53,8 +56,6 @@ final class TagsManager {
}
}
typealias TagNameSet = Set<String>
private extension TagsManager {
func cacheTagsForArticle(_ article: Article, tags: TagNameSet) {
@ -183,7 +184,7 @@ private extension TagsManager {
}
}
func fetchTagsForArticleIDs(_ articleIDs: Set<String>, database: FMDatabase) -> TagsTable] {
func fetchTagsForArticleIDs(_ articleIDs: Set<String>, database: FMDatabase) -> TagsTable {
var tagSpecifiers = TagsTable()
@ -193,7 +194,7 @@ private extension TagsManager {
while rs.next() {
guard let oneTagName = rs.string(forColumn: DatabaseKey.tagName), oneArticleID = rs.string(forColumn: DatabaseKey.articleID) else {
guard let oneTagName = rs.string(forColumn: DatabaseKey.tagName), let oneArticleID = rs.string(forColumn: DatabaseKey.articleID) else {
continue
}
if tagSpecifiers[oneArticleID] == nil {

View File

@ -135,6 +135,7 @@
84CFF56A1AC3D1B000CEA6C8 /* RSScaling.m in Sources */ = {isa = PBXBuildFile; fileRef = 84CFF5681AC3D1B000CEA6C8 /* RSScaling.m */; };
84CFF56D1AC3D20A00CEA6C8 /* NSImage+RSCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 84CFF56B1AC3D20A00CEA6C8 /* NSImage+RSCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
84CFF56E1AC3D20A00CEA6C8 /* NSImage+RSCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 84CFF56C1AC3D20A00CEA6C8 /* NSImage+RSCore.m */; };
84F20F831F16BA6200D8E682 /* PropertyList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F20F821F16BA6200D8E682 /* PropertyList.swift */; };
84FE9FC31C00453900081CE9 /* NSStoryboard+RSCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 84FE9FC11C00453900081CE9 /* NSStoryboard+RSCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
84FE9FC41C00453900081CE9 /* NSStoryboard+RSCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FE9FC21C00453900081CE9 /* NSStoryboard+RSCore.m */; };
84FEB4AC1D19D7F4004727E5 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FEB4AB1D19D7F4004727E5 /* Date+Extensions.swift */; };
@ -234,6 +235,7 @@
84CFF5681AC3D1B000CEA6C8 /* RSScaling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSScaling.m; sourceTree = "<group>"; };
84CFF56B1AC3D20A00CEA6C8 /* NSImage+RSCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+RSCore.h"; sourceTree = "<group>"; };
84CFF56C1AC3D20A00CEA6C8 /* NSImage+RSCore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage+RSCore.m"; sourceTree = "<group>"; };
84F20F821F16BA6200D8E682 /* PropertyList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyList.swift; sourceTree = "<group>"; };
84FE9FC11C00453900081CE9 /* NSStoryboard+RSCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSStoryboard+RSCore.h"; sourceTree = "<group>"; };
84FE9FC21C00453900081CE9 /* NSStoryboard+RSCore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSStoryboard+RSCore.m"; sourceTree = "<group>"; };
84FEB4AB1D19D7F4004727E5 /* Date+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
@ -377,6 +379,7 @@
84FEB4AB1D19D7F4004727E5 /* Date+Extensions.swift */,
84BB45421D6909C700B48537 /* NSMutableDictionary-Extensions.swift */,
8414CBA61C95F2EA00333C12 /* Set+Extensions.swift */,
84F20F821F16BA6200D8E682 /* PropertyList.swift */,
);
name = Foundation;
path = RSCore;
@ -686,6 +689,7 @@
8432B1861DACA0E90057D6DF /* NSResponder-Extensions.swift in Sources */,
849B08981BF7BCE30090CEE4 /* NSPasteboard+RSCore.m in Sources */,
842635571D7FA1C800196285 /* NSTableView+Extensions.swift in Sources */,
84F20F831F16BA6200D8E682 /* PropertyList.swift in Sources */,
84CFF5611AC3D0CE00CEA6C8 /* RSBinaryCache.m in Sources */,
84CFF5301AC3CB1900CEA6C8 /* NSDate+RSCore.m in Sources */,
84CFF5281AC3C9A200CEA6C8 /* NSArray+RSCore.m in Sources */,

View File

@ -0,0 +1,32 @@
//
// PropertyList.swift
// RSCore
//
// Created by Brent Simmons on 7/12/17.
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
//
import Foundation
// These functions eat errors.
public func propertyList(withData data: Data) -> Any? {
do {
return try PropertyListSerialization.propertyList(from: data, options: [], format: nil)
} catch {
return nil
}
}
// Create a binary plist.
public func data(withPropertyList plist: Any) -> Data? {
do {
return try PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0)
}
catch {
return nil
}
}