酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Azure Mobile Services(モバイルサービス)の通知ハブに登録されているiOSデバイスに向けてAPNsを使ってプッシュ通知する

スマートフォンを使っていると当たり前の機能のうちに「プッシュ通知」があります。

たとえば、iPhoneやiPadでGmailアプリなどのメールアプリを使っていると、メールを受け取るとユーザーに向けて「メールを受信しました」とプッシュ通知されます。

プッシュ通知を送るための仕組みは、iOS(Apple)、Windows(Microsoft)、Android(Google)とプラットフォームごとに用意されています。当然、実装に必要のための手順がバラバラにも拘わらず、モバイルアプリは複数のプラットフォームでリリースするのも当たり前になっています。プッシュ通知を送るための実装が異なることで工数が増大して苦しんでいた方も多いと思います。

各プラットフォームでのプッシュ通知の実装をラッピングして、簡単にプッシュ通知とデバイスの管理ができるようにするサードパーティ製のサービスには「Parse」や「Amaozon SNS」というものがあります。それらのひとつに「Azure Mobile Service(モバイルサービス)」があります。*1

事前準備

Apple Push Notification Service(APNs)でプッシュ通知を送る場合の処理については、Appleからプログラミングガイドが出ていますのでこのドキュメントを読みます。

用語は異なりますが基本的には「Windows PhoneでAzure Mobile ServiceとNotification Hubを利用して2ステップでプッシュ通知機能を実装する #wpdev_jp - 酢ろぐ!」で説明しているイメージ図と同じです。APNsへプッシュ通知を送信するのにあたり、通知ハブの機能を利用するには「Azure Notification Hubs を使用して iOS アプリにプッシュ通知を送信する | Microsoft Docs」を参考にしてください。

プッシュ通知を受け取るための準備

アプリ側の実装

アプリ側では以下のように実装します。iOS 7.1以前とiOS 8以降で実装が異なります。

-(BOOL)application:(UIApplication *)application 
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // ...

    // プッシュ通知を受けるためにデバイストークンの要求
    if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
    {
        // iOS 8 以降
        UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        [application registerUserNotificationSettings:settings];
    } else {
        // iOS 7.1 以前
        UIRemoteNotificationType types = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge;
        [application registerForRemoteNotificationTypes:types];
    }
    
    return YES;
}

- (void)application:(UIApplication *)application 
    didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    [application registerForRemoteNotifications];
}

- (void)application:(UIApplication*)application 
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
    // サーバーにデバイストークンを設定する処理を実装する
    NSString* serviceUrl = @"https://{service_name}.azure-mobile.net/";
    NSString* appKey = @"{your application key}";
        
    MSClient* client = [MSClient clientWithApplicationURLString:serviceUrl
                                                 applicationKey:appKey];
                                                    
    // 通知ハブを使う場合は以下のように実装する
    [self.client.push registerNativeWithDeviceToken:deviceToken
        tags:nil completion:nil]; 
}

iOSデバイスに向けてプッシュ通知する

バッチ処理等でiOSデバイスに向けてプッシュ通知を送る場合の処理です。

// 接続文字列
var connnectionString = "Endpoint=sb://example-ns.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=<your key>";

// ハブ名
var hubName = "<your hub name>";

// テスト送信か?
// Production(本番)にプッシュ通知を送信する場合は false
// Development(開発)にプッシュ通知を送信する場合は true
var enableTestSend = false;

// 接続文字列からNotificationHubClientオブジェクトを生成する
var hubClient = NotificationHubClient.CreateClientFromConnectionString(
    connnectionString, hubName, false);

// 通知を送信!!!!
var json = "{\"aps\":{\"alert\":\"hage\"},\"app\":{\"id\":\"kazuakix\"}}";
await client.SendAppleNativeNotificationAsync(json);

アプリで通知を受け取る

アプリでプッシュ通知を受け取った後の処理についてです。userInfoには送信されたプッシュ通知のペイロードがパースされた状態で格納されています。

-(void)application:(UIApplication *)application 
    didReceiveRemoteNotification:(NSDictionary *)userInfo 
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    if (application.applicationState == UIApplicationStateInactive) {
        // アプリプロセスがバックグラウンドで動作している状態
    } else if (application.applicationState == UIApplicationStateActive) {
        // アプリプロセスがフォアグランドで動作している状態
    }
    
    // pushのペイロードから読み取る
    NSDictionary *app = [userInfo objectForKey:@"app"];
    NSString *userId = [app objectForKey:@"id"];
    
    // 受け取ったよ!!!!!!
    UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"didReceiveRemoteNotification" 
        message:@"received!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alertView show];
}

*1:Windowsに関しては当事者