AWS AmplifyによるiOSアプリ開発入門（Part1）

## AWS Amplifyとは？

モバイルアプリケーションとWebアプリケーションを簡単に構築することをサポートするためのフレームワーク
例えば、iOSのアプリケーションを作成しようとすると、認証にCognitoの設定、アクセス用のAPI Gatewayの設定、などを個別に作成する必要があります。
ですが、AWS Amplifyを使えば、Cloudformation経由ですべての設定をAWS Amplifyから行うことが可能。
また、構築した環境もdeleteコマンドで一発で削除できます

## この記事で説明すること

AWS Amplifyを使ったiOS開発の基礎について説明します

* Cognitoを使ったiOSアプリのログイン機能の実装
* S3にデータをアップロード機能の実装
* App Syncを用いて、DynamoDBにデータを追加、データを取得、サブスクライブ

## 謝辞

基本的には公式のiOSサンプルを参考に内容を作成しています
ありがとうございます
https://aws-amplify.github.io/docs/

では、早速始めましょう

## 前提条件

Mac端末があること（Mac OS High Sierraで動作確認済み）
Xcodeがインストール済み（Xcode 10.1で動作確認済み）
cocoapodsがインストール済み（pod 1.5.3で動作確認済み）

## AWS Amplifyのインストール

```shell


$ npm install -g @aws-amplify/cli

# 途中でAWSマネジメントコンソールへのログインを求められるので、ログインする
# Amplify用のIAMユーザの作成(Admin権限あり)を求められるので、作成する
# AWS CLIですでに設定を行っている場合は、そちらの設定を利用可能なので、この手順は不要
$ amplify configure
```

# Cognitoを使ったiOSアプリのログイン機能の実装

## Xcodeプロジェクトのベースを作成

1. Xcodeのプロジェクト作成画面から「Create a new Xcode project」を選択
2. 「Single View App」を選択
3. 「Product Name」に任意のプロジェクト名を入力（今回はamplify-learnと入力）
4. Languageが「Swift」になっていることを確認
5. 「Include Unit Tests」と「Include UI Tests」のチェックは外す
6. 保存先フォルダを選択し、「Create」をクリックする（今回は~/Downloadsに保存）


## AWS Amplifyのベースを作成

```shell

# 保存したXcode Project内に移動
# 今回はDownloads直下に作成したので以下のコマンドで移動
$ cd ~/Downloads/amplify-learn

# 初期設定
$ amplify init
# デフォルトのエディタを聞かれるので、好きなものを選択（今回はNoneを選択）
# ビルドするアプリのタイプを聞かれるので、「ios」を選択
# AWSのプロファイルを使用するか聞かれるので、「y」を入力
# 設定しているプロファイルを選択する（使用するIAMキーのこと、admin権限が必要）
```

上記コマンドをすべて実行した時点で、Cloudformation上でスタックが作成されます


## 使用するiOS SDKのインストールと設定

```shell

# Xcodeプロジェクトのルートに移動
$ cd ~/Downloads/amplify-learn

# podファイル作成
$ pod init
```

上記コマンドを実行すると、Podfileというファイルが作成されます
そのファイルを以下のように編集してください

```:Podfile
target 'amplify-learn' do
  use_frameworks!

  # Pods for amplify-learn
  pod 'AWSMobileClient', '~> 2.7.0'
  pod 'AWSAuthUI', '~> 2.7.0'
  pod 'AWSUserPoolsSignIn', '~> 2.7.0' 
end
```

下記コマンドで、必要なSDKをXcodeプロジェクトに追加できます

```shell

# 必要なSDKを追加
$ pod install --repo-update
```

**これ以降は「amplify-learn.xcodeproj」ではなく、「amplify-learn.xcworkspace」を開くようにしてください**


## Cognitoの作成と設定

今回iOSアプリの認証にCognitoを使用します
そのため、ユーザープールとIDプール（フェデレーティッドアイデンティティ）を作成する必要があります

```shell
$ amplify add auth
# 設定をどうするか聞かれるので、デフォルト設定を選択する

# Authが追加されているか確認する
$ amplify status
```

以下のような表示になっていればOK

Category | Resource name   | Operation | Provider plugin   |
| -------- | --------------- | --------- | ----------------- |
| Auth     | cognitoa8619e4c | Create    | awscloudformation |


```shell

# Cognitoを作成するようにクラウド側に命令
$ amplify push
# 続けていいか聞かれるので、trueを選択
```

上記コマンドは少し時間がかかります
コマンドが終了後、「awsconfiguration.json」というファイルが作成されます
この中に、ユーザープルの情報やIDプールの情報などが記載されており、
アプリ側はこの情報を使って認証を行います


## Cognitoの中をちょっと覗いてみる

ユーザープールは、簡単に言うとユーザーの管理を行う機能です
例えば、サインアップやサインインなどを管理できます

IDプールは、ユーザーにどのような権限を与えるのかを管理する機能です
例えば、ユーザープールでログイン済みのユーザにはS3のアクセス権限を与える、などが出来ます

