React NativeとAWS MobileHubで作るサーバレスなモバイルアプリ開発

## AWS MobileHubとは

アプリのバックエンド側の構築・処理をまとめて管理し、速くシンプルにできるAWSのサービスです。
S3でデータ保存やdynamoDBとAPI Gatewayでデータ管理、Facebookでのユーザログインなど、モバイルアプリで利用することが多いAWSサービスを一元管理できるようになります。

詳しくはこちらの[公式サイト](https://aws.amazon.com/jp/mobile/)を確認してください。

## ここで記述すること

MobileHubを利用して、
- dynamoDBにデータの保存する方法
- S3に画像をアップロードする方法

## 準備
- React Nativeの開発環境
- AWSアカウント

## 手順

### 1. React Nativeアプリの作成

React Native公式の[Getting Started](https://facebook.github.io/react-native/docs/getting-started.html)よりアプリを作成


### 2. AWS MobileHubのセットアップ

1. [AWS MobileHub](https://console.aws.amazon.com/mobilehub/home#/)へアクセス
2. Your ProjectsのCreateから作成
3. Create a projectでプロジェクト名を入力
4. Select app platformでアプリケーションを選択<br>
React Nativeを選択し、Enable web hosting with your appにチェック
5. Set up your backendを参考にターミナル等で下記を実行

```shell:
# awsmobile-cliインストール
npm install -g awsmobile-cli

# awsmobileコマンドでセットアップ
cd (my-awesome-app)/client
awsmobile init (your-app-code)
 # 対話式で入力。ソースディレクトリは環境に合わせて入力、それ以外は基本デフォルトでOK
```

6.Connect to your backendでDone


**awsmobileコマンドのみでセットアップする場合**
こちらの[AWS公式](https://docs.aws.amazon.com/ja_jp/aws-mobile/latest/developerguide/react-native-getting-started.html)を参考にコマンドのみでセットアップもできます


※AMIユーザ作成がまだの場合[こちら](https://docs.aws.amazon.com/ja_jp/aws-mobile/latest/developerguide/aws-mobile-cli-credentials.html)を参考に作成しておく

```shell:
awsmobile configure
 # 対話式でAccess Key IDとSecret Access Keyを入力
```

### 3. Database作成

1. AWS MobileHubの対象プロジェクトでNoSQL Databaseを選択
2. Add Tableでテーブルを作成<br>
  ここでは**Notes**テーブル作成<br>
  項目
  - NoteId: string<br>
  - NoteTitle: string<br>
  - NoteContent: string<br>
3. ```awsmobile pull```実行し、ローカルに反映


### 4. CRUD API作成
APIを作成し、3.で作ったテーブルに登録します。
CRUD APIはAWS MobileHubからは現在作成できない？ため、ターミナルから作成し、それをpushします。

```shell:
awsmobile cloud-api enable --prompt
 # 対話式で設定
 # 下記を選択
 > Create CRUD API for an existing Amazon DynamoDB table 

 # 5.で作成したテーブル指定
 > Notes


# APIの設定をpush
awsmobile push
```

２つ目以降のCRUD APIを作成する場合、下記コマンドを実行

```shell:
awsmobile cloud-api configure
```

### 5. 必要パッケージインストール
```shell:
npm install aws-amplify --save
npm install aws-amplify-react-native --save
react-native link
```

### 6. ソースコード変更

```javascript:
import Amplify, { API } from 'aws-amplify-react-native';
import { awsmobile } from './aws-exports'; // aws-exports.jsのパス指定

Amplify.configure(awsmobile);
```

実際に登録するコード

```javascript:
async saveNote() {
  const newNote = {
    body: {
      NoteId: 'id001',
      NoteTitle: 'My first note!',
      NoteContent: 'This is so cool!'
    }
  }
  const path = "/Notes";

  // Use the API module to save the note to the database
  try {
    const apiResponse = await API.put("NotesCRUD", path, newNote)
    console.log("response from saving note: " + apiResponse);
  } catch (e) {
    console.log(e);
  }
}
```

登録したい箇所で下記を実行

```javascript:
this.saveNote();
```

### 7. とりあえず実行

```shell:
react-native run-ios
# or
react-native run-android
```
iOSの実機で実行する場合は、XcodeでRunする

## 画像をアップロードをする場合の手順

### 8. Storage(AWS S3)の有効化

1. AWS MobileHubの対象プロジェクトでUser Data Storageを選択
2. Store user dataを選択
3. Save chnages
4. ```awsmobile pull```実行し、ローカルに反映

これでS3にStorage用のバケットが作成されます。

### 9. 必要パッケージインストール

[react-native-image-picker](https://github.com/react-community/react-native-image-picker)
[react-native-fetch-blob](https://github.com/wkh237/react-native-fetch-blob)

```shell:
npm install react-native-fetch-blob --save
npm install react-native-image-picker@latest --save
npm install buffer
react-native link
```

### 10. files.js作成


files.jsを作成し、下記を記述

```javascript:
import { Buffer } from 'buffer';
import RNFetchBlob from 'react-native-fetch-blob';

export default {
  readFile(filePath) {
    return RNFetchBlob.fs.readFile(filePath, 'base64').then(data => new Buffer(data, 'base64'));
  },
};
```

### 11. ソースコードに追記

importに追記

```javascript:
import Amplify, { API, Storage } from 'aws-amplify-react-native';
import RNFetchBlob from 'react-native-fetch-blob';
import ImagePicker from 'react-native-image-picker';

import { awsmobile } from './aws-exports'; // aws-exports.jsのパス指定
import files from './files'; // files.jsのパス指定

Amplify.configure(awsmobile);
```

アップロードするコード

```javascript:
saveImage = () => {
  const options = {
    title: 'Select Avatar',
    storageOptions: {
      skipBackup: true,
      path: 'images'
    }
  };

  ImagePicker.showImagePicker(options, (response) => {
    console.log('Response = ', response);

    if (response.didCancel) {
      console.log('User cancelled image picker');
    }
    else if (response.error) {
      console.log('ImagePicker Error: ', response.error);
    }
    else if (response.customButton) {
      console.log('User tapped custom button: ', response.customButton);
    }
    else {
      RNFetchBlob
      .config({
        fileCache: true,
        appendExt: 'png',
      })
      .fetch('GET', response.uri, {
      })
      .then((res) => {
        // upload to storage
        files.readFile(res.data)
          .then(buffer => Storage.put('image.png', buffer, { level: 'public', contentType: 'image/png' }))
          .then(x => console.log('SAVED', x) || x);
      });
    }
  });
}
```

アップロードする箇所で下記を実行

```javascript:
this.saveImage();
```

アップロードした画像を表示する

```javascript:
// S3Imageを追加
import Amplify, { API, Storage, S3Image } from 'aws-amplify-react-native';

// ~ 省略 ~

// 表示
<S3Image imgKey={'image.png'} />
```

### 12. 実行

```shell:
react-native run-ios
# or
react-native run-android
```
iOSの実機で実行する場合は、XcodeでRunする



## 参考
- [AWS公式 MobileHub開発ガイド](https://docs.aws.amazon.com/ja_jp/aws-mobile/latest/developerguide/what-is-mobile-hub.html)
- [AWS公式 Mobile開発ガイド(英語)](https://docs.aws.amazon.com/ja_jp/aws-mobile/latest/developerguide/what-is-aws-mobile.html)
- [AWS公式 サンプルアプリ](https://github.com/awslabs/aws-mobile-react-native-starter)