Apple Push Notification サービス(APNs) の実装方法

プッシュ通知の理解には、以下のドキュメントは、以下のものが参考になる
Local NotificationおよびPush Notificationプログラミングガイド

実装については、以下の公開されているコードが参考になる
・ApnsPHP: Apple Push Notification & Feedback Provider
http://code.google.com/p/apns-php/

・EASY APNS
APPLE PUSH NOTIFICATION SERVICE USING PHP & MYSQL
http://www.easyapns.com/
サーバ(PHP)、クライアント(Objective-C) のコードが同梱されている
また、HP には実装の手順が動画で公開されている(約10分)

今回 検証には、ApnsPHP を使用した。
(EASY APNS は mysqli を使用しているが、私の検証用レンタルサーバで動かなかったため。(T_T))

プッシュ通知を実装するには、以下の作業が必要となる

1. プッシュ通知に対応したプロビジョニングプロファイルの作成とインストール

– iOS Provisioning Portal から “Push Notifications”に対応した”App IDs”を取得する
– App IDs を指定したプロビジョニング ファイルを取得する
– 開発環境、デバイスにインストールする

プッシュ通知には、プッシュ通知に適応したプロビジョニング ファイルを使用する必要がある。
既に作成しているプロビジョニング ファイルにプッシュ通知 属性を付加することができなかったため、
新たにプッシュ通知に適応した “App IDs” を作成し、その ID を指定したプロビジョニング ファイルを取得した。

2. デバイストークンを取得する

デバイストークンとは、iOS デバイスが Remote Notification を受信するために、APNS へデバイス登録を行った際に取得できるID。
(サービス提供者が iOS デバイスへ APNS を介してメッセージ通知する際に利用される)

以下のサンプルでは、iOS アプリ起動時に Remote Notification 受信するためにデバイスを登録し、APNS からデバイス トークンを受信している。
(ログにデバイス トークンが出力されるので、PHPスクリプトに適用して利用する)

アプリ起動時などでデバイスを APNS へ登録する

1
2
3
4
5
6
7
// アプリケーションが起動した際の処理
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Remote Notification を受信するためにデバイスを登録する
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge
                                                                           | UIRemoteNotificationTypeSound
                                                                           | UIRemoteNotificationTypeAlert)];

APNS から返されるデバイストークンに付加されている文字をカットする

1
2
3
4
5
6
7
// デバイストークンを受信した際の処理
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
 
    NSString *deviceToken = [[[[devToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] 
                                                      stringByReplacingOccurrencesOfString:@">" withString:@""] 
                                                      stringByReplacingOccurrencesOfString: @" " withString: @""];
    NSLog(@"deviceToken: %@", deviceToken);

プッシュ通知は、NSDictionary で渡される

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// プッシュ通知を受信した際の処理
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
#if !TARGET_IPHONE_SIMULATOR
    NSLog(@"remote notification: %@",[userInfo description]);
    NSDictionary *apsInfo = [userInfo objectForKey:@"aps"];
 
    NSString *alert = [apsInfo objectForKey:@"alert"];
    NSLog(@"Received Push Alert: %@", alert);
 
    NSString *sound = [apsInfo objectForKey:@"sound"];
    NSLog(@"Received Push Sound: %@", sound);
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
 
    NSString *badge = [apsInfo objectForKey:@"badge"];
    NSLog(@"Received Push Badge: %@", badge);
    application.applicationIconBadgeNumber = [[apsInfo objectForKey:@"badge"] integerValue];
#endif
}

3. SSL Distribution証明書、秘密暗号鍵の取得とサーバへの配置

SSL Distribution証明書と秘密暗号鍵を、APNsに接続するサーバにインストールする必要がある。

私は以下の手順で証明書、秘密暗号鍵を作成しました。
ネット上にはいくつかの手順が紹介されていますが、以下の資料が一番わかりやすかったです。
結果、以下のファイルが作成できれば OK。

・server_certificates_bundle_sandbox.pem
・entrust_root_certification_authority.pem

How to generate a Push Notification certificate and download the Entrust Root Authority certificate

http://code.google.com/p/apns-php/wiki/CertificateCreation

・CertificateCreation
How to create a Push Notification certificate in iPhone Developer Program Portal

・Generate a Push Certificate
Push Notification のセキュリティ認証情報の取得

・Verify peer using Entrust Root Certification Authority
Download the Entrust Root Authority certificate directly from Entrust Inc. website:

4. PHPサーバ スクリプトの対応

最後に、iPhone へプッシュ通知するコードを用意します。
ApnsPHP の sample_push.php (プッシュ通知用)にデバイス トークンを指定すれば OKです。
また、APNS と通信するので、以下の証明書を配置しておきます。

– server_certificates_bundle_sandbox.pem
– entrust_root_certification_authority.pem

ここまで作業すれば、無事、プッシュ通知できるはずです。
お疲れ様でした。^_^

もし、PHP スクリプトがエラー出力するようであれば、以下の点をチェックしてみてください。
では!

注意:
ApnsPHP (at revision 100) – ApnsPHP-r100.zip に含められている sample_push.php は、
以下のファイル名の綴りが間違っているようです。
私はこれで2分くらい悩みました。。。(T_T)
2分で済んで助かりましたが、運が悪いともっと悩んでいたはず。
恐ろしい罠ですね。

1
2
3
4
5
6
// Instanciate a new ApnsPHP_Push object
$push = new ApnsPHP_Push(
	ApnsPHP_Abstract::ENVIRONMENT_SANDBOX,
//	'server_cerificates_bundle_sandbox.pem'		// 綴りが間違ってる
  'server_certificates_bundle_sandbox.pem'
);

