プッシュ通知管理(iOS)

プッシュ通知証明書を設定する

BaaS@rakuzaからプッシュ通知を送信するにはp12(PKCS#12)形式の鍵と証明書が必要です。

プッシュ通知証明書を作成するには、Apple Developer Programopen in new windowに参加する必要があります。

ヒント

BaaS@rakuzaはp8形式の認証キーに現時点では対応していません。

注意

BaaS@rakuza SDK for iOSはiOS 11以降に対応しています。以降の説明は、ターゲットをiOS 11以降にしている前提で行います。

App IDを登録する

Apple Developeropen in new windowにアクセスして、「Account」をクリックします。

ログインした後に、「Certificates, Identifiers & Profiles」→「Identifiers」を開きます。

「Identifiers」の右側の+ボタンをクリックしてください。

App IDsを選択して、次に進みます。

Appを選択して、次に進みます。

Descriptionはアプリが分かるような名称を入力します。

Bundle IDはアプリを一意に識別するキーです。自身が所持するドメインを逆にした文字列を使用することが推奨されています。Explicitを選択して、Xcodeに設定しているBundle IDを入力してください。

Capabilitiesの「Push Notifications」にチェックを付けてください。

入力内容を確認して、問題なければRegisterをクリックします。これでApp IDができました。

CSRをアップロードして、証明書をダウンロードする

証明書を作成するためにCSR(証明書署名要求)を作成します。

CSRはMacのキーチェーンアクセスに付属する証明書アシスタントで作成することができます。キーチェーンアクセスを開き、メニューの「キーチェーンアクセス」→「証明書アシスタント」→「認証局に証明書を要求...」をクリックします。

ユーザのメールアドレスと通称を入力して、要求の処理は「ディスクに保存」を選択し、「続ける」をクリックしてCSRを保存してください。

Apple Developerに戻り、先ほど作成したApp IDのページを開きます。

Capabilitiesの「Push Notifications」の右横に表示されている「Edit」をクリックします。

「Create Certificate」をクリックします。

ヒント

APNsはDevelopment(開発用)とProduction(実稼働用)の2つの環境がありますが、BaaS@rakuzaは証明書より環境を自動で決定します。

そのため、Development環境に送信する場合は「Development SSL Certificate」、Production環境に送信する場合は「Production SSL Certificate」を選択してください。


先ほど作成したCSRを選択して、次に進みます。作成された証明書はダウンロードしてください。

証明書をインストールしてp12形式に変換する

ダウンロードした証明書(cerファイル)をダブルクリックすると、キーチェーンに保存されます。

キーチェーンアクセスを開き、保存した証明書を探します。証明書名は「Apple Push Services: <Bundle ID>」の様な形式になっているため、Bundle IDを元に検索することができます。

証明書を選択後に副ボタンをクリックして、表示されたメニューの「<証明書名>を書き出す...」をクリックします。

ファイル名を入力して、フォーマットは「個人情報交換(.p12)」のままファイルを保存してください。

証明書のパスワードを求められますが、パスワードは入力せずに保存してください。パスワードを設定するとBaaS@rakuzaに証明書をアップロードしてもプッシュ通知が送信できません。

プッシュ通知証明書を登録する

プッシュ通知をBaaS@rakuzaから送信するために、証明書を登録します。

管理画面の「プッシュ通知管理」→「プッシュ通知環境設定」を開きます。

続いて、「iOSプッシュ通知設定」→「Push通知証明書」のファイル選択をクリックして、先ほど作成したp12形式のファイルを選択して、「更新」ボタンをクリックします。

更新に成功すると、登録した証明書のBundle IDが表示されます。

ヒント

プッシュ通知証明書は1つだけ登録することができます。別のBundle IDを使用したい場合や、DevelopmentとProductionを切り替えたい場合は、都度証明書を登録し直してください。

プロビジョニングプロファイルを作成する

ヒント

プロビジョニングプロファイルはApp配布用証明書、App ID、端末の情報が一体になったファイルです。実機で動作確認するためには、プロビジョニングプロファイルが必要です。

Apple Developerの「Certificates, Identifiers & Profiles」を開き、App配布用証明書は「Certificates」から作成、端末の情報は「Devices」から登録して、以降の手順を実施してください。


Apple Developerの「Certificates, Identifiers & Profiles」→「Profiles」を開きます。

「Profiles」の右側の+ボタンをクリックしてください。

Development環境に送信する場合は「iOS App Development」、Production環境に送信する場合は「Ad Hoc」または「App Store」を選択してください。

ここでは、Ad Hocを選択します。

作成したApp IDを選択してください。

DistributionのApp配布用証明書を選択してください。

アプリをインストールするテスト用の端末を選択してください。

プロビジョニングプロファイル名を入力して、Generateをクリックします。

作成されたプロビジョニングプロファイルをダウンロードします。プロビジョニングプロファイルをダブルクリックすると、Xcodeに取り込まれます。

Xcodeで取り込まれたプロビジョニングプロファイルを選択してください。

デバイストークンを登録する

デバイストークンを取得してBaaS@rakuzaに登録します。デバイストークンをBaaS@rakuzaに登録することで、管理画面からデバイストークンに紐づく端末にプッシュ通知を送信することができます。

まず、Xcodeの「+Capability」から、Push Notificationsを選択します。

続いて、AppDelegateに以下のコードを追記してください。この状態でアプリケーションを起動すると、初回の場合に通知の許可確認ダイアログが表示されます。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let center = UNUserNotificationCenter.current()
    center.delegate = self
    center.requestAuthorization(options: [.sound, .alert, .badge], completionHandler: { (granted, error) in
        guard error == nil else {
            print("通知の許諾に失敗しました。")
            return
        }
        if granted {
            DispatchQueue.main.async {
                application.registerForRemoteNotifications()
            }
        } else {
            print("ユーザーは通知を拒否しました。")
        }
    })

    return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;
    [center requestAuthorizationWithOptions:UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"通知の許諾に失敗しました。");
            return;
        }
        if (granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [application registerForRemoteNotifications];
            });
        } else {
            NSLog(@"ユーザーは通知を拒否しました。");
        }
    }];

    return YES;
}

