From e246d86ce5e62846250d60838d81c9f487cfccad Mon Sep 17 00:00:00 2001 From: Ellen Shapiro Date: Tue, 28 Jul 2020 15:24:04 -0500 Subject: [PATCH] add ability to specify callback queue and have chain return value as funnel to callback queue --- Sources/Apollo/FinalizingInterceptor.swift | 3 +- .../Apollo/LegacyCacheReadInterceptor.swift | 9 +++-- .../Apollo/LegacyCacheWriteInterceptor.swift | 4 ++- Sources/Apollo/NetworkFetchInterceptor.swift | 5 ++- Sources/Apollo/RequestChain.swift | 35 +++++++++++++++---- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Sources/Apollo/FinalizingInterceptor.swift b/Sources/Apollo/FinalizingInterceptor.swift index d46998ca7a..16c4902fd9 100644 --- a/Sources/Apollo/FinalizingInterceptor.swift +++ b/Sources/Apollo/FinalizingInterceptor.swift @@ -22,6 +22,7 @@ public class FinalizingInterceptor: ApolloInterceptor { return } - completion(.success(parsed)) + chain.returnValueAsync(value: parsed, + completion: completion) } } diff --git a/Sources/Apollo/LegacyCacheReadInterceptor.swift b/Sources/Apollo/LegacyCacheReadInterceptor.swift index 4b07a2a5e2..3b391ec9bf 100644 --- a/Sources/Apollo/LegacyCacheReadInterceptor.swift +++ b/Sources/Apollo/LegacyCacheReadInterceptor.swift @@ -47,7 +47,8 @@ public class LegacyCacheReadInterceptor: ApolloInterceptor { response: response, completion: completion) case .success(let graphQLResult): - completion(.success(graphQLResult as! ParsedValue)) + chain.returnValueAsync(value: graphQLResult as! ParsedValue, + completion: completion) } // In either case, keep going asynchronously @@ -65,7 +66,8 @@ public class LegacyCacheReadInterceptor: ApolloInterceptor { completion: completion) case .success(let graphQLResult): // Cache hit! We're done. - completion(.success(graphQLResult as! ParsedValue)) + chain.returnValueAsync(value: graphQLResult as! ParsedValue, + completion: completion) } } case .returnCacheDataDontFetch: @@ -78,7 +80,8 @@ public class LegacyCacheReadInterceptor: ApolloInterceptor { response: response, completion: completion) case .success(let result): - completion(.success(result as! ParsedValue)) + chain.returnValueAsync(value: result as! ParsedValue, + completion: completion) } } } diff --git a/Sources/Apollo/LegacyCacheWriteInterceptor.swift b/Sources/Apollo/LegacyCacheWriteInterceptor.swift index 2eb01a554a..b4f3d28948 100644 --- a/Sources/Apollo/LegacyCacheWriteInterceptor.swift +++ b/Sources/Apollo/LegacyCacheWriteInterceptor.swift @@ -58,7 +58,9 @@ public class LegacyCacheWriteInterceptor: ApolloInterceptor { preconditionFailure(String(describing: error)) } } - completion(.success(result as! ParsedValue)) + + chain.returnValueAsync(value: result as! ParsedValue, + completion: completion) }.catch { error in chain.handleErrorAsync(error, request: request, diff --git a/Sources/Apollo/NetworkFetchInterceptor.swift b/Sources/Apollo/NetworkFetchInterceptor.swift index b77c526f1d..ba3e563dc2 100644 --- a/Sources/Apollo/NetworkFetchInterceptor.swift +++ b/Sources/Apollo/NetworkFetchInterceptor.swift @@ -22,7 +22,10 @@ public class NetworkFetchInterceptor: ApolloInterceptor, Cancellable { do { urlRequest = try request.toURLRequest() } catch { - completion(.failure(error)) + chain.handleErrorAsync(error, + request: request, + response: response, + completion: completion) return } diff --git a/Sources/Apollo/RequestChain.swift b/Sources/Apollo/RequestChain.swift index fa6a6d3b36..9e3e30160c 100644 --- a/Sources/Apollo/RequestChain.swift +++ b/Sources/Apollo/RequestChain.swift @@ -1,4 +1,7 @@ import Foundation +#if !COCOAPODS +import ApolloCore +#endif public class RequestChain: Cancellable { @@ -9,11 +12,12 @@ public class RequestChain: Cancellable { private let interceptors: [ApolloInterceptor] private var currentIndex: Int - public private(set) var isCancelled: Bool = false + public private(set) var callbackQueue: DispatchQueue + public private(set) var isCancelled = Atomic(false) /// Helper var for readability in guard statements public var isNotCancelled: Bool { - !self.isCancelled + !self.isCancelled.value } /// Something which allows additional error handling to occur when some kind of error has happened. @@ -21,9 +25,13 @@ public class RequestChain: Cancellable { /// Creates a chain with the given interceptor array. /// - /// - Parameter interceptors: The interceptors to use. - public init(interceptors: [ApolloInterceptor]) { + /// - Parameters: + /// - interceptors: The array of interceptors to use. + /// - callbackQueue: The `DispatchQueue` to call back on when an error or result occurs. Defauls to `.main`. + public init(interceptors: [ApolloInterceptor], + callbackQueue: DispatchQueue = .main) { self.interceptors = interceptors + self.callbackQueue = callbackQueue self.currentIndex = 0 } @@ -86,7 +94,7 @@ public class RequestChain: Cancellable { /// Cancels the entire chain of interceptors. public func cancel() { - self.isCancelled = true + self.isCancelled.value = true // If an interceptor adheres to `Cancellable`, it should have its in-flight work cancelled as well. for interceptor in self.interceptors { @@ -132,7 +140,9 @@ public class RequestChain: Cancellable { } guard let additionalHandler = self.additionalErrorHandler else { - completion(.failure(error)) + self.callbackQueue.async { + completion(.failure(error)) + } return } @@ -143,4 +153,17 @@ public class RequestChain: Cancellable { response: response, completion: completion) } + + public func returnValueAsync( + value: ParsedValue, + completion: @escaping (Result) -> Void) { + + guard self.isNotCancelled else { + return + } + + self.callbackQueue.async { + completion(.success(value)) + } + } }