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

# Building Place-based push notifications

## Languages used

* Swift / Objective-C (iOS)
* Kotlin / Java (Android)

## Features used

* [iOS SDK](https://docs.radar.com/sdk/ios) / [Android SDK](https://docs.radar.com/sdk/android)
* [Places](https://docs.radar.com/geofencing/places)
* [Campaigns](https://docs.radar.com/geofencing/campaigns)

## Steps

<Steps>
  <Step title="Sign up for Radar">
    If you haven't already, sign up for Radar to get your API key.

    [**Get API keys**](https://radar.com/signup)
  </Step>

  <Step title="Enable Places">
    Navigate to the [Settings page](https://radar.com/dashboard/settings) and enable Places. From there, configure the chain filters or category filters relevant to your use case.

    For example, to notify users when they arrive at a specific retail chain, add the chain slug (e.g., `target`, `home-depot`) to your chain filters. To notify users when they arrive at any location in a category, add the relevant category slug (e.g., `department-store`).

    View the [full list of chains](https://docs.radar.com/places/chains) and [full list of categories](https://docs.radar.com/places/categories).

    <Info>
      Places is available on the [Enterprise plan](https://radar.com/pricing). Contact your account manager to enable it for your project.
    </Info>

    Alternatively, you can create custom geofences and trigger notifications when a user enters those boundaries instead. See the [geofencing docs](https://docs.radar.com/geofencing/geofences#create-geofences) for setup instructions.
  </Step>

  <Step title="Install the SDK">
    For full setup instructions, see the [iOS SDK docs](https://docs.radar.com/sdk/ios) or [Android SDK docs](https://docs.radar.com/sdk/android). Once the SDK is installed, initialize it:

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

      Radar.initialize(publishableKey: "prj_live_pk_...")
      ```

      ```objc Objective-C theme={null}
      @import RadarSDK;

      [Radar initializeWithPublishableKey:@"prj_live_pk_..."];
      ```

      ```kotlin Kotlin theme={null}
      import io.radar.sdk.Radar

      Radar.initialize(this, "prj_live_pk_...")
      ```

      ```java Java theme={null}
      import io.radar.sdk.Radar;

      Radar.initialize(this, "prj_live_pk_...");
      ```
    </CodeGroup>
  </Step>

  <Step title="Identify the user">
    Set a stable user ID to link location events to the user in your system. Optionally, attach metadata to enable personalized experiences or campaign targeting.

    <CodeGroup>
      ```swift Swift theme={null}
      Radar.setUserId("user_123")

      Radar.setMetadata(["membershipTier": "gold"])
      ```

      ```objc Objective-C theme={null}
      [Radar setUserId:@"user_123"];

      [Radar setMetadata:@{@"membershipTier": @"gold"}];
      ```

      ```kotlin Kotlin theme={null}
      Radar.setUserId("user_123")

      val metadata = JSONObject()
      metadata.put("membershipTier", "gold")
      Radar.setMetadata(metadata)
      ```

      ```java Java theme={null}
      Radar.setUserId("user_123");

      JSONObject metadata = new JSONObject();
      metadata.put("membershipTier", "gold");
      Radar.setMetadata(metadata);
      ```
    </CodeGroup>
  </Step>

  <Step title="Request location permissions">
    Before tracking, you must request location permissions from the user. Always request foreground permissions before background permissions.

    <CodeGroup>
      ```swift Swift theme={null}
      // In AppDelegate or a location manager wrapper
      let locationManager = CLLocationManager()

      // Request foreground first
      locationManager.requestWhenInUseAuthorization()

      // Then request background after foreground is granted
      locationManager.requestAlwaysAuthorization()
      ```

      ```objc Objective-C theme={null}
      CLLocationManager *locationManager = [CLLocationManager new];

      // Request foreground first
      [locationManager requestWhenInUseAuthorization];

      // Then request background after foreground is granted
      [locationManager requestAlwaysAuthorization];
      ```

      ```kotlin Kotlin theme={null}
      // Request foreground permissions
      ActivityCompat.requestPermissions(
          this,
          arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
          foregroundLocationPermissionsRequestCode
      )

      // Request background permissions after foreground is granted (API 29+)
      ActivityCompat.requestPermissions(
          this,
          arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
          backgroundLocationPermissionsRequestCode
      )
      ```

      ```java Java theme={null}
      // Request foreground permissions
      ActivityCompat.requestPermissions(
          this,
          new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
          FOREGROUND_LOCATION_PERMISSION_CODE
      );

      // Request background permissions after foreground is granted (API 29+)
      ActivityCompat.requestPermissions(
          this,
          new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
          BACKGROUND_LOCATION_PERMISSION_CODE
      );
      ```
    </CodeGroup>

    <Info>
      Use a primer screen before triggering the OS permission prompt to explain the value the user will get from sharing their location. This significantly improves opt-in rates.
    </Info>
  </Step>

  <Step title="Start tracking">
    Once permissions are granted, start tracking the user's location. For most consumer use cases, use the `RESPONSIVE` preset, which provides a good balance of location update frequency and battery usage.

    <CodeGroup>
      ```swift Swift theme={null}
      Radar.startTracking(trackingOptions: RadarTrackingOptions.presetResponsive)
      ```

      ```objc Objective-C theme={null}
      [Radar startTrackingWithOptions:RadarTrackingOptions.presetResponsive];
      ```

      ```kotlin Kotlin theme={null}
      Radar.startTracking(RadarTrackingOptions.RESPONSIVE)
      ```

      ```java Java theme={null}
      Radar.startTracking(RadarTrackingOptions.RESPONSIVE);
      ```
    </CodeGroup>

    Radar will automatically evaluate the user's location against your enabled Places chains and categories and generate `user.entered_place` events when a user stops at a matching location.
  </Step>

  <Step title="Create a campaign">
    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.

    Navigate to the [Campaigns page](https://dashboard.radar.com/geofencing/campaigns) in the Radar dashboard and click **Create**.

    Select **Event based notification** as the campaign type and configure the following:

    * **Trigger event:** `Entered place`
    * **Place chains:** Select the chains you want to target (e.g., `target`, `home-depot`)
    * **Notification title and body:** Write the message to deliver to the user on arrival
    * **Deep link (optional):** Add a deep link URL to route users to a specific screen when they tap the notification

    Set the campaign to **Enabled** when ready.

    <Info>
      Event based notifications require background (`Always allow`) location permissions to deliver. For foreground-only notification delivery, use the **Client side geofence** campaign type instead (iOS only).
    </Info>

    If you prefer to manage campaigns outside of Radar, you can use one of the [Radar integrations](https://docs.radar.com/integrations/integrations) to trigger notifications through your existing messaging platform.
  </Step>

  <Step title="Listen for place events client-side (optional)">
    To handle `user.entered_place` events directly in your app, for example to trigger an in-app experience rather than a push notification, set up a delegate or receiver.

    On iOS, implement `RadarDelegate` in your `AppDelegate`:

    <CodeGroup>
      ```swift Swift theme={null}
      class AppDelegate: UIResponder, UIApplicationDelegate, RadarDelegate {

          func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
              Radar.initialize(publishableKey: "prj_live_pk_...")
              Radar.setDelegate(self)
              return true
          }

          func didReceiveEvents(_ events: [RadarEvent], user: RadarUser?) {
              for event in events {
                  if event.type == .enteredPlace {
                      let place = event.place
                      // do something with place?.name, place?.chain, place?.categories
                  }
              }
          }
      }
      ```

      ```objc Objective-C theme={null}
      @implementation AppDelegate

      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
          [Radar initializeWithPublishableKey:@"prj_live_pk_..."];
          [Radar setDelegate:self];
          return YES;
      }

      - (void)didReceiveEvents:(NSArray<RadarEvent *> *)events user:(RadarUser *)user {
          for (RadarEvent *event in events) {
              if (event.type == RadarEventTypeEnteredPlace) {
                  RadarPlace *place = event.place;
                  // do something with place.name, place.chain, place.categories
              }
          }
      }

      @end
      ```
    </CodeGroup>

    On Android, extend `RadarReceiver` and pass it to `Radar.initialize()`:

    <CodeGroup>
      ```kotlin Kotlin theme={null}
      class MyRadarReceiver : RadarReceiver() {
          override fun onEventsReceived(context: Context, events: Array<RadarEvent>, user: RadarUser?) {
              for (event in events) {
                  if (event.type == RadarEvent.RadarEventType.ENTERED_PLACE) {
                      val place = event.place
                      // do something with place?.name, place?.chain, place?.categories
                  }
              }
          }
      }
      ```

      ```java Java theme={null}
      public class MyRadarReceiver extends RadarReceiver {
          @Override
          public void onEventsReceived(Context context, RadarEvent[] events, RadarUser user) {
              for (RadarEvent event : events) {
                  if (event.type == RadarEvent.RadarEventType.ENTERED_PLACE) {
                      RadarPlace place = event.place;
                      // do something with place.name, place.chain, place.categories
                  }
              }
          }
      }
      ```
    </CodeGroup>

    <Info>
      Set your delegate or receiver before tracking begins to ensure events are not missed.
    </Info>
  </Step>

  <Step title="Log conversions">
    Logging conversions supports measuring the impact of your campaigns. When a user completes a key action after arriving at a place, log a conversion event so Radar can attribute it to the campaign that triggered the notification.

    Enable automatic conversion logging to track when users open the app from a notification:

    <CodeGroup>
      ```swift Swift theme={null}
      // In AppDelegate
      let options = RadarInitializeOptions()
      options.autoLogNotificationConversions = true
      Radar.initialize(publishableKey: "prj_live_pk_...", options: options)
      ```

      ```objc Objective-C theme={null}
      RadarInitializeOptions *options = [[RadarInitializeOptions alloc] init];
      options.autoLogNotificationConversions = YES;
      [Radar initializeWithPublishableKey:@"prj_live_pk_..." options:options];
      ```
    </CodeGroup>

    You can also log conversions manually when a user lands on a key screen after receiving a notification:

    <CodeGroup>
      ```swift Swift theme={null}
      // Log a page view conversion
      Radar.logConversion(name: "page_viewed", metadata: nil) { status, event in
          // do something with the conversion event
      }
      ```

      ```objc Objective-C theme={null}
      [Radar logConversionWithName:@"page_viewed"
                          metadata:nil
                 completionHandler:^(RadarStatus status, RadarEvent *event) {
          // do something with the conversion event
      }];
      ```

      ```kotlin Kotlin theme={null}
      // Log a page view conversion
      Radar.logConversion("page_viewed", null) { status, event ->
          // do something with the conversion event
      }
      ```

      ```java Java theme={null}
      Radar.logConversion("page_viewed", null, (status, event) -> {
          // do something with the conversion event
      });
      ```
    </CodeGroup>

    Or log a conversion with revenue when a user completes a purchase:

    <CodeGroup>
      ```swift Swift theme={null}
      // Log a revenue conversion (e.g. user completed an in-store purchase)
      Radar.logConversion(name: "in_store_purchase", revenue: 150.00, metadata: ["merchant": "target"]) { status, event in
          // do something with the conversion event
      }
      ```

      ```objc Objective-C theme={null}
      [Radar logConversionWithName:@"in_store_purchase"
                           revenue:@(150.00)
                          metadata:@{@"merchant": @"target"}
                 completionHandler:^(RadarStatus status, RadarEvent *event) {
          // do something with the conversion event
      }];
      ```

      ```kotlin Kotlin theme={null}
      // Log a revenue conversion (e.g. user completed an in-store purchase)
      val metadata = JSONObject()
      metadata.put("merchant", "target")
      Radar.logConversion("in_store_purchase", 150.00, metadata) { status, event ->
          // do something with the conversion event
      }
      ```

      ```java Java theme={null}
      JSONObject metadata = new JSONObject();
      metadata.put("merchant", "target");
      Radar.logConversion("in_store_purchase", 150.00, metadata, (status, event) -> {
          // do something with the conversion event
      });
      ```
    </CodeGroup>

    See the [conversions docs](https://docs.radar.com/sdk/ios#conversions) for more details.
  </Step>

  <Step title="Set up deep linking (iOS only)">
    Radar campaign notifications support deep linking into a specific screen in your app when a user taps a notification.

    **iOS:** This requires additional native setup in your `AppDelegate`. See the [deep linking docs](https://docs.radar.com/geofencing/campaigns#deep-linking-ios-only) for setup instructions.

    **Android:** Refer to the [official Android documentation](https://developer.android.com/training/app-links/deep-linking) to configure deep links for your Android app. No additional Radar-specific setup is required.
  </Step>
</Steps>

## Support

Have questions or feedback on this documentation? Contact us at [radar.com/support](https://radar.com/support).