通知が許可されるとAppDelegateapplication(_:didRegisterForRemoteNotificationsWithDeviceToken:)が呼び出されます。このメソッドにBaaS@rakuzaへデバイストークンを登録する処理を実装してください。

デバイストークンの登録はregistPushDeviceTokenメソッドで行います。

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let userAccessToken = "xxx"
    let deviceTokenStr = deviceToken.map { data in String(format: "%02.2hhx", data) }.joined() // 取得したデバイストークンを文字列に変換

    RKZService.sharedInstance().registPushDeviceToken(userAccessToken, deviceToken: deviceTokenStr) { statusCode, responseStatus in
        if responseStatus.isSuccess {
            // 成功時
            print("登録完了")
        } else {
            // 失敗
            print("statusCode:", responseStatus.statusCode.rawValue)
            print("message:", responseStatus.message ?? "")
        }
    }
    
    print("deviceToken:", deviceTokenStr)
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSString *userAccessToken = @"xxx";
    NSString *deviceTokenStr = [self hexStringFromData:deviceToken];
    
    [[RKZService sharedInstance] registPushDeviceToken:userAccessToken deviceToken:deviceTokenStr withBlock:^(RKZApiStatusCode statusCode, RKZResponseStatus * _Nonnull responseStatus) {
        if (responseStatus.isSuccess) {
            // 成功時
            NSLog(@"登録完了");
        } else {
            // 失敗
            NSLog(@"statusCode: %ld", responseStatus.statusCode);
            NSLog(@"message: %@", responseStatus.message);
        }
    }];
    
    NSLog(@"deviceToken: %@", deviceTokenStr);
}

- (NSString *)hexStringFromData:(NSData *)data {
    const unsigned char *bytes = (const unsigned char *)data.bytes;
    NSMutableString *hex = [NSMutableString new];
    for (NSInteger i = 0; i < data.length; i++) {
        [hex appendFormat:@"%02x", bytes[i]];
    }
    return [hex copy];
}

