iOSアプリからFirebase Cloud Messaging経由でプッシュ通知を送る

## はじめに

現在、個人開発してるアプリでチャットみたいな仕組み(サーバの話)を作っててFirebase Databaseにデータが追加されたら指定ユーザやトピックにプッシュ通知を配信するっていうのをできないかなぁって思ってやってみました。

元ネタは [Firebase で iOS クライアントからプッシュ通知を送る](http://qiita.com/plasticstraw/items/1c8d8e7f6833528b984f) です。

私はNode.jsが(デプロイ実装)できないのでPythonでしました。

## 環境

- Python 3.5.2 (3.6を入れ忘れてました。)
- Xcode8.2.1
- iOS10.2

## iOS側の準備

### プッシュ通知準備

```swift:AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound],
                                                  categories: nil)
    application.registerUserNotificationSettings(settings)
    application.registerForRemoteNotifications()
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    // APNS登録
    #if DEBUG
    FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: .sandbox)
    #else
    FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: .prod)
    #endif
}

func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
    // トピックに登録
    FIRMessaging.messaging().subscribe(toTopic: "/topics/news")
}
```

あとのプッシュ通知をするためのhogeをやっておいてください！

### Firebase Databaseにデータを追加する

```swift:Hoge.swift
class Hoge: NSObject {
    class func create() {
        let ref = FIRDatabase.database().reference()

        let data: [String : String] = ["title": "タイトル", "body": "本文"]
        ref("message").childByAutoId().setValue(data, withCompletionBlock: { (error, reference) in
            if error != nil {
                print("Success")
            } else {
                print("Failure")
            }
        }
    }
}
```

THE 雑ですがこんなもんでしょう

## Firebase Databaseを監視するコード

```:requirements.txt
docutils==0.13.1
gcloud==0.17.0
googleapis-common-protos==1.5.0
httplib2==0.9.2
jws==0.1.3
lockfile==0.12.2
oauth2client==3.0.0
protobuf==3.2.0rc1
pyasn1==0.1.9
pyasn1-modules==0.0.8
pycryptodome==3.4.3
Pyrebase==3.0.27
python-daemon==2.1.2
python-jwt==2.0.1
requests==2.11.1
requests-toolbelt==0.7.0
rsa==3.4.2
six==1.10.0
```

```python:app.py
import daemon
import json
import pyrebase
import requests

config = {
    # GoogleService-info.plistのAPI_KEY
    "apiKey": "API_KEY",
    "authDomain": "PROJECT_NAME",
    "databaseURL": "https://PROJECT_NAME.firebaseio.com/",
    # いらないかもしれない
    "storageBucket": "gs://PROJECT_NAME.appspot.com"
}

fcm_header = {
    'Content-type': 'application/json; charset=UTF-8',
    # このコードの下の画像を参照してください！
    'Authorization': 'key=API_KEY'
}

firebase = pyrebase.initialize_app(config)

def stream_handler(message):
    if message['data'] == None:
        return
    if message['path'] == '/':
        return

    if 'title' in message['data']:
        title = message['data']['title']
    else:
        title = '配信'

    if 'body' in message['data']:
        body = message['data']['body']
    else:
        body = ''

    payload = {'to': '/topics/news', 
               'priority': 'high', 
               'notification': {
                   'title': title,
                   'body': body,
                   'badge': 1,
                   'sound': 'default'
               }
              }

    r = requests.post('https://fcm.googleapis.com/fcm/send', headers = fcm_header, data = json.dumps(payload))
    print(r.json())

if __name__ == '__main__':
    db = firebase.database()
    dc = daemon.DaemonContext()
    with dc:
        stream = db.child("message").stream(stream_handler)
```

#### API_KEY場所

![スクリーンショット_2017-01-18_0_46_34.png](https://qiita-image-store.s3.amazonaws.com/0/97280/7e110341-7a68-7373-b198-684428e0e764.png "スクリーンショット_2017-01-18_0_46_34.png")

(プライバシーは保護させてください！！)

### 監視コードの説明

Pythonパッケージは `Pyrebase` と `python-daemon` を使用させていただきました。

```bash
$ pip install pyrebase python-daemon
```

- [Pyrebase](https://pypi.python.org/pypi/Pyrebase)
- [python-daemon](https://pypi.python.org/pypi/python-daemon)

Firebase Databaseの `/message` ディレクトリを見に行ってます。

```python
if message['data'] == None:
    return
if message['path'] == '/':
    return
```

ここらへんは、`/message` にデータがないときにエラーが起きた(?)ので例外処理です。監視コード起動時プッシュ通知を送ってくれるのにiOS側には通知は来てるけどアラートが表示がされない！っていうことがあったので入れています。

あとはサーバ側でスクリプトを実行するだけです。
ちなみに `python-daemon` を使用することでスクリプトをデーモン化してくれます！とてもありがたいですね！

```bash
$ python app.py
```

## 実行サンプル

### Firebase Databaseサンプル

![Firebase Databaseサンプル](https://qiita-image-store.s3.amazonaws.com/0/97280/21f3e335-d9a2-fca5-a846-f973d5a18075.png "スクリーンショット 2017-01-18 1.28.59.png")

### 通知結果

<img width="320" alt="47ad6b91-a6cd-ff84-c153-f0abd580a820.png" src="https://qiita-image-store.s3.amazonaws.com/0/97280/711b901f-3a9b-2710-9a66-2a8ad656b44f.png">