AWS の開始方法

iOS アプリケーションを構築する

AWS Amplify を使用してシンプルな iOS アプリケーションを作成する

モジュール 4: GraphQL API とデータベースを追加する

このモジュールでは、Amplify CLI とライブラリを使用して、アプリに GraphQL API を設定して追加します。

はじめに

ユーザー認証を使用するアプリを作成、設定できたので、API および CRUD (作成、読み取り、更新、削除) オペレーションをデータベースに追加しましょう。

このモジュールでは、Amplify CLI およびライブラリを使用して、アプリに API を追加します。作成する API は Amazon DynamoDB (NoSQL データベース) によってサポートされる GraphQL API であり、AWS AppSync (マネージド型 GraphQL サービス) を活用します。GraphQL の概要については、こちらのページにアクセスしてください。

これから構築するアプリは、ユーザーがメモを作成、削除、一覧表示するためのメモアプリです。このモジュールを実施することで、人気のあるさまざまな CRUD + L (作成、読み取り、更新、削除、一覧表示) アプリケーションを構築する方法がよくわかります。

学習内容

  • GraphQL API を作成してデプロイする
  • API と対話するためのフロントエンドコードを記述する

主要な概念

API – 複数のソフトウェア仲介者間の通信および相互作用を可能にするプログラミングインターフェイスを提供します。

GraphQL – アプリケーションの型付き表現に基づくクエリ言語とサーバー側 API の実装。この API 表現は、GraphQL 型システムに基づくスキーマを使用して宣言されます。(GraphQL の詳細については、このページにアクセスしてください)。

 所要時間

20 分

 使用するサービス

