Change to make sure all queue's get cleared before suspending the database. Issue #1389
This commit is contained in:
parent
77cf9a0036
commit
f159371967
|
@ -406,7 +406,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
}
|
||||
|
||||
public func suspend() {
|
||||
delegate.cancelAll(for: self)
|
||||
delegate.suspend()
|
||||
database.suspend()
|
||||
save()
|
||||
|
|
|
@ -22,7 +22,6 @@ protocol AccountDelegate {
|
|||
|
||||
var refreshProgress: DownloadProgress { get }
|
||||
|
||||
func cancelAll(for account: Account)
|
||||
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void))
|
||||
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void))
|
||||
|
|
|
@ -32,6 +32,7 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
private let feedbinBaseURL = URL(string: "https://api.feedbin.com/v2/")!
|
||||
private var transport: Transport!
|
||||
private var suspended = false
|
||||
|
||||
var credentials: Credentials?
|
||||
weak var accountMetadata: AccountMetadata?
|
||||
|
@ -41,8 +42,14 @@ final class FeedbinAPICaller: NSObject {
|
|||
self.transport = transport
|
||||
}
|
||||
|
||||
func cancelAll() {
|
||||
/// Cancels all pending requests rejects any that come in later
|
||||
func suspend() {
|
||||
transport.cancelAll()
|
||||
suspended = true
|
||||
}
|
||||
|
||||
func resume() {
|
||||
suspended = false
|
||||
}
|
||||
|
||||
func validateCredentials(completion: @escaping (Result<Credentials?, Error>) -> Void) {
|
||||
|
@ -51,6 +58,12 @@ final class FeedbinAPICaller: NSObject {
|
|||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
|
||||
transport.send(request: request) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(self.credentials))
|
||||
|
@ -78,6 +91,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: opmlData) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (_, data)):
|
||||
|
||||
|
@ -108,6 +126,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: FeedbinImportResult.self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (_, importResult)):
|
||||
completion(.success(importResult))
|
||||
|
@ -127,6 +150,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: [FeedbinTag].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, tags)):
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.tags, headers: response.allHeaderFields)
|
||||
|
@ -143,7 +171,20 @@ final class FeedbinAPICaller: NSObject {
|
|||
let callURL = feedbinBaseURL.appendingPathComponent("tags.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinRenameTag(oldName: oldName, newName: newName)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion)
|
||||
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveSubscriptions(completion: @escaping (Result<[FeedbinSubscription]?, Error>) -> Void) {
|
||||
|
@ -156,6 +197,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: [FeedbinSubscription].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, subscriptions)):
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.subscriptions, headers: response.allHeaderFields)
|
||||
|
@ -186,6 +232,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, data)):
|
||||
|
||||
|
@ -246,13 +297,38 @@ final class FeedbinAPICaller: NSObject {
|
|||
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(subscriptionID)/update.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinUpdateSubscription(title: newName)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion)
|
||||
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deleteSubscription(subscriptionID: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(subscriptionID).json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
transport.send(request: request, method: HTTPMethod.delete, completion: completion)
|
||||
transport.send(request: request, method: HTTPMethod.delete) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveTaggings(completion: @escaping (Result<[FeedbinTagging]?, Error>) -> Void) {
|
||||
|
@ -262,7 +338,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
|
||||
transport.send(request: request, resultType: [FeedbinTagging].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, taggings)):
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.taggings, headers: response.allHeaderFields)
|
||||
|
@ -291,6 +371,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, method: HTTPMethod.post, payload:payload) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, _)):
|
||||
if let taggingLocation = response.valueForHTTPHeaderField(HTTPResponseHeader.location),
|
||||
|
@ -313,7 +398,19 @@ final class FeedbinAPICaller: NSObject {
|
|||
let callURL = feedbinBaseURL.appendingPathComponent("taggings/\(taggingID).json")
|
||||
var request = URLRequest(url: callURL, credentials: credentials)
|
||||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
||||
transport.send(request: request, method: HTTPMethod.delete, completion: completion)
|
||||
transport.send(request: request, method: HTTPMethod.delete) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveEntries(articleIDs: [String], completion: @escaping (Result<([FeedbinEntry]?), Error>) -> Void) {
|
||||
|
@ -336,6 +433,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: [FeedbinEntry].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (_, entries)):
|
||||
completion(.success((entries)))
|
||||
|
@ -363,6 +465,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: [FeedbinEntry].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, entries)):
|
||||
|
||||
|
@ -399,6 +506,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: [FeedbinEntry].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, entries)):
|
||||
|
||||
|
@ -427,6 +539,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: [FeedbinEntry].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, entries)):
|
||||
|
||||
|
@ -449,6 +566,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
transport.send(request: request, resultType: [Int].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, unreadEntries)):
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.unreadEntries, headers: response.allHeaderFields)
|
||||
|
@ -465,14 +587,38 @@ final class FeedbinAPICaller: NSObject {
|
|||
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinUnreadEntry(unreadEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deleteUnreadEntries(entries: [Int], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinUnreadEntry(unreadEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.delete, payload: payload, completion: completion)
|
||||
transport.send(request: request, method: HTTPMethod.delete, payload: payload) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveStarredEntries(completion: @escaping (Result<[Int]?, Error>) -> Void) {
|
||||
|
@ -482,7 +628,11 @@ final class FeedbinAPICaller: NSObject {
|
|||
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
|
||||
transport.send(request: request, resultType: [Int].self) { result in
|
||||
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let (response, starredEntries)):
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.starredEntries, headers: response.allHeaderFields)
|
||||
|
@ -499,14 +649,38 @@ final class FeedbinAPICaller: NSObject {
|
|||
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinStarredEntry(starredEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deleteStarredEntries(entries: [Int], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinStarredEntry(starredEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.delete, payload: payload, completion: completion)
|
||||
transport.send(request: request, method: HTTPMethod.delete, payload: payload) { result in
|
||||
if self.suspended {
|
||||
completion(.failure(TransportError.suspended))
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -73,10 +73,6 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
|
||||
var refreshProgress = DownloadProgress(numberOfTasks: 0)
|
||||
|
||||
func cancelAll(for account: Account) {
|
||||
caller.cancelAll()
|
||||
}
|
||||
|
||||
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||
|
@ -560,11 +556,13 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
|
||||
/// Suspend the sync database so that it can close its SQLite file.
|
||||
func suspend() {
|
||||
caller.suspend()
|
||||
database.suspend()
|
||||
}
|
||||
|
||||
/// Resume the sync database — let it reopen its SQLite file.
|
||||
func resume() {
|
||||
caller.resume()
|
||||
database.resume()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,10 +97,6 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||
|
||||
// MARK: Account API
|
||||
|
||||
func cancelAll(for account: Account) {
|
||||
operationQueue.cancelAllOperations()
|
||||
}
|
||||
|
||||
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
assert(Thread.isMainThread)
|
||||
|
||||
|
@ -515,6 +511,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||
|
||||
/// Suspend the sync database so that it can close its SQLite file.
|
||||
func suspend() {
|
||||
operationQueue.cancelAllOperations()
|
||||
database.suspend()
|
||||
}
|
||||
|
||||
|
|
|
@ -79,10 +79,6 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
|
||||
var refreshProgress = DownloadProgress(numberOfTasks: 0)
|
||||
|
||||
func cancelAll(for account: Account) {
|
||||
caller.cancelAll()
|
||||
}
|
||||
|
||||
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(6)
|
||||
|
@ -441,6 +437,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
|
||||
/// Suspend the sync database so that it can close its SQLite file.
|
||||
func suspend() {
|
||||
caller.cancelAll()
|
||||
database.suspend()
|
||||
}
|
||||
|
||||
|
|
|
@ -46,12 +46,6 @@ final class FetchRequestOperation {
|
|||
}
|
||||
}
|
||||
|
||||
// The account manager may have been suspended while we were queued up
|
||||
if AccountManager.shared.isSuspended {
|
||||
callCompletionIfNeeded()
|
||||
return
|
||||
}
|
||||
|
||||
if isCanceled {
|
||||
callCompletionIfNeeded()
|
||||
return
|
||||
|
|
|
@ -229,6 +229,7 @@ private extension AppDelegate {
|
|||
}
|
||||
|
||||
// MARK: Go To Background
|
||||
|
||||
private extension AppDelegate {
|
||||
|
||||
func waitForSyncTasksToFinish() {
|
||||
|
@ -269,7 +270,7 @@ private extension AppDelegate {
|
|||
|
||||
func completeProcessing(_ suspend: Bool) {
|
||||
if suspend {
|
||||
AccountManager.shared.suspendAll()
|
||||
suspendApplication()
|
||||
}
|
||||
UIApplication.shared.endBackgroundTask(self.waitBackgroundUpdateTask)
|
||||
self.waitBackgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
|
||||
|
@ -299,6 +300,16 @@ private extension AppDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func suspendApplication() {
|
||||
CoalescingQueue.standard.performCallsImmediately()
|
||||
for scene in UIApplication.shared.connectedScenes {
|
||||
if let sceneDelegate = scene.delegate as? SceneDelegate {
|
||||
sceneDelegate.suspend()
|
||||
}
|
||||
}
|
||||
AccountManager.shared.suspendAll()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Background Tasks
|
||||
|
@ -342,9 +353,9 @@ private extension AppDelegate {
|
|||
if AccountManager.shared.isSuspended {
|
||||
AccountManager.shared.resumeAll()
|
||||
}
|
||||
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log) {
|
||||
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log) { [unowned self] in
|
||||
AccountManager.shared.saveAll()
|
||||
AccountManager.shared.suspendAll()
|
||||
self.suspendApplication()
|
||||
os_log("Account refresh operation completed.", log: self.log, type: .info)
|
||||
task?.setTaskCompleted(success: true)
|
||||
}
|
||||
|
|
|
@ -535,6 +535,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||
|
||||
// MARK: API
|
||||
|
||||
func suspend() {
|
||||
fetchAndMergeArticlesQueue.performCallsImmediately()
|
||||
fetchRequestQueue.cancelAllRequests()
|
||||
}
|
||||
|
||||
func shadowNodesFor(section: Int) -> [Node] {
|
||||
return shadowTable[section]
|
||||
}
|
||||
|
|
|
@ -73,6 +73,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
coordinator.handle(response)
|
||||
}
|
||||
|
||||
func suspend() {
|
||||
coordinator.suspend()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension SceneDelegate {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b2ef3e111339ba9dd9070ed0ded8e053135559cb
|
||||
Subproject commit 0631696e7383368664a1fe47c58ebcd0ee2d49f8
|
Loading…
Reference in New Issue