Skip to content

Commit 385be42

Browse files
update tutorial for request chain network transport
1 parent 620d139 commit 385be42

5 files changed

Lines changed: 64 additions & 41 deletions

File tree

File renamed without changes.
124 KB
Loading
Binary file not shown.
-70.2 KB
Binary file not shown.

docs/source/tutorial/tutorial-mutations.md

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,95 @@ In this section, you'll learn how to build authenticated mutations and handle in
88

99
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 Apollo Client works.
1010

11-
The `ApolloClient` uses something called a `NetworkTransport` under the hood. By default, the client creates an `HTTPNetworkTransport` instance to handle talking over HTTP to your server.
11+
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.
1212

13-
If you need to do anything before a request hits the wire but after Apollo has done most of the configuration for you, there's a delegate protocol called `HTTPNetworkTransportPreflightDelegate` that allows you to do that.
13+
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.
1414

15-
Open `Network.swift` and add an extension to conform to that delegate:
15+
The `RequestChainNetworkTransport` uses an object that conforms to the `InterceptorProivder` 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.
1616

17-
```swift:title=Network.swift
18-
extension Network: HTTPNetworkTransportPreflightDelegate {
17+
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
1918

20-
}
21-
```
19+
First, create the new interceptor. Go to **File > New > File...** and create a new **Swift File**. Name it **TokenAddingInterceptor.swift**. Open that file, and add the
2220

23-
You'll get an error telling you that protocol stubs must be implemented, and asking you if you want to fix this. Click **Fix**.
21+
```swift:title=TokenAddingInterceptor.swift
22+
import Foundation
23+
import Apollo
2424

25-
<img src="images/preflight_delegate_add_protocol_stubs.png" class="screenshot" alt="Do you wish to add protocol stubs with fix button"/>
25+
class TokenAddingInterceptor: ApolloInterceptor {
26+
func interceptAsync<Operation: GraphQLOperation>(
27+
chain: RequestChain,
28+
request: HTTPRequest<Operation>,
29+
response: HTTPResponse<Operation>?,
30+
completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
31+
32+
// TODO
33+
}
34+
}
35+
```
2636

27-
Two protocol methods will be added: `networkTransport(_:shouldSend:)` and `networkTransport(_:willSend:)`.
37+
Next, import `KeychainSwift` at the top of the file so you can access the key you've just stored in the keychain.
2838

29-
The `shouldSend` method enables you to make sure a request should go out to the network at all. This is useful for things like checking that your user is logged in before trying to make a request.
39+
```swift:title=TokenAddingInterceptor.swift
40+
import KeychainSwift
41+
```
3042

31-
However, you're not going to use that functionality in this application. Update the method to have it return `true` all the time:
43+
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:
3244

33-
```swift:title=Network.swift
34-
func networkTransport(_ networkTransport: HTTPNetworkTransport,
35-
shouldSend request: URLRequest) -> Bool {
36-
return true
37-
}
45+
```swift:title=TokenAddingInterceptor.swift
46+
let keychain = KeychainSwift()
47+
if let token = keychain.get(LoginViewController.loginKeychainKey) {
48+
request.addHeader(name: "Authorization", value: token)
49+
} // else do nothing
50+
51+
chain.proceedAsync(request: request,
52+
response: response,
53+
completion: completion)
3854
```
3955

40-
The `willSend` request is the last thing that can manipulate the request before it goes out to the network. Because the request is passed as an `inout` variable, you can manipulate its contents directly.
41-
42-
Update the `willSend` method to add your token as the value for the `Authorization` header:
56+
Next, since you're only adding one interceptor that can run at the very beginning of other interceptors, you can subclass the existing `LegacyInterceptorProvider` (which is the default interceptor provider).
4357

44-
```swift:title=Network.swift
45-
func networkTransport(_ networkTransport: HTTPNetworkTransport,
46-
willSend request: inout URLRequest) {
47-
let keychain = KeychainSwift()
48-
if let token = keychain.get(LoginViewController.loginKeychainKey) {
49-
request.addValue(token, forHTTPHeaderField: "Authorization")
50-
} // else do nothing
51-
}
52-
```
58+
Go to **File > New > File...** and create a new **Swift File**. Name it **NetworkInterceptorProvider.swift**. Add an initial Add code which inserts your `TokenAddingInterceptor` before the other interceptors provided by the `LegacyInterceptorProvider`:
5359

54-
Then, import `KeychainSwift` at the top of the file:
60+
```swift:title=NetworkInterceptorProvider.swift
61+
import Foundation
62+
import Apollo
5563

56-
```swift:title=Network.swift
57-
import KeychainSwift
64+
class NetworkInterceptorProvider: LegacyInterceptorProvider {
65+
override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
66+
var interceptors = super.interceptors(for: operation)
67+
interceptors.insert(TokenAddingInterceptor(), at: 0)
68+
return interceptors
69+
}
70+
}
5871
```
5972

60-
Next, you need to make sure that Apollo knows that this delegate exists. To do that, you need to do something that Apollo Client has thus far been doing for you under the hood: instantiating the `HTTPNetworkTransport`.
73+
> Another way to do this would be to copy the interceptors provided by the `LegacyInterceptorProvider` (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 just as simple to subclass.
6174
62-
In the primary declaration of `Network`, update your `lazy var` to create this transport and set the `Network` object as its delegate, then pass it through to the `ApolloClient`:
75+
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:
6376

6477
```swift:title=Network.swift
65-
private(set) lazy var apollo: ApolloClient = {
66-
let httpNetworkTransport = HTTPNetworkTransport(url: URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/")!)
67-
httpNetworkTransport.delegate = self
68-
return ApolloClient(networkTransport: httpNetworkTransport)
69-
}()
78+
class Network {
79+
static let shared = Network()
80+
81+
private(set) lazy var apollo: ApolloClient = {
82+
let client = URLSessionClient()
83+
let cache = InMemoryNormalizedCache()
84+
let store = ApolloStore(cache: cache)
85+
let provider = NetworkInterceptorProvider(client: client, store: store)
86+
let url = URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/")!
87+
let transport = RequestChainNetworkTransport(interceptorProvider: provider,
88+
endpointURL: url)
89+
return ApolloClient(networkTransport: transport)
90+
}()
91+
}
7092
```
7193

94+
Now, go back to **TokenAddingInterceptor.swift**.
7295
Click on the line numbers to add a breakpoint at the line where you're instantiating the `Keychain`:
7396

74-
<img alt="adding a breakpoint" class="screenshot" src="images/preflight_delegate_breakpoint.png"/>
97+
<img alt="adding a breakpoint" class="screenshot" src="images/interceptor_breakpoint.png"/>
7598

76-
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.
99+
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!
77100

78101
## Add Alert helper methods
79102

0 commit comments

Comments
 (0)