How to add header in Apollo GraphQL : iOS

How to add header in Apollo GraphQL : iOS


19

Hy I am working in a project with Apollo GraphQL method and its working fine. But now the client required for adding additional header with Apollo API’s. But after adding the header the API’s response return as unAuthorized.

I am adding the header as,

    let apolloAuth: ApolloClient = {
        let configuration = URLSessionConfiguration.default

        // Add additional headers as needed
        configuration.httpAdditionalHeaders = ["Authorization" : self.singleTonInstance.token]
        configuration.httpAdditionalHeaders = ["channel" : "mobile"]

        let url = URL(string: "https://xxx/graphql")!

        return ApolloClient(networkTransport: HTTPNetworkTransport(url: url, configuration: configuration))

    }()

Anyone please help me to find out how to add headers with Apollo GraphQL.

3

  • Did you try to set the header to "Bearer <token>" instead of "<token>"? What's the authorization method your server is using? Do you have a working cURL statement against the server that uses an authorization token?

    – marktani

    Apr 1, 2019 at 13:49

  • did you find any solution?

    – Arash Etemad

    Apr 29, 2019 at 12:01

  • Please refer my answer.

    – Angel F Syrus

    Apr 29, 2019 at 12:08

8 Answers
8


19

UPDATE: Solution for "Apollo Client v0.41.0" and "Swift 5"

I had the same issue with Apollo Client v0.41.0 and Swift 5.0 but none of the above solutions worked. Finally able to find a solution after the hours of try-out. The below solution is tested with Apollo Client v0.41.0 And Swift 5

import Foundation
import Apollo

class Network {
    static let shared = Network()
    
    private(set) lazy var apollo: ApolloClient = {

        let cache = InMemoryNormalizedCache()
        let store1 = ApolloStore(cache: cache)
        let authPayloads = ["Authorization": "Bearer <<TOKEN>>"]
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = authPayloads
        
        let client1 = URLSessionClient(sessionConfiguration: configuration, callbackQueue: nil)
        let provider = NetworkInterceptorProvider(client: client1, shouldInvalidateClientOnDeinit: true, store: store1)
        
        let url = URL(string: "https://<HOST NAME>/graphql")!
        
        let requestChainTransport = RequestChainNetworkTransport(interceptorProvider: provider,
                                                                 endpointURL: url)
        
        return ApolloClient(networkTransport: requestChainTransport,
                            store: store1)
    }()
}
class NetworkInterceptorProvider: DefaultInterceptorProvider {
    override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
        var interceptors = super.interceptors(for: operation)
        interceptors.insert(CustomInterceptor(), at: 0)
        return interceptors
    }
}

class CustomInterceptor: ApolloInterceptor {
    
    func interceptAsync<Operation: GraphQLOperation>(
        chain: RequestChain,
        request: HTTPRequest<Operation>,
        response: HTTPResponse<Operation>?,
        completion: @escaping (Swift.Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
        request.addHeader(name: "Authorization", value: "Bearer <<TOKEN>>")
        
        print("request :(request)")
        print("response :(String(describing: response))")
        
        chain.proceedAsync(request: request,
                           response: response,
                           completion: completion)
    }
}

7

  • Thanks! This is the only solution that works with Apollo 0.42.0

    – Jochen Holzer

    Apr 22, 2021 at 15:16

  • @Chamath Jeevan I have tried this solution with Apollo 0.43.0 version and it gives me failure reposne error as failure(Apollo.LegacyParsingInterceptor.LegacyParsingError.couldNotParseToLegacyJSON(data: 1839 bytes)) please suggest me what can I do further

    – Princess

    Apr 29, 2021 at 11:01

  • @JochenHolzer I too have tried the same code syntax but the request gives me HTML in failure block. Could you please help me to solve this issue Apollo GraphQL header always gives failure in iOS Swift

    – Princess

    May 3, 2021 at 6:08


  • 1

    @Princess Perhaps there is an error message in the HTML response. If I were you, I would ask the endpoint development team what information about your failed request is in the server log files.

    – Jochen Holzer

    May 3, 2021 at 12:26

  • @JochenHolzer I am using a Shopify GraphQL Admin API to fetch customer's order details from the Shopify store. The same URL request working with Android Apollo Client.

    – Princess

    May 4, 2021 at 4:41


10

As of Apollo Client v0.34.0 and above the code provided earlier won’t work since they rewrote how it used to work. Here is what worked for me… For more information consider going through this documentation in the link here.

class Network {
  static let shared = Network()
  