今回はユーザープールとIDプールを連携させる必要があります
そのために、ユーザープールでアプリクライアントというものを作成し、そのクライアントIDをIDプールの認証プロバイダーに設定します。
これによって、ユーザープールでログインしたユーザに定義したロールを当てて、AWSのリソースへのアクセス権限を付与することが出来る、という流れになります


## 認証機能の実装

### awsconfiguration.jsonをXcodeプロジェクトに追加

* amplify-learn.xcworkspaceを開く
* 「awsconfiguration.json」をXcodeのamplify-learnプロジェクト直下にドラッグ&ドロップする
* Copy items if neededのチェックを外し、Create groupsを選択し、「Finish」をクリックする

### Navigation Controllerの追加とUI周りの実装

* Main.storyboardを開き、Navigation Controllerを追加する
* Root View Controller Sceneも一緒に作成されるので、Root View Controller Sceneを削除
* Navigation Controller Sceneを選択し、attributes inspectorを選択、「is 
 initial View Controller」のチェックをつける

これで最初にナビゲーションコントローラーが表示されるようになります

次に、Navigation Controller SceneからView Controller Sceneに対して、root view controller セグエを追加
これで、View Controller Sceneがナビゲーションコントローラ配下に入ったことになります
※画像ではわかりやすいために、「サインイン完了」というラベルをつけています

<img width="756" alt="Main_storyboard_—_Edited.png" src="https://qiita-image-store.s3.amazonaws.com/0/192873/8a80696c-08b5-021b-79d1-627185d2b3fe.png">

### 認証周りのコード実装

次にコード側を実装します
ViewController.swiftを以下のように編集してください

```swift:ViewController.swift

import UIKit

// 以下を追加
import AWSMobileClient

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 以下を追加
        AWSMobileClient.sharedInstance().initialize { (userState, error) in
            if let userState = userState {
                switch(userState){
                case .signedIn:
                    DispatchQueue.main.async {
                        print("Sign In")
                    }
                case .signedOut:
                    AWSMobileClient.sharedInstance().showSignIn(navigationController: self.navigationController!, { (userState, error) in
                        if(error == nil){       //Successful signin
                            DispatchQueue.main.async {
                                print("Sign In")
                            }
                        }
                    })
                default:
                    AWSMobileClient.sharedInstance().signOut()
                }
            } else if let error = error {
                print(error.localizedDescription)
            }
        }
    }
}
```

コードの詳細な解説は避けますが、重要なのは以下の関数です。
この関数によって、「awsconfiguration.json」の設定ファイルが読み込まれ、設定済みのAWSMobileClientシングルトンインスタンスが返ります。
これによって、ユーザープールの設定など面倒くさい作業を一切しなくていいです。
やったね！！

```
AWSMobileClient.sharedInstance()
```

この状態で、Xcode左上の実行ボタンを押してエミュレータを起動すると、
以下のような画面が表示されてサインインとサインアップが出来るようになります


<img width="423" alt="iPhone_XR_-_12_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/192873/870f42f1-846f-b5fe-854b-44b4b218b482.png">


「create new account」をクリックして、サインアップをしてみましょう
入力が完了すると、メールアドレス宛に認証コードが送信されるので、
そちらを入力するとサインインが完了しサインイン画面に移動します

次に、サインアウト処理を追加しましょう

storyboardにログアウトボタンを配置し、ViewController.swiftに対してアクション接続を作成してください
ConnectionはActionでNameはpushSignoutButtonとして作成します

下記のようにpushSignoutButton関数を修正してください

```swift:ViewController.swift(抜粋)

@IBAction func pushSignoutButton(_ sender: Any) {
    // サインアウト処理
    AWSMobileClient.sharedInstance().signOut()
    
    // サインイン画面を表示
    AWSMobileClient.sharedInstance().showSignIn(navigationController: self.navigationController!, { (userState, error) in
        if(error == nil){       //Successful signin
            DispatchQueue.main.async {
                print("Sign In")
            }
        }
    })
}
```

これで、サインアウトボタンをクリックすると、サインアウトされ、サインイン画面が表示されと思います

最終的な画面レイアウトは以下の図のようになります

<img width="774" alt="Main_storyboard_—_Edited 2.png" src="https://qiita-image-store.s3.amazonaws.com/0/192873/bbf8c192-5a17-222b-4ec9-f353bd9852e3.png">


## まとめ

AWS Amplifyを使ったiOSアプリ開発の基礎について説明しました。
Amplifyを使えば、バックエンドのサービスの構築とアプリ側の設定なども一括して設定することが可能になり、便利です。
今回は認証機能（サインアップ、サインアウト、サインイン）の実装を行いましたが、認証機能はどのアプリでも必須の機能です。
認証機能が数十行で実装できるのは、非常に開発者にとっては負担が軽くなるので、ぜひ利用してみてはいかがでしょうか？

[AWS AmplifyによるiOSアプリ開発入門（Part2）](https://qiita.com/papi_tokei/items/f984abdef1518ae8918b)では、S3にデータをアップロードする方法について、説明します。