Search code examples
swiftapollo

Apollo GraphQL iOS: query function is not sent


I'm exploring GraphQL (which is new for me). I want to make an authenticated call but for some reason, it does not seem to be sent: Proxyman shows nothing and I don't receive an error.

I followed the official tutorial and copied/adapted much of the code.

Relevant code:

public final class Network {

    public static let shared = Network()

    public static func authenticatedApollo(_ authenticationData: GraphQLAuthenticationData) -> ApolloClient {
        let client = URLSessionClient()
        let cache = InMemoryNormalizedCache()
        let store = ApolloStore(cache: cache)
        let provider = NetworkAuthenticatedInterceptorProvider(client: client, store: store, authenticationData: authenticationData)
        let url = URL(string: "my instance url")!
        let transport = RequestChainNetworkTransport(interceptorProvider: provider, endpointURL: url)

        return ApolloClient(networkTransport: transport, store: store)
    }
}

private class AuthorizationInterceptor: ApolloInterceptor {
    public var id: String

    private let authenticationData: GraphQLAuthenticationData

    init(id: String = UUID().uuidString, authenticationData: GraphQLAuthenticationData) {
        self.id = id
        self.authenticationData = authenticationData
    }

    func interceptAsync<Operation>(chain: RequestChain,
                                   request: HTTPRequest<Operation>,
                                   response: HTTPResponse<Operation>?,
                                   completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void)
    where Operation: GraphQLOperation {
        request.addHeader(name: "Authorization", value: authenticationData.token)
        // adding other headers
        chain.proceedAsync(request: request, response: response, interceptor: self, completion: completion)
    }
}

private class NetworkAuthenticatedInterceptorProvider: DefaultInterceptorProvider {

    private let authenticationData: GraphQLAuthenticationData

    init(client: URLSessionClient = URLSessionClient(), shouldInvalidateClientOnDeinit: Bool = true, store: ApolloStore, authenticationData: GraphQLAuthenticationData) {
        self.authenticationData = authenticationData
        super.init(client: client, shouldInvalidateClientOnDeinit: shouldInvalidateClientOnDeinit, store: store)
    }

    override func interceptors<Operation>(for operation: Operation) -> [ApolloInterceptor] where Operation: GraphQLOperation {
        var interceptors = super.interceptors(for: operation)
        let interceptor = AuthorizationInterceptor(authenticationData: authenticationData)
        interceptors.insert(interceptor, at: 0)
        return interceptors
    }
}

When executing a query, I don't receive either success or failure:

Network.authenticatedApollo(authenticationData).fetch(query: DemoAuthenticatedCallQuery(), cachePolicy: .fetchIgnoringCacheData) {result in
    switch result {
    case .success(let data):
        print("🛸 authenticated data: <...>)")
    case .failure(let error):
        print("🛸 authenticated error: \(error.localizedDescription)")
    }
}

But if I add breakpoints, I see my interceptor is added to the list of the NetworkChain. I also validated that the code generation seemed to work well.

Any relevant tip is welcomed!


Solution

  • I figured it out. The issue with the approach above is one of memory management...

    Simply returning an ApolloClient from the function is not enough to adequately retain the client, it goes out of scope before the request is sent. To be clear, the scope of the Apollo client in my example is the function making the call.

    Creating a property in a struct (like private var apollo: ApolloClient?) corrects this and the client is retained in memory for the whole duration of the request.

    Hope this helps someone.