14
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Parse.com REST APIを使ったAPN(remote notification)を試した

Last updated at Posted at 2013-01-21

Parse.comでは、Push Notificationをサポートしています。
見た目にもわかりやすいUIのDashboardからPush notificationを送信できるのですが、
それ以外にREST APIを使って、アプリ側からPush notificationを送信できます。
DeviceTokenを使って細かく制御する方法が若干わかりにくかったので、それについて書きます。

##Parse APIへの送信

Push notificationは最低限alertがあればいいので、curlを使うと下記のように指定して送信します。
X-Parse-Application-Id(Application ID)とX-Parse-REST-API-Key(REST API Key)は、DashboardのOverviewから取得出来ます。
後はなんとログインした状態で、Documentを見ている場合、コード例に直接Keyが埋め込まれているためそのまま実行出来ます!!

curl -X POST \
  -H "X-Parse-Application-Id: <Application ID>" \
  -H "X-Parse-REST-API-Key: <REST API Key>" \
  -H "Content-Type: application/json" \
  -d '{
        "data": {
          "alert": "The Giants won against the Mets 2-3."
        }
      }' \
  https://api.parse.com/1/push

##ParseのNotificationフォーマットについて

簡単に表すと下記のJSONフォーマットになります。
whereのところに条件を指定するので、ここにdeviceTokenを指定すれば特定の端末にPush Notificaitonを送信することができます。

  • dataのところに、iOSまたはAndroidのnotificationデータを入れる
  • 送信日時、有効期限の指定が簡単
  • whereのところに、条件Queryを指定できる
    • $lt(less then)、$in(containd in)など、一般的な条件演算子がひと通り揃っているので、柔軟なリクエストが可能
parse_push.py
{'where': {'deviceToken': [],                   # Query APIを使った条件指定
    },
    'push_time': '2013-01-23T12:00:00Z',        # 通知開始日時(iso)
    'expiration_interval': '518400',            # 有効期限
    'expiration_time': '2013-01-23T21:08:39Z',  # 有効期限日時指定(iso)
    'data': {
        'alert': 'message',
        'badge': 'Increment',                   # Number or 'Increment'(iOS only).
        'sound': 'sound bundle',                # iOS only.
        'content-available': '1',               # Newsstand content count(iOS only).
        'action': 'Intent',                     # Android only.
        'title': 'Notification!!',              # Android only.
    }
}

条件演算子を指定した場合は、こんな感じです。

parse_push2.py
{'data': {'alert': 'push notification message!',
          'badge': 'Increment',
          'sound': 'default'},
 'where': {'deviceToken': {'$in': [u'<deviceToken1>', u'<deviceToken2>']}}}

###コード例

Pythonで書くとこんな感じになります。

apn.py
# coding: utf-8

PARSE_API_BASE = 'https://api.parse.com'
PARSE_PUSH_API = '/1/push'
PARSE_PUSH_URL = PARSE_API_BASE + PARSE_PUSH_API
# Push Notificationに必要なKeyをHTTP Headerで指定
HEADERS = {
    "X-Parse-Application-Id": "<Application ID>",
    "X-Parse-REST-API-Key": "<REST API Key>",
    "X-Parse-Master-Key": "<Master Key>",
    "Content-Type": "application/json"
}


class APNMessageQueue(object):
    """ APN(Apple Push notification)用メッセージを貯める
    """
    structure = {
        'user_id': lambda x: (x is not None and len(x) > 0),
        'notify_token': lambda x: (x is not None and len(x) > 0),
        'message': lambda x: (x is not None and len(x) > 0),
    }

    def __init__(self, *args, **kwargs):
        for k in self.structure.keys():
            self.__setattr__(k, kwargs.get(k, None))

    @classmethod
    def create(cls, user_id, notify_token, message):
        src_props = dict(
            user_id=user_id,
            notify_token=notify_token,
            message=message,
        )
        props = {}

        for k, func in cls.structure.iteritems():
            value = src_props.get(k)
            if func(value):
                props[k] = value

        if props:
            return APNMessageQueue(**props)
        return None


def create_notify_message(message, badge='Increment', sound='default'):
    """ Notificationメッセージから、PushNotificationフォーマット作成

    Args:
        message: Alertメッセージ.
        badge: バッチカウント('Increment'はParse.com独自).
        sound: Notification効果音.

    Return:
        PushNotificationフォーマット文字列.

    """
    return {'alert': message, 'badge': badge, 'sound': sound}


def create_request_data(notify_token, apn_data):
    """ Parse Notificationフォーマットテンプレ形式のデータ作成

    Args:
        notify_token: デバイストークンかそれのリスト.
        apn_data: Apple Push Notificationデータ.

    Returns:
        ParseNotificationフォーマットテンプレ形式のデータ.

    """
    request_data = {'data': apn_data}
    if isinstance(notify_token, list):
        request_data['where'] = {'deviceToken': {'$in': notify_token}}
    else:
        request_data['where'] = {'deviceToken': notify_token}
    return request_data


def process_do_send(request_data):
    """ ParseNotificationフォーマットテンプレ形式のデータを渡してPushNotification送信

    Args:
        request_data: ParseNotificationフォーマットテンプレ形式のデータ

    Returns:
        正常終了かエラーかを返す

    """
    import json
    from urllib2 import Request, urlopen, URLError

    payload = json.dumps(request_data)
    request = Request(PARSE_PUSH_URL, payload, HEADERS)
    try:
        response = urlopen(request)
        data = json.loads(response.read())
        if isinstance(data, dict):
            return data.get('result', False)
    except URLError, e:
        if hasattr(e, 'reason'):
            print 'We failed to reach a server.'
            print 'Reason: %s' % e.reason
        elif hasattr(e, 'code'):
            print 'The server couldn\'t fulfill the request.'
            print 'Error code: %d' % e.code
    return False


def send_notification(notify_queue_list):
    """ APNsへPushNotificationを送信

    APNsサーバーへPushNotificationメッセージを一斉送信

    Args:
        notify_queue_list: APNMessageQueueのlist

    """
    from itertools import groupby
    notify_sorted_queue_list = sorted(notify_queue_list, key=lambda x: x.message)
    # 同じメッセージはまとめて送る
    for message, group in groupby(notify_sorted_queue_list, key=lambda x: x.message):
        distict_tokens = list(set([user.notify_token for user in group]))
        aps_data = create_notify_message(message)
        request_data = create_request_data(distict_tokens, aps_data)
        print request_data
        process_do_send(request_data)

使う側はこう

main.py
# coding: utf-8

def send_push_parse_for_apn(message):
    import models
    import apn

    # ユーザーデータから、Notification用deviceToken一覧を取得
    targets = [x for x in models.User.find_by_existing_notify_token() if x is not None]
    notify_token_list = [apn.APNMessageQueue(x.user_id, x.notify_token, message) for x in targets]

    if len(notify_token_list) > 0:
        # 発射ー
        apn.send_notification(notify_token_list)

if __name__ == '__main__':
    send_push_parse_for_apn('あボーーん!!')
14
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?