v3.3.0から、registUserメソッド、editUserメソッドでもデバイストークンを登録できるようになりました。

let user = RKZUserData()
user.user_name = "People Taro"
user.attributes = [
    "push_device_token": "発行したデバイストークン", // プッシュ通知を送りたい場合に指定
    "smartphonesb_cd": "0002" // プッシュ通知を送りたい場合に指定
]

RKZService.sharedInstance().registUser(user) { user, responseStatus in
    if responseStatus.isSuccess, let user = user {
        // 成功時
        print("user_no:", user.user_no ?? "") // ユーザーを一意に識別する顧客番号
        print("user_access_token:", user.user_access_token ?? "") // ユーザーアクセストークン
        print("user_name:", user.user_name ?? "")
        print("push_device_token:", user.attributes?["push_device_token"] ?? "")
    } else {
        // 失敗
        print("statusCode:", responseStatus.statusCode.rawValue)
        print("message:", responseStatus.message ?? "")
    }
}
RKZUserData *user = [[RKZUserData alloc] init];
user.user_name = @"People Taro";
user.attributes = @{
    @"push_device_token": @"発行したデバイストークン", // プッシュ通知を送りたい場合に指定
    @"smartphonesb_cd": @"0002" // プッシュ通知を送りたい場合に指定
};

[[RKZService sharedInstance] registUser:user withBlock:^(RKZUserData * _Nullable userData, RKZResponseStatus * _Nonnull responseStatus) {
    if (responseStatus.isSuccess) {
        // 成功時
        NSLog(@"user_no: %@", userData.user_no); // ユーザーを一意に識別する顧客番号
        NSLog(@"user_access_token: %@", userData.user_access_token); // ユーザーアクセストークン
        NSLog(@"user_name: %@", userData.user_name);
        NSLog(@"push_device_token: %@", userData.attributes[@"push_device_token"]);
    } else {
        // 失敗
        NSLog(@"statusCode: %ld", responseStatus.statusCode);
        NSLog(@"message: %@", responseStatus.message);
    }
}];

デバイストークンを取得に失敗した場合はapplication(_:didFailToRegisterForRemoteNotificationsWithError:)が呼び出されます。アプリケーション側で適宜処理を行ってください。

プッシュ通知を送信する

お知らせをプッシュ通知したい場合、管理画面の「お知らせ管理」→「お知らせ一覧」を開き、通知したいお知らせのPushボタンをクリックします。

抽出タイミング、タイトル、配信日時に変更があれば編集を行います。編集が終わったら次へをクリックします。

確認して問題なければ、通知の予約を行ってください。

予約したプッシュ通知は管理画面の「プッシュ通知」→「プッシュ通知履歴」から確認することができます。

また、直接プッシュ通知の内容を指定して送信することができます。管理画面の「プッシュ通知管理」より、通知対象の条件絞り込みに応じて以下の機能を使用してください。

  • 一斉プッシュ通知: すべてのユーザーに通知を送信する
  • ユーザー属性プッシュ通知: ユーザーの属性で絞り込みを行い通知を送信する
  • コンタクト履歴プッシュ通知: コンタクト履歴を条件に絞り込みを行い通知を送信する

ヒント

管理者向けSDKを使うと、プログラムによるプッシュ通知の配信予約を行うことができます。詳細はプッシュ通知の配信予約を参照ください。

受信したプッシュ通知を処理する

受信したプッシュ通知の情報をハンドリングすることができます。

BaaS@rakuzaではカスタムペイロードとして以下の項目を送信しています。カスタムペイロードはプッシュ通知を受信した時や、通知バナーをタップした時に参照することができます。

項目名概要備考
push_noプッシュ番号プッシュ通知の予約単位で発番されるキー。開封率の計測で使用します。
news_idお知らせID※お知らせをプッシュ通知した場合のみ送信
news_tenant_idお知らせを配信しているテナントID※お知らせをプッシュ通知した場合のみ送信
urlURL
custom_varsカスタム情報

プッシュ通知を受信すると、フォアグラウンドの場合は、UNUserNotificationCenterDelegateuserNotificationCenter(_:willPresent:withCompletionHandler:)メソッドが呼び出されます。

