public enum Source {
case network
case disk
}
/// An interceptor which actually fetches data from the network or from disk
public class FetchInterceptor: ApolloInterceptor, Cancellable {
let client: URLSessionClient
let diskClient: ApolloDiskClient
private var currentTask: URLSessionTask?
var source: Source
/// Designated initializer.
///
/// - Parameter client: The `URLSessionClient` to use to fetch data
public init(client: URLSessionClient, source: Source, diskClient: ApolloDiskClient) {
self.client = client
self.source = source
self.diskClient = diskClient
}
public func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>?,
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
let urlRequest: URLRequest
do {
urlRequest = try request.toURLRequest()
} catch {
chain.handleErrorAsync(error,
request: request,
response: response,
completion: completion)
return
}
guard self.source == .network else {
// TODO use an async closure!
DispatchQueue.global(qos: .userInitiated).async {[weak self] in
guard let self = self else {
return
}
let result = self.diskClient.handle(operation: request.operation)
switch result {
case .success(let data):
let response = HTTPResponse<Operation>(response: HTTPURLResponse(url: urlRequest.url!, statusCode: 200, httpVersion: "2.0", headerFields: nil)!,
rawData: data,
parsedResponse: nil)
chain.proceedAsync(request: request,
response: response,
completion: completion)
case .failure(let error):
chain.handleErrorAsync(error,
request: request,
response: response,
completion: completion)
}
}
return
}
self.currentTask = self.client.sendRequest(urlRequest) { [weak self] result in
guard let self = self else {
return
}
defer {
self.currentTask = nil
}
guard chain.isNotCancelled else {
return
}
switch result {
case .failure(let error):
chain.handleErrorAsync(error,
request: request,
response: response,
completion: completion)
case .success(let (data, httpResponse)):
let response = HTTPResponse<Operation>(response: httpResponse,
rawData: data,
parsedResponse: nil)
chain.proceedAsync(request: request,
response: response,
completion: completion)
}
}
}
public func cancel() {
self.currentTask?.cancel()
}
}