Skip to content

Instantly share code, notes, and snippets.

@andr-ec
Last active November 28, 2020 22:32
Show Gist options
  • Save andr-ec/a16114e44d6cd4b923660f05c7c5bfa6 to your computer and use it in GitHub Desktop.
Save andr-ec/a16114e44d6cd4b923660f05c7c5bfa6 to your computer and use it in GitHub Desktop.
FetchInterceptor
ApolloDiskClient
public class ApolloDiskClient {
    public init() {
        
    }
    func handle<Operation: GraphQLOperation>(operation: Operation) -> Result<Data, Error> {
        if operation.operationType == .query {
            return QueryOperation.init(rawValue: operation.operationName)!.handle()
        } else if operation.operationType == .mutation {
            return MutationOperation.init(rawValue: operation.operationName)!.handle()
        } else {
            return .failure(ParseableError.notYetImplemented)
        }
    }
}

enum MutationOperation: String {
    case save
    func handle() -> Result<Data, Error> {
        switch self {
        case .save:
            return .success(Data())
        }
    }
}

enum QueryOperation: String {
    case GetUserData
    
    func handle() -> Result<Data, Error> {
        switch self {
        case .GetUserData:
            return self.GetUserData()
        }
    }
    
    func GetUserData() -> Result<Data, Error> {
        do {
            let data = try Disk.retrieve(self.rawValue, from: .documents, as: Data.self)
            
            print(data)
            return .success(data)
        } catch {
            let newUser = GetUserDataQuery.Data.init(usersByPk: .init(profile: .init()))
            do {
                let data = try JSONSerializationFormat.serialize(value: ["data": newUser.jsonObject])
                try Disk.save(data, to: .documents, as: self.rawValue)
                return .success(data)
            } catch let error2 {
                return .failure(error2)
            }
        }
    }
}
FetchInterceptor
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()
    }
}
NetworkInterceptorProvider
struct NetworkInterceptorProvider: InterceptorProvider {
    
    // These properties will remain the same throughout the life of the `InterceptorProvider`, even though they
    // will be handed to different interceptors.
    private let store: ApolloStore
    private let client: URLSessionClient
    private let source: Source
    
    init(store: ApolloStore,
         client: URLSessionClient,
         source: Source = .disk
    ) {
        self.store = store
        self.client = client
        self.source = source
    }
    
    func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
        return [
            MaxRetryInterceptor(),
            LegacyCacheReadInterceptor(store: self.store),
            FetchInterceptor(client: client, source: source, diskClient: ApolloDiskClient()),
            AuthenticationInterceptor(),
            ResponseCodeInterceptor(),

            LegacyParsingInterceptor(cacheKeyForObject: self.store.cacheKeyForObject),
            AutomaticPersistedQueryInterceptor(),
            LegacyCacheWriteInterceptor(store: self.store)
        ]
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment