メッセージの受信

サーバー上変更のプッシュ通知によるメッセージは、プッシュ通知の受信ハンドラー に示すプッシュ通知のハンドラーで受け取ります。

以下のサンプルコードでは、AppDelegate クラスの application(_:didReceiveRemoteNotification:fetchCompletionHandler:) メソッドで、プッシュ通知の詳細を取得する例を示します。

Swift:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
  print("Received notification : \(userInfo)")

  // Create a KiiReceivedMessage instance from userInfo.
  let message = KiiReceivedMessage(fromAPNS: userInfo)

  // Get the changed KiiObject.
  if (message.containsKiiObject()) {
    let anObject = message.eventSourceObject()
    // Do something.
  }

  // Get the subscribed bucket.
  if (message.containsKiiBucket()) {
    let aBucket = message.eventSourceBucket();
    // Do something.
  }

  // Get the sender of the push message.
  if (message.senderUser() != nil) {
    // If the sender of the message is a user, get the user.
    let aUser = message.senderUser()
    // Refresh the user and then access it.
  } else if (message.senderThing() != nil) {
    // If the sender of the message is a thing, get the thing.
    let aThing = message.senderThing()
    // Refresh the thing and then access it.
  } else {
    // The message has no sender information.
  }

  // Determine the scope.
  let scopeType = message.getValueOf(.SCOPE_TYPE)!
  switch(scopeType){
  case "APP_AND_GROUP" :
    // If the subscribed bucket is in a group scope, get the group.
    let aGroup = message.eventSourceGroup()
    // Refresh the group and then access it.
    break
  case "APP_AND_USER" :
    // If the subscribed bucket is in a user scope, get the user.
    let aUser = message.eventSourceUser()
    // Refresh the user and then access it.
    break
  case "APP_AND_THING" :
    // If the subscribed bucket is in a thing scope, get the thing.
    let aThing = message.eventSourceThing()
    // Refresh the thing and then access it.
    break

  default:
    // The subscribed bucket is in the application scope.
    break;
  }
  // Get the bucket name.
  // "KiiMessage_BUCKET_ID" is an enum that is defined in KiiMessageField.
  let bucketName = message.getValueOf(.BUCKET_ID)

  // Get the event type.
  // "KiiMessage_TYPE" is an enum that is defined in KiiMessageField.
  let type = message.getValueOf(.TYPE)

  completionHandler(.newData)
}

Objective-C:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result)) completionHandler {
  NSLog(@"Received notification: %@", userInfo);

  // Create a KiiReceivedMessage instance from userInfo.
  KiiReceivedMessage *message = [KiiReceivedMessage messageFromAPNS:userInfo];

  // Get the changed KiiObject.
  if (message.containsKiiObject) {
    KiiObject *anObject = message.eventSourceObject;
    // Do something.
  }

  // Get the subscribed bucket.
  if (message.containsKiiBucket) {
    KiiBucket *aBucket = message.eventSourceBucket;
    // Do something.
  }

  // Get the sender of the push message.
  if (message.senderUser != nil) {
    // If the sender of the message is a user, get the user.
    KiiUser *aUser = message.senderUser;
    // Refresh the user and then access it.
  } elseif (message.senderThing != nil) {
    // If the sender of the message is a thing, get the thing.
    KiiThing *aThing = message.senderThing;
    // Refresh the thing and then access it.
  } else {
    // The message has no sender information.
  }

  // Determine the scope.
  NSString *scopeType = [message getValueOfKiiMessageField:KiiMessage_SCOPE_TYPE];

  if ([@"APP_AND_GROUP" isEqualToString: scopeType]) {
    // If the subscribed bucket is in a group scope, get the group.
    KiiGroup *aGroup = message.eventSourceGroup;
    // Refresh the group and then access it.
  } else if ([@"APP_AND_USER" isEqualToString: scopeType]) {
    // If the subscribed bucket is in a user scope, get the user.
    KiiUser *aUser = message.eventSourceUser;
    // Refresh the user and then access it.
  } else if ([@"APP_AND_THING" isEqualToString: scopeType]) {
    // If the subscribed bucket is in a thing scope, get the thing.
    KiiThing *aThing = message.eventSourceThing;
    // Refresh the thing and then access it.
  } else {
    // The subscribed bucket is in the application scope.
  }

  // Get the bucket name.
  // "KiiMessage_BUCKET_ID" is an enum that is defined in KiiMessageField.
  NSString *bucketName = [message getValueOfKiiMessageField:KiiMessage_BUCKET_ID];

  // Get the event type.
  // "KiiMessage_TYPE" is an enum that is defined in KiiMessageField.
  NSString *type = [message getValueOfKiiMessageField:KiiMessage_TYPE];

  completionHandler(UIBackgroundFetchResultNewData);
}

