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

# Silent push

Silent push notifications allow us to remotely request a fresh location from a user. Radar supports requesting these locations through an API endpoint, starting with iOS and Android SDK version `3.24.0`.

There are two types of silent push notifications that can be sent to a device: regular app silent push and location service extension push.

**App silent push** can be used to wake the app to perform a track if the app is in the foreground or background.
This can be used to restart tracking if tracking was stopped due to device memory or CPU resource constraints.
However the silent push notification will not work if the app has been manually killed by the user.

**Location extension service** can be used to request a location for devices even if the app was manually terminated.
The app will wake for \~30 seconds to respond to the request and perform a track request with a fresh location.
The location extension service acts as a separate app, so it can only restart continuous tracking for 30 seconds before the extension is shutdown.

## Radar dashboard setup

[Obtain an encryption key for APN](https://developer.apple.com/help/account/keys/create-a-private-key) (Apple push notification).

Navigate to Settings tab, and enable push notifications.

Then enter in the app's bundle ID, team ID, key ID, and key for iOS. Enter the project ID, client email, and private key for Android. Then save the settings.

Your private key should begin and end with `-----BEGIN/END PRIVATE KEY-----`, include this in your key.

## Build configuration

### Native iOS and Android

For iOS, Add the "Push Notifications" app capabilities, and turn on "Remote notifications" in the "Background Modes" capability.

For Android, [Add Firebase to your project](https://firebase.google.com/docs/android/setup) and then generate a service account from the Firebase console. You'll need the account details to send silent push notifications.

Add the following firebase messaging service configuration to your project

```
<manifest>
    ...
    <application>
        ...
        <service android:name="io.radar.sdk.RadarFirebaseMessagingService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
    </application>
</manifest>
```

Add firebase messaging dependency to gradle

```
dependencies {
    ...
    implementation platform('com.google.firebase:firebase-bom:34.5.0')
    implementation "com.google.firebase:firebase-messaging"
}
```

### React Native

The Android manifest service entry and Gradle dependencies above are for native Android projects only. For React Native, the `react-native-radar` package automatically registers `RadarFirebaseMessagingService` in your `AndroidManifest.xml`.

For iOS, add `"remote-notification"` to `UIBackgroundModes` in your `Info.plist`. If using Expo, add it to `app.json`:

```json theme={null}
{
  "expo": {
    "ios": {
      "infoPlist": {
        "UIBackgroundModes": ["remote-notification"]
      }
    }
  }
}
```

Add the "Push Notifications" capability in Xcode under *Signing & Capabilities*.

For Android, install `@react-native-firebase/messaging` and ensure you have [added Firebase to your Android project](https://firebase.google.com/docs/android/setup) with the `google-services` plugin applied:

```bash theme={null}
npm install --save @react-native-firebase/app @react-native-firebase/messaging
```

For location extension service support (iOS), see [Location extension service setup](#location-extension-service-setup-ios-only).

## App setup (automated setup)

Initialize Radar with `RadarInitializeOptions.silentPush = true`.

<CodeGroup>
  ```swift Swift theme={null}
  import RadarSDK

  @UIApplicationMain
  class AppDelegate: UIResponder, UIApplicationDelegate {

      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
          // initialization Radar with the silent push option
          let radarInitializeOptions = RadarInitializeOptions()
          radarInitializeOptions.silentPush = true
          Radar.initialize(publishableKey: "prj_test_pk_...", options: radarInitializeOptions)

          return true
      }

  }
  ```

  ```kotlin Kotlin theme={null}
  import com.google.firebase.FirebaseApp
  import io.radar.sdk.Radar
  import io.radar.sdk.RadarInitializeOptions

  class MainActivity : AppCompatActivity() {

      override fun onCreate(savedInstanceState: Bundle?) {
          // first the firebase app needs to be initialized
          FirebaseApp.initializeApp(this)

          // initialization Radar with the silent push option
          val radarInitializeOptions = RadarInitializeOptions(
              silentPush = true,
          )
          Radar.initialize(this, "prj_test_pk_...", radarInitializeOptions)
      }

  }
  ```

  ```javascript React Native theme={null}
  import Radar from 'react-native-radar';
  import { Platform } from 'react-native';
  import { getMessaging, getToken, onTokenRefresh } from '@react-native-firebase/messaging';

  Radar.initialize('prj_test_pk_...', false, {
    silentPush: Platform.OS === 'ios',
  });

  async function registerPushToken() {
    if (Platform.OS === 'android') {
      const messaging = getMessaging();
      const token = await getToken(messaging);
      if (token) {
        Radar.setPushNotificationToken(token);
      }
      onTokenRefresh(messaging, (newToken) => {
        Radar.setPushNotificationToken(newToken);
      });
    }
  }

  registerPushToken();
  ```
</CodeGroup>

On iOS, `silentPush: true` handles push token registration automatically. On Android, you must also pass the FCM token to Radar via `Radar.setPushNotificationToken()`.

## Manual setup

Initialize Radar, register for silent push notifications and pass the push token to Radar.

<CodeGroup>
  ```swift Swift theme={null}
  import UIKit
  import RadarSDK

  @UIApplicationMain
  class AppDelegate: UIResponder, UIApplicationDelegate {
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
          // initialize Radar, silent push option defaults to false
          Radar.initialize(publishableKey: "prj_test_pk_...")

          application.registerForRemoteNotifications()

          return true
      }

      func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) async -> UIBackgroundFetchResult {
          await Radar.didReceivePushNotificationPayload(userInfo)
          return .newData
      }
  }
  ```

  ```kotlin Kotlin theme={null}

  class MainActivity : AppCompatActivity() {

      override fun onCreate(savedInstanceState: Bundle?) {
          // first the firebase app needs to be initialized
          FirebaseApp.initializeApp(this)

          // initialize Radar, silent push option defaults to false
          Radar.initialize(this, "prj_test_pk_...")
          
          // register push notification with firebase, then pass the push token to Radar
          FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
              Radar.setPushNotificationToken(token)
          }
      }
  }
  ```

  ```javascript React Native theme={null}
  import Radar from 'react-native-radar';
  import { Platform } from 'react-native';
  import { getMessaging, getToken, onTokenRefresh } from '@react-native-firebase/messaging';

  Radar.initialize('prj_test_pk_...');

  async function registerPushToken() {
    if (Platform.OS === 'android') {
      const messaging = getMessaging();
      const token = await getToken(messaging);
      if (token) {
        Radar.setPushNotificationToken(token);
      }
      onTokenRefresh(messaging, (newToken) => {
        Radar.setPushNotificationToken(newToken);
      });
    }
  }

  registerPushToken();
  ```
</CodeGroup>

## Location extension service setup (iOS only)

The location extension service requires a native `CLLocationPushServiceExtension` target, an [app group](https://developer.apple.com/documentation/Xcode/configuring-app-groups) shared between the main app and the extension, and the `com.apple.developer.location.push` entitlement on both targets.

### Build configuration

Add the following capabilities to both the main app target and the location push extension target:

* **App Groups** — use a shared group (e.g., `group.yourApp.data`)
* **Push Notifications**

Add the `com.apple.developer.location.push` entitlement to both targets.

For React Native with Expo, you can configure this in `app.json`:

```json theme={null}
{
  "expo": {
    "ios": {
      "entitlements": {
        "com.apple.security.application-groups": ["group.yourApp.data"],
        "com.apple.developer.location.push": true
      }
    }
  }
}
```

The location push extension is a native iOS target and must be created in Xcode (or with a tool like `@bacons/apple-targets` for Expo). The extension target needs the `RadarSDK` pod, `CoreLocation` framework, and the same App Group and `com.apple.developer.location.push` entitlements.

### Main app setup

Initialize Radar, set the app group, and start monitoring for location extension pushes.

<CodeGroup>
  ```swift Swift theme={null}
  import UIKit
  import RadarSDK

  @UIApplicationMain
  class AppDelegate: UIResponder, UIApplicationDelegate {

      let locationManager = CLLocationManager()

      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
          let radarInitializeOptions = RadarInitializeOptions()
          radarInitializeOptions.silentPush = true // (or true if you only want location extension)
          Radar.initialize(publishableKey: "prj_test_pk_...", options: radarInitializeOptions)
          Radar.setAppGroup("group.yourApp.data")

          locationManager.startMonitoringLocationPushes() { data, error in
              Radar.setLocationExtensionToken(data?.map { String(format: "%02x", $0) }.joined())
          }

          return true
      }
  }
  ```

  ```javascript React Native theme={null}
  import Radar from 'react-native-radar';

  Radar.setAppGroup('group.yourApp.data');
  Radar.initialize('prj_test_pk_...', false, { silentPush: true });
  ```
</CodeGroup>

<Info>
  `CLLocationManager.startMonitoringLocationPushes()` is not exposed by the React Native SDK. You need to bridge this call natively using either a custom Expo Module, a React Native native module, or by calling it from your `AppDelegate`. Once you have the token, pass it to Radar from JavaScript:
</Info>

```javascript React Native theme={null}
Radar.setLocationExtensionToken(token);
```

### Extension setup

Create a `CLLocationPushServiceExtension` target. When a location payload is received, initialize Radar using the shared app group and pass the payload to the SDK.

<CodeGroup>
  ```swift Swift theme={null}
  import CoreLocation
  import RadarSDK

  class LocationPushService: NSObject, CLLocationPushServiceExtension {
      func didReceiveLocationPushPayload(_ payload: [String : Any], completion: @escaping () -> Void) {
          Radar.initialize(withAppGroup: "group.yourApp.data")
          Radar.didReceivePushNotificationPayload(payload, completionHandler: completion)
      }
  }
  ```
</CodeGroup>

This extension code is the same whether your main app is native Swift or React Native.

## Usage

Silent push or location extension push notifications can be triggered by sending a POST API request to the `/users/refresh` endpoint. See the [API reference](/api#send-remote-track-requests-to-users) for full details.

## iOS Environments

APNs operate in two separate environments for production and non-production app builds.
When using the live Radar environment, Radar sends silent push notifications to the production APN environment,
and when using the test Radar environment, Radar sends silent push notifications to the non-production environment.

When using the live Radar environment, your device must be in a production build, and vice versa. You will receive "BadDeviceToken" if there is a mismatch.
