new FirebaseとReact NativeでiOS, Android向けチャットを５分で作る

Firebaseが[新しくなりましたね](https://firebase.googleblog.com/2016/05/firebase-expands-to-become-unified-app-platform.html)！

せっかくなのでFirebaseの強みを活かして、リアルタイムなチャットアプリを作成してみましょう！React Nativeを活用して、iOS版とAndroid版を同時に作ります！
素早く（気持ち的に５分くらい）作ることを目標にするので実用性は無視します(/･ω･)/

## 1. Firebaseプロジェクトを作る。

新しくなったコンソール画面から新プロジェクトを作ります。適当な名前をつけて下さい。
https://console.firebase.google.com/

<img width="640" alt="Firebase.png" src="https://qiita-image-store.s3.amazonaws.com/0/29793/0025d6d7-ac2c-6e8e-7881-a6ab58ce8f45.png">

## 2. Databaseのセキュリティを変更する。

さて、作成後はプロジェクトの詳細画面に飛ばされてたと思います。
次にDatabaseのセキュリティを設定します。

Firebaseはエンドポイントさえ指定すればマルチプラットフォームで使えてしまいます。
その代わりセキュリティルールを指定して投稿や閲覧の権限を制限します。
デフォルトだとログインしないと投稿できないようになっており、今回ログイン機能には触れないので、とりあえず非ログインでのみ読み書き出来るようにワイルドに書き換えます。

以下の様な感じです。変更があると公開ボタンが出ると思うので公開します。誰でも投稿できるようになったので、自己責任で実験的な使用に留めておきましょう。

```
{
  "rules": {
    ".read": "auth == null",
    ".write": "auth == null"
  }
}
```

<img width="637" alt="Firebase.png" src="https://qiita-image-store.s3.amazonaws.com/0/29793/b22ec2f1-c6df-6a89-6095-806d0ae6ef25.png">

## 3. React Nativeのプロジェクトを作る

ReactNativeのインストールは完了している前提で進めます。
適当なプロジェクトを作成して、必要なライブラリを入れます。

```
react-native init ReactNativeFirebaseSample
cd ReactNativeFirebaseSample
npm install --save firebase@2.4.2 react-native-gifted-messenger
```

`firebase`のバージョンの指定は最新版(2016/05/19時点)だとReact Nativeに対応してないからです。
`react-native-gifted-messenger`はチャットビューのコンポーネントです。ありがたや！

## 4. 送受信のコードを書く

チャットのビューは先程インストールした`react-native-gifted-messenger`が出力してくれるので、送信、受信の処理だけ書いてみましょう。

まず、Firebaseを読み込みます。

``` index.ios.js
const Firebase = require('firebase');
```

次にチャットのコンポーネントを作ります。
心から５分という数字にこだわりを持っている方はGithubに[全ソース](https://github.com/imaimiami/react-native-firebase-chat-demo)をあげるので参考にしてください。100行くらいです。

Firebaseで作成したプロジェクトのエンドポイントURLを渡してFirebaseオブジェクトを作ります。

``` src/containers/MessageContainer.js
class MessengerContainer extends Component {

  constructor(props) {
    super(props);

    this._messagesRef = new Firebase("https://react-native-chat-sample.firebaseio.com/messages");
    this._messages = [];

    this.state = {
      messages: this._messages,
      typingMessage: ''
    };
  }
}
```

エンドポイントの記載は以下にあります。今回は`messages`以下にメッセージを収めるので`<firebase-url>/messages`のように指定します。

<img width="639" alt="Firebase 2.png" src="https://qiita-image-store.s3.amazonaws.com/0/29793/bde97563-8027-7393-14c2-4f7211e7a9ce.png">

送受信の処理を書きます。
`this._messagesRef.on('child_added', () => {})`は新規メッセージ追加時に発火します。特に条件を指定していないので、初回メッセージ読み込みと新規投稿時まとめて監視してます。

`this._messagesRef.push({})`でmessagesに新規オブジェクトを追加します。`UserName`と`AvatarUrl`は今回ざっくりiosとandroidで違うものをいれるようにしてます。ちゃんとやる時はFirebaseのログイン機能を使って、ユーザーのkeyなどをいれましょう。

``` src/containers/MessageContainer.js
class MessengerContainer extends Component {

  ...

  componentDidMount() {
    this._messagesRef.on('child_added', (child) => {
      this.handleReceive({
        text: child.val().text,
        name: child.val().name,
        image: {uri: child.val().avatarUrl || 'https://facebook.github.io/react/img/logo_og.png'},
        position: child.val().name == UserName && 'right' || 'left',
        date: new Date(child.val().date),
        uniqueId: child.key()
      });
    });
  }

  setMessages(messages) {
    this._messages = messages;

    this.setState({
      messages: messages,
    });
  }

  handleSend(message = {}) {
    this._messagesRef.push({
      text: message.text,
      name: UserName,
      avatarUrl: AvatarUrl,
      date: new Date().getTime()
    })
  }

  handleReceive(message = {}) {
    this.setMessages(this._messages.concat(message));
  }

  render() {
    return (
      <View style={{marginTop: CONTAINER_MARGIN}}>
        <GiftedMessenger
          styles={{
            bubbleRight: {
              marginLeft: 70,
              backgroundColor: '#007aff',
            },
          }}
          messages={this.state.messages}
          handleSend={this.handleSend.bind(this)}
          maxHeight={Dimensions.get('window').height - STATUS_BAR_HEIGHT - CONTAINER_MARGIN}
        />
      </View>
    );
  }
}
```

キモな部分は以上ですが、プロジェクト全体のコードは以下に置いときます。
https://github.com/imaimiami/react-native-firebase-chat-demo

## 5. 完成！

さて起動してみましょう。
`react-native run-ios`と適当なシミュレーターを起動して`react-native run-android`で走らせます。

![chat.gif](https://qiita-image-store.s3.amazonaws.com/0/29793/abab2c35-3c70-6465-a6c2-c9528f567f5b.gif)

片方に投稿すると同時に画面が更新されるのが見えると思います。

## 感想

ちなみに`react-native-gifted-messenger`には単にメッセージを表示するだけでなく、既読や画像表示などもっと高度な機能が備わっています。

本題のFirebaseですが、使用したデータストアの機能は今回のアップデートよりも前から使えた基本的なものです。ただ今回のアップデートで前よりグッと実際のアプリで活用してみたいと思える魅力的なサービスになったのではないでしょうか。

React Native+FirbaseでいうとjsSDKにReact Native向けのログイン画面は備わってないと思います(あったら教えてください)。今のところ自前でログインフォームを作ったり、twitter,Facebookのtokenを取得してFirebaseに渡さないといけないはずです。めちゃ大変なことではないですが、このへんもネイティブのSDK同様サポートされたらいいな...とちょっと期待してます。

以上！