> ## 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.

# Extensions de service mobile

> Implémentez les extensions de service de notification iOS et Android dans votre application.

Les extensions de service de notification vous permettent d'intercepter et de modifier les notifications push avant qu'elles ne soient affichées à l'utilisateur. Cela permet la gestion des données en arrière-plan, les styles personnalisés, les pièces jointes de médias enrichis, la livraison confirmée et les options de boutons d'action.

<Note>
  Vous pouvez accéder aux données de vos notifications push envoyées depuis OneSignal via la [classe OSNotification](./osnotification-payload)
</Note>

***

## Extension de service de notification Android

Vous permet de traiter la notification avant qu'elle ne soit affichée à l'utilisateur. Les cas d'usage courants incluent :

* Recevoir des données en arrière-plan avec ou sans affichage de notification.
* Remplacer des paramètres de notification spécifiques en fonction de la logique d'application côté client, comme la couleur d'accentuation personnalisée, le motif de vibration ou toute autre option `NotificationCompat` disponible.

Pour plus de détails, consultez [la documentation d'Android sur les options NotificationCompat.](https://developer.android.com/reference/androidx/core/app/NotificationCompat)

### Étape 1 : Créer une classe pour l'extension de service

Créez une classe qui implémente `INotificationServiceExtension` et implémentez la méthode `onNotificationReceived`.

Le paramètre de la méthode `onNotificationReceived` est `event` de type [INotificationReceivedEvent](https://github.com/OneSignal/OneSignal-Android-SDK/blob/25924dc3739fbe3ae64a73efc7b504449a18cdea/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/notifications/INotificationReceivedEvent.kt#L46).

<CodeGroup>
  ```Java Java theme={null}
  package your.package.name

  import androidx.annotation.Keep;
  import com.onesignal.notifications.IActionButton;
  import com.onesignal.notifications.IDisplayableMutableNotification;
  import com.onesignal.notifications.INotificationReceivedEvent;
  import com.onesignal.notifications.INotificationServiceExtension;

  @Keep // Keep is required to prevent minification from renaming or removing your class
  public class NotificationServiceExtension implements INotificationServiceExtension {

       @Override
       public void onNotificationReceived(INotificationReceivedEvent event) {
          IDisplayableMutableNotification notification = event.getNotification();

          if (notification.getActionButtons() != null) {
             for (IActionButton button : notification.getActionButtons()) {
                // you can modify your action buttons here
             }
          }

       /* Add customizations here. See examples below for additional methods to modify the notification*/
       }
  }

  ```

  ```kotlin Kotlin theme={null}
  package your.package.name

  import androidx.annotation.Keep
  import com.onesignal.notifications.INotificationReceivedEvent
  import com.onesignal.notifications.INotificationServiceExtension

  @Keep
  class NotificationServiceExtension : INotificationServiceExtension {
      override fun onNotificationReceived(event: INotificationReceivedEvent) {
          val notification = event.notification
          val context = event.context

          notification.actionButtons?.forEach { button ->
              // Modify action buttons here
          }
      }
  }
  ```
</CodeGroup>

<Note>
  L'annotation `@Keep` est nécessaire pour empêcher ProGuard ou R8 de renommer ou de supprimer votre classe lors de la minification.
</Note>

### Étape 2 : Personnaliser la notification

Les exemples suivants illustrent les personnalisations courantes que vous pouvez implémenter dans la classe d'extension de service de notification.

<Tabs>
  <Tab title="Empêcher l'affichage de la notification">
    Utilisez `event.preventDefault()` pour supprimer l'affichage de la notification. Vous pouvez ensuite appeler `event.getNotification().display()` pour l'afficher plus tard, ou ne jamais l'appeler pour la rejeter silencieusement.

    <CodeGroup>
      ```Java Java theme={null}
      event.preventDefault();

      //Do some async work, then decide to show or dismiss
      new Thread(() -> {
          try {
              Thread.sleep(1000);
          } catch (InterruptedException ignored) {}

          //Manually show the notification
          event.getNotification().display();
      }).start();
      ```

      ```Kotlin Kotlin theme={null}
      event.preventDefault()

      //Do some async work, then decide to show or dismiss
      Thread{
          try {
              Thread.sleep(1000)
          } catch (ingored: InterruptedException) {}

          //Manually show the notification
          event.notification.display()
      }.start()

      ```
    </CodeGroup>

    <Warning>
      Appeler `event.preventDefault()` sans jamais appeler `display()` rejettera la notification silencieusement. Pour plus d'informations, consultez [Notifications dupliquées](./duplicated-notifications#android-notification-service-extension).
    </Warning>
  </Tab>

  <Tab title="Ajouter un champ personnalisé">
    <CodeGroup>
      ```Java Java theme={null}
      String promoCode = notification.getAdditionalData() != null
          ? notification.getAdditionalData().optString("promo_code", null) 
          : null;

      if (promoCode != null) {
          String updatedBody = notification.getBody() + " Use code: " + promoCode;
          notification.setExtender(builder -> {
              builder.setContentText(updatedBody);
          });
      }
      ```

      ```Kotlin Kotlin theme={null}
      val promoCode = notification.additionalData?.optString("promo_code", null)

      promoCode?.let {
          val updatedBody = "${notification.body}\nUse code: $promoCode"
          notification.setExtender { builder ->
              builder.setContentText(updatedBody)
          }
      }

      ```
    </CodeGroup>
  </Tab>

  <Tab title="Changer la couleur et l'icône de la notification">
    <CodeGroup>
      ```Java Java theme={null}
      int iconResId = R.drawable.icon_default; 
      String type = notitifcation.getAdditionalData() != null
          ? notification.getAdditionalData().optString("type", "") 
          : "";

      switch (type) {
          case "sale":
              iconResId = R.drawable.icon_sale;
              break;
          case "reminder":
              iconResId = R.drawable.icon_reminder;
              break;
      }

      int finalIconResId = iconResId;
      notification.setExtender(builder -> {
          builder.setColor(0xFF0000FF).setSmallIcon(finalIconResId);
      });

      ```

      ```Kotlin Kotlin theme={null}
      val type = notification.additionalData?.optString("type", "") ?: ""

      val iconResId = when (type) {
      "sale" -> R.drawable.icon_sale
          "reminder" -> R.drawable.icon_reminder
          else -> R.drawable.icon_default
      }

      notification.setExtender { builder ->
          builder.setColor(0xFF0000FF).setSmallIcon(iconResId)
      }

      ```
    </CodeGroup>

    <Note>
      Les icônes référencées ici doivent exister dans le répertoire `res/drawable` de votre projet Android.
    </Note>
  </Tab>
</Tabs>

### Étape 3 : Ajouter l'extension de service à votre `AndroidManifest.xml`

Ajoutez le nom de la classe et la valeur en tant que `meta-data` dans le fichier `AndroidManifest.xml` dans la balise application. Ignorez les avertissements "unused".

```XML XML theme={null}
<application>
  <meta-data
    android:name="com.onesignal.NotificationServiceExtension"
    android:value="com.onesignal.example.NotificationServiceExtension" />
</application>
```

Remplacez `com.onesignal.example.NotificationServiceExtension` par le nom complet de votre classe.

***

## Extension de service de notification iOS

L'[UNNotificationServiceExtension](https://developer.apple.com/reference/usernotifications/unnotificationserviceextension) vous permet de modifier le contenu des notifications push avant qu'elles ne soient affichées à l'utilisateur et est requis pour d'autres fonctionnalités importantes comme :

* [Images et médias enrichis](./rich-media).
* [Livraison confirmée](./confirmed-delivery)
* [Badges](./badges)
* [Boutons d'action](./action-buttons)
* [Ouvertures influencées avec Firebase Analytics](./google-analytics-for-firebase)

Vous avez probablement déjà configuré cela si vous avez suivi nos instructions de [configuration du SDK mobile](./mobile-sdk-setup) pour votre application, mais cette section expliquera comment accéder aux données de charge utile de notification OneSignal et résoudre les problèmes que vous pourriez rencontrer.

### Obtenir la charge utile push iOS

La substitution de `didReceive(_:withContentHandler:)` appelle `OneSignalExtension.didReceiveNotificationExtensionRequest`, qui transmet le `bestAttemptContent` à OneSignal avant qu'il ne soit affiché à l'utilisateur. Vous pouvez lire ou modifier `bestAttemptContent` avant que cette méthode ne soit appelée.

Dans cet exemple, nous envoyons une notification avec les données suivantes :

```json JSON theme={null}
{
  "app_id": "YOUR_APP_ID",
  "target_channel": "push",
  "headings": {"en": "The message title"},
  "contents": {"en": "The message contents"},
  "data":{
    "additional_data_key_1":"value_1",
    "additional_data_key_2":"value_2"
    },
  "include_subscription_ids": ["SUBSCRIPTION_ID_1"]
}
```

Accédez à ces `data` supplémentaires dans la OneSignalNotificationServiceExtension via la clé `a` dans le dictionnaire `custom` de `userInfo` :

<CodeGroup>
  ```swift Swift theme={null}
  if let bestAttemptContent = bestAttemptContent {

      if let customData = bestAttemptContent.userInfo["custom"] as? [String: Any],
         let additionalData = customData["a"] as? [String: Any] {

          if let jsonData = try? JSONSerialization.data(withJSONObject: additionalData, options: .prettyPrinted),
             let jsonString = String(data: jsonData, encoding: .utf8) {
              print("The additionalData dictionary in JSON format:\n\(jsonString)")
          } else {
              print("Failed to convert additionalData to JSON format.")
          }
      }

      if let messageData = bestAttemptContent.userInfo["aps"] as? [String: Any],
         let apsData = messageData["alert"] as? [String: Any],
         let body = apsData["body"] as? String,
         let title = apsData["title"] as? String {
          print("The message contents is: \(body), message headings is: \(title)")
      } else {
          print("Unable to retrieve apsData")
      }

      OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest,
                                                                with: bestAttemptContent,
                                                                withContentHandler: self.contentHandler)
  }
  ```

  ```objc Objective-C theme={null}
  if (bestAttemptContent) {
      NSDictionary *customData = bestAttemptContent.userInfo[@"custom"];
      if ([customData isKindOfClass:[NSDictionary class]]) {
          NSDictionary *additionalData = customData[@"a"];
          if ([additionalData isKindOfClass:[NSDictionary class]]) {
              NSError *error;
              NSData *jsonData = [NSJSONSerialization dataWithJSONObject:additionalData options:NSJSONWritingPrettyPrinted error:&error];
              if (jsonData) {
                  NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
                  NSLog(@"The additionalData dictionary in JSON format:\n%@", jsonString);
              } else {
                  NSLog(@"Failed to convert additionalData to JSON format: %@", error.localizedDescription);
              }
          }
      }

      NSDictionary *messageData = bestAttemptContent.userInfo[@"aps"];
      if ([messageData isKindOfClass:[NSDictionary class]]) {
          NSDictionary *apsData = messageData[@"alert"];
          if ([apsData isKindOfClass:[NSDictionary class]]) {
              NSString *body = apsData[@"body"];
              NSString *title = apsData[@"title"];
              if ([body isKindOfClass:[NSString class]] && [title isKindOfClass:[NSString class]]) {
                  NSLog(@"The message content is: %@, message heading is: %@", body, title);
              }
          } else {
              NSLog(@"Unable to retrieve apsData");
          }
      }

      [OneSignalExtension didReceiveNotificationExtensionRequest:self.receivedRequest
                                               withNotification:bestAttemptContent
                                                withContentHandler:self.contentHandler];
  }
  ```
</CodeGroup>

**Exemple de sortie console :**

```
The additionalData dictionary in JSON format:
{
  "additional_data_key_1" : "value_1",
  "additional_data_key_2" : "value_2"
}
The message contents is: The message contents, message headings is: The message title
```

### Dépannage de l'extension de service de notification iOS

Ce guide est destiné au débogage des problèmes avec les images, les boutons d'action ou les livraisons confirmées qui ne s'affichent pas sur les applications mobiles iOS.

#### Vérifiez vos paramètres Xcode

Dans **General > Targets**, assurez-vous que votre **cible d'application principale** et la cible **OneSignalNotificationServiceExtension** ont les mêmes paramètres corrects :

* **Supported Destinations**
* **Minimum Deployment** (iOS 14.5 ou supérieur)

<Warning>
  Si vous utilisez Cocoapods, assurez-vous que ceux-ci correspondent à votre cible principale dans le Podfile pour éviter les erreurs de compilation.
</Warning>

<Frame caption="Exemple de cible d'application principale dans Xcode">
  <img src="https://mintcdn.com/onesignal/RWA35uTjv8voG5iC/images/mobile/main-app-target-general-settings.png?fit=max&auto=format&n=RWA35uTjv8voG5iC&q=85&s=eb9dd4d9dbec5f2b9f351ff168087557" alt="Xcode General tab showing Supported Destinations and Minimum Deployment for the main app target" width="2988" height="1824" data-path="images/mobile/main-app-target-general-settings.png" />
</Frame>

<Frame caption="Exemple de cible OneSignalNotificationServiceExtension dans Xcode">
  <img src="https://mintcdn.com/onesignal/x4RdPY-EcasyyQ-o/images/mobile/onesignal-notification-service-extension-target-general-settings.png?fit=max&auto=format&n=x4RdPY-EcasyyQ-o&q=85&s=ea6f697fda7b4a338bb7e3a4dac3856f" alt="Xcode General tab showing Supported Destinations and Minimum Deployment for the notification service extension target" width="2988" height="1824" data-path="images/mobile/onesignal-notification-service-extension-target-general-settings.png" />
</Frame>

En continuant dans l'onglet **OneSignalNotificationServiceExtension > Info**, développez la clé `NSExtension`. Assurez-vous de voir :

```XML XML theme={null}
 <dict>
   <key>NSExtensionPointIdentifier</key>
   <string>com.apple.usernotifications.service</string>
   <key>NSExtensionPrincipalClass</key>
   <string>$(PRODUCT_MODULE_NAME).NotificationService</string>
 </dict>
```

Exemple :

<Frame caption="Exemple de clé NSExtension dans l'onglet Info">
  <img src="https://mintcdn.com/onesignal/RWA35uTjv8voG5iC/images/mobile/onesignal-notification-service-extension-info-tab.png?fit=max&auto=format&n=RWA35uTjv8voG5iC&q=85&s=06e65f878427e94127ef7391903d583e" alt="Xcode Info tab showing the NSExtension dictionary with NSExtensionPointIdentifier and NSExtensionPrincipalClass keys" width="3282" height="1888" data-path="images/mobile/onesignal-notification-service-extension-info-tab.png" />
</Frame>

<Warning>
  Si vous utilisez Objective-C, au lieu de `$(PRODUCT_MODULE_NAME).NotificationService`, utilisez `NotificationService`.
</Warning>

#### Désactiver "Copy only when installing"

Sélectionnez votre **cible d'application principale > Build Phases > Embed App Extensions**. Assurez-vous que "Copy only when installing" n'est PAS coché. Décochez-le s'il l'est :

<Frame caption="Paramètres de phase de compilation de la cible d'application principale">
  <img src="https://mintcdn.com/onesignal/RWA35uTjv8voG5iC/images/mobile/main-app-target-build-phases.png?fit=max&auto=format&n=RWA35uTjv8voG5iC&q=85&s=cd507862b8e5bb0934b6f6b082d0b26e" alt="Xcode Build Phases tab showing Embed App Extensions with Copy only when installing unchecked" width="3282" height="1888" data-path="images/mobile/main-app-target-build-phases.png" />
</Frame>

### Débogage de l'extension de service de notification iOS

Suivez ces étapes pour vérifier que l'extension de service de notification est correctement configurée.

#### 1. Mettre à jour le code OneSignalNotificationServiceExtension

Ouvrez `NotificationService.m` ou `NotificationService.swift` et remplacez tout le contenu du fichier par le code ci-dessous. Cela ajoute des journaux pour aider à vérifier que l'extension s'exécute.

Remplacez `YOUR_BUNDLE_ID` par votre identifiant de bundle réel.

<CodeGroup>
  ```swift Swift theme={null}
  import UserNotifications
  import OneSignalExtension
  import os.log

  class NotificationService: UNNotificationServiceExtension {

      var contentHandler: ((UNNotificationContent) -> Void)?
      var receivedRequest: UNNotificationRequest!
      var bestAttemptContent: UNMutableNotificationContent?

      override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
          self.receivedRequest = request
          self.contentHandler = contentHandler
          self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

          let userInfo = request.content.userInfo
          let custom = userInfo["custom"]
          print("Running NotificationServiceExtension: userInfo = \(userInfo.description)")
          print("Running NotificationServiceExtension: custom = \(custom.debugDescription)")
          os_log("%{public}@", log: OSLog(subsystem: "YOUR_BUNDLE_ID", category: "OneSignalNotificationServiceExtension"), type: OSLogType.debug, userInfo.debugDescription)

          if let bestAttemptContent = bestAttemptContent {
              print("Running NotificationServiceExtension")
              bestAttemptContent.body = "[Modified] " + bestAttemptContent.body

              OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler)
          }
      }

      override func serviceExtensionTimeWillExpire() {
          if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
              OneSignalExtension.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
              contentHandler(bestAttemptContent)
          }
      }

  }

  ```

  ```objc Objective-C theme={null}
  #import <OneSignalExtension/OneSignalExtension.h>

  #import "NotificationService.h"

  @interface NotificationService ()

  @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
  @property (nonatomic, strong) UNNotificationRequest *receivedRequest;
  @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

  @end

  @implementation NotificationService

  - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
      self.receivedRequest = request;
      self.contentHandler = contentHandler;
      self.bestAttemptContent = [request.content mutableCopy];

      NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.YOUR_BUNDLE_ID.onesignal"];
      NSLog(@"NSE player_id: %@", [userDefault stringForKey:@"GT_PLAYER_ID"]);
      NSLog(@"NSE app_id: %@", [userDefault stringForKey:@"GT_APP_ID"]);

      NSLog(@"Running NotificationServiceExtension");
      self.bestAttemptContent.body = [@"[Modified] " stringByAppendingString:self.bestAttemptContent.body];

      [OneSignal.Debug setLogLevel:ONE_S_LL_VERBOSE];

      [OneSignalExtension didReceiveNotificationExtensionRequest:self.receivedRequest
                                                withNotification:self.bestAttemptContent
                                              withContentHandler:self.contentHandler];
  }

  - (void)serviceExtensionTimeWillExpire {
      [OneSignalExtension serviceExtensionTimeWillExpireRequest:self.receivedRequest
                                               withNotification:self.bestAttemptContent];

      self.contentHandler(self.bestAttemptContent);
  }

  @end
  ```
</CodeGroup>

<Note>
  Les types de journaux de débogage doivent être activés dans la Console via **Action > Include Debug Messages**.
</Note>

#### 2. Changer votre schéma actif

Définissez votre schéma actif sur `OneSignalNotificationServiceExtension`.

<Frame caption="Sélection du schéma actif Xcode">
  <img src="https://mintcdn.com/onesignal/x4RdPY-EcasyyQ-o/images/mobile/xcode-active-scheme-selection.png?fit=max&auto=format&n=x4RdPY-EcasyyQ-o&q=85&s=27407135e39bc606a02e005efd3a56ff" alt="Xcode toolbar showing the active scheme dropdown set to OneSignalNotificationServiceExtension" width="3282" height="1888" data-path="images/mobile/xcode-active-scheme-selection.png" />
</Frame>

#### 3. Compiler et exécuter le projet

Compilez et exécutez le projet dans Xcode sur un appareil réel.

#### 4. Ouvrir la console

Dans Xcode, sélectionnez **Window > Devices and Simulators**.

<Frame caption="Fenêtre Devices and Simulators de Xcode">
  <img src="https://mintcdn.com/onesignal/x4RdPY-EcasyyQ-o/images/mobile/xcode-devices-and-simulators-selection.png?fit=max&auto=format&n=x4RdPY-EcasyyQ-o&q=85&s=2bc1305ee0fd45087f33a078a7aeaffb" alt="Xcode menu showing the Window dropdown with Devices and Simulators selected" width="2518" height="1374" data-path="images/mobile/xcode-devices-and-simulators-selection.png" />
</Frame>

Vous devriez voir votre appareil connecté. Sélectionnez **Open Console**.

<Frame caption="Bouton d'accès à la console de l'appareil">
  <img src="https://mintcdn.com/onesignal/x4RdPY-EcasyyQ-o/images/mobile/xcode-device-console-access-button.png?fit=max&auto=format&n=x4RdPY-EcasyyQ-o&q=85&s=7ffa64afd28d452e200241ad4751c102" alt="Xcode Devices window showing the Open Console button for a connected device" width="2304" height="1624" data-path="images/mobile/xcode-device-console-access-button.png" />
</Frame>

#### 5. Vérifier la console

Dans la console :

1. Sélectionnez **Action > Include Debug Messages**
2. Recherchez `OneSignalNotificationServiceExtension` comme CATEGORY
3. Sélectionnez **Start**

<Frame caption="Configuration du débogage de la console">
  <img src="https://mintcdn.com/onesignal/x4RdPY-EcasyyQ-o/images/mobile/xcode-console-debugging-configuration.png?fit=max&auto=format&n=x4RdPY-EcasyyQ-o&q=85&s=82593a0287811e6b787cee686ab7196d" alt="macOS Console app showing the category filter and Start button for debugging the notification service extension" width="3368" height="1232" data-path="images/mobile/xcode-console-debugging-configuration.png" />
</Frame>

Envoyez une notification avec un message à cet appareil (utilisez la propriété `contents` si vous envoyez depuis l'API [Créer une notification](/reference/push-notification)). Dans cet exemple, la charge utile est :

```curl cURL theme={null}
curl --request POST \
 --url 'https://api.onesignal.com/notifications' \
 --header 'Authorization: Key YOUR_API_KEY' \
 --header 'accept: application/json' \
 --header 'content-type: application/json' \
 --data '
{
"app_id": "YOUR_APP_ID",
"target_channel": "push",
"headings": {"en": "The message title"},
"contents": {"en": "The message contents"},
"data":{"additional_data_key_1":"value_1","additional_data_key_2":"value_2"},
"include_subscription_ids": [
"SUBSCRIPTION_ID_1"
]
}'
```

Vous devriez voir un message enregistré avec l'application en cours d'exécution et non en cours d'exécution.

<Frame caption="Exemple de sortie de débogage de la console">
  <img src="https://mintcdn.com/onesignal/x4RdPY-EcasyyQ-o/images/mobile/xcode-console-debug-output.png?fit=max&auto=format&n=x4RdPY-EcasyyQ-o&q=85&s=d93e3fc36610941e1c01b83abc0234b3" alt="macOS Console app showing debug log output from the OneSignalNotificationServiceExtension" width="3604" height="1776" data-path="images/mobile/xcode-console-debug-output.png" />
</Frame>

Si vous ne voyez pas de message, supprimez OneSignal de votre application et suivez à nouveau la [Configuration du SDK mobile](./mobile-sdk-setup) pour vérifier l'intégration.

## FAQ

**Pourquoi mon extension de service de notification ne s'exécute-t-elle pas sur iOS ?**

L'extension ne s'exécute que lorsque `mutable-content` est défini dans la charge utile de la notification. OneSignal le définit automatiquement pour les notifications avec des pièces jointes ou des boutons d'action. Vérifiez que vos paramètres Xcode correspondent à la [section de dépannage](#troubleshooting-the-ios-notification-service-extension).

**Puis-je empêcher l'affichage d'une notification sur Android ?**

Oui. Appelez `event.preventDefault()` pour supprimer l'affichage. Appelez ensuite `event.getNotification().display()` pour l'afficher plus tard, ou ne l'appelez jamais pour la rejeter silencieusement. Consultez [Notifications dupliquées](./duplicated-notifications#android-notification-service-extension) pour plus d'informations.

**Ai-je besoin de l'annotation @Keep sur Android ?**

Oui. Elle empêche ProGuard ou R8 de renommer ou de supprimer votre classe implémentant `INotificationServiceExtension` lors de la minification.

## Pages associées

<Columns cols={2}>
  <Card title="Configuration du SDK mobile" icon="mobile" href="./mobile-sdk-setup">
    Installez et configurez le SDK OneSignal pour iOS et Android.
  </Card>

  <Card title="Images et médias enrichis" icon="image" href="./rich-media">
    Joignez des images, des GIFs et des vidéos aux notifications push.
  </Card>

  <Card title="Livraison confirmée" icon="check" href="./confirmed-delivery">
    Suivez la livraison confirmée des notifications aux appareils.
  </Card>

  <Card title="Notifications dupliquées" icon="clone" href="./duplicated-notifications">
    Résolvez les notifications push dupliquées sur toutes les plateformes.
  </Card>
</Columns>