    private(set) lazy var apollo: ApolloClient = {
        let client = URLSessionClient()
        let cache = InMemoryNormalizedCache()
        let store = ApolloStore(cache: cache)
        let provider = NetworkInterceptorProvider(client: client, store: store)
        let url = URL(string: "https://www.graphqlapi.com/")!
        let transport = RequestChainNetworkTransport(interceptorProvider: provider,
                                                     endpointURL: url)
        return ApolloClient(networkTransport: transport)
    }()
}

class NetworkInterceptorProvider: LegacyInterceptorProvider {
    override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
        var interceptors = super.interceptors(for: operation)
        interceptors.insert(CustomInterceptor(), at: 0)
        return interceptors
    }
}

class CustomInterceptor: ApolloInterceptor {
    // Find a better way to store your token this is just an example
    let token = "YOUR TOKEN"
    
    func interceptAsync<Operation: GraphQLOperation>(
        chain: RequestChain,
        request: HTTPRequest<Operation>,
        response: HTTPResponse<Operation>?,
        completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
        
        request.addHeader(name: "authorization", value: "Bearer: (token)")

        chain.proceedAsync(request: request,
                           response: response,
                           completion: completion)
    }
}

1

  • 1

    What a nightmare API! NetworkInterceptorProvider: LegacyInterceptorProvider!

    – SmileBot

    Mar 1, 2022 at 20:50


9

Finally I found the answer. Add the header in the following way,

 let apolloAuth: ApolloClient = {
        let configuration = URLSessionConfiguration.default

        let token = UserDefaults.standard.value(forKey: "token")
        // Add additional headers as needed
        configuration.httpAdditionalHeaders = ["authorization":"(token!)", "channel":"mobile"]
        let url = URL(string: "https://xxxx/graphql")!

        return ApolloClient(networkTransport: HTTPNetworkTransport(url: url, configuration: configuration))

    }()

Hope it will help someone.

1

  • 5

    it stops working from version 0.34, there is no HTTPNetworkTransport anymore

    – QuangLoc

    Nov 10, 2020 at 9:46


5

The accepted solution is now outdated as the HTTPNetworkTransport no longer takes in a configuration argument, instead accepting a URLSession directly rather than a URLSessionConfiguration

Here’s an example how to send an authorization header on every request to Apollo Client, retrieving it, if found, from UserDefaults in version 0.21.0 of Apollo Client(iOS)

import Foundation
import Apollo

class Network {
    static let shared = Network()

    private(set) lazy var apollo: ApolloClient = {
        let token = UserDefaults.standard.string(forKey: "accessToken") ?? ""
        let url = URL(string: "https://localhost:4000/graphql")!

        let configuration = URLSessionConfiguration.default

        configuration.httpAdditionalHeaders = ["authorization": "Bearer (token)"]

        return ApolloClient(
            networkTransport: HTTPNetworkTransport(url: url, session: URLSession(configuration: configuration))
        )
    }()
}

0


4

UPDATE: This solution is deprecated after "Apollo Client v0.34.0"

The previous answers are old, this is now done through a delegate:

"This protocol allows pre-flight validation of requests, the ability to bail out before modifying the request, and the ability to modify the URLRequest with things like additional headers."

import Foundation
import Apollo

    final class Network {
        static let shared = Network()
        private lazy var networkTransport: HTTPNetworkTransport = {        

        let transport = HTTPNetworkTransport(url: URL(string: "https://example.com/graphql")!)
        transport.delegate = self

        return transport
    }()

    private(set) lazy var apollo = ApolloClient(networkTransport: self.networkTransport)
}

extension Network: HTTPNetworkTransportPreflightDelegate {
    func networkTransport(_ networkTransport: HTTPNetworkTransport, shouldSend request: URLRequest) -> Bool {
        return true
    }

    func networkTransport(_ networkTransport: HTTPNetworkTransport, willSend request: inout URLRequest) {
        var headers = request.allHTTPHeaderFields ?? [String: String]()
        headers["Authorization"] = "Bearer (YOUR_TOKEN)"

        request.allHTTPHeaderFields = headers
    }
}

Here’s a link to the documentation for more details:

https://www.apollographql.com/docs/ios/initialization/

