Trikalabs
  • Home
  • Best online TDD videos
  • Book Suggestions
  • About Me
  • Contact
Trikalabs
No Result
View All Result

AWS: Build data-driven apps with real-time and offline capabilities by using AppSync and Swift.

by fragi
July 25, 2019
in AWS
Share on FacebookShare on Twitter

In this article, we will use the AppSync framework to fetch and create data for our iOS app.

By using AppSync we can build data-driven apps with real-time and offline capabilities. 

Create a project

Firstly let’s create our iOS project. Open Xcode and create a project. Let’s name it AppSync.

Install & configure Amplify

  1. Install the Node.js and npm. (Skip if it is already installed)
  2. On the command line execute:npm install -g @aws-amplify/cli
  3. In the command line, navigate to the root folder of the Xcode project.
  4. On the command line execute: amplify configure. You will be prompted to sign in to the AWS administrator account: https://console.aws.amazon.com/. Tap “Enter” on the command line when you sign in.
  5. Now you have to specify the AWS Region. Choose the one that is nearest to your users’ location. For example, most of my users are in London so I will select the region eu-west-2
  6.  Specify a name for the new IAM user.  Identity and Access Management allow us to access our AWS services and resources securely. Choose a name or tap enter to use the suggested. You will have to tap on the provided link and go to the AWS console to finish the creation of the user. Follow the steps by using the default settings but take note of the accessKeyID and secretAccessKey before you finish the creation. (* The default settings give administrator access to the IAM user. A good practice is to give only the minimum required privileges).
  7. Enter the accessKeyID and secretAccessKey on the command line
  8. Now choose a profile. For this example select the default.
  9. Now, execute: amplify init
  10. Enter a project name, an environment name, a default editor and choose iOS
  11. Choose the default profile.

Cocoapods

  1. Install cocoapods if it is not installed.
  2. In the command line, navigate to the root folder of the Xcode project.
  3. execute pod init
  4. Open the podfile and add the following pods:
    Swift
    1
      pod 'AWSMobileClient', '~> 2.9.0'      <br>  pod 'AWSUserPoolsSignIn', '~> 2.9.0'<br>  pod 'AWSAppSync', '~> 2.14.0'

Update amplify & cocoapods

Now, let’s push our changes. In command line execute: amplify push. You should see a result like:

Now, let’s update our pods in the project. In command line execute: pod install –repo-update

Lastly, let’s drag and drop to our app the awsconfiguration.json file.

 Add support for Authentication

In the command line execute: amplify add auth. Then follow the instructions and choose the Default configuration. Choose whatever you prefer the users to use for sign in and don’t configure any advanced settings.

Then execute: amplify push

Enter yes to continue.

Great, all done with the setup.

Add Appsync support

In the command line execute: amplify add api

Then choose GraphQL and provide an API name. As authorization type choose Amazon Cognito User Pool. Choose that we don’t have an annotated GraphQL schema and select the guided schema creation. Lastly, choose Single object with fields and type yes to edit the schema

In the schema we see the following model:

Swift
1
2
3
4
5
type Todo @model {
  id: ID!
  name: String!
  description: String
}

Now, let’s execute: amplify push

And select Yes to continue.

Now select to automatically generate GraphQL code for our API. Select the default file name pattern and also select to generate/update all possible GRaphQL operations. Select 2 as the maximum statement depth and API.swift as the file name of the generated code:

Drag the API.swift file into the project.

The API.swift file contains all the code for creating and fetching data.

Let’s try to set it up in the ViewController. In  production code we should not put all this code in a ViewController, instead, we can follow a more modular approach as the example: http://trikalabs.com/aws-build-an-authentication-feature-with-clean-architecture/

Swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import UIKit
import AWSAppSync
import AWSMobileClient
 
class ViewController: UIViewController {
 
    private var appSyncClient: AWSAppSyncClient?
 
    override func viewDidLoad() {
        super.viewDidLoad()
        setupAppSync()
    }
    
