Overview
We're logged into our app, but we haven't put our authorization token to work yet!
In this lesson, we will:
- Introduce Apollo iOS Interceptors
- Implement an
ApolloInterceptor
class - Finalize the logic to include our authorization token with all requests
Introducing Apollo iOS Interceptors
In order to actually book a trip, we need to pass our authentication token along to the server. To understand how we can do that, let's first dig a little deeper into how Apollo iOS's ApolloClient
works.
To send operation to a GraphQL server, ApolloClient
uses a class called RequestChainNetworkTransport
. This class (the default implementation of the NetworkTransport
protocol) handles HTTP communication with the server by generating a RequestChain
object for each request.
A RequestChain
consists of a sequence of interceptors—special objects that can operate upon the request that passes through them. They can mutate the request, check the cache before the request hits the network, and even do additional work after a response is received from the server.
Actually creating the RequestChain
, the array of interceptors, is a job for an InterceptorProvider
. We get a few providers out of the box by default; one, the DefaultInterceptorProvider
, returns a fairly standard array of interceptors.
This default sequence of interceptors represents the "chain" that each request passes through on its way to the GraphQL server. But we can also take matters into our own hands: adding our own interceptors to the chain anywhere we need to perform custom actions. In this case, we want to include an interceptor that adds our token to operations.
Note: To read more about this pattern, see Request Chain Customization in the Apollo docs.
We'll bring this custom logic into our application in a few steps.
- First, we'll create our
AuthorizationInterceptor
. This job of this class will be to add our authorization token (if one is available) to the headers of every request that passes through it. - Next, we'll create a a new
InterceptorProvider
for our app to use. This provider will make sure that our new interceptor is included as part of the request chain that every request passes through. - Then we'll hook up our new
InterceptorProvider
to our app!
1. Create AuthorizationInterceptor
First up: creating the interceptor that will add our authorization token to a request's headers.
Go to File > New > File... and create a new Swift file called AuthorizationInterceptor.swift
. (Make sure it's added to the RocketReserver
target.)
Open that file, and add the following code:
import Foundationimport Apolloimport ApolloAPIclass AuthorizationInterceptor: ApolloInterceptor {public var id: String = UUID().uuidStringfunc interceptAsync<Operation>(chain: RequestChain,request: HTTPRequest<Operation>,response: HTTPResponse<Operation>?,completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) where Operation : GraphQLOperation {// TODO}}
Next, we'll import KeychainSwift
at the top of the file so we can access the key we stored in the keychain in the last step of the tutorial:
import Foundationimport Apolloimport ApolloAPI+ import KeychainSwift
Then, we'll replace the TODO
within the interceptAsync
method with code to get the token from the keychain, and add it to our headers if it exists:
let keychain = KeychainSwift()if let token = keychain.get(LoginView.loginKeychainKey) {request.addHeader(name: "Authorization", value: token)}chain.proceedAsync(request: request,response: response,interceptor: self,completion: completion)
Our app is already configured to use the DefaultInterceptorProvider
: it's the class that creates the RequestChain
consisting of the array of interceptors each request should flow through.
We still want to use the DefaultInterceptorProvider
's request chain (it provides a lot of helpful interceptors, after all!), but we also want to make sure that our new AuthorizationInterceptor
runs before all the other interceptors in the chain. We can achieve this by subclassing the DefaultInterceptorProvider
class, and inserting our new interceptor at the start of the chain.
Create NetworkInterceptorProvider
Go to File > New > File... and create a new Swift file called NetworkInterceptorProvider.swift
. (Make sure it's added to the RocketReserver
target.)
Add the following code to insert the AuthorizationInterceptor
before the other interceptors provided by the DefaultInterceptorProvider
:
import Foundationimport Apolloimport ApolloAPIclass NetworkInterceptorProvider: DefaultInterceptorProvider {override func interceptors<Operation>(for operation: Operation) -> [ApolloInterceptor] where Operation : GraphQLOperation {var interceptors = super.interceptors(for: operation)interceptors.insert(AuthorizationInterceptor(), at: 0)return interceptors}}
Note: 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 our custom interceptors in the points in the array where we want them. But because we just want to place our custom interceptor at the front of the list, it's simpler to subclass.
Use the interceptor
Next, return to the Network
class. Replace the ApolloClient
with an updated lazy var
which creates the RequestChainNetworkTransport
manually, using your custom interceptor provider:
class Network {static let shared = Network()- private(set) lazy var apollo = ApolloClient(url: URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/graphql")!)+ 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://apollo-fullstack-tutorial.herokuapp.com/graphql")!+ let transport = RequestChainNetworkTransport(interceptorProvider: provider, endpointURL: url)++ return ApolloClient(networkTransport: transport, store: store)+ }()}
Great! Now, let's return to AuthorizationInterceptor.swift
. Click on the line numbers to add a breakpoint at the line where we've instantiated the Keychain
:
let keychain = KeychainSwift()
Build and run the application. Whenever a network request goes out, that breakpoint should now get hit. If we're logged in, our token will be sent to the server whenever we make a request!
Now let's make sure that we remove the breakpoint going forward.
Up next
Now that our operations are being authenticated, it's time to define additional mutations to book and cancel trips.
Share your questions and comments about this lesson
This course is currently in
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.