houstonライブラリを利用してpush通知を送信する

hustonライブラリは、Apple Push通知を簡単に送信できるRubyのgemライブラリです。CLIからも簡単に実行することができ、手軽に利用できます。ここでは、gemライブラリのインストール方法、Push通知を行う/受け取るサンプルプログラムなどを解説していきます。

01: 環境

    Push通知を受け取る
  • macOS Sierra バージョン10.12.3
  • Xcode バージョン8.2.1
  • iPhone(iOS10.2.1)

    Push通知を行う
  • Raspberry Pi3

02: push通知を受け取るプログラム

Capabilitiesを設定する。
  • Push Notifications: ON
  • Background Modes; ON
    • Background fetch: チェック
    • Remote notifications: チェック
ViewController.swiftを編集する。

ViewController.swift


/**
 * houstonを使ってAPNsのPush通知を行う。
 * @file AppDelegate.swift
 * @author xxxx xxxx
 * @date xxxx/xx/xx
 */

import UIKit

/**
 * アプリケーションのイベントを処理し、UIApplicationオブジェクトによって呼び出されるメソッドを定義する。
 */
/*
 * @UIApplicationMainを付けることで、初期UIを起動するmain関数が自動的に実装される。
 */
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    /*! @copydoc UIApplicationDelegate::application(UIApplication,[NSObject: AnyObject]?) */
    /**
     * デリゲートへ起動プロセスが完了し、アプリケーションの実行準備ができたことを伝える。
     * @param _: アプリケーションオブジェクト
     * @param didFinishLaunchingWithOptions: アプリが起動された理由
     * @retval true: 下記以外の場合
     * @retval false: アプリケーションがURLリソースを処理できない場合やユーザアクティビティを続行できない場合
     */
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
        UIApplication.shared.registerUserNotificationSettings(settings)
        UIApplication.shared.registerForRemoteNotifications()
        
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }
    
    /**
     * アプリケーションがAPNsに正常に登録された時に呼び出される。
     * @param[in] application: リモート通知登録プロセスを開始したアプリケーションオブジェクト
     * @param[in] didRegisterForRemoteNotificationsWithDeviceToken: APNsに送信するデバイストークン
     */
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        
        // deviceTokenの要素を文字列に変換
        let token = deviceToken.map {String(format: "%.2hhx", $0)}.joined()
        print("device token = " + token)
        
        var deviceTokenString = "\(token)"
            .trimmingCharacters(in: NSCharacterSet(charactersIn:"<>") as CharacterSet)
            .replacingOccurrences(of: " ", with: "")
        
        print("device token string = " + deviceTokenString)
        UserDefaults.standard.set(deviceTokenString, forKey: "deviceToken")
    }
    
    /**
     * リモート通知が到着したことをアプリケーションに伝える。
     * (application:didReceiveRemoteNotification:が実装されているが、こちらが優先されて呼び出される。)
     * @param[in] application: アプリケーションオブジェクト
     * @param[in] didReceiveRemoteNotification: アプリアイコンのバッジ番号、アラート音、
     *  ユーザに表示するアラートメッセージ、通知識別子、及びカスタムデータを含むリモート通知に関する情報を含む辞書
     * @param[in] fetchCompletionHandler: ダウンロード操作が完了した時に呼び出されるメソッド。
     * 30秒以内に呼び出す必要がある。
     */
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
        
        print("background")
        print(userInfo)
        completionHandler(.noData)
    }
    
    /**
     * アプリケーションがリモート通知を受け取った時に呼び出される。(呼び出されない。)
     * @param[in] application: リモート通知を受け取ったアプリケーションオブジェクト
     * @param[in] didReceiveRemoteNotification: アプリアイコンのバッジ番号、アラート音、
     *  ユーザに表示するアラートメッセージ、通知識別子、及びカスタムデータを含むリモート通知に関する情報を含む辞書
     */
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
        
        print("foreground")
        print("userInfo: \(userInfo)")
    }
}
iOSアプリケーションを実行する。

iOSアプリケーション実行後、APNsから送られてくるdevice tokenはpush通知を行うプログラムで必要になるので記録しておく。

03: push通知を行うプログラム

Houstonライブラリをインストールする。

bash


# Gemfileを作成
$ bundle init

# Gemfileの編集
$ nano Gemfile

# Houstonライブラリをインストール
$ bundle install --path vendor/bundle

# RSA秘密鍵を生成
$ openssl pkcs12 -in <APNs用証明書(.p12)> -out push_development.pem -password pass:<パスワード> -nodes

GemFile


# frozen_string_literal: true
source "https://rubygems.org"

gem 'houston'
push通知を行うプログラムを作成する。

bash


# Rubyプログラムを作成
$ nano publish.rb 

publish.rb


require 'houston'

# Environment variables are automatically read, or can be overridden by any specified options. You can also
# conveniently use `Houston::Client.development` or `Houston::Client.production`.
APN = Houston::Client.development
APN.certificate = File.read("/home/pi/Desktop/Huston/SW0201-Houston/push_development.pem")

# An example of the token sent back when a device registers for notifications
token = "<734a781b597e5f2b8fe27732954ad2bf0403b585bcf16daec24c1ddcd3f8284e>"

# Create a notification that alerts a message to the user, plays a sound, and sets the badge on the app
notification = Houston::Notification.new(device: token)
notification.alert = "Hello, World!"

# Notifications can also change the badge count, have a custom sound, have a category identifier, indicate available Newsstand content, or pass along arbitrary data.
notification.badge = 57
notification.sound = "sosumi.aiff"
notification.category = "INVITE_CATEGORY"
notification.content_available = true
notification.custom_data = {foo: "bar"}

# And... sent! That's all it takes.
result = APN.push(notification)
push通知を行う。

bash


# Rubyプログラムを実行
$ bundle exec ruby publish.rb

# もしくは以下のコマンドを実行
$ bundle exec apn push "<878580a71ac3eb54b8b38ed2244b71fe68a399f5698e78a9d49e36548f406cb0>" -c push_development.pem -m "Hello, World!"

04: push通知結果

iOSアプリケーション

iPhone