9. Write your first subscription
In this section, you will use subscriptions to get notified whenever someone books a flight 🚀! Subscriptions allow you to be notified in real time whenever an event happens on your server. The fullstack backend supports subscriptions based on WebSockets.
Write your subscription
Open your Sandbox back up, click on the Schema tab at the far left. In addition to queries
and mutations
, you will see a third type of operations, subscriptions
. Click on subscriptions to see the tripsBooked
subscription:
This subscription doesn't take any argument and returns a single scalar named tripsBooked
. Since you can book multiple trips at once, tripsBooked
is an Int
. It will contain the number of trips booked at once or -1 if a trip has been cancelled.
Click the play button to the far right of tripsBooked
to open the subscription in Explorer. Open a new tab, then check the tripsBooked
button to have the subscription added:
Again, rename your subscription so it's easier to find:
Click the Submit Operation button, and your subscription will start listening to events. You can tell it's up and running because a panel will pop up at the lower left where subscription data will come in:
Test your subscription
Open a new tab in Explorer. In this new tab, add code to book a trip like on step 8, but with a hard-coded ID:
1mutation BookTrip {
2 bookTrips(launchIds: ["93"]){
3 message
4 }
5}
Do not forget to include the authentication header. At the bottom of the Sandbox Explorer pane where you add operations, there's a Headers
section:
Click the Submit Operation button. If everything went well, you just booked a trip! At the top of the right panel, you'll see the success JSON for your your BookTrip
mutation, and below it, updated JSON for the TripsBooked
subscription:
Continue booking and/or canceling trips, you will see events coming in the subscription panel in real time. After some time, the server might close the connection and you'll have to restart your subscription to keep receiving events.
Add the subscription to the project
Now that your subscription is working, add it to your project. Create an empty file named TripsBooked.graphql
next to your other GraphQL files and paste the contents of the subscription. The process is similar to what you've already done for queries and mutations:
1subscription TripsBooked {
2 tripsBooked
3}
Build your project, and the subscription will be picked up and added to your API.swift
file.
Configure your ApolloClient to use subscriptions
This tutorial uses the
graphql-ws
protocol, implemented by thesubscriptions-transport-ws
library. That library is no longer actively maintained. We recommend using thegraphql-ws
library instead, which implements its own WebSocket subprotocol,graphql-transport-ws
. Note that the two libraries do not use the same WebSocket subprotocol and you need to ensure that your servers, clients, and tools use the same library and subprotocol. For more information and examples, see GraphQL over WebSocket protocols.
In Network.swift
, you'll need to set up a transport which supports subscriptions in addition to general network usage. In practice, this means adding a WebSocketTransport
which will allow real-time communication with your server.
First, at the top of the file, add an import for the ApolloWebSocket framework to get access to the classes you'll need:
1import ApolloWebSocket
Next, in the lazy declaration of the apollo
variable, immediately after transport
is declared, set up what you need to add subscription support to your client:
1// 1
2let webSocket = WebSocket(
3 url: URL(string: "wss://apollo-fullstack-tutorial.herokuapp.com/graphql")!,
4 protocol: .graphql_ws
5)
6
7// 2
8let webSocketTransport = WebSocketTransport(websocket: webSocket)
9
10// 3
11let splitTransport = SplitNetworkTransport(
12 uploadingNetworkTransport: transport,
13 webSocketNetworkTransport: webSocketTransport
14)
15
16// 4
17return ApolloClient(networkTransport: splitTransport, store: store)
What's happening here?
You've created a web socket with the server's web socket URL -
wss://
is the protocol for a secure web socket.You've created a
WebSocketTransport
, which allows the Apollo SDK to communicate with the web socket.You've created a
SplitNetworkTransport
, which can decide whether to use a web socket or not automatically, with both theRequestChainNetworkTransport
you had previously set up, and theWebSocketTransport
you just set up.You're now passing the
splitTransport
into theApolloClient
, so that it's the main transport being used in yourApolloClient
.
Now, you're ready to actually use your subscription!
Display a view when a trip is booked/cancelled
In LaunchesViewController
, add a new variable just below activeRequest
to hang on to a reference to your subscription so it doesn't get hammered by ARC as soon as it goes out of scope:
1private var activeSubscription: Cancellable?
Next, just above the code for handling Segues, add code for starting and handling the result of a subscription:
1// MARK: - Subscriptions
2
3private func startSubscription() {
4 activeSubscription = Network.shared.apollo.subscribe(subscription: TripsBookedSubscription()) { result in
5 switch result {
6 case .failure(let error):
7 self.showAlert(title: "NetworkError",
8 message: error.localizedDescription)
9 case .success(let graphQLResult):
10 if let errors = graphQLResult.errors {
11 self.showAlertForErrors(errors)
12 } else if let tripsBooked = graphQLResult.data?.tripsBooked {
13 self.handleTripsBooked(value: tripsBooked)
14 } else {
15 // There was no data and there were no errors, do nothing.
16 }
17 }
18 }
19}
20
21private func handleTripsBooked(value: Int) {
22 print("Trips booked: \(value)")
23}
Finally, add a line to viewDidLoad
which actually starts the subscription:
1override func viewDidLoad() {
2 super.viewDidLoad()
3 self.startSubscription()
4 self.loadMoreLaunchesIfTheyExist()
5}
Build and run your app and go back to Sandbox Explorer, and select the tab where you set up the BookTrip
mutation. Book a new trip while your app is open, you'll see a log print out:
1Trips booked: 1
Cancel that same trip, and you'll see another log:
1Trips booked: -1
Now, let's display that information in a view! Replace the print
statement in handleTripsBooked
with code to use the included NotificationView
to show a brief alert at the bottom of the screen with information about a trip being booked or cancelled:
1private func handleTripsBooked(value: Int) {
2 var message: String
3 switch value {
4 case 1:
5 message = "A new trip was booked! 🚀"
6 case -1:
7 message = "A trip was cancelled! ðŸ˜"
8 default:
9 self.showAlert(title: "Unexpected value",
10 message: " Subscription returned unexpected value: \(value)")
11 return
12 }
13
14 NotificationView.show(in: self.navigationController!.view,
15 with: message,
16 for: 4.0)
17}
Build and run the application to your simulator, then use Studio to send bookings and cancellations again, and your iOS app should see some shiny new notifications pop up:
And you've done it! You've completed the tutorial.
More resources
There are way more things you can do with the Apollo iOS SDK, and the rest of this documentation includes info on more advanced topics like:
Using fragments
Working with custom scalars
Feel free to ask questions by either opening an issue on our GitHub repo, or joining the community.
And if you want dig more and see GraphQL in real-world apps, you can take a look at these open source projects using Apollo iOS:
[open a PR if you have an example app that should be here!]