Front-End Web & Mobile

User settings sync for iOS with AWS AppSync

Amazon Cognito Sync is a service that you can use for syncing application-related user profile data across devices. The client library caches the data locally so that the app can read and write data, regardless of the device connectivity state. The data stored is generally shown as a key-value pair, where the keys and values are both strings.

However, many organizations need to store data in a common store. For example, you might want to do backend searches to enable you to segment users, do A/B testing, store binary data like profile pictures, or link the data to alternate data sources.

In a previous article, I created a data store for storing user settings by using AWS AppSync, which provides a similar structure as Amazon Cognito Sync, but has some additional functionality. AWS AppSync is a GraphQL-based managed service with real-time and offline capabilities.

In this article, I build a simple iOS application that demonstrates the ability to synchronize application-related user profile data across devices by using the data store that I created earlier. If you haven’t already done so, return to the previous article and set up the cloud data store.

Option 1: Quick start (15 minutes)

In this quick start option, I take an existing project that you can download, update it to specify the location of the user settings store in the AWS Cloud, run the sample mobile app, then verify that the data is stored in the appropriate place.

  1. Download the sample repository. Choose Clone or download, and then choose Download ZIP.
  2. Unpack the ZIP file.
  3. Open the Terminal app, and then use cd to change directory to the location of the unpacked files.
  4. Run the following: pod install –repo-update
  5. Open the project in Xcode by double-clicking on UserSettingsSync.xcworkspace from within Finder.
  6. Expand UserSettingsSync to find the Constants.swift file.
  7. Open Constants.swift in the editor. Change the following variables:
    • Set AppSyncRegion to the region that your GraphQL API is deployed to.
    • Set AppSyncEndpointURL to the URL of the GraphQL API.

You can find these values from the AWS AppSync console. The API is listed immediately after choosing the API from the API list. The region is embedded in the URL.

  1. Download the awsconfiguration.json file and add it to your project:
    • Open the AWS Mobile Hub console, and then choose your project.
    • Choose the Integrate button on the iOS app panel.
    • Choose Download Cloud Config. This downloads the awsconfiguration.json file.
    • Drag and drop the awsconfiguration.json file onto your project in the same location as the project Info.plist file.
    • Choose Finish.
  2. Run your app in the simulator.

The sample mobile app provides four theme options. Changing the theme also changes the colors that are associated with the app. If you check the backend data store, you’ll see that your theme is stored in the settings store. Also, if you implement the Android version of the app, you’ll notice that changes to the theme on iOS are also reflected in the Android app.

Option 2: Access the settings store from your own app

When you integrate user settings with your own app, you need to specify the model and operations. Then you create a repository pattern to synchronize the settings with the AWS AppSync backend. You can use any project for this option. For this walkthrough, I’m using the AWS Mobile iOS Notes app, and have already walked through the complete tutorial for integrating analytics, authentication and data access. Ensure that you have a working app before starting this section.

Step 1: Create the GraphQL schema and operations files

Create a new folder in the root of your iOS project for the GraphQL files. The root folder is called MyNotes and contains the Info.plist file for the project. To create the folder within Xcode:

  • Right-click MyNotes and choose New Group.
  • Type the name GraphQLOperations as the name of the new group.

Download the AWS AppSync schema:

  • Open the AWS AppSync console and choose your GraphQL API.
  • At the bottom of the API page, choose the iOS tab.
  • Under step 3, choose schema.json from the Export schema drop-down.
    • Do NOT download the schema.graphql file because that’s not needed for the code generation process that follows.
  • Drag and drop the schema.json file into the GraphQLOperations folder within Xcode.
  • Choose Finish.

Create an operations.graphql file:

  • Right-click GraphQLOperations and choose New File.
  • Type Empty in the search field, then press Enter. Choose Empty in the Other section.
  • Choose Next.
  • Type operations.graphql in the Save As field, and then choose Create.
  • Copy the following code into the newly created file:
query GetSettings {
    getSettings {
        theme
        displayName
    }
}

mutation StoreSettings($settings: SettingsInput!) {
    storeSettings(settings: $settings) {
        theme
        displayName
    }
}

Step 2: Generate the iOS code for GraphQL

First, install the code generator. In a terminal, run:

$ npm install -g aws-appsync-codegen

This only needs to be done once. Then change the directory to your project directory (this contains the Info.plist file). To generate the appropriate code, run:

