広告

iOS 11 で Apple Push Notification Service (APNs)

!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!

iOS 11 と Xcode 9 と java-apns で試してみました。実に 2 年以上振りです。

過去のポスト

iOS 8 のときの投稿。

iOS 8.x と Swift と Java で Apple Push Notification (APNs)
https://hirooka.pro/?p=7912

iOS 7 のときの投稿。

[iOS SDK] Apple Push Notification Service (APNs) を試してみる件.
https://hirooka.pro/?p=2395

[iOS 7 SDK] APNs (Apple Push Notification Service) の Background Mode – Remote Notifications を試してみる件.
https://hirooka.pro/?p=4799

証明書の作成手順は変わってなかったです。iOS SDK のほうはいろいろと変わってました。java-apns はあまり変化ないですね。Java だと最近では Pushy 辺りが盛り上がってるっぽい?

前置き

iOS 上で動作するアプリケーションを「アプリ」、Push を行うサーバ上で動作するアプリケーションを「プロバイダ」と表現します。

Certificate Signing Request (CSR) の作成

macOS の Application – Utilities の Keychain Access を起動します。
macOS のメニューバーの Keychain Access – Certificate Assistant – Request a Certificate from a Certificate Authority… を選択します.
Certificate Information ウインドウが開きます.

User Email Address Apple Developer Program に登録しているアドレス
Common Name 適当な名前
CA Email Address
Request is Save to disk
Let me specify key pair information チェックなし

Continue ボタンを押します。デフォルトでは Desktop に CertificateSigningRequest.certSigningRequest という名前のファイルが生成されます。Save As が上記のファイル名、Tags は空、Where は Desktop となっています。そのまま Save ボタンを押してファイルを生成、保存します。

Keychain Access の Keychains を login、Category を Keys と選択します。先ほど作成した Common Name の public key と private key が表示されることを確認します。

private key を選択し、右クリックメニューで Export “作成した Common Name” … を選択します。File Format を Personal Information Exchange (.p12) と設定し、Save ボタンを押し、適当な場所に保存します。Save As は、作成した Common Name、Tags はブランク、Where は Documents となっています。Save ボタンを押すと、パスワードの入力を要求されるので適当なパスワードを設定しておきます。p12 ファイルが生成されていることを確認します。

Xcode でアプリのプロジェクトの作成

Xcode で動作確認用のプロジェクトを作成します。例えば、exchange-notification-receiver と。ここで作成したプロジェクトの Bundle Identifier を後で使用するのでメモしておきます。

Apple Developer サイトで App ID の作成

Apple Developer Account https://developer.apple.com/account/ の Certificates, Identifiers & Profiles へ行きます。

  1. Identifiers の App IDs を選択
  2. 画面右上のプラスボタンを押して App ID を登録
  3. App ID Description は適当に
  4. App ID Prefix は固定値
  5. App ID Suffix では、Explicit App ID を選択し、Bundle ID に Xcode で作成したプロジェクトの Bundle Identifier を
  6. App Services では Push Notifications にチェックを

Continue ボタンを押すと Confirm your App ID 画面に遷移するので Register ボタンを押します。iOS App IDs に作成した App ID が登録されていることを確認します。

Apple Developer サイトで Certificate

Apple Developer Account https://developer.apple.com/account/ の Certificates, Identifiers & Profiles へ行きます。

  1. Identifiers の App IDs を選択
  2. iOS App IDs で、先ほど作成した App ID を選択
  3. Edit ボタンを押す
  4. Push Notifications の Development SSL Certificate の Create certificate… ボタンを押す
  5. About Creating a Certificate Signing Request (CSR) 画面に遷移
  6. Continue ボタンを押す
  7. Generate your certificate. 画面に遷移
  8. Upload CSR file. の Choose File… で、先ほど作成した CertificateSigningRequest.certSigningRequest ファイルを選択
  9. Generate ボタンを押す
  10. Your certificate is ready. 画面に遷移
  11. Download ボタンを押して certificate をダウンロード

ダウンロードされるファイル名は aps_development.cer となります。

ダウンロードされた cer ファイルをダブルクリックすると、Keychain Access が起動します。Keychains は login、Category は Keys となっています。先ほど作成した Common Name の private key の下に Apple Development iOS Push Services が追加されていることを確認します。

Provisioning Profile

Apple Developer Account https://developer.apple.com/account/ の Certificates, Identifiers & Profiles へ行きます。

  1. Provisioning Profiles の Development を選択
  2. iOS Provisioning Profiles (Development) の画面右上のプラスボタンを押す
  3. Add iOS Provisioning Profile 画面に遷移
  4. What type of provisioning profile do you need? で Development の iOS App Development を選択し、Continue ボタンを押す
  5. Select App ID. で適切なものを選択し、Continue ボタンを押す
  6. Select certificates. で、適切なものを Select して Continue ボタンを押す
  7. Select devices. で、必要なものにチェックして Continue ボタンを押す
  8. Name this profile and generate. で適当な Profile Name を設定し、Continue ボタンを押す
  9. Your provisioning profile is ready. 画面に遷移
  10. Download ボタンを押して Provisioning Profile をダウンロード

ダウンロードされるファイル名は name.mobileprovision となります。

Xcode が起動された状態で、ダウンロードされた mobileprovision ファイルをダブルクリックすると、Xcode がフォアグラウンドになります。この時点で、Provisioning Profile がインストールされるようです。