    func setupAppSync() {
        
        do {
            // You can choose the directory in which AppSync stores its persistent cache databases
            let cacheConfiguration = try AWSAppSyncCacheConfiguration()
            
            // Initialize the AWS AppSync configuration
            let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig(),
                                                                  userPoolsAuthProvider: {
                                                                    class MyCognitoUserPoolsAuthProvider : AWSCognitoUserPoolsAuthProviderAsync {
                                                                        func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void) {
                                                                            AWSMobileClient.sharedInstance().getTokens { (tokens, error) in
                                                                                if error != nil {
                                                                                    callback(nil, error)
                                                                                } else {
                                                                                    callback(tokens?.idToken?.tokenString, nil)
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                    return MyCognitoUserPoolsAuthProvider()}(),
                                                                  cacheConfiguration: cacheConfiguration)
            
            // Initialize the AWS AppSync client
            appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
        } catch {
            print("Error initializing appsync client. \(error)")
        }
        
        save()
    }
    
    func save() {
        let temporaryLocalID = "TEMP-\(UUID().uuidString)"
        
        let input = CreateTodoInput(id: temporaryLocalID, name: "a name", description: "desc")
        let addEventMutation = CreateTodoMutation(input: input)
        
        appSyncClient?.perform(mutation: addEventMutation, optimisticUpdate: { transaction in
            do {
                // Update our normalized local store immediately for a responsive UI.
                try transaction?.update(query: ListTodosQuery()) { (data: inout ListTodosQuery.Data) in
                    let i = ListTodosQuery.Data.ListTodo.Item.init(id: temporaryLocalID, name: input.name, description: input.description)
                    data.listTodos?.items?.append(i)
                }
                
            } catch {
                print("Error updating the cache with optimistic response: \(error)")
            }
        }) { [weak self] (result, error) in
            guard error == nil else {
                print("Error occurred posting a new item: \(error!.localizedDescription )")
                return
            }
            
            guard let createResponce = result?.data?.createTodo else {
                print("Result unexpectedly nil posting a new item")
                return
            }
            print("New item returned from server and stored in local cache, server-provided id: \(createResponce.id)")
            
            let newItem = ListTodosQuery.Data.ListTodo.Item(id: createResponce.id, name: createResponce.name, description: createResponce.description)
            
            _ = self?.appSyncClient?.store?.withinReadWriteTransaction(){ transaction in
                try transaction.update(query: ListTodosQuery()) { (data: inout ListTodosQuery.Data) in
                    guard data.listTodos != nil  else {
                        return
                    }
                    
                    var updatedItems = data.listTodos?.items?.filter({ $0?.id != temporaryLocalID })
                    updatedItems?.append(newItem)
                    
                    data.listTodos?.items = updatedItems
                }
            }
        }
        
    }
 
}

If we try to run our code, we notice that we are unable to save our todo item. “Error occurred posting a new item: Failed to authenticate request. Error: notSignedIn(message: “User is not signed in, please sign in to use this API.”)”

The reason is that we have to authenticate our user first. Let’s do it by creating the AuthenticationManager (for more details: http://trikalabs.com/aws-integrating-user-pools-for-ios-apps/).

Also, let’s open the storyboard and embed the UIViewController in a UINavigationController:

 

Also let’s add a UIButton, and create a didTapSignIn IBAction:

Now let’s add the following code, to enable users to register and sign in:

Swift
1
2
3
4
5
6
7
8
9
10
11
    let authenticationManager = AuthenticationManager()
    @IBAction func didTapSignIn(_ sender: Any) {
        guard let navigationController = self.navigationController else {
            return
        }
        authenticationManager.signIn(navigationController: navigationController) { [weak self] (userState, error) in
            if userState == UserState.signedIn {
                self?.save()
            }
        }
    }

Now we can delete the save() call from the setupAppSync method. 

Let’s test that everything is working as expected.

  1. Run the app
  2. Tap on the sign in button
  3. Register a user
  4. Open your email and enter the confirmation code
  5. Go to the AWS console
  6. Go to DynamoDB and open the table and check that our todo item is created:

If you want to have a look at how to support real-time data with subscriptions check the following article: http://trikalabs.com/aws-appsync-real-time-data-with-subscriptions/

Resources:

https://aws-amplify.github.io/docs/ios/api

https://github.com/aws-samples/aws-mobile-appsync-events-starter-ios

Tags: AmplifyAppSync
fragi

fragi

Related Posts

AWS: AppSync – Real-time data with subscriptions.
AWS

AWS: AppSync – Real-time data with subscriptions.

July 25, 2019

In the previous article (http://trikalabs.com/aws-appsync-swift/) we set up AppSync into our app. In this article, we will see how we...

AWS: Use Amazon PinPoint to collect analytics
AWS

AWS: Use Amazon PinPoint to collect analytics

July 25, 2019

In this article, we will add analytics to an iOS app by using Amplify and PinPoint. Create a project Firstly...

AWS: Integrate User Pools with Clean Architecture
AWS

AWS: Integrate User Pools with Clean Architecture

July 25, 2019

In the previous article(http://trikalabs.com/aws-integrating-user-pools-for-ios-apps/) we integrated User Pools into an iOS project. Also in the article http://trikalabs.com/clean-architecture/,we learned how to use...

AWS: Integrate User Pools on iOS Apps
AWS

AWS: Integrate User Pools on iOS Apps

July 25, 2019

Authenticate and manage users is a common task/need for many apps. In this article, we will set up an Amazon Cognito...

Next Post
AWS: AppSync – Real-time data with subscriptions.

AWS: AppSync - Real-time data with subscriptions.

Add SwiftLint to your iOS app

Add SwiftLint to your iOS app

Continuous Integration with TravisCI

Continuous Integration with TravisCI

  • Advertise
  • Privacy & Policy
  • Contact

© 2019 Trikalabs Ltd. All rights reserved.

No Result
View All Result
  • Home
  • About Me
  • A curated list with the best free online TDD videos
  • Book Suggestions
  • Pinner Code Club

© 2019 Trikalabs Ltd. All rights reserved.