前回のつづき

PUSH通知(Remote Notification)の典型的な使用目的は、新着メッセージを通知することでしょう。facebookやLINEなどでもおなじみですね。

今回は、

  • 新着メッセージを通知
  • 未読のメッセージ件数をアプリアイコンに表示(バッジと呼ぶ)
  • 既読にしたらバッジの未読件数を更新する

という実装例を説明したいと思います。

新着メッセージを通知

これはサーバー側の実装となります。気をつけるべきことは、PUSH通知で送信できるデータ量には限りがあるということです。つまりPUSH通知ですべてのデータを送信しようとするのではなく、あくまで新着件数を通知するなどにとどめるべきです。実際に必要となるデータ(メッセージの内容など)はアプリから何らかのタイミングでサーバーに問い合わせて取得することになります。

サーバー側からのPUSH通知で含めるデータは、新着メッセージ数くらいに留めるのが良いと思います。新着メッセージすべてのデータをPUSH通知に含めるのは現実的ではありません。

未読のバッジ数を表示

サーバーからのPUSH通知にバッジ数を指定できます。アプリ側で何もしないと、そのバッジ数が表示されます。しかし、多く場合はバッジ数表示はアプリ側で制御したくなると思います。どのメッセージが既読か未読かはアプリ内でデータベースで管理することになるでしょう。既読状況をサーバーで管理する場合も同様に、アプリ内で同期した状態を管理することになります。

実装例

上記を踏まえて以下のように実装すると良いと思います。

※ Myで始まるクラス名は例で、実際はアプリケーションに応じて自分で宣言・実装する必要があります。

PUSH通知が届いたことを検出した時

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{  
  // 新着メッセージ数
  NSUInteger numberOfNewMessages = [userInfo objectForKey:@"numberOfNewMessages"];

  // データベースから未読数を得る
  NSUInteger unreadCount = [MyMessageDatabase getUnreadCount];
  // 今回の新着メッセージ分を足す
  unreadCount += numberOfNewMessages;

  // 未読数をバッジ表示する
  UIApplication *app = [UIApplication sharedApplication];
  app.applicationIconBadgeNumber = unreadCount + numberOfNewMessages;
}

前回書いたようにバックグラウンドモードの時は、代わりにapplication:didReceiveRemoteNotification:fetchCompletionHandler:を実装します。この場合、新着メッセージを問い合わせ処理を入れてもよいでしょう。当然、その処理は非同期で完了したらfetchCompletionHandlerを呼ばなければなりません。

アプリがActiveになったとき

アプリが起動していないときにPUSH通知を受信しても、アプリ自身は通知検出できません。アプリが起動していてもバックグラウンドモードでなければ、バックグラウンド時は通知検出できません。またネットワーク状況がオフだったり悪かったりするとPUSH通知が届かないことがあります。よって新着メッセージの検出をPUSH通知だけに頼るのは良くないです。

アプリがActiveになったタイミングで新着メッセージを確認すると良いでしょう。ただしアクティブになる度に確認するとサーバーに負荷がかかります。最後に新着メッセージを得てから一定期間以上経過していたら、新着メッセージを確認するなどの処理を入れたほうが良いでしょう。

- (void)applicationDidBecomeActive:(UIApplication *)application
{
  // サーバーAPIへ新着メッセージを問い合わせる(非同期)
  // 前回の問い合わせから一定時間経過してから問い合わせる処理も入れたほうが良い
  [MyServerAPI getNewMessagesWithSuccess:^(NSArray *newMessages){
    [MyMessageDatabase saveMessages:newMessages];

    NSUInteger unreadCount = [MyMessageDatabase getUnreadCount];    
    // 未読数をバッジ表示する
    UIApplication *app = [UIApplication sharedApplication];
    app.applicationIconBadgeNumber = unreadCount;
  } failure:^(NSError *error){
    TODO:エラー処理
  }];
}

アプリによってはActiveになったときではなく、メッセージ一覧画面を表示するタイミングで新着メッセージを得るでも良いかもしれません。

サーバーでも未読・既読状態を管理する場合

バッジ数などに正確さを求めると上記の例よりもう少し工夫が必要となりそうです。サーバーでは未読であるが、クライアントでは既読であるなど、同期が取れていない状態でのPUSH通知のやりとりも起きうると思います。

  • PUSH通知で送信する内容にサーバー側で検知している「既読の最も新しいメッセージの作成日時(A)」と「未読数(B)」を含める
  • アプリはPUSH通知を受け取ったら、「B」から「データベースにあるAより新しいメッセージの既読数」を差し引いたものをバッジ数として表示

などの処理が必要になると思います。

まとめ

  • PUSH通知に含めるデータは新着が何件あるかなどに留める
  • 実際の新着データなどはアプリ側から適切なタイミングで問い合わせる