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!
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.