> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 重複通知

> SDK の競合、サーバー側の再試行、マルチアプリ環境、プラットフォーム固有のバグによる重複プッシュ通知のトラブルシューティング。

重複通知とは、同じデバイスが同じメッセージ内容を複数回受け取ることです。このガイドでは、最も一般的な原因とその解決方法を説明します。

同じユーザーが複数のデバイス（スマートフォン、タブレット、デスクトップ）で通知を受け取る場合、それはターゲット設定（セグメント、外部 ID など）に基づく想定どおりの動作です。重複するアプリ内メッセージについては、[アプリ内メッセージのトラブルシューティング](./in-app-message-troubleshooting#duplicated-in-app-messages)ガイドを参照してください。

<Info>
  Apple は、iOS 17 で重複を引き起こすバグを認めました。これは iOS 17.3 で修正されました。[詳細はこちら](https://forums.developer.apple.com/forums/thread/747044)。
</Info>

## まずここから

状況に最も合うセクションを選択してください：

* サーバーが失敗した API 呼び出しを再試行するか、バックエンドパイプラインが重複送信する可能性がある → [同じメッセージが複数回送信される](#same-message-sent-multiple-times)
* アプリが OneSignal と並行して Firebase または別のプッシュ SDK を使用している → [サードパーティ通知ハンドラー](#third-party-notification-handlers)
* Android アプリがコードで通知をカスタマイズしている（サービス拡張機能またはフォアグラウンドリスナー）→ [Android 通知ハンドラー](#android-notification-handlers)
* iOS アプリが独自の `UNUserNotificationCenterDelegate` を実装している → [iOS フォアグラウンドハンドラー](#ios-foreground-handler)
* 開発ビルドまたは複数のインストールでのみ重複が見られる → [複数のアプリインスタンス](#multiple-app-instances)

## 同じメッセージが複数回送信される

重複通知の最も一般的な原因は、OneSignal API 経由で同じ通知ペイロードを複数回送信することです。一般的な理由：

* サーバーが最初のリクエストが成功したかどうかを確認せずにリクエストを再試行します。
* バックエンドパイプラインのロジックが同じ通知を 2 回送信しています。
* OneSignal に移行中だが、以前のプロバイダーからまだ通知を送信している。両方のシステムから同時に送信しないでください。

<Card title="冪等 API リクエスト" icon="rotate" href="/reference/idempotent-notification-requests">
  再試行時に冪等キーを再利用することで重複メッセージを防止します。
</Card>

## サードパーティ通知ハンドラー

アプリ内の他のコードが OneSignal のプッシュペイロードを処理し、OneSignal に加えて独自の通知を表示する場合に重複が発生します。これには以下が含まれます：

* 同じペイロードを受信する他のリモートプッシュ SDK（例：Firebase Cloud Messaging）。
* Android の `FirebaseMessagingService` や iOS の `UNUserNotificationCenterDelegate` などのカスタムメッセージハンドラーが受信ペイロードを読み取り、ローカル通知を構築する場合。

OneSignal SDK は、唯一のハンドラーである場合に OneSignal ペイロードを自動的にフィルタリングします。サードパーティのコードは、明示的に指定しない限りフィルタリング方法を知りません。

### OneSignal 通知の識別

すべての OneSignal 通知には、OneSignal 通知 UUID を含む `i` キーを持つ `custom` オブジェクトが含まれます：

```json JSON theme={null}
{
  "custom": {
    "i": "the-notification-id"
  }
}
```

完全なペイロードリファレンスについては、[カスタム OneSignal ペイロード構造](./osnotification-payload#custom-onesignal-payload-structure)を参照してください。

### サードパーティハンドラーから OneSignal 通知をフィルタリングする

OneSignal 以外のすべてのハンドラーで、`custom.i` キーを確認し、存在する場合は早期リターンします。OneSignal は自身のペイロードを処理し、他のコードは独自のペイロードの処理を続けます。

<Tabs>
  <Tab title="Android">
    カスタム `FirebaseMessagingService` または他のリモートメッセージハンドラーで：

    ```kotlin Kotlin theme={null}
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        val customJson = remoteMessage.data["custom"]
        if (customJson != null) {
            try {
                val custom = JSONObject(customJson)
                if (custom.has("i")) {
                    // OneSignal 通知——OneSignal SDK に処理を任せる
                    return
                }
            } catch (e: JSONException) {
                // OneSignal ペイロードではないため、処理を続行する
            }
        }

        // ここで独自の通知を処理する
    }
    ```
  </Tab>

  <Tab title="iOS">
    `UNUserNotificationCenterDelegate` またはカスタム通知ハンドラーで：

    ```swift Swift theme={null}
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo

        if let custom = userInfo["custom"] as? [String: Any], custom["i"] != nil {
            // OneSignal 通知——OneSignal SDK に処理を任せる
            completionHandler([])
            return
        }

        // ここで独自の通知を処理する
    }
    ```
  </Tab>
</Tabs>

<Tip>
  他のプロバイダーから送信されるペイロード構造を制御できる場合は、独自のマーカーキー（例：`my_app_notification: true`）を追加し、双方向でフィルタリングしてください——自分のコードではマーカーを含む通知のみを処理し、OneSignal には `custom.i` キーを含む通知を処理させます。
</Tip>

## Android 通知ハンドラー

OneSignal は、通知が表示される前に傍受してカスタマイズできる 2 つの Android コールバックを提供します：

* [通知サービス拡張機能](./service-extensions)の `onNotificationReceived`——アプリがバックグラウンドにある場合も含め、すべてのプッシュで実行されます。
* [フォアグラウンドライフサイクルリスナー](./mobile-sdk-reference#addforegroundlifecyclelistener-push)の `onWillDisplay`——アプリがフォアグラウンドにある場合のみ実行されます。

これらのコールバックが OneSignal の自動表示に加えて通知を表示すると、重複が発生します。

### `preventDefault()` と `display()` のルール

OneSignal は、`event.preventDefault()` で抑制しない限り、各通知を自動的に表示します。2 つのよくある間違いが重複を引き起こします：

* 最初に `event.preventDefault()` を呼び出さずに `event.getNotification().display()` を呼び出す——通知が 2 回表示されます。
* OneSignal がオリジナルを表示している間に [`NotificationManagerCompat.notify()`](https://developer.android.com/reference/androidx/core/app/NotificationManagerCompat) で別の通知を投稿する。

アクティブな表示パスは 1 つのみにする必要があります——OneSignal の `display()` か、独自の `NotificationManagerCompat.notify()` のどちらか一方のみで、両方は使用しないでください。

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onNotificationReceived(event: INotificationReceivedEvent) {
      event.preventDefault()

      val notification = event.notification
      notification.setExtender { builder ->
          builder.setColor(0xFF0000FF.toInt())
      }
      notification.display()
  }
  ```

  ```java Java theme={null}
  @Override
  public void onNotificationReceived(INotificationReceivedEvent event) {
      event.preventDefault();

      IDisplayableMutableNotification notification = event.getNotification();
      notification.setExtender(builder ->
          builder.setColor(0xFF0000FF)
      );
      notification.display();
  }
  ```
</CodeGroup>

同じルールがフォアグラウンドライフサイクルリスナーの `onWillDisplay` にも適用されます：

```kotlin Kotlin theme={null}
OneSignal.Notifications.addForegroundLifecycleListener(object : INotificationLifecycleListener {
    override fun onWillDisplay(event: INotificationWillDisplayEvent) {
        event.preventDefault()

        // 独自の UI をレンダリングするか、event.notification.display() を呼び出して OneSignal 通知を表示する
    }
})
```

<Warning>
  `preventDefault()` を呼び出しても `display()` を呼び出さない場合、通知は静かに破棄され、ユーザーには表示されません。すべてのコードパスが `display()` をちょうど 1 回呼び出すか、意図的に通知を抑制するようにしてください。
</Warning>

<Warning>
  通知サービス拡張機能とフォアグラウンドライフサイクルリスナーの両方を使用する場合は、特定のアプリ状態に対してどちらか一方のハンドラーのみが通知を表示するようにしてください。両方から `display()` を呼び出すと重複が発生します。
</Warning>

### コールバック内の非同期処理

表示前にバックグラウンド処理（ネットワーク呼び出し、データベース検索）を行う場合は、コールバック内で同期的に `preventDefault()` を呼び出し、非同期処理が完了した後にのみ `display()` を呼び出してください。`preventDefault()` なしでコールバックから戻ると OneSignal が通知を表示してしまい、その後に `display()` を呼び出すと重複が発生します。

## iOS フォアグラウンドハンドラー

OneSignal は、SDK の初期化時に自身を `UNUserNotificationCenterDelegate` として設定します。アプリがフォアグラウンド通知処理も実装している場合、通知が 2 回表示される可能性があります——OneSignal からの 1 回とコードからの 1 回です。

重複を引き起こす一般的なパターン：

* OneSignal への呼び出しを転送せずに独自の `UNUserNotificationCenterDelegate` を実装し、同じメソッドから `completionHandler([.banner, .sound])` を呼び出してカスタムアプリ内アラートを表示する。
* OneSignal も表示している受信プッシュに応答してローカル通知（`UNUserNotificationCenter.current().add(...)`）をスケジュールする。

重複を引き起こさずにフォアグラウンド表示を制御するには、OneSignal の[フォアグラウンドライフサイクルリスナー](./mobile-sdk-reference#addforegroundlifecyclelistener-push)を使用し、自分で通知をレンダリングしたい場合に `event.preventDefault()` を呼び出してください：

```swift Swift theme={null}
OneSignal.Notifications.addForegroundLifecycleListener(self)

func onWillDisplay(event: OSNotificationWillDisplayEvent) {
    event.preventDefault()

    // 独自の UI をレンダリングするか、event.notification.display() を呼び出して OneSignal 通知を表示する
}
```

## 複数のアプリインスタンス

<Tabs>
  <Tab title="Android">
    アプリの本番バージョンと開発バージョンの両方がインストールされている場合、重複が発生する可能性があります。各アプリには一意のパッケージ名があり、独自のプッシュトークンを受け取ります。

    通知を長押しして、どのアプリインスタンスから送信されたかを確認します。
  </Tab>

  <Tab title="iOS">
    OneSignal の 2 つのデバイスレコードが同じプッシュトークンを共有している場合、デバイスは 2 つの通知を受け取る可能性があります。これは、同じトークンを複数回インポートまたは登録したときに発生する可能性があります。OneSignal にはこれを防ぐチェックが含まれていますが、エッジケースはそれらを回避する可能性があります。
  </Tab>

  <Tab title="Web">
    Web プッシュの重複は、同じ OneSignal アプリ ID を使用して複数のオリジンにサブスクライブすることによって引き起こされます。[オリジン](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy)とは、ブラウザがプッシュサブスクリプションのスコープを設定するために使用するスキーム、ホスト、ポートの組み合わせです。

    たとえば、`https://example.com` と `https://sub.example.com` にサブスクライブしているユーザーは 2 つのサブスクリプションとしてカウントされ、同じアプリ ID を使用した送信は両方に届きます。

    修正方法：

    1. オリジンごとに[ブラウザデータとプッシュ権限をリセット](./troubleshooting-web-push)します。
    2. 多くのユーザーが影響を受けている場合は、新しい OneSignal アプリを作成し、サイトの init コードで新しいアプリ ID を使用します。
    3. ユーザーにサイトを再訪問して再サブスクライブしてもらいます。

    複数のブラウザまたはブラウザプロファイルでサブスクライブすると、複数の通知が届くことになります。
  </Tab>
</Tabs>

## 診断のヒント

重複問題のデバッグを迅速に行うために、次の情報を収集して OneSignal サポートに送信してください：

* 重複を受け取ったデバイスの OneSignal サブスクリプション ID
* OneSignal メッセージ ID またはダッシュボード内のメッセージへのリンク
* アプリ内の他のライブラリまたはプラグインのリスト
* [問題を再現するデバッグログ](./capturing-a-debug-log)
* 詳細な再現手順

## FAQ

### アプリに 2 つの通知 SDK がある場合はどうなりますか？

両方の SDK が同じ通知を独立して処理・表示する可能性があります。OneSignal は自身の通知を自動的にフィルタリングしますが、他の SDK はフィルタリングしません。`custom.i` キーを確認することで、他の SDK のハンドラーから OneSignal ペイロードをフィルタリングしてください。コード例については、[サードパーティ通知ハンドラー](#third-party-notification-handlers)を参照してください。

### 以前のプロバイダーと OneSignal から同時にプッシュを送信するには？

段階的に移行できますが、両方のプロバイダーから同じメッセージを送信しないようにしてください。

* **Android**：OneSignal を統合してアプリをリリースする際に、古い SDK の通知処理コードを削除します。ユーザーが更新すると、古いプロバイダーからのプッシュを受信しなくなります。
* **iOS**：ユーザーが更新している間、一時的に古いプロバイダーから送信を続けることができます。完全に移行したら、重複を避けるために OneSignal からのみ送信してください。

### 急速に変化する更新に対して複数の通知を防ぐには？

[通知作成](/reference/push-notification) API で [`collapse_id`](/reference/push-notification#body-parameters) を使用して、通知を積み重ねる代わりに以前の通知を置き換えます。複数の通知が同じ `collapse_id` を共有する場合、各新しい通知はトレイ内の前の通知を置き換えます。これは株価更新、ライブスコア、配送の到着予定時刻、および同様の頻繁な更新に役立ちます。

## 関連ページ

<Columns cols={2}>
  <Card title="モバイル SDK トラブルシューティング" icon="mobile" href="./mobile-troubleshooting">
    iOS および Android でのプッシュ通知配信に関する一般的な問題を解決します。
  </Card>

  <Card title="Web プッシュトラブルシューティング" icon="browser" href="./troubleshooting-web-push">
    Web プッシュのサブスクリプション、Service Worker、配信の問題をデバッグします。
  </Card>

  <Card title="アプリ内メッセージトラブルシューティング" icon="message" href="./in-app-message-troubleshooting">
    アプリ内メッセージの表示、トリガー、重複に関する問題を修正します。
  </Card>

  <Card title="モバイルサービス拡張機能" icon="gear" href="./service-extensions">
    iOS および Android で表示前にプッシュ通知を傍受してカスタマイズします。
  </Card>

  <Card title="通知が表示されない（Web）" icon="bell-slash" href="./notifications-not-shown-web-push">
    送信されたが表示されない Web プッシュ通知のトラブルシューティング。
  </Card>
</Columns>
