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 processes your request through a series of specialized interceptor types that handle different aspects of request execution. The RequestChain separates interceptor responsibilities into four discrete types:

  • GraphQLInterceptor - Handles pre-flight and post-flight work on the GraphQLRequest and GraphQLResponse

  • CacheInterceptor - Handles cache read and write operations

  • HTTPInterceptor - Handles pre-flight and post-flight work on HTTP requests and responses

  • ResponseParsingInterceptor - Handles parsing of HTTP response data into GraphQLResponse objects

The RequestChainNetworkTransport uses an object that conforms to the InterceptorProvider protocol to provide these interceptors for each operation it executes. The DefaultInterceptorProvider provides a standard set of interceptors, but you can create your own InterceptorProvider to customize this behavior.

You can also add your own interceptors to perform custom actions. In this case, you'll add an HTTPInterceptor that adds your authentication token to each request.

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
5struct AuthorizationInterceptor: HTTPInterceptor {    
6    func intercept(
7      request: URLRequest,
8      next: NextHTTPInterceptorFunction
9    ) async throws -> HTTPResponse {
10        // TODO
11    }
12}

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
1var request = request
2let keychain = KeychainSwift()
3if let token = await keychain.get(LoginView.loginKeychainKey) {
4    request.addValue(token, forHTTPHeaderField: "Authorization")
5}
6
7return try await next(request)

Create a custom interceptor provider

The InterceptorProvider protocol defines four separate methods that provide different types of interceptors for each operation:

  • graphQLInterceptors(for:) - Returns an array of GraphQLInterceptors for pre-flight and post-flight work

  • cacheInterceptor(for:) - Returns a single CacheInterceptor for cache operations

  • httpInterceptors(for:) - Returns an array of HTTPInterceptors for HTTP request/response handling

  • responseParser(for:) - Returns a single ResponseParsingInterceptor for parsing responses

The DefaultInterceptorProvider uses the protocol's default implementations to provide a standard set of interceptors. To add your custom AuthorizationInterceptor, implement InterceptorProvider and override the httpInterceptors(for:) method to insert your interceptor at the beginning of the interceptor array.

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 that inserts your AuthorizationInterceptor before the other HTTP interceptors provided by the default implementation:

Swift
NetworkInterceptorProvider.swift
1import Foundation
2import Apollo
3import ApolloAPI
4
5struct NetworkInterceptorProvider: InterceptorProvider {
6    
7    func httpInterceptors<Operation: GraphQLOperation>(for operation: Operation) -> [any HTTPInterceptor] {
8        return [AuthorizationInterceptor()] + DefaultInterceptorProvider.shared.httpInterceptors(for: operation)
9    }
10    
11}

Use the interceptor

Next, go back to your ApolloClient+Setup.swift class. Replace the implementation of the static ApolloClient var with the following code that creates and uses your new InterceptorProvider:

Swift
ApolloClient+Setup.swift
1static let shared: ApolloClient = {
2    let cache = InMemoryNormalizedCache()
3    let store = ApolloStore(cache: cache)
4    let url = URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/graphql")!
5    
6    let networkTransport = RequestChainNetworkTransport(
7        urlSession: URLSession.shared,
8        interceptorProvider: NetworkInterceptorProvider(),
9        store: store,
10        endpointURL: url
11    )
12    return ApolloClient(networkTransport: networkTransport, store: store)
13}()

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

Ask Community