はじめに
MobileBackend
というのはAndroid
のGCM
やiPhone
のAPNs
に対してプッシュ通知を送ってくれるサービスである。
サイトはこちら。
http://mb.cloud.nifty.com/
僕はプッシュ通知を送るために通知用のサーバーをRack
を使ってRuby
で作ってました。
自前で作っただけあって、たまにプッシュ通知が届かないと言う事態も発生して困ったりしてました。
そうこうしているうちにAmazonSNS
と言うサービスが出てきたのですが、色々あって結局移行できずにその現場を去ってしまいました。
で、今回もプッシュ通知を送る必要が出てきたのでMobileBackend
を使ったのですが・・・
と言うお話です。
なお、クライアントサイドの話ではなくプッシュ通知を送るサーバーサイドの話です。
困った点
MobileBackend
は非常に便利なサービスだったのですが、タイトルにも書いているようにドキュメントがとても少ないです。
これといったGem
も無いためサーバーサイドで実装するためには、自前でプログラムを組む必要がありました。
しかし、ドキュメントがあまり無いためサーバーサイドを実装するのに困りました・・・。
自前でプッシュ通知を送る場合
自分でプッシュ通知を送る場合は以下の手順が必要になります。
- プッシュサーバー構築を行う。
- プッシュ通知を行うプログラムを書く
- サーバーにGCMのキーとAPNsの証明書を設定する。
- クライアント(Android/iPhone)からトークン(デバイストークン/Registration ID)をサーバーに送ってもらう。
-
4
で送られたトークンとユーザー情報を紐づけてデータとして保存する。 - 必要に応じて
2
のプログラムで送信する。
僕はiOS
にはhouston
、Android
にはAPNS
で送ってたので、AndroidとiOSで送信プログラムが違い似たような処理が二つできてました。
また、通知エラーの取得方法がAndroidとiOSで違うため変な実装が出来上がってしまいました。(iOSは定期的にpollingしてエラー情報を取りに行かなければならない。)
証明書が切れるとサーバー内に入って証明書を切り替える必要がありました。
APNs/GCMのレスポンスに時間がかかってサーバーリソースを喰わないようにするために、送信データを一時的にキュー(Redis)にためて送ってました。
大変でした・・・(初めて作ったので・・・)。
MobileBackendとは
MobileBackend
はこれらのことを代替えしてくれるサービスです。
トークンの保持も証明書の管理も全てMobileBackend
が行ってくれます。
当然、テスト用のプッシュ通知も簡単に飛ばせます。
非常に便利なのですが、、、とにかくドキュメントが少ないです。
実装方法
ドキュメントが少ない中で以下のサイトを見つけれたのは幸運でした。
ニフティクラウドmobile backendでPush通知できるRubyスクリプト
このプログラムコードがなければRest通信
をしてゴリゴリ書くしかなかったです。
上記のプログラムをRailsで使用したコードがこちらです。
gem "rest-client", ">= 1.7.3"
gem "ruby-hmac", ">= 0.4.0"
上記のGemを入れた後にModel
に以下のコードを追加しました。
# プッシュ通知送信処理
class PushNotifications
# MobileBackendへ送信するために使用
require 'rest_client'
require 'json'
require 'time'
require 'hmac'
require 'hmac-sha2'
require 'base64'
require 'openssl'
# MobileBackendで使用する固定パラメータ
ENDPOINT='mb.api.cloud.nifty.com'
VERSION='2013-09-01'
SIG_METHOD='HmacSHA256'
SIG_VERSION=2
PATH='/push'
# iOS/Androidを別々に送らないと送信できなかったためクッション用のメソッドを準備
# 引数なしで呼び出すと全端末へサイレントプッシュが送れる。
def self.send_mobile_backend(message = nil, channel = nil)
begin
# チャンネルを指定して、対象の端末にプッシュ通知を送る。
self.send_mobile_backend_target(message, channel, "ios")
rescue => e
Rails.logger.error "プッシュ通知の送信に失敗しました。#{e}"
end
begin
self.send_mobile_backend_target(message, channel, "android")
rescue => e
Rails.logger.error "プッシュ通知の送信に失敗しました。#{e}"
end
end
# 実際のPUSH通知を行います。
def self.send_mobile_backend_target(message = nil, channel = nil, target)
# モバイルバックエンドのAPPキーを取得
application_key = AppConfig.get(:env, Rails.env, :mobile_backend, :app_key)
# モバイルバックエンドのクライアントキーを取得
client_key = AppConfig.get(:env, Rails.env, :mobile_backend, :client_key)
timestamp = PushNotifications.get_timestamp
signature = PushNotifications.create_signature(
"POST",
ENDPOINT,
VERSION,
PATH,
client_key,
"X-NCMB-Application-Key" => application_key,
"X-NCMB-Timestamp" => timestamp,
"SignatureMethod" => SIG_METHOD,
"SignatureVersion" => SIG_VERSION,
)
# 即時配信を指定して、iOS/Androidの指定を行います。
params = {
'immediateDeliveryFlag' => true,
'target' => [
target
],
}
# メッセージの指定がある場合はメッセージを追加します。
params = params.merge('message' => message) if message
# メッセージがあって、iOSの場合はサウンドとバッチをつけます。
# Androidの場合に送るとエラーが発生します。
params = params.merge('sound' => 'default', "badgeIncrementFlag" => false, "badgeSetting" => 1) if message && target == "ios"
# クライアントへ渡したいデータがある場合は以下のように指定できます。
params = params.merge("userSettingValue" => {"hoge" => "foo"})
# チャンネルの指定がある場合にはチャンネルを指定します。
# こんなコードにしているのはsearchConditionが無いとおちるかもと思って、つけてます。(思考錯誤した後に検証してなかった・・・)
search_condition = {"searchCondition" => {}}
search_condition = {"searchCondition" => {"channels" => channel}} if channel
params = params.merge(search_condition)
# 実際に送信します。
response = RestClient.post( "https://#{ENDPOINT}/#{VERSION}#{PATH}",
params.to_json,
{
:content_type => 'application/json',
'X-NCMB-Application-Key' => application_key,
'X-NCMB-Timestamp' => timestamp,
'X-NCMB-Signature' => signature
})
# 送信結果をログに出します。
json = JSON.parse(response)
Rails.logger.info json
end
# 以下は上記のURLの通り実装。
def self.create_signature(method, endpoint, version, path, client_key, options = {})
canonical_querystring = options.sort.collect { |key, value| [key, value].join('=') }.join('&')
string_to_sign = "#{method}\n#{endpoint}\n/#{version}#{path}\n#{canonical_querystring}"
hmac = HMAC::SHA256.new(client_key)
hmac.update(string_to_sign)
Base64.encode64(hmac.digest.to_s).chomp
end
def self.get_timestamp
Time.now.utc.xmlschema.gsub("Z", ".456Z")
end
end
kakakikikeke
さんのおかげで無事に送ることができました。
なお、ドキュメントは以下になります。
REST API リファレンス
iOS特有の項目などに関しては一切記述がありませんが、sound
やbadgeSetting
をAndroidに向けて送信するとエラーが出ます。
しかも、なんでエラーが出たかわかりません・・・。
なので一気にパラメータを追加して送るのではなく、パラメータを追加しては試しながら進めると良いと思います。
まとめ
チャンネルを使って送信できるので、例えば特定のカテゴリーに所属している人だけに送る場合は「"category_#{category_id}"
のようなチャンネルに送る」といったように使えます。
とても便利です。
便利ですが・・・使うためにはコツがいります。
ということで、MobildeBackendを使う方の参考になればと思います。
最後になりますが、kakakikikeke
さんありがとうございました。