実装

  • GraphQL API サービスおよびデータベースを作成する

    GraphQL API およびそれをサポートするデータベースを作成するには、ターミナルを開き、プロジェクトディレクトリからこのコマンドを実行します。

    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

    プロジェクトを初期化 (amplify init) した際に選択したデフォルトのテキストエディタが、事前構築済みのデータスキーマと共に開かれます。

    スキーマを削除し、アプリの GraphQL スキーマに置き換えます。

    type NoteData
    @model
    @auth (rules: [ { allow: owner } ]) {
        id: ID!
        name: String!
        description: String
        image: String
    }

    データモデルは NoteData という 1 つのクラスおよび 4 つのプロパティで構成されます。id と name は必須ですが description と image は任意の文字列です。

    @model トランスフォーマーが、データを保存するためのデータベースを作成しようとしていることを示しています。

    @auth トランスフォーマーが、これらのデータへのアクセスを許可する認証ルールを追加します。このプロジェクトでは、NoteData の所有者だけがアクセスできるようにします。

    完了したら、必ず保存してください。

  • クライアントサイドコードを生成する

    作成した GraphQL データモデル定義に基づき、Amplify によってクライアントサイドコード (すなわち Swift コード) が生成され、アプリ内のデータが記述されます。

    コードを生成するには、ターミナルで次のコマンドを実行します。

    amplify codegen models

    これにより、ご覧のとおり amplify/generated/models ディレクトリに Swift ファイルが作成されます。

    ➜  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

    これらのファイルを Xcode プロジェクトにインポートします。Finder でファイルを見つけたら、Xcode のプロジェクトにドラッグアンドドロップします。

    iOSTutorial-Module4-Step1
  • API サービスおよびデータベースをデプロイする

    作成したバックエンド API およびデータベースをデプロイするには、ターミナルでこのコマンドを実行します。

    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

    数分後、成功したことを示すメッセージが表示されます。

    ✔ All resources are updated in the cloud
    
    GraphQL endpoint: https://yourid.appsync-api.eu-central-1.amazonaws.com/graphql
  • API クライアントライブラリを Xcode プロジェクトを追加する

    コードに進む前に、Amplify API ライブラリをプロジェクトの依存関係に追加します。Podfile ファイルを開き、AmplifyPlugins/AWSAPIPlugin という文字列を含む行を追加するか、または次のファイル全体をコピーして貼り付けます。

    # you need at least version 13.0 for this tutorial, more recent versions are valid too
    platform :ios, '13.0'
    
    target 'getting started' do
      # Comment the next line if you don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for getting started
      pod 'Amplify', '~> 1.0'                             # required amplify dependency
      pod 'Amplify/Tools', '~> 1.0'                       # allows to call amplify CLI from within Xcode
    
      pod 'AmplifyPlugins/AWSCognitoAuthPlugin', '~> 1.0' # support for Cognito user authentication
      pod 'AmplifyPlugins/AWSAPIPlugin', '~> 1.0'         # support for GraphQL API
    
    end

    ターミナルでこのコマンドを実行します。

    pod install

    このコマンドは完了するまでにしばらく時間がかかります。次のように表示されます (実際のバージョン番号は異なる場合があります)。

    Analyzing dependencies
    Downloading dependencies
    Installing AmplifyPlugins 1.0.4
    Installing AppSyncRealTimeClient (1.1.6)
    Installing ReachabilitySwift (5.0.0)
    Installing Starscream (3.0.6)
    Generating Pods project
    Integrating client project
    Pod installation complete! There are 4 dependencies from the Podfile and 11 total pods installed.
  • 実行時に Amplify ライブラリを初期化する

    Xcode に戻って Backend.swift を開き、private init() メソッドの Amplify 初期化シーケンスに 1 行を追加します。コードブロックが完成すると、以下のようになります。

    // 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)")
    }
  • GraphQL データモデルとアプリモデル間のブリッジングを追加する

    プロジェクトには Note を表すデータモデルが既にあります。そのため、引き続きそのモデルを使って簡単に NoteData を Note に変換できる設計にすることにしました。ContentView.swift を開き、この初期化子を Note クラスに追加します。

    convenience init(from data: NoteData) {
        self.init(id: data.id, name: data.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: self.id,
                                name: self.name,
                                description: self.description,
                                image: self.imageName)
        }
    
        return _data!
    }
  • API CRUD メソッドを Backend クラスに追加する

    API を呼び出す 3 つのメソッドを追加しましょう。メモをクエリするメソッド、新しいメモを作成するメソッド、メモを削除するメソッドです。これらのメソッドは、アプリデータモデル (Note) 上で動作して、ユーザーインターフェイスから簡単にやり取りできるようになっています。これらのメソッドは透過的に Note を GraphQL の NoteData オブジェクトに変換します。

    Backend.swift ファイルを開き、Backend クラスの末尾に次のスニペットを追加します。

        // MARK: API Access
    
        func queryNotes() {
    
            _ = Amplify.API.query(request: .list(NoteData.self)) { event in
                switch event {
                case .success(let result):
                    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)
                            DispatchQueue.main.async() {
                                UserData.shared.notes.append(note)
                            }
                        }
    
                    case .failure(let error):
                        print("Can not retrieve result : error  \(error.errorDescription)")
                    }
                case .failure(let error):
                    print("Can not retrieve Notes : error \(error)")
                }
            }
        }
    
        func createNote(note: Note) {
    
            // use note.data to access the NoteData instance
            _ = Amplify.API.mutate(request: .create(note.data)) { event in
                switch event {
                case .success(let result):
                    switch result {
                    case .success(let data):
                        print("Successfully created note: \(data)")
                    case .failure(let error):
                        print("Got failed result with \(error.errorDescription)")
                    }
                case .failure(let error):
                    print("Got failed event with error \(error)")
                }
            }
        }
    
        func deleteNote(note: Note) {
    
            // use note.data to access the NoteData instance
            _ = Amplify.API.mutate(request: .delete(note.data)) { event in
                switch event {
                case .success(let result):
                    switch result {
                    case .success(let data):
                        print("Successfully deleted note: \(data)")
                    case .failure(let error):
                        print("Got failed result with \(error.errorDescription)")
                    }
                case .failure(let error):
                    print("Got failed event with error \(error)")
                }
            }
        }

    同じ Backend.swift ファイルで、updateUserData(withSignInStatus:) メソッドを以下のように更新します。

    // change our internal state, this triggers an UI update on the main thread
    func updateUserData(withSignInStatus status : Bool) {
        DispatchQueue.main.async() {
            let userData : UserData = .shared
            userData.isSignedIn = status
    
            // when user is signed in, query the database, otherwise empty our model
            if status {
                self.queryNotes()
            } else {
                userData.notes = []
            }
        }
    }

    後は、新しいメモの作成とリストからのメモの削除を行うためのユーザーインターフェイス部分を作るだけです。

  • メモを追加するための編集ボタンを追加する

    バックエンドおよびデータモデル部分が準備できたので、このセクション最後のステップとして、ユーザーがメモを新規作成したり、削除したりできるようにします。

    Xcode で ContentView.swift を開きます。

    a.ContentView 構造体に、ユーザーインターフェイスにバインドされた状態変数を追加します。

    // 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.ファイルの任意の場所に View 構造体を追加して、ユーザーが新しいメモを作成できるようにします。

    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
                        self.userData.notes.append(note)
                    }) {
                        Text("Create this note")
                    }
                }
            }
        }
    }

    c.ナビゲーションバーに、メモを作成するためのシートを表す [+] ボタンを追加します。

    ContentView 構造体に戻り、navigationBarItems(leading SignOutButton()) を以下に置き換えます。

        .navigationBarItems(leading: SignOutButton(),
                            trailing: Button(action: {
            self.showCreateNote.toggle()
        }) {
            Image(systemName: "plus")
        })
    }.sheet(isPresented: $showCreateNote) {
        AddNoteView(isPresented: self.$showCreateNote, userData: self.userData)
  • 「スワイプして削除」の操作を追加する

    最後に、ContentView で「スワイプして削除」の操作を追加します。ForEach 構造体には .onDelete { } メソッドを追加します。

    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)
        }
    }
  • 構築およびテストする

    すべてが想定どおりに動作することを確認するには、プロジェクトを構築して実行します。[Product (製品)] メニューをクリックして [Run (実行)] を選択するか、または ⌘R を押します。何のエラーも発生しないはずです。

    引き続きサインイン済みの状態であれば、アプリが起動して空のリストが表示されます。メモを追加するための [+] ボタンが追加されています。+ 記号をタップし、[Create this Note (このメモを作成)] をタップすると、リストにメモが表示されます。

    AddNoteView は下方向に引っ張ると閉じます。iOS シミュレーターでは、[+] を再度タップすることはできません。最初にリストを下方向に「引っ張って更新」する必要があることに注意してください。

    メモは行を左にスワイプすることで削除できます。

    完全なフローは以下のとおりです。

    iOSTutorial-Module4-step2
    iOSTutorial-Module4-step3
    iOSTutorial-Module4-step4
    iOSTutorial-Module4-step5

まとめ

iOS アプリを作成できました。 AWS Amplify を使用して、GraphQL API を追加し、作成、読み取り、削除機能をアプリに設定しました。

次のモジュールでは、写真を管理するための UI と動作を追加します。

このモジュールは役に立ちましたか?

ありがとうございます
このチュートリアルで良かった点をお聞かせください。
閉じる
ご期待に添えず申し訳ありません
古い説明、わかりにくい説明、間違った説明はございませんでしたか? このチュートリアルの改善のために、ぜひフィードバックをお寄せください。
閉じる

ストレージを追加する