2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity(iOS)でメッセージアプリみたいなPush通知を送る

Last updated at Posted at 2025-12-24

本記事は QualiArts Advent Calendar 2025 の24日目の記事になります。

株式会社QualiArtsでUnityエンジニアをしています長濱です。

近年のゲームアプリでは、キャラクターからの送信を模したPush通知がよく使われるのですが、デフォルトだと下の画像のようにアプリアイコンがデカデカと表示されてしまう問題があります。
右にアイコンを小さく表示すること自体は可能なのですが、あまり見た目自体良くないです。

スクリーンショット 2025-12-24 19.51.18.png

この記事では解決策として、Communication Notificationsを紹介します

Communication Notifications

iOS15から追加された機能で、名前の通りコミュニケーションアプリで使われる機能になります。本来の目的として即時通知や、後述する送信者情報を使ってユーザーが選択した送信者の通知のみに集中モードを貫通させることができます。
ただ、今回の記事ではアプリアイコンを任意の画像で置き換える機能にフォーカスして紹介します。

Sample

検証したプロジェクトは以下のリポジリトリにあげてあります。この記事ではUnity側のコードに関しては解説しないので必要であれば参照してください
Unity 6000.55f1を使用しています。

Xcode側の設定

Communication Notificationsを使うためにいくつか設定が必要です。
設定するターゲットがそれぞれ違うので気をつけてください

Capability追加

  1. TARGETS から Unity-iPhone を選択
  2. 上部タブメニュー "Signing & Capabilities" を選択
  3. "+ Capability" をクリック
  4. Communication Notifications を選択

スクリーンショット 2025-12-24 19.10.59.png

Target Properties更新

  1. TARGETS から Unity-iPhone を選択
  2. 上部タブメニュー "Info" を選択
  3. Custom "" Target Properties に ArrayのNSUserActivityTypes を追加
  4. NSUserActivityTypes に INSendMessageIntent を追加

スクリーンショット 2025-12-24 19.11.39.png

Framework追加

  1. TARGETS から UnityFramework を選択
  2. 上部タブメニュー "General" を選択
  3. Frameworks and Libraries に Intents.framework を追加

スクリーンショット 2025-12-24 19.10.05.png

ネイティブ