3

  • Am getting this errors Cannot find type 'HTTPNetworkTransport' in scope and Cannot find type 'HTTPNetworkTransportPreflightDelegate' in scope any help? Am using Appoloclient v0.34.1.

    – G B

    Oct 10, 2020 at 1:59


  • @GB I checked out the docs in the link above, and they have deprecated HTPPNetworkTransport. This can still be done using "LegacyInterceptorProvider", or you could look into the new protocol: NetworkTransport".

    – jsonkuan

    Oct 12, 2020 at 9:18


  • I provided a solution you can have a look at it.

    – G B

    Oct 12, 2020 at 14:33


4

Import these two elements

import UIKit
import Apollo

Create a class Network and paste below source code

    struct Network {
    static var request = Network()
    private(set) lazy var apollo: ApolloClient = {

        let cache = InMemoryNormalizedCache()
        let store1 = ApolloStore(cache: cache)
        let authPayloads = ["Authorization": "Bearer <TOKEN>"]
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = authPayloads
        
        let client1 = URLSessionClient(sessionConfiguration: configuration, callbackQueue: nil)
        let provider = NetworkInterceptorProvider(client: client1, shouldInvalidateClientOnDeinit: true, store: store1)
        
        let url = URL(string: "https://xxx/graphql")!
        
        let requestChainTransport = RequestChainNetworkTransport(interceptorProvider: provider,
                                                                 endpointURL: url)
        
        return ApolloClient(networkTransport: requestChainTransport,
                            store: store1)
    }()
}

add NetworkInterceptorProvider in Network class

class NetworkInterceptorProvider: LegacyInterceptorProvider {
override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
    var interceptors = super.interceptors(for: operation)
    interceptors.insert(CustomInterceptor(), at: 0)
    return interceptors
}

}

add CustomInterceptor also in Network class

class CustomInterceptor: ApolloInterceptor {

func interceptAsync<Operation: GraphQLOperation>(
    chain: RequestChain,
    request: HTTPRequest<Operation>,
    response: HTTPResponse<Operation>?,
    completion: @escaping (Swift.Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
    request.addHeader(name: "Authorization", value: "Bearer <TOKEN>")
    
    print("request :(request)")
    print("response :(String(describing: response))")
    
    chain.proceedAsync(request: request,
                       response: response,
                       completion: completion)
}

}

finally call this method from ViewController

func todoQueryCloud(){
    Network.request.apollo.fetch(query: ProgressionsQuery()){result in
        // 3
        switch result {
        case .success(let graphQLResult):
            guard let data = try? result.get().data else { return }
            if graphQLResult.data != nil {
                // 4
                print("Loaded data (String(describing: data.progressions))")
                self.collectionView.reloadData()
            }
            
        case .failure(let error):
            // 5
            print("Error loading data (error)")
        }
    }
}

1


1

NetworkInterceptorProvider: LegacyInterceptorProvider changed as a "DefaultInterceptorProvider"

class NetworkInterceptorProvider: DefaultInterceptorProvider {
override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
    var interceptors = super.interceptors(for: operation)
    interceptors.insert(CustomInterceptor(), at: 0)
    return interceptors
}

}

0


0

Updated solution for Apollo 1.6.1

import Foundation
import Apollo

class DataCoordinator: NSObject {
    // MARK: Variables
    static let shared: DataCoordinator = DataCoordinator()
    private(set) lazy var apolloClient: ApolloClient = {
        let cache = InMemoryNormalizedCache()
        let store = ApolloStore(cache: cache)
        let authPayloads = ["Authorization": "Bearer (token)"]
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = authPayloads
        let client = URLSessionClient(sessionConfiguration: configuration, callbackQueue: nil)
        let provider = NetworkInterceptorProvider(store: store, client: client)
        let url = URL(string: "https://xxxxxxxxx/graphql")!
        let requestChainTransport = RequestChainNetworkTransport(interceptorProvider: provider, endpointURL: url)
        return ApolloClient(networkTransport: requestChainTransport, store: store)
    }()
}

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
    
    init(store: ApolloStore,
         client: URLSessionClient) {
        self.store = store
        self.client = client
    }
    
    func interceptors<Operation>(for operation: Operation) -> [ApolloInterceptor] {
        return [
            MaxRetryInterceptor(),
            CacheReadInterceptor(store: self.store),
            NetworkFetchInterceptor(client: self.client),
            ResponseCodeInterceptor(),
            JSONResponseParsingInterceptor(),
            AutomaticPersistedQueryInterceptor(),
            CacheWriteInterceptor(store: self.store)
        ]
    }
}

Happy Coding …



Leave a Reply

Your email address will not be published. Required fields are marked *