How to add header in Apollo GraphQL : iOS
Asked Answered
V

9

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.

Vannavannatta answered 28/3, 2019 at 10:43 Comment(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?Garter
did you find any solution?Shaikh
Please refer my answer.Vannavannatta
V
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.

Vannavannatta answered 29/4, 2019 at 12:7 Comment(1)
it stops working from version 0.34, there is no HTTPNetworkTransport anymoreDagmar
A
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)
    }
}
Autotomy answered 4/3, 2021 at 5:5 Comment(8)
Thanks! This is the only solution that works with Apollo 0.42.0Fabianfabianism
@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 furtherAlliber
@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 SwiftAlliber
@Alliber 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.Fabianfabianism
@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.Alliber
This worked for 0.51.2 version. Note: Tokens expires. So using singleton won't work. Apollo recommends using UserManagementInterceptor for expiring tokens. github.com/apollographql/apollo-ios/discussions/1535Foran
Can you update it to version 1.0Grimes
I am passing header like the above one but every time I get the response nil. Any solution? I am using pod 0.50.0Glynda
E
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)
    }
}
End answered 11/10, 2020 at 22:30 Comment(1)
What a nightmare API! NetworkInterceptorProvider: LegacyInterceptorProvider!Myelencephalon
V
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.

Vannavannatta answered 29/4, 2019 at 12:7 Comment(1)
it stops working from version 0.34, there is no HTTPNetworkTransport anymoreDagmar
E
6

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))
        )
    }()
}
Enolaenormity answered 3/1, 2020 at 21:39 Comment(0)
N
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/

Neuralgia answered 26/5, 2020 at 9:5 Comment(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.End
@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".Neuralgia
I provided a solution you can have a look at it.End
T
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)")
        }
    }
}
Trackless answered 9/3, 2021 at 11:32 Comment(1)
For me this isn't working I have asked a new question Apollo GraphQL header always gives failure in iOS Swift please check this.Alliber
R
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
}

}

Rosariorosarium answered 18/2, 2022 at 11:38 Comment(0)
J
1

I've been following the tutorial on https://www.apollographql.com/docs/ios/tutorial/tutorial-execute-first-query

and if just adding a custom header is your goal, then the simplest way to do so is

import Apollo
import Foundation

class Network {
    static let shared = Network()

    private(set) lazy var apollo: ApolloClient = {
        let url = URL(string: "https://example.com/v1/graphql")!

        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = ["Authorization": "Bearer token"] // Add your headers here

        let client = URLSessionClient(sessionConfiguration: configuration)
        let store = ApolloStore(cache: InMemoryNormalizedCache())
        let provider = DefaultInterceptorProvider(client: client, store: store)
        let networkTransport = RequestChainNetworkTransport(interceptorProvider: provider, endpointURL: url)

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

hope this can be of help!

Jacinto answered 13/11, 2023 at 21:38 Comment(0)
T
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 ...

Teresitateressa answered 26/10, 2023 at 17:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.