Unity向けということもあり導入が簡単なObjective-Cで既述しています。
ただ、最近UnityのiOSネイティブ部分をSwiftに変える発表がされていたのでswiftで書いてもよかったかもです。

    void SendCommunicationNotification(const char* senderName, const char* message, const char* imagePath, int delaySeconds) {
        @autoreleasepool {
            // ========================================
            // 1. 通知センターの取得
            // ========================================
            // iOSの通知を管理するUNUserNotificationCenterのインスタンスを取得
            UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

            // ========================================
            // 2. 通知コンテンツの基本設定
            // ========================================
            // 通知の内容を設定するためのオブジェクトを作成
            UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
            // メッセージ本文をC文字列からNSStringに変換して設定
            content.body = [NSString stringWithUTF8String:message];
            // 通知音にシステムデフォルトのサウンドを設定
            content.sound = [UNNotificationSound defaultSound];

            // ========================================
            // 3. アバター画像の読み込み
            // ========================================
            // 送信者のアバター画像を表示するためのINImageオブジェクト
            INImage *avatarImage = nil;

            // 画像パスが指定されている場合のみ画像を読み込む
            if (imagePath != NULL && strlen(imagePath) > 0) {
                // C文字列のファイルパスをNSStringに変換
                NSString *imagePathStr = [NSString stringWithUTF8String:imagePath];

                // ファイルパスから画像を読み込む
                UIImage *uiImage = [UIImage imageWithContentsOfFile:imagePathStr];

                if (uiImage != nil) {
                    // UIImageをPNG形式のNSDataに変換
                    // Intents FrameworkのINImageはNSDataから作成する必要がある
                    NSData *imageData = UIImagePNGRepresentation(uiImage);

                    if (imageData != nil) {
                        // NSDataからINImageを作成
                        // このINImageがCommunication通知のアバター画像として使用される
                        avatarImage = [INImage imageWithImageData:imageData];
                    }
                }
            }

            // ========================================
            // 4. 送信者(INPerson)の作成
            // ========================================
            // 送信者の名前をC文字列からNSStringに変換
            NSString *senderNameStr = [NSString stringWithUTF8String:senderName];

            // 人物の名前を構造化して保持するオブジェクト
            // nicknameに設定することで、通知に表示される名前が決まる
            NSPersonNameComponents *nameComponents = [[NSPersonNameComponents alloc] init];
            nameComponents.nickname = senderNameStr;

            // 人物の連絡先ハンドル(電話番号やメールアドレスなど)を作成
            // ここでは実際の連絡先情報は不要なので、ダミーの値を設定
            INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:@"unity_sender"
                                                                            type:INPersonHandleTypeUnknown];

            // INPersonオブジェクトの作成
            // これがCommunication通知の送信者情報として使用される
            INPerson *sender = [[INPerson alloc] initWithPersonHandle:personHandle
                                                        nameComponents:nameComponents  // 名前情報
                                                           displayName:senderNameStr    // 表示名
                                                                 image:avatarImage      // アバター画像(nilの場合はアイコンなし)
                                                     contactIdentifier:nil              // 連絡先ID(任意)
                                                      customIdentifier:nil              // カスタムID(任意)
                                                                  isMe:NO               // 自分自身かどうか(受信メッセージなのでNO)
                                                        suggestionType:INPersonSuggestionTypeNone];  // 提案タイプ

            // ========================================
            // 5. メッセージ送信Intent(INSendMessageIntent)の作成
            // ========================================
            // Communication通知として認識されるために、INSendMessageIntentを使用
            // このIntentがiOSに「これはメッセージアプリからの通知だ」と認識させる

            // グループ名(会話名)を設定
            INSpeakableString *groupName = [[INSpeakableString alloc] initWithSpokenPhrase:@"Unity Message"];

            // メッセージ送信Intentの作成
            INSendMessageIntent *intent = [[INSendMessageIntent alloc] initWithRecipients:nil  // 受信者(不要)
                                                                      outgoingMessageType:INOutgoingMessageTypeOutgoingMessageText  // メッセージタイプ(テキスト)
                                                                                  content:nil                                       // メッセージ内容(通知コンテンツで指定するのでnil)
                                                                     speakableGroupName:groupName                                   // グループ名
                                                                 conversationIdentifier:@"unity_conversation"                      // 会話の一意識別子
                                                                            serviceName:nil                                        // サービス名(任意)
                                                                                 sender:sender                                     // 送信者情報(ここが重要!)
                                                                            attachments:nil];                                      // 添付ファイル(任意)

            // ========================================
            // 6. インタラクションのDonate(SiriKitへの登録)
            // ========================================
            // INInteractionを使ってIntentをシステムに登録(Donate)する
            // これにより、iOSがこのアプリがメッセージを受信したことを学習する
            INInteraction *interaction = [[INInteraction alloc] initWithIntent:intent response:nil];

            // インタラクションの方向を「受信」に設定
            // これにより、メッセージが外部から来たものであることをシステムに伝える
            interaction.direction = INInteractionDirectionIncoming;

            // インタラクションをシステムにDonateする
            // これがCommunication通知として表示されるための重要なステップ
            [interaction donateInteractionWithCompletion:nil];

            // ========================================
            // 7. 通知トリガーの作成
            // ========================================
            // 通知を発火させるタイミングを設定
            UNTimeIntervalNotificationTrigger *trigger = nil;

            if (delaySeconds > 0) {
                // 指定された秒数後に通知を発火(繰り返しなし)
                trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delaySeconds repeats:NO];
            } else {
                // 即座に通知を発火(最小値として1秒を設定)
                trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats:NO];
            }

            // ========================================
            // 8. IntentでコンテンツをCommunication通知に更新
            // ========================================
            // iOS 15以降で利用可能なcontentByUpdatingWithProvider:error:メソッドを使用
            // INSendMessageIntentをプロバイダーとして渡すことで、
            // 通常の通知がCommunication Notificationスタイルに変換される
            id<UNNotificationContentProviding> provider = (id<UNNotificationContentProviding>)intent;
            NSError *error = nil;

            // Intentの情報(送信者、アバター画像など)を通知コンテンツに適用
            UNNotificationContent *updatedContent = [content contentByUpdatingWithProvider:provider error:&error];

            if (updatedContent != nil) {
                // 更新されたコンテンツを可変コピーして使用
                content = [updatedContent mutableCopy];
            } else if (error != nil) {
                // エラーが発生した場合はログ出力
                NSLog(@"Error updating notification content: %@", error.localizedDescription);
            }

            // ========================================
            // 9. 通知リクエストの作成とスケジュール
            // ========================================
            // 通知を一意に識別するためのUUIDを生成
            NSString *identifier = [[NSUUID UUID] UUIDString];

            // 通知リクエストを作成(識別子、コンテンツ、トリガーを含む)
            UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier
                                                                                  content:content
                                                                                  trigger:trigger];

            // 通知センターに通知リクエストを追加してスケジュール
            // トリガーの時間になると通知が表示される
            [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
                if (error != nil) {
                    NSLog(@"Error scheduling notification: %@", error.localizedDescription);
                } else {
                    NSLog(@"Communication notification scheduled successfully");
                }
            }];
        }
    }

結果

このように、キャラのアイコンをメインで表示することができました。

スクリーンショット 2025-12-24 22.13.08.png

最後に

最後に懸念として、コミュニケーションアプリ以外でもちゃんと申請が通るのかが問題としてあります。分かり次第追記したいと思います。

追記: ゲームアプリでも申請は通っているようです。

最後まで読んでいただきありとうございます。明日がアドカレ最終日になります。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?