NCMBのReact Native SDKを使ってカメラメモアプリを作る（その2：データの保存について）

NCMBでは公式SDKの他、コミュニティSDKとして各種言語、フレームワーク向けにSDKを提供しています。今回はReact Native SDKを使ったカメラメモアプリ開発を解説します。カメラで写真を撮影して、そこに説明を付けて保存できるというアプリです。ファイルストアとデータストアの機能を利用します。

[前回は各画面やコンポーネントの説明、そしてSDKの導入方法について解説](https://qiita.com/goofmint/items/6bd992a90f713ce6850a)しました。今回は写真の撮影と、データの保存について解説します。

## コードについて

今回のコードは[NCMBMania/react-native-camera-app: React Native SDKを使ったカメラメモアプリのデモです](https://github.com/NCMBMania/react-native-camera-app)にアップロードしてあります。また、Expo Snackでも使用できます。

[カメラメモアプリ](https://snack.expo.dev/@git/github.com/NCMBMania/react-native-camera-app)

※ Expo SnackのWebの場合、アイコン表示と画像のリスト表示に不具合があります

## 写真の撮影について

写真の撮影はHome.jsのonPressにて実行します。実行環境がWebではない場合（iOSやAndroid）、あらかじめ `ImagePicker.requestMediaLibraryPermissionsAsync` を使ってメディアライブラリを利用する許諾を取っておきます。

```js
export default function Home({ navigation }) {
  // メディアライブラリを利用する許可を得る関数
  const requestMediaLibraryPermissions = async () => {
    const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
    // 許可が得られなかった場合はアラート
    if (status !== 'granted') {
      alert('Sorry, we need camera roll permissions to make this work!');
    }
  }

  // 表示された際に1回だけ実行する関数
  useEffect(() => {
    // Webじゃなければライブラリ利用の許可を得る
    if (Platform.OS !== 'web') {
      requestMediaLibraryPermissions();
    }
  }, []);

  // 表示内容
  return (
    <View style={styles.container}>
      <IconButton icon="camera" size={100} onPress={onPress} />
    </View>
  );
}
```

### onPressの処理

onPressでは、写真を取得し、入力画面（InputScreen）へ遷移します。

```js
// カメラアイコンをタップした際の処理
const onPress = async () => {
  // ライブラリにアクセスして画像を取得する
  const image = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [4, 3],
    quality: 0.7,
  });
  // 入力画面に遷移する
  navigation.navigate('Input', { image });
}
```

## 入力画面について

入力画面（InputScreen）では、受け取った画像を表示します。実際の表示はInputForm.jsにて行います。メモはtextという変数をuseStateで準備しておき、そこに入ります。

```js
export default function InputForm({ image, navigation }) {
  // メモの内容
  const [ text, setText ] = useState('');

  // 表示内容
  return (
    <View>
      <View>
        <Image source={{uri: image.uri}} style={styles.image} />
      </View>
      <View>
        <TextInput
          label="メモ"
          value={text}
          style={styles.textInput}
          multiline={true}
          numberOfLines={3}
          onChangeText={(text) => setText(text)}
        />
      </View>
      <View style={styles.save}>
        <IconButton
          icon="content-save"
          size={25}
          onPress={onPress}
        />
      </View>
    </View>
  )
}
```

### onPressの処理

onPressの処理で、保存処理を実行します。

```js
// 保存アイコンをタップした際のイベント
const onPress = async () => {
  try {
    // 処理はこの中に書きます
  } catch (e) {
    alert(e.message);
  }
}
```

まずファイル名を適当に生成します。

```js
// ファイル名をランダムに生成
const fileName = `${Math.random().toString(32).substring(2)}.jpg`;
```

次にPromiseが入る配列を準備します。ここに、ファイルストアへのアップロード処理を入れます。

```js
const promises = []; // 非同期処理を入れる配列
// ファイルストアへのアップロード
promises.push(NCMBFile.upload(fileName, image));
```

そしてデータストアのオブジェクトを用意します。これはDBでいう行相当のデータです。

```js
// データストアのオブジェクト（DBでいう行相当）を準備
const obj = new NCMBObject('Memo');
```

オブジェクトには set メソッドを使って、データを設定します。1つ目の引数がカラム名、2つ目の引数が実際の値だと考えてください。

```js
obj
  .set('text', text)           // メモの内容
  .set('fileName', fileName);  // アップロードしたファイル名
```

後は保存を実行します。この処理もPromiseの配列にし、Promise.allで処理完了を待ちます。

```js
// 保存実行
promises.push(obj.save());
// Promise.allで処理完了待ち
await Promise.all(promises);
```

保存が完了したら、前の画面に戻ります。

```js
// 前の画面に戻る
navigation.goBack();
```

これでデータストアとファイルストアへのデータ保存処理が完成します。

## まとめ

今回はデータストアとファイルストアへのデータ保存について解説しました。[次回はデータストアからの取得、そしてファイルダウンロードを解説](https://qiita.com/goofmint/items/28aeb59dee79b32b4897)します。

[NCMBMania/ncmb-react-native: NCMB SDK for React Native](https://github.com/NCMBMania/ncmb-react-native)

## おまけ

NCMBのReact Native SDKを使ったハンズオンは随時開催しています。ぜひチェックしてください。

[【お昼のハンズオン】React Native × NCMBでTodoアプリを作ろう \- connpass](https://ncmb.connpass.com/) など