diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index f3d418c5c..fc020e5ad 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -231,14 +231,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, return delegate.refreshProgress } - private var operationQueue = MainThreadOperationQueue() - private enum OperationName { - static let FetchAllUnreadCounts = "FetchAllUnreadCounts" - static let FetchFeedUnreadCount = "FetchFeedUnreadCount" - static let FetchUnreadCountsForFeeds = "FetchUnreadCountsForFeeds" - } - private static let discardableOperationNames = [OperationName.FetchAllUnreadCounts, OperationName.FetchFeedUnreadCount, OperationName.FetchUnreadCountsForFeeds] - init?(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) { switch type { case .onMyMac: @@ -422,9 +414,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, } public func suspendDatabase() { - operationQueue.suspend() - cancelDiscardableOperations() - database.suspend() + database.cancelAndSuspend() save() metadataFile.suspend() webFeedMetadataFile.suspend() @@ -436,7 +426,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, public func resumeDatabaseAndDelegate() { database.resume() delegate.resume() - operationQueue.resume() } /// Reload OPML, etc. @@ -460,12 +449,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, delegate.accountWillBeDeleted(self) } - public func cancelDiscardableOperations() { - for operationName in Self.discardableOperationNames { - operationQueue.cancelOperations(named: operationName) - } - } - func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) { var feedsToAdd = Set() @@ -1242,43 +1225,34 @@ private extension Account { fetchUnreadCounts(feeds, completion) } else { - fetchAllUnreadCounts() + fetchAllUnreadCounts(completion) } } func fetchUnreadCount(_ feed: WebFeed, _ completion: VoidCompletionBlock?) { - let operation = database.createFetchFeedUnreadCountOperation(feedID: feed.webFeedID) - operation.name = OperationName.FetchFeedUnreadCount - operation.completionBlock = { operation in - let fetchOperation = operation as! FetchFeedUnreadCountOperation - if let unreadCount = fetchOperation.unreadCount { + database.fetchUnreadCount(feed.webFeedID) { result in + if let unreadCount = try? result.get() { feed.unreadCount = unreadCount } completion?() } - - operationQueue.add(operation) } func fetchUnreadCounts(_ feeds: Set, _ completion: VoidCompletionBlock?) { - let feedIDs = feeds.map { $0.webFeedID } - let operation = database.createFetchUnreadCountsForFeedsOperation(feedIDs: Set(feedIDs)) - operation.name = OperationName.FetchUnreadCountsForFeeds - operation.completionBlock = { operation in - let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation - if let unreadCountDictionary = fetchOperation.unreadCountDictionary { + let webFeedIDs = Set(feeds.map { $0.webFeedID }) + database.fetchUnreadCounts(for: webFeedIDs) { result in + if let unreadCountDictionary = try? result.get() { self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds) } completion?() } - - operationQueue.add(operation) } - func fetchAllUnreadCounts() { + func fetchAllUnreadCounts(_ completion: VoidCompletionBlock? = nil) { fetchingAllUnreadCounts = true - database.fetchAllUnreadCounts { (result) in + database.fetchAllUnreadCounts { result in guard let unreadCountDictionary = try? result.get() else { + completion?() return } self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedWebFeeds()) @@ -1290,6 +1264,7 @@ private extension Account { self.isUnreadCountsInitialized = true self.postUnreadCountDidInitializeNotification() } + completion?() } } diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift index 7d7d0fa2d..8b96e1f02 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift @@ -148,7 +148,27 @@ public final class ArticlesDatabase { } operationQueue.add(operation) } - + + /// Fetch unread count for a single feed. + public func fetchUnreadCount(_ webFeedID: String, _ completion: @escaping SingleUnreadCountCompletionBlock) { + let operation = FetchFeedUnreadCountOperation(webFeedID: webFeedID, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) + operation.completionBlock = { operation in + let fetchOperation = operation as! FetchFeedUnreadCountOperation + completion(fetchOperation.result) + } + operationQueue.add(operation) + } + + /// Fetch non-zero unread counts for given webFeedIDs. + public func fetchUnreadCounts(for webFeedIDs: Set, _ completion: @escaping UnreadCountDictionaryCompletionBlock) { + let operation = FetchUnreadCountsForFeedsOperation(webFeedIDs: webFeedIDs, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) + operation.completionBlock = { operation in + let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation + completion(fetchOperation.result) + } + operationQueue.add(operation) + } + public func fetchUnreadCountForToday(for webFeedIDs: Set, completion: @escaping SingleUnreadCountCompletionBlock) { fetchUnreadCount(for: webFeedIDs, since: todayCutoffDate(), completion: completion) } @@ -199,33 +219,25 @@ public final class ArticlesDatabase { articlesTable.createStatusesIfNeeded(articleIDs, completion) } - // MARK: - Operations - - public func cancelOperations() { - operationQueue.cancelAllOperations() - } - - /// Create an operation that fetches the unread count for a single given feedID. - public func createFetchFeedUnreadCountOperation(feedID: String) -> FetchFeedUnreadCountOperation { - return FetchFeedUnreadCountOperation(feedID: feedID, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) - } - - /// Create an operation that fetches unread counts for a number of feedIDs. - public func createFetchUnreadCountsForFeedsOperation(feedIDs: Set) -> FetchUnreadCountsForFeedsOperation { - return FetchUnreadCountsForFeedsOperation(feedIDs: feedIDs, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) - } - // MARK: - Suspend and Resume (for iOS) + /// Cancel current operations and close the database. + public func cancelAndSuspend() { + cancelOperations() + suspend() + } + /// Close the database and stop running database calls. /// Any pending calls will complete first. public func suspend() { + operationQueue.suspend() queue.suspend() } /// Open the database and allow for running database calls again. public func resume() { queue.resume() + operationQueue.resume() } // MARK: - Caches @@ -272,4 +284,10 @@ private extension ArticlesDatabase { // 24 hours previous. This is used by the Today smart feed, which should not actually empty out at midnight. return Date(timeIntervalSinceNow: -(60 * 60 * 24)) // This does not need to be more precise. } + + // MARK: - Operations + + func cancelOperations() { + operationQueue.cancelAllOperations() + } } diff --git a/Frameworks/ArticlesDatabase/Operations/FetchAllUnreadCountsOperation.swift b/Frameworks/ArticlesDatabase/Operations/FetchAllUnreadCountsOperation.swift index 52c3c4ff2..24b81f87e 100644 --- a/Frameworks/ArticlesDatabase/Operations/FetchAllUnreadCountsOperation.swift +++ b/Frameworks/ArticlesDatabase/Operations/FetchAllUnreadCountsOperation.swift @@ -12,7 +12,7 @@ import RSDatabase public final class FetchAllUnreadCountsOperation: MainThreadOperation { - public var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended) + var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended) // MainThreadOperation public var isCanceled = false diff --git a/Frameworks/ArticlesDatabase/Operations/FetchFeedUnreadCountOperation.swift b/Frameworks/ArticlesDatabase/Operations/FetchFeedUnreadCountOperation.swift index 80b8a7cf0..d3b0bd2d1 100644 --- a/Frameworks/ArticlesDatabase/Operations/FetchFeedUnreadCountOperation.swift +++ b/Frameworks/ArticlesDatabase/Operations/FetchFeedUnreadCountOperation.swift @@ -13,8 +13,7 @@ import RSDatabase /// Fetch the unread count for a single feed. public final class FetchFeedUnreadCountOperation: MainThreadOperation { - public var unreadCount: Int? - public let feedID: String + var result: SingleUnreadCountResult = .failure(.isSuspended) // MainThreadOperation public var isCanceled = false @@ -25,9 +24,10 @@ public final class FetchFeedUnreadCountOperation: MainThreadOperation { private let queue: DatabaseQueue private let cutoffDate: Date + private let webFeedID: String - init(feedID: String, databaseQueue: DatabaseQueue, cutoffDate: Date) { - self.feedID = feedID + init(webFeedID: String, databaseQueue: DatabaseQueue, cutoffDate: Date) { + self.webFeedID = webFeedID self.queue = databaseQueue self.cutoffDate = cutoffDate } @@ -54,7 +54,7 @@ private extension FetchFeedUnreadCountOperation { func fetchUnreadCount(_ database: FMDatabase) { let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0 and (starred=1 or dateArrived>?);" - guard let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID, cutoffDate]) else { + guard let resultSet = database.executeQuery(sql, withArgumentsIn: [webFeedID, cutoffDate]) else { informOperationDelegateOfCompletion() return } @@ -64,7 +64,8 @@ private extension FetchFeedUnreadCountOperation { } if resultSet.next() { - unreadCount = resultSet.long(forColumnIndex: 0) + let unreadCount = resultSet.long(forColumnIndex: 0) + result = .success(unreadCount) } resultSet.close() diff --git a/Frameworks/ArticlesDatabase/Operations/FetchUnreadCountsForFeedsOperation.swift b/Frameworks/ArticlesDatabase/Operations/FetchUnreadCountsForFeedsOperation.swift index d3051df68..4b842bab5 100644 --- a/Frameworks/ArticlesDatabase/Operations/FetchUnreadCountsForFeedsOperation.swift +++ b/Frameworks/ArticlesDatabase/Operations/FetchUnreadCountsForFeedsOperation.swift @@ -13,8 +13,7 @@ import RSDatabase /// Fetch the unread counts for a number of feeds. public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation { - public var unreadCountDictionary: UnreadCountDictionary? - public let feedIDs: Set + var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended) // MainThreadOperation public var isCanceled = false @@ -25,9 +24,10 @@ public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation { private let queue: DatabaseQueue private let cutoffDate: Date + private let webFeedIDs: Set - init(feedIDs: Set, databaseQueue: DatabaseQueue, cutoffDate: Date) { - self.feedIDs = feedIDs + init(webFeedIDs: Set, databaseQueue: DatabaseQueue, cutoffDate: Date) { + self.webFeedIDs = webFeedIDs self.queue = databaseQueue self.cutoffDate = cutoffDate } @@ -52,11 +52,11 @@ public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation { private extension FetchUnreadCountsForFeedsOperation { func fetchUnreadCounts(_ database: FMDatabase) { - let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))! + let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(webFeedIDs.count))! let sql = "select distinct feedID, count(*) from articles natural join statuses where feedID in \(placeholders) and read=0 and userDeleted=0 and (starred=1 or dateArrived>?) group by feedID;" var parameters = [Any]() - parameters += Array(feedIDs) as [Any] + parameters += Array(webFeedIDs) as [Any] parameters += [cutoffDate] as [Any] guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else { @@ -64,11 +64,12 @@ private extension FetchUnreadCountsForFeedsOperation { return } if isCanceled { + resultSet.close() informOperationDelegateOfCompletion() return } - var d = UnreadCountDictionary() + var unreadCountDictionary = UnreadCountDictionary() while resultSet.next() { if isCanceled { resultSet.close() @@ -77,12 +78,12 @@ private extension FetchUnreadCountsForFeedsOperation { } let unreadCount = resultSet.long(forColumnIndex: 1) if let webFeedID = resultSet.string(forColumnIndex: 0) { - d[webFeedID] = unreadCount + unreadCountDictionary[webFeedID] = unreadCount } } resultSet.close() - unreadCountDictionary = d + result = .success(unreadCountDictionary) informOperationDelegateOfCompletion() } }