Build an iOS Application

Create a simple iOS application using AWS Amplify

Module 4: Add a GraphQL API and Database

In this module, you will use the Amplify CLI and libraries to configure and add a GraphQL API to your app


Now that we've created and configured the app with user authentication, let's add an API and create, read, update, delete (CRUD) operations on a database.
In this module, you will add an API to our app using the Amplify CLI and libraries. The API you will be creating is a GraphQL API that uses AWS AppSync (a managed GraphQL service) which is backed by Amazon DynamoDB (a NoSQL database). For an introduction to GraphQL, visit this page.

The app we will be building is a note-taking app that allows users to create, delete, and list notes. This example gives you a good idea of how to build many popular types of CRUD+L (create, read, update, delete, and list) applications.

What you will accomplish

In this module, you will:
  • Create and deploy a GraphQL API
  • Write frontend code to interact with the API

Key concepts

API – Provides a programming interface that allows communication and interactions between multiple software intermediaries.

GraphQL – A query language and server-side API implementation based on a typed representation of your application. This API representation is declared using a schema based on the GraphQL type system. (To learn more about GraphQL, visit this page.)

 Time to complete

20 minutes

 Services used


Create a GraphQL API service and a database

To create the GraphQL API and its backing database, open a terminal and run this command from your project directory:

amplify add api

? Please select from one of the below mentioned services: select GraphQL and press enter
? Provide API name: select the default, press enter
? Choose the default authorization type for the API: use the arrow key to select Amazon Cognito User Pool and press enter
? Do you want to configure advanced settings for the GraphQL API: select the default No, I am done and press enter
? Do you have an annotated GraphQL schema? keep the default N and press enter
? What best describes your project: choose any model, we are going to replace it with our own anyway. Press enter
? Do you want to edit the schema now? type Y and press enter

The default text editor that you chose when you initialized the project (amplify init) opens with a prebuild data schema.

Delete the schema and replace it with our app GraphQL schema.