アプリ実装

AppDelegate.swift の application に追記します。

...

// ADD
import UserNotifications

...

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        // ADD
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.alert, .badge, .sound], completionHandler: {(granted, error) in
            if granted {
                DispatchQueue.main.async(execute: {
                    application.registerForRemoteNotifications()
                })
            }
        })
        
        return true
    }

この状態で、実機かシミュレータでアプリを実行してみます。

アプリ起動時、ポップアップが表示されます。

「"アプリ名"は通知を送信します。よろしいですか?」
通知方法は、テキスト、サウンド、アイコンバッジが利用できる可能性があります。
通知方法は"設定"で設定できます。

[設定] – [通知センター] にもアプリが追加されます.

次に、トークン取得を確認するための実装を AppDelegate.swift に追記します。その前に Xcode で Capabilities の Push Notifications を ON にしておきます。そして、AppDelegate.swift に下記を追記します。

func application(_ application: UIApplication,
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data){
        let tokenString = deviceToken.map { String(format: "%.2hhx", $0) }.joined()
        print("deviceToken = \(tokenString)")
    }

    func application(_ application: UIApplication,
                     didFailToRegisterForRemoteNotificationsWithError error: Error){
        print("Failed to get deviceToken, error: \(error.localizedDescription)")
    }

再度アプリを実行し,deviceToken の文字列を取得できることを確認します。今回はアプリとプロバイダとの間でトークンをやり取りするまではしないため、トークンをメモしておきます。今回は最低限の動作確認ができればいいので、後でプロバイダ側でトークンをハードコードします。

証明書の変換

今回、プロバイダとして Java で実装されている java-apns を使用してみます。その場合、作成した p12 ファイルを openssl 等で変換させる必要があります。

例えば、ファイル名を下記の通りだとします。

my_apns.p12 macOS の Keychain Access で作成した p12 ファイル
aps_development.cer Apple Developer からダウンロードした cer ファイル

macOS の openssl で変換してみます。

openssl x509 -in aps_development.cer -inform DER -out aps_development.pem -outform PEM
openssl pkcs12 -nocerts -in my_apns.p12 -out my_apns.pem
openssl pkcs12 -export -inkey my_apns.pem -in aps_development.pem -out my_apns_for_java_apns.p12

openssl で生成された my_apns_for_java_apns.p12 を java-apns で使用します.

プロバイダを実行するマシンに .p12 ファイルをアップロードしておきます。

プロバイダ実装

Push するサーバ側、つまりはプロバイダを実装します。Java で実装されている java-apns を使用してみます。GitHub で、Java かつ APNs で検索してみると最もスターが多く、まあまあ更新されているようですし。

java-apns
https://github.com/notnoop/java-apns

ビルドツールが Gradle の場合,build.gradle の dependencies に追記します.

compile "com.notnoop.apns:apns:1.0.0.Beta6"

実装は例えば下記の通り.

ApnsService service =
    APNS.newService()
        .withCert("/path/to/example.p12", "mypass")
        .withSandboxDestination()
        .build();
String payload = APNS.newPayload().alertBody("やっはろー").build();
String token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
service.push(token, payload);

このコードを実行後、アプリがバックグラウンドで動作している iOS に通知画面が表示されることを確認します。

なお、通知を送った際に Apple Watch を振動させたい場合は、適当な引数で sound メソッドを追加するとよいかもしれません。

String payload = APNS.newPayload().alertBody("やっはろー").sound("").build();

Background Mode – Remote Notifications

APNs とバッググラウンドモードを組み合わせた Remote Notifications も確認してみました。

Xcode のプロジェクトの Capabilities の Background Modes を ON にし、Remote notifications にチェックを入れます。

AppDelegate.swift に、下記を追加してみます。

例えば,

...

var result = UIBackgroundFetchResult.newData

...

func application(_ application: UIApplication,
                     didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void){
        print("userInfo = \(userInfo)")
        
        switch result {
        case UIBackgroundFetchResult.newData:
            print("\(UIBackgroundFetchResult.newData.rawValue)")
        case UIBackgroundFetchResult.failed:
            print("\(UIBackgroundFetchResult.failed.rawValue)")
        case UIBackgroundFetchResult.noData:
            print("\(UIBackgroundFetchResult.noData.rawValue)")
        }
        completionHandler(result)
    }

また,java-apns 側のペイロードの作成方法は下記の通りです。

Remote Notification

String payload = APNS.newPayload().instantDeliveryOrSilentNotification().alertBody("やっはろー").build();

このときのペイロードは

{"aps":{"alert":"やっはろー","content-available":1}}

アプリがバックグラウンドで動作している状態で java-apns を実行してみます。

Remote Notification の場合は、プッシュ通知があると同時に didReceiveRemoteNotification が実行されます。

このときの userInfo は

userInfo = [AnyHashable("aps"): {
    alert = "\U3084\U3063\U306f\U308d\U30fc";
    "content-available" = 1;
}]

Silent な Remote Notification

String payload = APNS.newPayload().instantDeliveryOrSilentNotification().build();

このときのペイロードは

{"aps":{"content-available":1}}

アプリがバックグラウンドで動作している状態で java-apns を実行してみます。

Silent な Remote Notification の場合は、プッシュ通知が無いまま didReceiveRemoteNotification が実行されます。

このときの userInfo は

userInfo = [AnyHashable("aps"): {
    "content-available" = 1;
}]

広告