11. Authenticate your operations


In this section you will learn how to add your login authorization token to your operations.

Create the AuthorizationInterceptor

Before you can book a trip, you need to be able to pass your authentication token along to the example server. To do that, let's dig a little deeper into how iOS's ApolloClient works.

The ApolloClient uses something called a NetworkTransport under the hood. By default, the client creates a RequestChainNetworkTransport instance to handle talking over HTTP to your server.

A RequestChain runs your request through an array of ApolloInterceptor objects which can mutate the request and/or check the cache before it hits the network, and then do additional work after a response is received from the network.

The RequestChainNetworkTransport uses an object that conforms to the InterceptorProvider protocol in order to create that array of interceptors for each operation it executes. There are a couple of providers that are set up by default, which return a fairly standard array of interceptors.

The nice thing is that you can also add your own interceptors to the chain anywhere you need to perform custom actions. In this case, you want to have an interceptor that will add your token.

First, create the new interceptor. Go to File > New > File... and create a new Swift File. Name it AuthorizationInterceptor.swift, and make sure it's added to the RocketReserver target. Open that file, and add the following code:

Swift
AuthorizationInterceptor.swift
1import Foundation
2import Apollo
3import ApolloAPI
4
5class AuthorizationInterceptor: ApolloInterceptor {
6    public var id: String = UUID().uuidString
7    
8    func interceptAsync<Operation>(
9        chain: RequestChain,
10        request: HTTPRequest<Operation>,
11        response: HTTPResponse<Operation>?,
12        completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void
13    ) where Operation : GraphQLOperation {
14        // TODO
15    }
16    
17}

Next, import KeychainSwift at the top of the file so you can access the key you stored in the keychain in the last step of the tutorial:

Swift
AuthorizationInterceptor.swift
1import KeychainSwift

Then, replace the TODO within the interceptAsync method with code to get the token from the keychain, and add it to your headers if it exists:

Swift
AuthorizationInterceptor.swift
1let keychain = KeychainSwift()
2if let token = keychain.get(LoginView.loginKeychainKey) {
3    request.addHeader(name: "Authorization", value: token)
4}
5
6chain.proceedAsync(
7    request: request,
8    response: response,
9    interceptor: self,
10    completion: completion)

An array of ApolloInterceptors which are handed off to each request to perform in order is set up by an object conforming to the InterceptorProvider protocol. There's a DefaultInterceptorProvider which has an array with most of the Interceptors you'd want to use.

You can also make your own object conforming to InterceptorProvider - or, in this case, since the interceptor only needs to be added to the beginning of the list to run before all the other interceptors, you can subclass the existing DefaultInterceptorProvider.

Go to File > New > File... and create a new Swift File. Name it NetworkInterceptorProvider.swift, and make sure it's added to the RocketReserver target. Add code which inserts your AuthorizationInterceptor before the other interceptors provided by the DefaultInterceptorProvider:

Swift
NetworkInterceptorProvider.swift
1import Foundation
2import Apollo
3import ApolloAPI
4
5class NetworkInterceptorProvider: DefaultInterceptorProvider {
6    
7    override func interceptors<Operation>(for operation: Operation) -> [ApolloInterceptor] where Operation : GraphQLOperation {
8        var interceptors = super.interceptors(for: operation)
9        interceptors.insert(AuthorizationInterceptor(), at: 0)
10        return interceptors
11    }
12    
13}

Another way to do this would be to copy and paste the list interceptors provided by the DefaultInterceptorProvider (which are all public), and then place your interceptors in the points in the array where you want them. However, since in this case we can run this interceptor first, it's simpler to subclass.

Use the interceptor

Next, go back to your Network class. Replace the ApolloClient with an updated lazy var which creates the RequestChainNetworkTransport manually, using your custom interceptor provider:

Swift
Network.swift
1class Network {
2    
3    static let shared = Network()
4    
5    private(set) lazy var apollo: ApolloClient = {
6        let client = URLSessionClient()
7        let cache = InMemoryNormalizedCache()
8        let store = ApolloStore(cache: cache)
9        let provider = NetworkInterceptorProvider(client: client, store: store)
10        let url = URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/graphql")!
11        let transport = RequestChainNetworkTransport(interceptorProvider: provider, endpointURL: url)
12
13        return ApolloClient(networkTransport: transport, store: store)
14    }()
15    
16}

Now, go back to AuthorizationInterceptor.swift. Click on the line numbers to add a breakpoint at the line where you're instantiating the Keychain:

Swift
AuthorizationInterceptor.swift
1let keychain = KeychainSwift()

Build and run the application. Whenever a network request goes out, that breakpoint should now get hit. If you're logged in, your token will be sent to the server whenever you make a request!

Now that your operations are being authenticated, it's time to define additional mutations to be able to book and cancel trips.

Feedback

Edit on GitHub

Forums