vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

Sinatra + HoustonでPush通知

Sinatra + houstonでPush通知

再びSinatraに戻って、今度はiPhoneへのPush通知送信に挑戦してみました。

はじめに

Rubyを使って通知を送信できるライブラリはいくつかあるようですが、比較的易しそうな感じがしたのでHoustonを試してみることに。

Pushの送信側は以前作成したものに追加する形としました。

pemファイルの作成

まずは証明書や送信側で必要となるpemファイルの作成から(実はここを曖昧なまま進めたせいでどハマりしました)。

以下のようなサイトを参考に、Push Notificationを有効にした証明書を作成します。

「2. サーバ用PEMファイルの作成」に従ってpemファイルを作成しても問題ありませんが、HoustonのREADME > Converting Your Certificateの通り、Keychainで[Apple Development IOS Push Services:自分のアプリのID]の証明書、秘密鍵をShiftキーで両方選択 > 2個を書き出す... でp12ファイルを作成し、以下のコマンドを実行すると一発で目的のpemファイルを作成できます。

openssl pkcs12 -in 書き出したp12ファイル名.p12 -out 出力するpemファイル名.pem -nodes -clcerts

作成したpemファイルをSinatraのプロジェクトフォルダ以下に置きます。

なお、どハマりしていた時の症状は、Push通知を送信すると成功した旨のメッセージが返るのに、デバイスに通知は来ない、というもの。

同じような症状に見舞われた場合は証明書やpemファイルの作成を疑ってみると良いかもしれません。

iOSアプリの作成とトークンの取得

pemファイルの作成時に指定したアプリのIDで、iOSアプリ(プロジェクト)を作成します。
今回も[Single View Application]でプロジェクトを作成してみました。

プロジェクトのAppDelegate.mを以下のように変更します。

iOS8ではHoustonのREADMEで紹介されているコードは一部Deprecatedになったようで、丸写しすると警告が表示されました。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Push Notificationの準備
  UIUserNotificationType types = UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
  UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
  [application registerUserNotificationSettings:mySettings];
  
  return YES;
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
  // Push通知がユーザーに許可されたらメソッドを呼び出してトークンを取得する.
  [application registerForRemoteNotifications];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
  // Push通知送信側で使用するトークンが取得できる.
  NSLog(@"Token: %@", deviceToken);
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
  // エラー.
  NSLog(@"Error: %@", error);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
  // Push通知を受け取ったら実行.notification.custom_dataの内容がuserInfoに含まれる.
    NSLog(@"UserInfo: %@",userInfo);
}

これを実機(今回はiPhoen)で実行すると、Push通知の許可を求めるアラートが表示され、OKならNSLogでトークンが出力されます。

このトークンと、pemファイルの作成で作成したpemファイルを使って以下のコマンドをTerminalで実行するとPush通知がiPhoneに届きます。

apn push "" -c /pemファイルのパス/pemファイル名.pem -m "世界さんチーっす"

ちなみに"-m"以降を削除した場合、VimでPush通知本文の入力が求められますが、deleteキーを使うと前の文字が大文字になったりして通常のものとは若干操作が違っていてちょっと苦労しました
(Vimの操作に慣れていないだけかもしれませんが)。

あと、トークンは最初に実行した時はエラーは出ないもののうまく取得できず、半日ほど放置したら取れるようになった、ということがありました。
証明書などの登録に時間がかかるなどあるのかもしれません。

SinatraへのPush通知送信機能の追加

Houstonのサイト > Usageのコードを、[localhost:XXXX/push]にアクセスした場合に実行されるようにしてみました。

app.rb

require 'coffee-script'
require 'sass'
require 'sinatra'
require 'sinatra/base'
# サーバーを再起動しなくてもリロードする
require 'sinatra/reloader'
require 'slim'
require 'houston'

class MainApp < Sinatra::Base
  isUninitialized = true

  # localhost:XXXXにアクセスしたらTopページを表示する
  get '/' do
    slim :top
  end
  get '/about' do
    slim :top
  end

  get '/css/stylesheet.css' do
    sass :'css/style'
  end

  get '/js/javascript.js' do
    coffee :'js/script'
  end
  
  get '/push' do
    # 初期化済みならスルー.
    if isUninitialized
      # 開発用のURLを指定.
      APN = Houston::Client.development
      APN.certificate = File.read("forPushTest.pem")

      # デバイスから取得したトークンをセット.
      # TODO: Appからトークンを送信するようにして、それを使用するよう変更.
      token = ""

      # 取得したトークンから通知を作成する.
      notification = Houston::Notification.new(device: token)
      notification.alert = "世界さんチーっす!"

      # 受信した時に付与されるバッジ数の設定.
      notification.badge = 1
      notification.sound = "sosumi.aiff"
      notification.category = "INVITE_CATEGORY"
      notification.content_available = true
      # 独自のパラメータを付与.
      notification.custom_data = {pushtest: "送信しました"}

      isUninitialized = false
    end

    # Push通知を送る.
    APN.push(notification)
  end
end
  • 今回追加したところは緑にしています。
  • Gemfileにも[gem 'houston', '2.2.1']を追加しています。

Xcodeで作成したiOSアプリを実行して、didReceiveRemoteNotificationで取得できるuserInfoを調べてやると、以下のように[notification.custom_data]でセットしたパラメータを受け取っていることが確認できます。

f:id:mslGt:20141217003155p:plain

今回はほぼサンプルのままでしたが、何かのタイミングでiPhoneからURLにアクセス -> Push通知 としても面白そうですね。

参考

Houston