AWS AppSync
AWS AppSync Developer Guide

Building an iOS Client App

AWS AppSync helps you build data-driven apps with real-time and offline capabilities. The AppSync iOS SDK enables you to integrate your app with the AWS AppSync service. The SDK supports multiple authorization models, handles subscription handshake protocols for real-time updates to data, and has built-in capabilities for offline support that makes it easy to integrate AppSync into your app.

You can integrate with AWS AppSync using the following steps:

  1. Setup the API endpoint and authentication information in the client side configuration.

  2. Generate Swift code from the API schema.

  3. Write app code to run queries, mutations and subscriptions.

The AWS Amplify CLI provides support for AppSync that make this process easy. Using the CLI, you can configure an AWS AppSync API, download required client side configuration files, and generate client side code within minutes by running a few simple commands on the command line.

For a step-by-step tutorial that describes how to build an iOS application, including code generation for Swift types, by using AWS AppSync, see iOS App Tutorial.

The following section provides further details on how the AWS iOS SDKs work, and how to generate code for your AppSync application.

Application Configuration

The AWS SDKs support configuration through a centralized file called awsconfiguration.json that defines your AWS regions and service endpoints. You obtain this file in one of two ways, depending on whether you are creating your AppSync API in the AppSync console or using the Amplify CLI.

  • If you are creating your API in the console, navigate to the Getting Started page, and follow the steps in the Integrate with your app section. The awsconfiguration.json file you download is already populated for your specific API. Place the file in the root directory of your iOS project, and add it to your XCode project.

  • If you are creating your API with the Amplify CLI (using amplify add api), the awsconfiguration.json file is automatically downloaded and updated each time you run amplify push to update your cloud resources. The file is placed in the root directory of your iOS project, and you need to add it to your XCode project. To learn learn more about creating APIs using the Amplify CLI, see Native development with Amplify CLI and AWS AppSync.

Code Generation

To execute GraphQL operations in iOS you need to run a code generation process, which requires both the GraphQL schema and the statements (for example, queries, mutations, or subscriptions) that your client defines. The Amplify CLI toolchain helps you do this by automatically pulling down your schema and generating default GraphQL queries, mutations, and subscriptions before kicking off the code generation process. If your client requirements change, you can alter these GraphQL statements. You can install the CLI with the following command:

Install the Amplify CLI

To install the Amplify CLI use npm. For more information, see npm.

npm install -g @aws-amplify/cli

Code Generation for AppSync APIs Created in the Console

Open a terminal, go to your XCode project root, and then run the following:

amplify init amplify add codegen --apiId XXXXXX

The XXXXXX is the unique AppSync API identifier that you can find in the console in the root of your API's integration page. When you run this command you can accept the defaults, which create an API.swift file, and a graphql folder with your statements, in your root directory. For next steps, see Native development with Amplify CLI and AWS AppSync.

Code Generation for AppSync APIs Created Using the Amplify CLI

If you are creating your AppSync API using the Amplify CLI, see Native development with Amplify CLI and AWS AppSync.

Import SDK and Config

To use AppSync in your XCode project, modify your Podfile with a dependency of the AWS AppSync SDK as follows:

target 'PostsApp' do use_frameworks! pod 'AWSAppSync', ' ~> 2.6.20' end

Run pod install from your terminal and open up the *.xcworkspace XCode project. Add the API.swift and awsconfiguration.json files to your project (File->Add Files to ..->Add) and then build your project, ensuring there are no issues.

Client Initialization

Initialize the AppSync client your application delegate by creating AWSAppSyncClientConfiguration and AWSAppSyncClient like the following:

import AWSAppSync @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var appSyncClient: AWSAppSyncClient? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { //You can choose your database location let databaseURL = URL(fileURLWithPath:NSTemporaryDirectory()).appendingPathComponent("database_name") do { //AppSync configuration & client initialization let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncClientInfo: AWSAppSyncClientInfo(),databaseURL: databaseURL) appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig) } catch { print("Error initializing appsync client. \(error)") } //other methods return true }

The AWSConfiguration represents the configuration information present in awsconfiguration.json file. By default, the information under Default section of the json is used. Next, in your application code, you reference this in an appropriate lifecycle method such as viewDidLoad():

import AWSAppSync class Todos: UIViewController{ //Reference AppSync client var appSyncClient: AWSAppSyncClient? override func viewDidLoad() { super.viewDidLoad() //Reference AppSync client from App Delegate let appDelegate = UIApplication.shared.delegate as! AppDelegate appSyncClient = appDelegate.appSyncClient } }

Run a Query

Now that the client is set up, you can run a GraphQL query. The syntax is appSyncClient?.fetch(query: <NAME>Query() {(result, error)}) where <NAME> comes from the GraphQL statements that amplify codegen created. For example, if you have a ListTodos query your code will look like the following:

//Run a query appSyncClient?.fetch(query: ListTodosQuery()) { (result, error) in if error != nil { print(error?.localizedDescription ?? "") return } result?.data?.listTodos?.items!.forEach { print(($0?.name)! + " " + ($0?.description)!) } }

Optionally, you can set a cache policy on the query as follows:

appSyncClient?.fetch(query: ListTodosQuery(), cachePolicy: .returnCacheDataAndFetch) { (result, error) in

returnCacheDataAndFetch pulls results from the local cache first before retrieving data over the network. This gives a snappy UX and offline support.

Run a Mutation

To add data you need to run a GraphQL mutation. The syntax is appSyncClient?.perform(mutation: <NAME>Mutation() {(result, error)}) where <NAME> comes from the GraphQL statements that amplify codegen created. However, most GraphQL schemas organize mutations with an input type for maintainability, which is what the AppSync console and Amplify CLI do as well. Therefore, you need to pass this as a parameter called input, as in the following example:

let mutationInput = CreateTodoInput(name: "Use AppSync", description:"Realtime and Offline") appSyncClient?.perform(mutation: CreateTodoMutation(input: mutationInput)) { (result, error) in if let error = error as? AWSAppSyncClientError { print("Error occurred: \(error.localizedDescription )") } if let resultError = result?.errors { print("Error saving the item on server: \(resultError)") return } }

Subscribe to Data

Finally, it's time to set up a subscription to real-time data. The syntax appSyncClient?.subscribe(subscription: <NAME>Subscription() {(result, transaction, error)}) where <NAME> comes from the GraphQL statements that amplify codegen created. Note that the AppSync console and Amplify GraphQL transformer have a common nomenclature that puts the word On in front of a subscription as in the following example:

//Set a variable to discard at the class level var discard: Cancellable? //In your app code do { discard = try appSyncClient?.subscribe(subscription: OnCreateTodoSubscription(), resultHandler: { (result, transaction, error) in if let result = result { print(result.data!.onCreateTodo!.name + " " + result.data!.onCreateTodo!.description!) } else if let error = error { print(error.localizedDescription) } }) } catch { print("Error starting subscription.") }

Subscriptions can also take input types like mutations, in which case they will be subscribing to particular events based on the input. To learn more about subscription arguments, see Real-Time data.