$ aws-appsync-codegen generate GraphQLOperations/*.graphql \
    --schema GraphQLOperations/schema.json \
    --output API.swift

This should be entered on a single line. Add the generated file to your Xcode project:

  • Right-click the MyNotes folder within your Xcode project.
  • Choose Add Files to “MyNotes”.
  • Find and choose API.swift.
  • Choose Add.

Step 3: Add the AWS Mobile Client SDK to the project

Close Xcode. Open a terminal at your project root (the directory contains a Podfile). Open the Podfile, and add the following dependencies (example):

platform :ios, '10.0'

target 'MyNotes' do
  use_frameworks!
  pod 'AWSPinpoint', '~> 2.6.16'
  pod 'AWSUserPoolsSignIn', '~> 2.6.16'
  pod 'AWSAuthUI', '~> 2.6.16'
  pod 'AWSMobileClient', '~> 2.6.16'
  pod 'AWSAppSync', '~> 2.6.15'
end

Save the file, and then run pod install. After the installation is completed, re-open Xcode by double-clicking the MyNotes.xcworkspace file. Rebuild your app by using Project > Build.

Step 4: Initialize the AWS AppSync client

Create a new Swift file called Constants.swift with the following contents:

import Foundation
import AWSAppSync

let AppSyncRegion: AWSRegionType = AWSRegionType.USWest2
let AppSyncEndpointURL: URL = URL(string: "https://c3ncrdpstng5fhimf7mil76cli.appsync-api.us-west-2.amazonaws.com/graphql")!
let database_name: String = "local-appsync-db"

As before, replace the region and endpoint URL with the values from the AWS AppSync console. Then open AppDelegate.swift. Add the following variable to the AppDelegate class:

import AWSAppSync

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
    var window: UIWindow?
    
    // AWS AppSync Client
    var appSyncClient: AWSAppSyncClient?

    // Rest of class…
}

Add the initialization code to the application() method for customizing the application launch:

        // Initialize AWSMobileClient
        let didFinishLaunching = AWSMobileClient.sharedInstance().interceptApplication(application, didFinishLaunchingWithOptions: launchOptions)
        
        let databaseURL = URL(fileURLWithPath:NSTemporaryDirectory()).appendPathComponent(database_name)
        do {
            let appSyncConfig = try AWSAppSyncClientConfiguration(
                url: AppSyncEndpointURL,
                serviceRegion: AppSyncRegion,
                credentialsProvider: AWSMobileClient.getCredentialsProvider(),
                databaseURL: databaseURL)
            appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
            appSyncClient?.appolloClient?.cacheKeyForObject = { $0["userId"] }
        } catch {
            print("Error initializing appsync client. \(error)")
        }

Step 5: Obtain (and store) the settings

Edit the MasterViewController.swift file. Add the following to the top of the file:

import AWSAppSync

// Extension to UIColor to use RGB (hex) values
extension UIColor {
    convenience init(red: Int, green: Int, blue: Int) {
        self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
    }
    
    convenience init(rgb: Int) {
        self.init(red: (rgb >> 16) & 0xFF, green: (rgb >> 8) & 0xFF, blue: rgb & 0xFF)
    }
}

This code imports the AWS AppSync library. It also extends UIColor to allow us to specify RGB hex values (such as those used in HTML and CSS). Add the following variable to the top of the MasterViewController class:

        // Get a reference to the AWS AppSync Client
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appSyncClient = appDelegate.appSyncClient!

        // Change the header background color to the default theme
        let theme: String = "Default"
        changeHeaderByTheme(theme)
        
        // Update based on AWS AppSync settings
        getSettingsQuery()

Add the following function to the class to change the color of the header based on the theme:

  // Set the color of the bar based on the theme
    func changeHeaderByTheme(_ theme: String) {
        var uiColor: UIColor = UIColor(rgb: 0x008577)
        if (theme == "Theme1") {
            uiColor = UIColor(rgb: 0x2196F3)
        } else if (theme == "Theme2") {
            uiColor = UIColor(rgb: 0x673AB7)
        } else if (theme == "Theme3") {
            uiColor = UIColor(rgb: 0xF44336)
        }
        self.navigationController?.navigationBar.barTintColor = uiColor
        self.title = "My Notes (\(theme))"
    }

Finally, add the following function to the class to perform the query and update the theme with the returned values:

    func getSettingsQuery() {
        appSyncClient?.fetch(query: GetSettingsQuery()) {
            (result, error) in
                if let error = error as? AWSAppSyncClientError {
                    print("Error occurred while making request: \(error.localizedDescription)")
                    return
                }
                let theme: String? = result?.data?.getSettings?.theme
                self.changeHeaderByTheme(theme!)
        }
    }

The GetSettingsQuery() is a class that’s created during code generation. You can add a new view controller to update the settings by using a mutation as well. Refer to the AWS AppSync Developer Guide for more details on running a mutation.

Run the Notes app to see your stored theme. The navigation bar changes color quickly within the simulator to the theme you selected in the sample mobile app. Now close the app and open the sample mobile app. Choose a different theme, then switch back to the Notes app and note that the navigation bar has changed color.

Conclusion

AWS AppSync provides many benefits over Amazon Cognito Sync for application settings, and we’ve just scratched the surface here. In AWS AppSync, you can store basic key-value pairs, complex objects, or even users’ profile pictures—and this data will be synchronized to all user devices. In addition, AWS AppSync offers a single-data access layer for application data with real-time updates and offline capabilities. This makes it an ideal tool for cloud-enabled data-driven apps. Learn more about AWS AppSync now.