type NoteData
@auth (rules: [ { allow: owner } ]) {
    id: ID!
    name: String!
    description: String
    image: String
The data model is made of one class NoteData and four properties: id and name are mandatory; description and image are optional Strings.
The @model transfomer indicates we want to create a database to store these data.
The @auth transformer adds authentication rules to allow access to these data. For this project, we want only the owner of NoteDatas to have access to them.
Once done, do not forget to Save. Go back to your terminal to tell Amplify CLI you're done.
? Press enter to continue, press enter.

After a few seconds, you should see a success message:

GraphQL schema compiled successfully.

Generate client-side code

Based on the GraphQL data model definition we just created, Amplify generates client-side code (specifically, Swift code) to represent the data in our app.

To generate the code, run the following command in your terminal.

amplify codegen models

This creates Swift files in the amplify/generated/models directory, as you can see with:

➜  iOS Getting Started git:(master) ✗ ls -al amplify/generated/models
total 24
drwxr-xr-x  5 stormacq  admin  160 Jul  9 14:20 .
drwxr-xr-x  3 stormacq  admin   96 Jul  9 14:20 ..
-rw-r--r--  1 stormacq  admin  380 Jul  9 14:20 AmplifyModels.swift
-rw-r--r--  1 stormacq  admin  822 Jul  9 14:20 NoteData+Schema.swift
-rw-r--r--  1 stormacq  admin  445 Jul  9 14:20 NoteData.swift

Import these files into your Xcode project: Locate them in the Finder and drag and drop them to the project in Xcode.

Deploy the API service and database

To deploy the backend API and database we have just created, go to your terminal and run this command:

amplify push
# press Y when asked to continue

? Are you sure you want to continue? accept the default Y and press enter
? Do you want to generate code for your newly created GraphQL API type N and press enter

After a few minutes, you should see a success message:

✔ All resources are updated in the cloud

GraphQL endpoint:

Add API client library to the Xcode project

Before going to the code, you add the Amplify API library to the dependencies of your project. Navigate back to the General tab of your target and select AWSAPIPlugin and then choose Add.

You have now added AWSAPIPlugin as a dependency for your project.

Initialize Amplify libraries at runtime

Return to Xcode and open Backend.swift. Add a line in the Amplify initialization sequence in the private init() method. The code block should look like this:

// at top of file
import AWSAPIPlugin

// initialize amplify
do {
   try Amplify.add(plugin: AWSCognitoAuthPlugin())
   try Amplify.add(plugin: AWSAPIPlugin(modelRegistration: AmplifyModels()))
   try Amplify.configure()
   print("Initialized Amplify")
} catch {
   print("Could not initialize Amplify: \(error)")

Add bridging between GraphQL data model and app model

Our project already has a data model to represent a Note. For this tutorial, we made a design decision to continue to use that model and provide an easy way to convert a NoteData to a Note.

Open ContentView.swift and add this initializer in the Note class.

convenience init(from data: NoteData) {
    self.init(id:, name:, description: data.description, image: data.image)
    // store API object for easy retrieval later
    self._data = data

fileprivate var _data : NoteData?

// access the privately stored NoteData or build one if we don't have one.
var data : NoteData {

    if (_data == nil) {
        _data = NoteData(id:,
                            description: self.description,
                            image: self.imageName)

    return _data!

Add API CRUD methods to the backend class

We will now add three methods to call our API: a method to query the Note, a method to create a new Note, and a method to delete a Note. Notice that these methods work on the app data model (Note) to make it easy to interract from the user interface. These methods transparently convert Note to GraphQL's NoteData objects.

Open the Backend.swift file and add the following snippet at the end of Backend class:

// MARK: API Access
func queryNotes() {
        Task {
            do {
                let result = try await Amplify.API.query(request: .list(Transaction.self))
                switch result {
                case .success(let notesData):
                    print("Successfully retrieved list of Notes")

                    // convert an array of NoteData to an array of Note class instances
for n in notesData {
                        let note = Note.init(from: n)

                case .failure(let error):
                    print("Can not retrieve result : error  \(error.errorDescription)")
            } catch {
                print("Can not retrieve Notes : error \(error)")

    func createNote(note: Note) {

        Task {
            do {
                // use to access the NoteData instance
let result = try await Amplify.API.mutate(request: .create(
                switch result {
                    case .success(let data):
                        print("Successfully created note: \(data)")
                    case .failure(let error):
                        print("Got failed result with \(error.errorDescription)")
            } catch {
                print("Got failed result with error \(error)")

    func deleteNote(note: Note) {

        // use to access the NoteData instance
Task {
            do {
                let result = try await Amplify.API.mutate(request: .delete(
                switch result {
                case .success(let data):
                    print("Successfully deleted note: \(data)")
                case .failure(let error):
                    print("Got failed result with \(error.errorDescription)")
            } catch {
                print("Got failed result with error \(error)")

Finally, we must call the API to query the list of Note for the currently signed-in user when the application starts. Add this piece of code in the backend's private init() method:

Task {
    do {
        let session = try await Amplify.Auth.fetchAuthSession()

        // let's update UserData and the UI
await self.updateUserData(withSignInStatus: session.isSignedIn)
    } catch {
        print("Fetch auth session failed with error - \(error)")

In the same Backend.swift file, update the updateUserData(withSignInStatus:) method to look like this:

// change our internal state, this triggers an UI update on the main thread
func updateUserData(withSignInStatus status : Bool) async {
    await {
        let userData : UserData = .shared
        userData.isSignedIn = status

        // when user is signed in, query the database, otherwise empty our model
if status {
        } else {
            userData.notes = []

Now, all that is left is to create a UI component for creating a new note and deleting a note from the list.

Add an edit button to add note

Now that the backend and data model pieces are in place, the last step in this section is to allow users to create a new note and to delete notes.

In Xcode, open ContentView.swift.

a. In ContentView struct, add state variables bound to the user interface.

// add at the begining of ContentView class
@State var showCreateNote = false

@State var name : String        = "New Note"
@State var description : String = "This is a new note"
@State var image : String       = "image"

b. Anywhere in the file, add a View struct to let the user create a new note.

struct AddNoteView: View {
    @Binding var isPresented: Bool
    var userData: UserData

    @State var name : String        = "New Note"
    @State var description : String = "This is a new note"
    @State var image : String       = "image"
    var body: some View {
        Form {

            Section(header: Text("TEXT")) {
                TextField("Name", text: $name)
                TextField("Name", text: $description)

            Section(header: Text("PICTURE")) {
                TextField("Name", text: $image)

            Section {
                Button(action: {
                    self.isPresented = false
                    let noteData = NoteData(id : UUID().uuidString,
                                            name: self.$name.wrappedValue,
                                            description: self.$description.wrappedValue)
                    let note = Note(from: noteData)

                    // asynchronously store the note (and assume it will succeed)
                    Backend.shared.createNote(note: note)

                    // add the new note in our userdata, this will refresh UI
                }) {
                    Text("Create this note")

c. Add a + button on the navigation bar to present a sheet for creating a note.

In ContentView struct, replace navigationBarItems(leading SignOutButton()) with

    .navigationBarItems(leading: SignOutButton(),
                        trailing: Button(action: {
    }) {
        Image(systemName: "plus")
}.sheet(isPresented: $showCreateNote) {
    AddNoteView(isPresented: self.$showCreateNote, userData: self.userData)

Add a swipe-to-delete behavior

Finally, in ContentView, add the swipe to delete behavior: add the .onDelete { } method to the ForEach struct:

ForEach(userData.notes) { note in
    ListRow(note: note)
}.onDelete { indices in
    indices.forEach {
        // removing from user data will refresh UI
        let note = self.userData.notes.remove(at: $0)

        // asynchronously remove from database
        Backend.shared.deleteNote(note: note)

Build and test

To verify everything works as expected, build and run the project. Select the Product menu and then select Run or press ⌘R. There should be no error.

Assuming you are still signed in, the app starts on the empty List. It now has a + button to add a note. Choose the + sign, choose Create this Note, and the note should appear in the list.

You can close the AddNoteView by pulling it down. Note that, on the iOS simulator, it is not possible to tap + a second time; you need to pull-to-refresh the List first.

You can delete the note by swiping a row left.

Here is the complete flow.

Empty list

Create a note

One note in the list

Delete a note


You have now created an iOS app. Using AWS Amplify, you added a GraphQL API and configured create, read, and delete functionality in your app.

In the next section, we will add UI and behavior to manage pictures.

Was this page helpful?

Add file storage