3. Write your first mutation
8m

Overview

We're familiar with queries, but when we want to actually change, insert, or delete data, we need to reach for a new tool: .

In this lesson, we will:

  • Implement a to login with an email
  • Enable our app's login view and execute the

Building a mutation

A is an we use to change data on the server. In our application, the login mutation will create a session based on our email address.

Note: The way you log in to this particular server might differ from the way you log in with your own server. Login is often handled by middleware, or a layer totally separate from , like OAuth.

Also note that a typical authentication flow should require a password—but for this tutorial, anyone is allowed to book flights with a valid email address!

Let's take a look at the that exist in our app's schema.

Open up Sandbox, or expand the embedded Sandbox below.

Let's return to the Schema tab. Just below the Query tab, we'll find Mutation. Let's click on this type and scroll down to take a look at the login :

https://studio.apollographql.com/sandbox/schema

A screenshot of Sandbox's Schema page, highlighting the Mutation type and the login field

Click the play button on the right to open that in the Explorer tab. When it opens, click the plus sign next to login to add the :

https://studio.apollographql.com/sandbox/explorer

Sandbox Explorer opened to the Documentation tab, showing the login field and highlighting the plus button

Adding this prefills the Operation panel with the following syntax.

mutation Login {
login {
}
}

Notice the red error indicator that shows up in Explorer? This is because the type returned by the is User, which is is an object type: this means we need to choose at least one of the User type's for the to return. For our purposes, we only need the token , so add it by clicking the plus sign next to it.

You'll also notice that email wasn't automatically added as an even though it doesn't seem to have a default value: that's because email is of type String—without an exclamation point (!)—which remember, in , means that it's not required (although obviously you won't get too far without it).

Click the plus sign next to the email to add that argument.

https://studio.apollographql.com/sandbox/explorer

The Operation panel showing our updated query with the email variable

We'll also notice that Explorer has added a to your Variables section to match the login email.

Click the Submit Operation button to run the . Because we sent null for the email address, we'll get back null for the login:

https://studio.apollographql.com/sandbox/explorer

The Response panel reflecting a null response for our query with a null email

Now, replace null in the Variables section with an actual email address:

Variables panel
{
"email": "me@example.com"
}

Submit the , and this time you'll get an actual response:

https://studio.apollographql.com/sandbox/explorer

The Response panel reflecting a valid response for our query sent with an actual email

Next, copy the , either manually or using the three-dot menu's Copy operation option.

Add the mutation to the project

Now that the is working, we'll add it to our project. Create an empty file named Login.graphql (don't add it to the app target) next to your other files and paste the contents of the :

Login.graphql
mutation Login($email: String!) {
login(email: $email) {
token
}
}

Note: We've also marked the email as non-nullable by adding ! to the end of the type, since we always want to pass a value for it.

Run code generation in terminal to generate the code for the .

Code generation command
./apollo-ios-cli generate

Implement the login logic

Next, let's update our app to use this . Open up LoginViewModel.swift. We'll add a few new imports:

LoginViewModel.swift
import Swift
import Apollo
import RocketReserverAPI
import KeychainSwift

Next, scroll down to the TODO inside the login(with email: String?) method. Clear out the comment and replace it with the following code.

LoginViewModel.swift
Network.shared.apollo.perform(mutation: LoginMutation(email: email)) { [weak self] result in
defer {
self?.isSubmitEnabled = true
}
switch result {
case .success(let graphQLResult):
if let token = graphQLResult.data?.login?.token {
// TODO - store token securely
self?.isPresented = false
}
if let errors = graphQLResult.errors {
self?.appAlert = .errors(errors: errors)
}
case .failure(let error):
self?.appAlert = .errors(errors: [error])
}
}

Next, we need to store the login credential that's returned by the server. Login credentials should always be stored in the Keychain, but interacting with it directly is challenging, so we'll be using the KeychainSwift library which has already been added as a Swift Package to this project.

Now we'll use the KeychainSwift we imported earlier. Replace the TODO - store token securely after unwrapping the token with the following:

LoginViewModel.swift
let keychain = KeychainSwift()
keychain.set(token, forKey: LoginView.loginKeychainKey)

Display the login view

Next we need to check if the user is logged in when booking or cancelling a trip. We'll use this state to determine whether or not we show the LoginView.

To do this, go to the DetailViewModel.swift. Scroll down until you locate the bookOrCancel method. We'll notice that this method already contains some code to call isLoggedIn(); if that returns false, it sets the flag to show the login view.

DetailViewModel.swift
func bookOrCancel() {
guard self.isLoggedIn() else {
isShowingLogin = true
return
}
// TODO
}

Currently this method always returns false, so let's update that now.

First add the following import to DetailViewModel.swift:

DetailViewModel.swift
import SwiftUI
import RocketReserverAPI
+ import KeychainSwift

Next, replace the contents of the isLoggedIn() method (all the way at the bottom of the class) with the following:

DetailViewModel.swift
private func isLoggedIn() -> Bool {
let keychain = KeychainSwift()
return keychain.get(LoginView.loginKeychainKey) != nil
}

Let's put it all together, and try out our !

Test the login mutation

Build and run the application, select a from the list to get to the DetailView. You should see that clicking the Book now! button in the UI shows the login view.

A screenshot of the desktop view of our IDE and the simulator. We can see the login form, filled in with an email.

If you login with this email, me@example.com, subsequent presses of Book now! no longer show the login view.

Task!

Up next

We've implemented our mechanism to "log in", but there's more work to do: in the next lesson, we'll learn how to authenticate with our login token.

Previous

Share your questions and comments about this lesson

This course is currently in

beta
. Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.