APNsは正常に動作しているのか?

2022年10月17日

開発環境
OS:macOS Monterey
SDK:Xcode14.1

概要

プッシュ通知が実装されたandroidアプリとiosアプリ。

プッシュ通知の動作確認のため、androidとiphoneのデバイスでデバッグをするものの、

Firebase経由で発行したプッシュ通知がandroidには届くが、iosには届かない。

なぜiosに届かないのか調査します。

APNs

APNs(Apple Push Notification service)とは、アップルプッシュ通知サービスのこと。

プッシュ通知におけるandroidとiphoneの大きな違いはAPNsの存在。

androidの場合、Firebase → androidデバイスとプッシュ通知が配信されるのに対し、

iosの場合、Firebase → APNs → iPhoneデバイスとプッシュ通知が配信されます。

iPhoneにプッシュ通知が届かない。

しかし、Firebaseではプッシュ通知が配信されている。(androidは受信できているので)

それが確かであるなら、iphoneに届かない理由は、「APNsが正常に動作していない」からに絞られます。

そこでAPNsが正常に動作しているかどうかを確認します。

確認手段は、cURLコマンド。

curlコマンドでAPNsに直接、プッシュ通知依頼をかけ、iPhoneにプッシュ通知を配信できているかを確認します。

curlコマンド

まずはcurlコマンドを実行するためのバッチファイルを作成します。

#!/bin/bash

set -x

AUTH_KEY_FILE_PATH="xxxxxxxx.p8"
AUTH_KEY_ID="xxxxxxxx"
TEAM_ID="xxxxxxxxx"

APP_BUNDLE_INDENTIFER="com.sample.test"
DEVICE_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
API_ENDPOINT="https://api.development.push.apple.com/3/device/"

base64() {
   openssl base64 -e -A | tr -- '+/' '-_' | tr -d =
}

sign() {
   printf "$1"| openssl dgst -binary -sha256 -sign "${AUTH_KEY_FILE_PATH}" | base64
}

TIME=$(date +%s)
HEADER=$(printf '{ "alg": "ES256", "kid": "%s" }' "${AUTH_KEY_ID}" | base64)
CLAIMS=$(printf '{ "iss": "%s", "iat": %d }' "${TEAM_ID}" "${TIME}" | base64)
JWT="${HEADER}.${CLAIMS}.$(sign ${HEADER}.${CLAIMS})"

curl -v \
   -d '{"aps":{"alert":{"title":"Test","body":"Hello from request with P8 certificates"}}}' \
   -H "Content-Type: application/json" \
   -H "Authorization: Bearer ${JWT}" \
   -H "apns-topic: ${APP_BUNDLE_INDENTIFER}" \
   --http2 \
   ${API_ENDPOINT}${DEVICE_TOKEN}

上記内容を定義したcommandファイルを作成し、認証キー(p8ファイル)と同階層に配置します。

ちなみにバッチファイル内の可変項目は以下の5つ。

1)AUTH_KEY_FILE_PATH → Apple Developerで取得した認証キー(p8ファイル)名

2)AUTH_KEY_ID → Apple Developerで定義されたキーID

3)TEAM_ID → Apple Developerで定義されたチームID

4)APP_BUNDLE_INDENTIFER → アプリに定義したバンドルID

5)DEVICE_TOKEN → アプリから取得したAPNsデバイストークン

APNsデバイストークンはAPNsとの通信で取得するため、アプリでログ出力して取得します。

func application(_ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Messaging.messaging().apnsToken = deviceToken

    let token = deviceToken.map { String(format: "02.2hhx", $0)}.joined()
    printf("apnsToken:%s\n",token)
}

いざ、実行

バッチファイルの準備が整ったら、実行します。

bash apns.command

そして、実行した結果が、

HTTP/2 400 BadDeviceToken

異常です。

デバイストークンが無効とのこと。

やはりAPNsが正常に動作していなかったようです。

Apple Developer再定義

Apple Developerの定義を見直します。

ここでApple Developerに詳しい方にヘルプ。

定義を確認したもらった結果、2点の間違いが発覚。

①デバッグ用デバイスの定義漏れ

②開発用定義になっていない。(Distoribution → Development)

改めて定義をしなおし、Provisioning Profileを読み直してリビルドします。

そして、再度、apns.commandを実行してみると、

結果は、HTTP/2 200となり、プッシュ通知が届きました。

実際にiPhoneによるデバッグでもプッシュ通知が届くことを確認。

なかなかにAppleの管理は厳密ですね。