Edited at
ElixirDay 11

Elixir+PhoenixでAPNsを利用したPush通知を送る簡単なAPIをつくってみる

More than 3 years have passed since last update.


はじめに

Advent Calendar童貞を捨てに来ました。


前提

以前"Elixir+PhoenixでGCMのAPIを叩いてPush通知を送る簡単なAPIをつくってみた"という記事で、ElixirでAndroidアプリにPush通知するWebAPIをつくったので、その続きとしてiOSアプリにPush通知するWebAPIをつくってみます。

ですので、環境構築などは前回の記事をご覧いただければと思います。


Apns4exインストール

最初張り切って自前で実装しようとした(無謀)のですが、Apns4exというよさげなライブラリがあったので、そちらを利用させていただきました。

~mix.exsのdepsとapplicationの箇所にApns4exの設定を追加します。


mix.exs

defp deps do

[{:phoenix, "~> 1.0.3"},
{:phoenix_ecto, "~> 1.1"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.1"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:cowboy, "~> 1.0"},
{:httpoison, "~> 0.7.2"},
{:apns, "== 0.0.10"}]
end


mix.exs

def application do

[mod: {PushApp, []},
applications: [:phoenix, :phoenix_html, :cowboy, :logger,
:phoenix_ecto, :postgrex, :httpoison, :apns]]
end

そして、mixコマンドでダウンロード!

$ mix deps.get


ルーター設定

GET /push_apns/:messageというエンドポイントを追加します。


router.ex

scope "/api", PushApp do

pipe_through :api

get "/push_gcm/:message/", PageController, :push_gcm
get "/push_apns/:message/", PageController, :push_apns
end



APNs設定

次に証明書やPoolingの設定をします。


config.exs

# Configure APNS

config :apns,
# Here goes "global" config applied as default to all pools started if not overwritten by pool-specific value
callback_module: PushApp.PageController,
timeout: 30,
feedback_interval: 1200,
reconnect_after: 1000,
support_old_ios: true,
# Here are pools configs. Any value from "global" config can be overwritten in any single pool config
pools: [
# app1_dev_pool is the pool_name
app_prod_pool: [
env: :prod,
certfile: "/PATH/TO/DIR/xxxx.pem",
pool_size: 100,
pool_max_overflow: 50
],
]

今回はプロダクション環境向けの証明書を作成したので、env: :prodを設定しましたが、サンドボックス用の証明書は、:devを設定してください。

(話逸れますが、将来的には証明書が1つになるようです!)

また、APNsのエラーやフィードバックが発生した時用ののコールバック関数の定義場所をcallback_module: PushApp.PageControllerと設定しました。

他の設定はデフォルトのままにしています。(Pooling周りは今勉強中)


メイン処理

~web/controllers/page_controller.exにpush_apnsメソッドを以下のように追加します。


page_controller.ex

def push_apns(conn, %{"message" => alert}) do

token = "hogehoge"

message = APNS.Message.new
message = message
|> Map.put(:token, token)
|> Map.put(:alert, alert)
|> Map.put(:badge, 1)

APNS.push :app_prod_pool, message

json conn, %{status: "done"}

end



コールバック関数

先程設定ファイルで定義したコールバック用の関数を追加します。


page_controller.ex

def error(%APNS.Error{error: error, message_id: message_id}) do

# エラー発生時の処理を書く
Logger.error "[APNS] Error \"#{error}\" for message #{inspect message_id}"
end

def feedback(%APNS.Feedback{token: token}) do
# フィードバック発生時の処理を書く
Logger.info "[APNS] Feedback received for token #{token}"
end


ご存知の方も多いと思いますが、APNsはエラーハンドリングが結構厄介で、Apns4exは非同期でエラーやフィードバック時の処理できパフォーマンス的にとても優れているのですが、僕の頭では非同期で素敵なエラーハンドリングが書けず、悪戦苦闘しつつ、最終的には来年Apple様が用意してくれると言われているHTTP2ベースのAPNs APIを待つことになりました←

と言いつつたまにゴニョゴニョ頑張っているので、よい書き方があれば教えてくださいm(__)m


API実行

開発が完了したら、サーバーを起動してAPIを実行!

$ curl http://localhost:4000/api/push_apns/finalfantasy

{"status":"done"}

上手く行けば、iPhoneアプリに"finalfantasy"というPushメッセージが通知されます。

エラーの場合(デバイストークンが不正など)は、ログが出力されます。


まとめ

Elixir版gaurunつくりたい。

APNS4exのソースはElixir/Erlangを理解するのにいい勉強になりそう。

早くHTTP2ベースのAPNs APIリリースしてください!

【追記】

新しいAPIでましたキタ━━━━(゚∀゚)━━━━!!

APNs Provider API


参考サイト

iOSのPUSH通知(APNS)の特徴・ノウハウまとめ(iOS 9まで対応):

http://qiita.com/mono0926/items/df03c61adc56934e2e7a

Apple Push Notification Serviceのエラー処理について:

http://hagino3000.blogspot.jp/2014/05/apple-push-notification-service.html