react-native-camera を使ってカメラアプリ(バーコード読み込み)を作成

バージョンなど問題があり、少しハマったので現バージョンの情報をベースに構築した内容をメモ

# 実行環境

Mac: MacBook Air 13'' Early 2015
macOS 10.13.1 (High Sierra)

Xcode 9.2
node.js: v6.11.5

react-native-cli: 2.0.1
react-native-camera: v1beta

# react-native インストール

npm コマンドで react-native-cli をインストール。

```
npm install -g react-native-cli
```

# プロジェクトの作成

react-native コマンドを使って初期プロジェクトを作成。

```
react-native init cameraapp
```

出来上がったプロジェクトのパッケージを確認してみる。

```
cd cameraapp
cat package.json
```

実行結果

react:16.0.0
react-native: 0.51.0
がインストールされた。

```
{
  "name": "cameraapp",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
  },
  "dependencies": {
    "react": "16.0.0",
    "react-native": "0.51.0"
  },
  "devDependencies": {
    "babel-jest": "21.2.0",
    "babel-preset-react-native": "4.0.0",
    "jest": "21.2.1",
    "react-test-renderer": "16.0.0"
  },
  "jest": {
    "preset": "react-native"
  }
}
```

# react-native-camera インストール

続いて react-native-camera をインストール。

