Ensures refresh tokens occurs before syncing. Implements cancelAll for Feedly.

This commit is contained in:
Kiel Gillard 2019-11-07 20:44:51 +11:00
parent 6bef313937
commit 4c9e98c150
1 changed files with 22 additions and 12 deletions

View File

@ -15,6 +15,8 @@ import os.log
final class FeedlyAccountDelegate: AccountDelegate { final class FeedlyAccountDelegate: AccountDelegate {
/// Feedly has a sandbox API and a production API.
/// This property is referred to when clients need to know which environment it should be pointing to.
static var environment: FeedlyAPICaller.API { static var environment: FeedlyAPICaller.API {
#if DEBUG #if DEBUG
// https://developer.feedly.com/v3/developer/ // https://developer.feedly.com/v3/developer/
@ -61,11 +63,18 @@ final class FeedlyAccountDelegate: AccountDelegate {
private let database: SyncDatabase private let database: SyncDatabase
private weak var currentSyncAllOperation: FeedlySyncAllOperation? private weak var currentSyncAllOperation: FeedlySyncAllOperation?
private let operationQueue: OperationQueue
init(dataFolder: String, transport: Transport?, api: FeedlyAPICaller.API) { init(dataFolder: String, transport: Transport?, api: FeedlyAPICaller.API) {
self.operationQueue = OperationQueue()
// Many operations have their own operation queues, such as the sync all operation.
// Making this a serial queue at this higher level of abstraction means we can ensure,
// for example, a `FeedlyRefreshAccessTokenOperation` occurs before a `FeedlySyncAllOperation`,
// improving our ability to debug, reason about and predict the behaviour of the code.
self.operationQueue.maxConcurrentOperationCount = 1
if let transport = transport { if let transport = transport {
caller = FeedlyAPICaller(transport: transport, api: api) self.caller = FeedlyAPICaller(transport: transport, api: api)
} else { } else {
@ -83,7 +92,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
} }
let session = URLSession(configuration: sessionConfiguration) let session = URLSession(configuration: sessionConfiguration)
caller = FeedlyAPICaller(transport: session, api: api) self.caller = FeedlyAPICaller(transport: session, api: api)
} }
let databaseFilePath = (dataFolder as NSString).appendingPathComponent("Sync.sqlite3") let databaseFilePath = (dataFolder as NSString).appendingPathComponent("Sync.sqlite3")
@ -93,7 +102,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
// MARK: Account API // MARK: Account API
func cancelAll(for account: Account) { func cancelAll(for account: Account) {
// TODO: Implement me please operationQueue.cancelAllOperations()
} }
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) { func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
@ -128,7 +137,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
currentSyncAllOperation = operation currentSyncAllOperation = operation
OperationQueue.main.addOperation(operation) operationQueue.addOperation(operation)
} }
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) { func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
@ -139,7 +148,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
completion(.success(())) completion(.success(()))
} }
} }
OperationQueue.main.addOperation(send) operationQueue.addOperation(send)
} }
/// Attempts to ensure local articles have the same status as they do remotely. /// Attempts to ensure local articles have the same status as they do remotely.
@ -159,18 +168,18 @@ final class FeedlyAccountDelegate: AccountDelegate {
let group = DispatchGroup() let group = DispatchGroup()
let getUnread = FeedlySyncUnreadStatusesOperation(account: account, credentials: credentials, service: caller, newerThan: nil, log: log) let syncUnread = FeedlySyncUnreadStatusesOperation(account: account, credentials: credentials, service: caller, newerThan: nil, log: log)
group.enter() group.enter()
getUnread.completionBlock = { syncUnread.completionBlock = {
group.leave() group.leave()
} }
let getStarred = FeedlySyncStarredArticlesOperation(account: account, credentials: credentials, service: caller, log: log) let syncStarred = FeedlySyncStarredArticlesOperation(account: account, credentials: credentials, service: caller, log: log)
group.enter() group.enter()
getStarred.completionBlock = { syncStarred.completionBlock = {
group.leave() group.leave()
} }
@ -178,7 +187,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
completion(.success(())) completion(.success(()))
} }
OperationQueue.main.addOperations([getUnread, getStarred], waitUntilFinished: false) operationQueue.addOperations([syncUnread, syncStarred], waitUntilFinished: false)
} }
func importOPML(for account: Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) { func importOPML(for account: Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
@ -477,10 +486,11 @@ final class FeedlyAccountDelegate: AccountDelegate {
let client = FeedlyAccountDelegate.oauthAuthorizationClient let client = FeedlyAccountDelegate.oauthAuthorizationClient
let refreshAccessToken = FeedlyRefreshAccessTokenOperation(account: account, service: self, oauthClient: client, log: log) let refreshAccessToken = FeedlyRefreshAccessTokenOperation(account: account, service: self, oauthClient: client, log: log)
OperationQueue.main.addOperation(refreshAccessToken) operationQueue.addOperation(refreshAccessToken)
} }
static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, completion: @escaping (Result<Credentials?, Error>) -> Void) { static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, completion: @escaping (Result<Credentials?, Error>) -> Void) {
fatalError() assertionFailure("An `account` instance should enqueue an \(FeedlyRefreshAccessTokenOperation.self) instead.")
completion(.success(credentials))
} }
} }