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: "http://xxx/graphql")!

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

    }()

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

Angel F Syrus
  • 1,984
  • 8
  • 23
  • 43

7 Answers7

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)
    }
}
Chamath Jeevan
  • 5,072
  • 1
  • 24
  • 27
  • Thanks! This is the only solution that works with Apollo 0.42.0 – Jochen Holzer Apr 22 '21 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 '21 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](https://stackoverflow.com/questions/67318381/apollo-graphql-header-always-gives-failure-in-ios-swift) – Princess May 03 '21 at 06: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 03 '21 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 04 '21 at 04:41
  • This worked for 0.51.2 version. Note: Tokens expires. So using singleton won't work. Apollo recommends using UserManagementInterceptor for expiring tokens. https://github.com/apollographql/apollo-ios/discussions/1535 – Gamma-Point Apr 19 '22 at 07:26
  • Can you update it to version 1.0 – Bijender Singh Shekhawat May 10 '23 at 08:27
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)
    }
}
G B
  • 2,323
  • 3
  • 18
  • 32
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: "http://xxxx/graphql")!

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

    }()

Hope it will help someone.

Angel F Syrus
  • 1,984
  • 8
  • 23
  • 43
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: "http://localhost:4000/graphql")!

        let configuration = URLSessionConfiguration.default

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

        return ApolloClient(
            networkTransport: HTTPNetworkTransport(url: url, session: URLSession(configuration: configuration))
        )
    }()
}
Dan Barclay
  • 5,827
  • 2
  • 19
  • 22
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/

jsonkuan
  • 191
  • 1
  • 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 '20 at 01: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 '20 at 09:18
  • I provided a solution you can have a look at it. – G B Oct 12 '20 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: "http://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)")
        }
    }
}
Asim
  • 41
  • 3
  • For me this isn't working I have asked a new question [Apollo GraphQL header always gives failure in iOS Swift](https://stackoverflow.com/questions/67318381/apollo-graphql-header-always-gives-failure-in-ios-swift?noredirect=1&lq=1) please check this. – Princess May 03 '21 at 06:05
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
}

}