Apple Push Notification サービス(APNs) の実装方法」への12件のフィードバック

  1. プッシュ通知に対応したプロビジョニングプロファイルの作成とインストールのところで困っています。

    プロビジョニングファイルを指定して、実機でのテストを試みた居ますが、TARGETSのCodeSignでは
    ワイルドカードのiOS Team Provisioning Profileしか選択できず、実行してみると
    「Appの有効な”aps-environment”エンタイトルメント文字列が見つかりません」と
    エラーとなってしまいます。

    PROJECTのCodeSignでは、プッシュ通知用に設定したプロビジョニングファイルを
    選択でいるのですが。。。

    このような状況にはまったことはありませんか?
    わかりましたら、アドバイスを頂けるとうれしいです。

    よろしくお願い致します。

    • 初めまして。^_^
      私は以下のメッセージを見たことがありませんが、プロビジョニング ファイルが プッシュ通知用の物が正しく適用されていない状態というのは間違いないようですね。

      「Appの有効な”aps-environment”エンタイトルメント文字列が見つかりません」と
      エラーとなってしまいます。

      たぶん、App IDs の設定が xcode の Bundle Identifier に反映されていないため、xcodeプロジェクトファイルの TARGETS – Build Settings – Code Signing Identity から APN に対応したプロビジョニング ファイルが選択できないのだと思います。

      TARGETS – Info の Bundle identifier で初期状態で付加されているアプリ名マクロを削除しておけば、APN に対応したプロビジョニング ファイルを選択することができると思います。
      (アプリリリース時には App ID を適切に指定する必要がありますが、)開発段階ではこれで OK かと思います。

  2. redwing1300さん、早速の回答、ありがとうございました。

    上記のように、開発用の環境を変えて、やってみたところ
    エラーがなくなり、無事に通信できました。

    本当にありがとうございました。

  3. 無事解決できて良かったですね。
    初めて、プッシュ通知できたときは結構感動しますよね。(^^)

    連絡どうもです。

  4. 上記は解決したのはいいのですが、Windowsサーバーから、まだ
    メッセージ送信が出来ていません。

    PHPより実行して、APNsとの間でソケット接続を行うところでエラーとなってしまいます。
    恥ずかしいことに理由がわかりません。

    以下、エラー内容

    Warning: stream_socket_client() [function.stream-socket-client]: SSL operation failed with code 1. OpenSSL Error messages: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure in C:\xampp\htdocs\web\MessagePush\push.php on line 64

    Warning: stream_socket_client() [function.stream-socket-client]: Failed to enable crypto in C:\xampp\htdocs\web\MessagePush\push.php on line 64

    Warning: stream_socket_client() [function.stream-socket-client]: unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in C:\xampp\htdocs\web\MessagePush\push.php on line 64

    Macで作成した証明書(PEM)の変換の仕方がおかしいのか、
    保存場所がおかしいのか。。。

    ただいま、いろいろなサイトで検索して、対処方法を模索中。。。

    本当に情けない。。。でも、頑張ります(^_^)v

  5. お世話になってます。

    まさに今、PUSH通知をPHPで組んでます。
    が、公式ドキュメントを読みながら、今回はAPNSとの接続が切れたときに、詳細なエラー情報を返却する
    「拡張形式」での実装を考えていますが、具体的に、「単純形式」と「拡張形式」との実装の違いが理解できません。。
    ご教授いただけますでしょうか。。。

  6. こんにちは。(^^)
    え。。。「単純形式」「拡張形式」って知らなかったので、”Local NotificationおよびPush Notificationプログラミングガイド”を参照してみました。
    電文形式のことですね。
    APNs の動作検証時には、サーバ側のコードに ApnsPHP を利用したので、詳細分かりません。(ノД`)
    ApnsPHP では実装されている箇所が参考にされるのがよいのではないでしょうか?
    → Push.php / Server.php とかでしょうか?

  7. ありがとうございます。
    ライブラリの記述を見てみました。

    $sRet = pack(‘CNNnH*’, self::COMMAND_PUSH, $nMessageID, $nExpire > 0 ? time() +              $nExpire : 0, self::DEVICE_BINARY_SIZE, $sDeviceToken);
    $sRet .= pack(‘n’, $nPayloadLength);
    $sRet .= $sPayload;

    と、上記の用に、Commandには1を指定して、拡張形式とし、identifierにはデバイストークンを指定し、有効期限(APNsサーバに情報を残しておく期限)は7日を指定していますね。

    ちょっとpackに指定している第一引数は調べないとわからないですが、スクラッチでやるなら、一旦この記述をそのまま真似してみようと思います!!

    エラーが発生した際には

    $sErrorMessage = fread(fp,6);
    で取得したレスポンスに
    unpack(‘Ccommand/CstatusCode/Nidentifier’, $sErrorMessage);
    で復元し、後はcase文で、公式にあるエラーNOに沿ったメッセージをExceptionとして返せば
    いけるのかな??とは思います!!

    問題はテスト方法ですが。。どうテストするべきかが読めません。。

  8. 参考になって良かったです。
    私も上記のコードをみて、参考になるんじゃないかな?と思ったんですよ。

    ちなみに、私も pack() の第一引数が意味不明だったので、その際調べたんですが、フォーマットの指定みたいですね。
    http://php.net/manual/ja/function.pack.php

    では、頑張ってください。(^^)

  9. ピンバック: APNsのProvisioning Profileが有効にならないときは、ここを疑おう « 大阪のアンドロイド開発会社 ノーティス

  10. ピンバック: Easy APNSとEasyAPNSPanel

redwing1300 へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です