このサンプルコードでは、KiiReceivedMessage インスタンスから以下の情報を取得しています。

  1. プッシュメッセージよりデータを抽出します。ペイロードに Bucket の情報が含まれるかどうかを containsKiiBucket() メソッドで確認し、Bucket を eventSourceBucket() メソッドで取得します。また、ペイロードに Object の情報が含まれるかどうかを containsKiiObject() メソッドで確認し、Object を eventSourceObject() メソッドで取得します。

  2. 通知を発生させたユーザーまたは Thing を、senderUser() メソッドまたは senderThing() メソッドで取得します。

  3. getValueOf(_:) メソッドを実行し、プッシュメッセージより以下のフィールドを抽出します。

    • .SCOPE_TYPE:Bucket のスコープ
    • .BUCKET_ID:監視対象 Bucket の ID
    • .TYPE:対象 Bucket にて発生したイベントを示す enum 値

プッシュメッセージの解釈の方法や、フィールド値の抽出方法の詳細については appledoc を参照してください。

実際のプログラムでは、実現したいプッシュ通知の機能に基づいて、複数回のハンドラーの呼び出しに対する対応が必要になります。受信メソッドの組み合わせ に示すメソッドの呼び出しタイミングに従って、必要な機能を作り込んでください。

サーバー上変更のプッシュ通知だけを使用する場合、ユーザーアクションのハンドラー を指定する必要がありません。サーバー上変更のプッシュ通知では APNs のペイロードのカスタマイズができないため、通知センター経由でハンドラーが呼び出されることはありません。

プッシュ通知を期待どおりに受け取れない場合、トラブルシューティング を参考に問題を解決してください。また、プッシュ通知設定チュートリアル にあるシンプルな実装によって、プッシュ通知の動作だけを検証することもできます。

プッシュ通知の種類の判定

プッシュ通知の受信ハンドラーと、ユーザーアクションのハンドラーでは、このモバイルアプリに対するすべてのプッシュ通知を受け取ります。

モバイルアプリに対して通知されたメッセージが、サーバー上変更のプッシュ通知、ユーザープッシュ通知、管理者メッセージのプッシュ通知のうちどの機能で生成されたかは、ペイロード内のキーを確認することで識別できます。appledoc の KiiReceivedMessage の説明には、機能ごとに参照可能なフィールドが記載されています。これを元に、プッシュ通知の機能の種類を判断してください。

プッシュメッセージの例

以下は、プッシュメッセージを受け取ったとき、userInfo によって取得できるデータのイメージです。サーバー上変更のプッシュ通知では、更新があった Bucket や Object、その更新の種類(追加された、削除されたなど)のような情報を上記のメソッドによって取得できます。これらのデータの詳細は appledoc を参照してください。

{
    aps = {
    };
    bi = bucketName;
    bt = rw;
    oi = "fedcba09-8765-4321-fedc-ba0987654321"
    sa = 0123abcd;
    st = "APP_AND_USER";
    su = "01234567-89ab-cdef-0123-456789abcdef";
    t = "DATA_OBJECT_CREATED";
    w = 1403844866395;
}

サーバー上変更のプッシュ通知では、プッシュメッセージの内容をカスタマイズできません。content-available キーに 1 がセットされた状態でメッセージ送信されます。特に、通知センターにプッシュメッセージを表示したい場合、メッセージのカスタマイズ を参照してください。

プッシュ通知のループに対する注意

プッシュ通知の設計や実装を行うときには、プッシュ通知のループが発生しないように十分に注意してください。プッシュ通知の受信処理で再びプッシュが発生する処理を行うと、永久ループから抜け出せなくなります。

サーバー上変更のプッシュ通知(Push to App)では、プッシュメッセージの受信処理を契機に、再び Bucket の更新を行うと、プッシュ通知のループが発生する可能性があります。

万一、永久ループがリリースモジュールで発生してしまった場合、対処が非常に困難です。本質的には、すべてのエンドユーザーのモバイルアプリを対処済みバージョンに更新する必要があります。更新完了までは、開発者ポータルのプッシュ通知のキーや証明書を削除したり、ユーザーごとに REST API で購読を解除したりするなどの対処が必要になります。