react-native: 0.51 では react-native-camera v0.4, v0.6 が動かなかったため、
ホームページより v1 のページの readme[https://github.com/lwansbrough/react-native-camera/tree/v1] を見て実行。

```
npm install rnpm --global
npm install react-native-camera@https://github.com/lwansbrough/react-native-camera.git --save
rnpm link react-native-camera
```

rnpm@1.9.0
react-native-camera@0.12.0
がインストールされた。

# カメラコードの追加

ホームページにある Usage を貼り付けても動く動かないので、少しアレンジしてバーコード読み込み処理とメッセージダイアログ(Modal)を貼り付け。
Modal は FaceBook の API ドキュメントのサンプルを参考に実装。

```
vi App.js 
```

コード内容

```
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  Platform,
  Dimensions,
  StyleSheet,
  Text,
  TouchableOpacity,
  TouchableHighlight,
  Modal,
  View
} from 'react-native';

import Camera from 'react-native-camera';


const instructions = Platform.select({
  ios: 'バーコードを読み取ります。',
  android: 'Double tap R on your keyboard to reload,\n' + 'Shake or press menu button for dev menu',
});

export default class App extends Component<{}> {
  state = {
    visibleModal: 0,
    barcode: { type:null, data:null, bounds:null },
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Camera app
        </Text>
        <Text style={styles.instructions}>
          Read the Barcode
        </Text>
        <Camera
          ref={(cam) => {
            this.camera = cam;
          }}
          style={styles.preview}
          onBarCodeRead={this.onBarcodeRead.bind(this)}
          aspect={Camera.constants.Aspect.fill}>
          <Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text>
        </Camera>
        <Modal visible={this.state.visibleModal === 1}>
          {this.renderModalContent()}
        </Modal>
      </View>
    );
  }

  takePicture() {
    this.camera.capture()
      .then((data) => console.log(data))
      .catch(err => console.error(err));
  }
  onBarcodeRead(data) {
    console.log(data);
    this.setState({ visibleModal: 1, barcode: data })
  }

  renderModalContent = () => (
    <View style={styles.modalContent}>
      <Text>type:{this.state.barcode.type}</Text>
      <Text>data:{this.state.barcode.data}</Text>
      {this.renderButton('Close', () => this.setState({ visibleModal: null }))}
    </View>
  )
  renderButton = (text, onPress) => (
    <TouchableOpacity onPress={onPress}>
      <View style={styles.button}>
        <Text>{text}</Text>
      </View>
    </TouchableOpacity>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  preview: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center',
    height: Dimensions.get('window').height,
    width: Dimensions.get('window').width
  },
  capture: {
    flex: 0,
    backgroundColor: '#fff',
    borderRadius: 5,
    color: '#000',
    padding: 10,
    margin: 40
  },
  button: {
    backgroundColor: 'lightblue',
    padding: 12,
    margin: 16,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 4,
    borderColor: 'rgba(0, 0, 0, 0.1)',
  },
  modalContent: {
    backgroundColor: 'white',
    padding: 22,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 4,
    borderColor: 'rgba(0, 0, 0, 0.1)',
  },


});
```

# アプリのコンパイル・実行

react-native run-ios コマンドでコンパイル＆実行

```
react-native run-ios
```

しばらくするとシミュレータが起動する。

# xcode 上の調整

今回の環境では、そのままではシミュレータが起動してアプリ実行後ブラックアウトのまましばらくすると落ちてしまうので、xcode 上で微調整を実施。
ホームページ上の Manual install 手順を実行。

修正作業は以下3点
- libRTCamera.a の参照設定
- Signing Certificate の設定
- Info.plist への追記

## libRTCamera.a の参照設定

xcode を起動して、作成したプロジェクトを読み込む。

ファイルチューザーで cameraapp/ios/cameraapp.xcodeproj を選択

ファイルリストから Libraries を選択

Add File to "cameraapp" を選択

cameraapp/node_modeules/

![スクリーンショット 2017-12-21 3.15.21.png](https://qiita-image-store.s3.amazonaws.com/0/141148/b7d6596b-1bfb-55dd-8c11-d516706055fa.png)

## Signing Certificate の設定

Apple Developer Program に参加していない場合、プロジェクトの General / Signing タブにて証明書の設定を手動で行う必要がある。

Bundle Identifier の設定変更

![スクリーンショット 2017-12-21 3.16.47.png](https://qiita-image-store.s3.amazonaws.com/0/141148/3939d7fa-8263-7540-515b-7aee0e9ac06e.png)

General タブの Identity / Bundle Identifier を適切な名前に変更する。

Team のセレクトボックスにて、Add an Account を選択して、AppleID を入力する。

Bundle Identifier の設定変更


## Info.plist への追記

以下の設定を Info.plist へ追加する。

```
<key>NSCameraUsageDescription</key>
<string>Your message to user when the camera is accessed for the first time</string>

<!-- Include this only if you are planning to use the camera roll -->
<key>NSPhotoLibraryUsageDescription</key>
<string>Your message to user when the photo library is accessed for the first time</string>

<!-- Include this only if you are planning to use the microphone for video recording -->
<key>NSMicrophoneUsageDescription</key>
<string>Your message to user when the microphone is accessed for the first time</string>

<!-- Include this only if you are planning to use the camera roll -->
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Your message to user when the photo library is accessed for the first time</string>

```

![スクリーンショット 2017-12-24 12.08.09.png](https://qiita-image-store.s3.amazonaws.com/0/141148/6609fa0e-2a7b-9227-e24c-7361df159c36.png)


直接編集せず、xcode 上から Info.plist をダブルクリックして開き、適当なパラメータの＋ボタンでキー名を入れて登録した。

react-native run-ios コマンドにて実行。

![スクリーンショット 2017-12-24 12.06.25.png](https://qiita-image-store.s3.amazonaws.com/0/141148/e92304d4-9b93-5f96-6316-87993335a87d.png)

エミュレータではカメラキャプチャ部分は黒く表示される。

## 実機での確認

iPhone をUSB で接続して、xcode を開く。

XCode から　Prodcut => Desitination にて自分の iPhone を選択

Product => Run または ⌘＋R で実行。

デベロッパープログラムに参加しないで開発を行っているので、iphone 上でデベロッパーAPPの認証を行う必要がある。
iphoneで 設定　=> 一般 => プロファイルとデバイス管理を開くと、先ほど指定した自分の Appleアカウントのデベロッパー名が表示されるので、タップして信頼をする。

![スクリーンショット 2017-12-24 12.29.38.png](https://qiita-image-store.s3.amazonaws.com/0/141148/c4709d24-e03a-fd7e-fcef-1d59a21bb8c7.png)


バーコードが認識された場合、ポップアップにてコード種別とコードが表示される。

![スクリーンショット 2017-12-24 12.24.46.png](https://qiita-image-store.s3.amazonaws.com/0/141148/39d83f1a-06be-b73c-f119-3e045b145d73.png)


# 参考

https://facebook.github.io/react-native/docs/getting-started.html