カスタムペイロードはnotification.request.content.userInfoから取得することができます。

extension AppDelegate: UNUserNotificationCenterDelegate {
    // ...
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        if let newsId = userInfo["news_id"] as? String {
            // カスタムペイロードのお知らせIDを取得
            print("newsId:", newsId)
        }
        
        // 通知を表示する方法を指定する
        completionHandler([.badge, .alert, .sound])
    }
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
    NSDictionary *userInfo = notification.request.content.userInfo;
    NSString *newsId = userInfo[@"news_id"];
    if (newsId != nil) {
        // カスタムペイロードのお知らせIDを取得
        NSLog(@"newsId: %@", newsId);
    }
    
    // 通知を表示する方法を指定する
    completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

通知バナーをタップすると、UNUserNotificationCenterDelegateuserNotificationCenter(_:didReceive:withCompletionHandler:)メソッドが呼び出されます。

extension AppDelegate: UNUserNotificationCenterDelegate {
    // ...
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        if let newsId = userInfo["news_id"] as? String {
            // カスタムペイロードのお知らせIDを取得
            print("newsId:", newsId)
        }
        completionHandler()
    }
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    NSString *newsId = userInfo[@"news_id"];
    if (newsId != nil) {
        // カスタムペイロードのお知らせIDを取得
        NSLog(@"newsId: %@", newsId);
    }
    completionHandler();
}

開封率を計測する

開封率を計測するには、プッシュ通知を受信した際、アプリ側に計測用のコードを埋め込む必要があります。

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    let userAccessToken = "xxx"
    let userInfo = response.notification.request.content.userInfo
    if let pushNo = userInfo["push_no"] as? String, let pushNo = Int(pushNo) {
        RKZService.sharedInstance().openPush(userAccessToken, pushNo: NSNumber(value: pushNo)) { statusCode, responseStatus in
            if responseStatus.isSuccess {
                // 成功時
                print("計測完了")
            } else {
                // 失敗
                print("statusCode:", responseStatus.statusCode.rawValue)
                print("message:", responseStatus.message ?? "")
            }
        }
    }
    completionHandler()
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
    NSString *userAccessToken = @"xxx";
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    NSString *pushNo = userInfo[@"push_no"];
    if (pushNo != nil) {
        [[RKZService sharedInstance] openPush:userAccessToken pushNo:[NSNumber numberWithInteger:[pushNo integerValue]] withBlock:^(RKZApiStatusCode statusCode, RKZResponseStatus * _Nonnull responseStatus) {
            if (responseStatus.isSuccess) {
                // 成功時
                NSLog(@"計測完了");
            } else {
                // 失敗
                NSLog(@"statusCode: %ld", responseStatus.statusCode);
                NSLog(@"message: %@", responseStatus.message);
            }
        }];
    }
    completionHandler();
}

計測した開封率は管理画面の「プッシュ通知」→「プッシュ通知履歴」→「開封件数 / 配信済件数」から確認することができます。

デバイストークンをクリアする

ユーザーがログアウトするなど、デバイストークンが不要になった場合はクリアすることができます。

デバイストークンのクリアはclearPushDeviceTokenメソッドで行います。

let userAccessToken = "xxx"

RKZService.sharedInstance().clearPushDeviceToken(userAccessToken) { statusCode, responseStatus in
    if responseStatus.isSuccess {
        // 成功時
        print("クリア完了")
    } else {
        // 失敗
        print("statusCode:", responseStatus.statusCode.rawValue)
        print("message:", responseStatus.message ?? "")
    }
}
NSString *userAccessToken = @"xxx";

[[RKZService sharedInstance] clearPushDeviceToken:userAccessToken withBlock:^(RKZApiStatusCode statusCode, RKZResponseStatus * _Nonnull responseStatus) {
    if (responseStatus.isSuccess) {
        // 成功時
        NSLog(@"クリア完了");
    } else {
        // 失敗
        NSLog(@"statusCode: %ld", responseStatus.statusCode);
        NSLog(@"message: %@", responseStatus.message);
    }
}];