diff --git a/Frameworks/Database/AttachmentsTable.swift b/Frameworks/Database/AttachmentsTable.swift index 8a617e86c..ca13cf7fb 100644 --- a/Frameworks/Database/AttachmentsTable.swift +++ b/Frameworks/Database/AttachmentsTable.swift @@ -14,7 +14,7 @@ final class AttachmentsTable: DatabaseRelatedObjectsTable { let name: String let databaseIDKey = DatabaseKey.attachmentID - var cache = [String: Attachment]() + var cache = DatabaseObjectCache() init(name: String) { @@ -23,39 +23,6 @@ final class AttachmentsTable: DatabaseRelatedObjectsTable { // MARK: DatabaseRelatedObjectsTable - func fetchObjectsWithIDs(_ databaseIDs: Set, in database: FMDatabase) -> [DatabaseObject]? { - - if databaseIDs.isEmpty { - return nil - } - - var cachedAttachments = Set() - var databaseIDsToFetch = Set() - - for attachmentID in databaseIDs { - if let cachedAttachment = cache[attachmentID] { - cachedAttachments.insert(cachedAttachment) - } - else { - databaseIDsToFetch.insert(attachmentID) - } - } - - if databaseIDsToFetch.isEmpty { - return cachedAttachments.databaseObjects() - } - - guard let resultSet = selectRowsWhere(key: databaseIDKey, inValues: Array(databaseIDsToFetch), in: database) else { - return cachedAttachments.databaseObjects() - } - let fetchedDatabaseObjects = objectsWithResultSet(resultSet) - let fetchedAttachments = Set(fetchedDatabaseObjects.map { $0 as Attachment }) - cacheAttachments(fetchedAttachments) - - let allAttachments = cachedAttachments.union(fetchedAttachments) - return allAttachments.databaseObjects() - } - func objectWithRow(_ row: FMResultSet) -> DatabaseObject? { if let attachment = attachmentWithRow(row) { @@ -76,7 +43,7 @@ final class AttachmentsTable: DatabaseRelatedObjectsTable { return true }) - cacheAttachments(attachmentsToSave) + cache.add(attachmentsToSave.databaseObjects()) insertRows(attachmentsToSave.databaseDictionaries(), insertType: .orIgnore, in: database) } @@ -84,13 +51,6 @@ final class AttachmentsTable: DatabaseRelatedObjectsTable { private extension AttachmentsTable { - func cacheAttachments(_ attachments: Set) { - - for attachment in attachments { - cache[attachment.attachmentID] = attachment - } - } - func attachmentWithRow(_ row: FMResultSet) -> Attachment? { // attachmentID is non-null in database schema. diff --git a/Frameworks/Database/AuthorsTable.swift b/Frameworks/Database/AuthorsTable.swift index e867b644e..d7fe83f70 100644 --- a/Frameworks/Database/AuthorsTable.swift +++ b/Frameworks/Database/AuthorsTable.swift @@ -21,14 +21,15 @@ final class AuthorsTable: DatabaseRelatedObjectsTable { let name: String let databaseIDKey = DatabaseKey.authorID + var cache = [String: Author]() init(name: String) { self.name = name } - // MARK: DatabaseTable Methods - + // MARK: DatabaseRelatedObjectsTable + func objectWithRow(_ row: FMResultSet) -> DatabaseObject? { if let author = Author.authorWithRow(row) { @@ -38,7 +39,20 @@ final class AuthorsTable: DatabaseRelatedObjectsTable { } func save(_ objects: [DatabaseObject], in database: FMDatabase) { - // TODO - } + + let attachments = objects.map { $0 as! Author } + + // Authors in cache must already exist in database. Filter them out. + let authorsToSave = Set(attachments.filter { (attachment) -> Bool in + if let _ = cache[attachment.attachmentID] { + return false + } + return true + }) + + cacheAttachments(attachmentsToSave) + + insertRows(attachmentsToSave.databaseDictionaries(), insertType: .orIgnore, in: database) +} } diff --git a/Frameworks/Database/TagsTable.swift b/Frameworks/Database/TagsTable.swift index d224d37b0..428d0342e 100644 --- a/Frameworks/Database/TagsTable.swift +++ b/Frameworks/Database/TagsTable.swift @@ -21,6 +21,8 @@ final class TagsTable: DatabaseRelatedObjectsTable { let name: String let databaseIDKey = DatabaseKey.tagName + let cache = DatabaseObjectCache() // Unused, but protocol requires it + init(name: String) { self.name = name diff --git a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj index a28818ebc..472ed023e 100755 --- a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj +++ b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 8400AC101E0CFD6B00AA7C57 /* RSDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F22C581B52E0D9000060CE /* RSDatabase.h */; settings = {ATTRIBUTES = (Public, ); }; }; 840405DB1F1C158C00DF0296 /* DatabaseTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840405DA1F1C158C00DF0296 /* DatabaseTable.swift */; }; 840405DC1F1C15EA00DF0296 /* DatabaseTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840405DA1F1C158C00DF0296 /* DatabaseTable.swift */; }; + 84314F4A1F68ECC600F710B2 /* DatabaseObjectCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84314F491F68ECC600F710B2 /* DatabaseObjectCache.swift */; }; 84419AD61B5ABD6D00C26BB2 /* FMDatabase+RSExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 84419AD41B5ABD6D00C26BB2 /* FMDatabase+RSExtras.h */; settings = {ATTRIBUTES = (Public, ); }; }; 84419AD71B5ABD6D00C26BB2 /* FMDatabase+RSExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = 84419AD51B5ABD6D00C26BB2 /* FMDatabase+RSExtras.m */; }; 84419ADA1B5ABD7400C26BB2 /* NSString+RSDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 84419AD81B5ABD7400C26BB2 /* NSString+RSDatabase.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -67,6 +68,7 @@ 8400ABFA1E0CFBD800AA7C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8400AC0E1E0CFC5600AA7C57 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; }; 840405DA1F1C158C00DF0296 /* DatabaseTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseTable.swift; sourceTree = ""; }; + 84314F491F68ECC600F710B2 /* DatabaseObjectCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DatabaseObjectCache.swift; path = RSDatabase/DatabaseObjectCache.swift; sourceTree = ""; }; 84419AD41B5ABD6D00C26BB2 /* FMDatabase+RSExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "FMDatabase+RSExtras.h"; path = "RSDatabase/FMDatabase+RSExtras.h"; sourceTree = ""; }; 84419AD51B5ABD6D00C26BB2 /* FMDatabase+RSExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "FMDatabase+RSExtras.m"; path = "RSDatabase/FMDatabase+RSExtras.m"; sourceTree = ""; }; 84419AD81B5ABD7400C26BB2 /* NSString+RSDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+RSDatabase.h"; path = "RSDatabase/NSString+RSDatabase.h"; sourceTree = ""; }; @@ -178,6 +180,7 @@ 84419AD91B5ABD7400C26BB2 /* NSString+RSDatabase.m */, 84C6DD001F395C13009AFB47 /* DatabaseObject.swift */, 840405DA1F1C158C00DF0296 /* DatabaseTable.swift */, + 84314F491F68ECC600F710B2 /* DatabaseObjectCache.swift */, 848E22531F66528C0031D7C5 /* Related Objects */, 84DDF18A1C94FC45005E6CF5 /* FMDB */, 84F22C5A1B52E0D9000060CE /* Info.plist */, @@ -389,6 +392,7 @@ files = ( 84419AE71B5ABD7F00C26BB2 /* RSDatabaseQueue.m in Sources */, 84419AD71B5ABD6D00C26BB2 /* FMDatabase+RSExtras.m in Sources */, + 84314F4A1F68ECC600F710B2 /* DatabaseObjectCache.swift in Sources */, 84C6DD011F395C13009AFB47 /* DatabaseObject.swift in Sources */, 84419ADB1B5ABD7400C26BB2 /* NSString+RSDatabase.m in Sources */, 840405DB1F1C158C00DF0296 /* DatabaseTable.swift in Sources */, diff --git a/Frameworks/RSDatabase/RSDatabase/DatabaseObjectCache.swift b/Frameworks/RSDatabase/RSDatabase/DatabaseObjectCache.swift new file mode 100644 index 000000000..feb400873 --- /dev/null +++ b/Frameworks/RSDatabase/RSDatabase/DatabaseObjectCache.swift @@ -0,0 +1,33 @@ +// +// DatabaseObjectCache.swift +// RSDatabase +// +// Created by Brent Simmons on 9/12/17. +// Copyright © 2017 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +public final class DatabaseObjectCache { + + private var d = [String: DatabaseObject]() + + public init() { + // + } + public func add(_ databaseObjects: [DatabaseObject]) { + + for databaseObject in databaseObjects { + self[databaseObject.databaseID] = databaseObject + } + } + + public subscript(_ databaseID: String) -> DatabaseObject? { + get { + return d[databaseID] + } + set { + d[databaseID] = newValue + } + } +} diff --git a/Frameworks/RSDatabase/Related Objects/DatabaseRelatedObjectsTable.swift b/Frameworks/RSDatabase/Related Objects/DatabaseRelatedObjectsTable.swift index 67c5273a1..31ec1b159 100644 --- a/Frameworks/RSDatabase/Related Objects/DatabaseRelatedObjectsTable.swift +++ b/Frameworks/RSDatabase/Related Objects/DatabaseRelatedObjectsTable.swift @@ -13,7 +13,8 @@ import Foundation public protocol DatabaseRelatedObjectsTable: DatabaseTable { var databaseIDKey: String { get} - + var cache: DatabaseObjectCache { get } + func fetchObjectsWithIDs(_ databaseIDs: Set, in database: FMDatabase) -> [DatabaseObject]? func objectsWithResultSet(_ resultSet: FMResultSet) -> [DatabaseObject] func objectWithRow(_ row: FMResultSet) -> DatabaseObject? @@ -27,10 +28,34 @@ public extension DatabaseRelatedObjectsTable { func fetchObjectsWithIDs(_ databaseIDs: Set, in database: FMDatabase) -> [DatabaseObject]? { - guard let resultSet = selectRowsWhere(key: databaseIDKey, inValues: Array(databaseIDs), in: database) else { + if databaseIDs.isEmpty { return nil } - return objectsWithResultSet(resultSet) + + var cachedObjects = [DatabaseObject]() + var databaseIDsToFetch = Set() + + for databaseID in databaseIDs { + if let cachedObject = cache[databaseID] { + cachedObjects += [cachedObject] + } + else { + databaseIDsToFetch.insert(databaseID) + } + } + + if databaseIDsToFetch.isEmpty { + return cachedObjects + } + + guard let resultSet = selectRowsWhere(key: databaseIDKey, inValues: Array(databaseIDsToFetch), in: database) else { + return cachedObjects + } + + let fetchedDatabaseObjects = objectsWithResultSet(resultSet) + cache.add(fetchedDatabaseObjects) + + return cachedObjects + fetchedDatabaseObjects } func objectsWithResultSet(_ resultSet: FMResultSet) -> [DatabaseObject] {