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

# RadarDelegate Protocol

> Implement RadarDelegate to receive location updates, events, and debug logs on the client

## Overview

The `RadarDelegate` protocol provides client-side delivery of location updates, events, and debug logs. Implementing this delegate allows you to respond to location changes and Radar events in real-time within your app.

## Setting the Delegate

Set your delegate implementation after initializing the Radar SDK:

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

  class AppDelegate: UIResponder, UIApplicationDelegate, RadarDelegate {
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
          Radar.initialize(publishableKey: "prj_test_pk_...")
          Radar.setDelegate(self)
          return true
      }
  }
  ```

  ```objective-c Objective-C theme={null}
  #import <RadarSDK/RadarSDK.h>

  @interface AppDelegate () <RadarDelegate>
  @end

  @implementation AppDelegate

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

  @end
  ```
</CodeGroup>

<Info>
  All delegate methods are optional. Implement only the methods you need for your use case.
</Info>

## Delegate Methods

### didReceiveEvents

Called when events are generated by Radar, such as geofence entries/exits, place visits, or trip updates.

<CodeGroup>
  ```swift Swift theme={null}
  func didReceiveEvents(_ events: [RadarEvent], user: RadarUser?) {
      for event in events {
          switch event.type {
          case .userEnteredGeofence:
              if let geofence = event.geofence {
                  print("Entered geofence: \(geofence.tag ?? "unknown")")
              }
              
          case .userExitedGeofence:
              if let geofence = event.geofence {
                  print("Exited geofence: \(geofence.tag ?? "unknown")")
              }
              
          case .userEnteredPlace:
              if let place = event.place {
                  print("Entered place: \(place.name)")
              }
              
          case .userArrivedAtTripDestination:
              print("User arrived at trip destination")
              // Update UI, send notification, etc.
              
          case .userStoppedTrip:
              print("User stopped trip")
              // Complete delivery workflow
              
          default:
              print("Received event: \(event.type)")
          }
      }
      
      // Access updated user state
      if let user = user {
          print("User location: \(user.location?.coordinate ?? CLLocationCoordinate2D())")
          print("User geofences: \(user.geofences?.count ?? 0)")
      }
  }
  ```

  ```objective-c Objective-C theme={null}
  - (void)didReceiveEvents:(NSArray<RadarEvent *> *)events user:(RadarUser *)user {
      for (RadarEvent *event in events) {
          switch (event.type) {
              case RadarEventTypeUserEnteredGeofence:
                  if (event.geofence) {
                      NSLog(@"Entered geofence: %@", event.geofence.tag);
                  }
                  break;
                  
              case RadarEventTypeUserExitedGeofence:
                  if (event.geofence) {
                      NSLog(@"Exited geofence: %@", event.geofence.tag);
                  }
                  break;
                  
              case RadarEventTypeUserEnteredPlace:
                  if (event.place) {
                      NSLog(@"Entered place: %@", event.place.name);
                  }
                  break;
                  
              case RadarEventTypeUserArrivedAtTripDestination:
                  NSLog(@"User arrived at trip destination");
                  break;
                  
              case RadarEventTypeUserStoppedTrip:
                  NSLog(@"User stopped trip");
                  break;
                  
              default:
                  NSLog(@"Received event type: %ld", (long)event.type);
                  break;
          }
      }
      
      if (user) {
          NSLog(@"User geofences: %lu", (unsigned long)user.geofences.count);
      }
  }
  ```
</CodeGroup>

**Parameters:**

* `events`: Array of `RadarEvent` objects representing the events that were generated
* `user`: The updated `RadarUser` object, or `nil` if anonymous tracking is enabled

<Tip>
  Use this method to update your UI, trigger notifications, or update your app's state based on location events.
</Tip>

### didUpdateLocation

Called when the user's location is updated and successfully synced to the server.

<CodeGroup>
  ```swift Swift theme={null}
  func didUpdateLocation(_ location: CLLocation, user: RadarUser) {
      print("Location updated: \(location.coordinate.latitude), \(location.coordinate.longitude)")
      print("Accuracy: \(location.horizontalAccuracy)m")
      
      // Access user state
      if let place = user.place {
          print("At place: \(place.name)")
      }
      
      if let country = user.country {
          print("In country: \(country.name)")
      }
      
      // Update map or UI with new location
      updateMapWithLocation(location)
  }
  ```

  ```objective-c Objective-C theme={null}
  - (void)didUpdateLocation:(CLLocation *)location user:(RadarUser *)user {
      NSLog(@"Location updated: %f, %f", location.coordinate.latitude, location.coordinate.longitude);
      NSLog(@"Accuracy: %fm", location.horizontalAccuracy);
      
      if (user.place) {
          NSLog(@"At place: %@", user.place.name);
      }
      
      if (user.country) {
          NSLog(@"In country: %@", user.country.name);
      }
      
      [self updateMapWithLocation:location];
  }
  ```
</CodeGroup>

**Parameters:**

* `location`: The `CLLocation` object representing the user's current location
* `user`: The updated `RadarUser` object with current context and state

<Note>
  This method is only called for location updates that are successfully synced to the server. Use `didUpdateClientLocation` if you need all location updates.
</Note>

### didUpdateClientLocation

Called when the device's location is updated, even if not synced to the server. Use this for more frequent location updates.

<CodeGroup>
  ```swift Swift theme={null}
  func didUpdateClientLocation(_ location: CLLocation, stopped: Bool, source: RadarLocationSource) {
      print("Client location updated: \(location.coordinate.latitude), \(location.coordinate.longitude)")
      print("Stopped: \(stopped)")
      
      switch source {
      case .foregroundLocation:
          print("Source: Foreground location")
      case .backgroundLocation:
          print("Source: Background location")
      case .manualLocation:
          print("Source: Manual location")
      case .visitArrival:
          print("Source: Visit arrival")
      case .visitDeparture:
          print("Source: Visit departure")
      case .geofenceEnter:
          print("Source: Geofence enter")
      case .geofenceExit:
          print("Source: Geofence exit")
      default:
          print("Source: Other")
      }
  }
  ```

  ```objective-c Objective-C theme={null}
  - (void)didUpdateClientLocation:(CLLocation *)location stopped:(BOOL)stopped source:(RadarLocationSource)source {
      NSLog(@"Client location updated: %f, %f", location.coordinate.latitude, location.coordinate.longitude);
      NSLog(@"Stopped: %@", stopped ? @"YES" : @"NO");
      
      switch (source) {
          case RadarLocationSourceForegroundLocation:
              NSLog(@"Source: Foreground location");
              break;
          case RadarLocationSourceBackgroundLocation:
              NSLog(@"Source: Background location");
              break;
          case RadarLocationSourceManualLocation:
              NSLog(@"Source: Manual location");
              break;
          case RadarLocationSourceVisitArrival:
              NSLog(@"Source: Visit arrival");
              break;
          case RadarLocationSourceVisitDeparture:
              NSLog(@"Source: Visit departure");
              break;
          case RadarLocationSourceGeofenceEnter:
              NSLog(@"Source: Geofence enter");
              break;
          case RadarLocationSourceGeofenceExit:
              NSLog(@"Source: Geofence exit");
              break;
          default:
              NSLog(@"Source: Other");
              break;
      }
  }
  ```
</CodeGroup>

**Parameters:**

* `location`: The `CLLocation` object representing the device's location
* `stopped`: A boolean indicating whether the device is considered stopped
* `source`: The `RadarLocationSource` indicating what triggered the location update

<Info>
  This method provides all location updates, including those that may not be synced to the server. Use this for real-time UI updates or analytics.
</Info>

### didFail

Called when a Radar request fails.

<CodeGroup>
  ```swift Swift theme={null}
  func didFail(status: RadarStatus) {
      switch status {
      case .errorPublishableKey:
          print("Error: SDK not initialized")
          
      case .errorPermissions:
          print("Error: Location permissions not granted")
          // Show permission request UI
          
      case .errorLocation:
          print("Error: Location services error or timeout")
          
      case .errorNetwork:
          print("Error: Network error or timeout")
          
      case .errorUnauthorized:
          print("Error: Invalid API key")
          
      case .errorRateLimit:
          print("Error: Rate limit exceeded")
          
      default:
          print("Error: \(status)")
      }
  }
  ```

  ```objective-c Objective-C theme={null}
  - (void)didFailWithStatus:(RadarStatus)status {
      switch (status) {
          case RadarStatusErrorPublishableKey:
              NSLog(@"Error: SDK not initialized");
              break;
              
          case RadarStatusErrorPermissions:
              NSLog(@"Error: Location permissions not granted");
              break;
              
          case RadarStatusErrorLocation:
              NSLog(@"Error: Location services error or timeout");
              break;
              
          case RadarStatusErrorNetwork:
              NSLog(@"Error: Network error or timeout");
              break;
              
          case RadarStatusErrorUnauthorized:
              NSLog(@"Error: Invalid API key");
              break;
              
          case RadarStatusErrorRateLimit:
              NSLog(@"Error: Rate limit exceeded");
              break;
              
          default:
              NSLog(@"Error: %ld", (long)status);
              break;
      }
  }
  ```
</CodeGroup>

**Parameters:**

* `status`: The `RadarStatus` error code indicating what went wrong

### didLog

Called when the SDK generates debug log messages. Useful for debugging and troubleshooting.

<CodeGroup>
  ```swift Swift theme={null}
  func didLog(message: String) {
      print("[Radar] \(message)")
      
      // You can also log to your analytics service
      // Analytics.log("radar_sdk", message: message)
  }
  ```

  ```objective-c Objective-C theme={null}
  - (void)didLogMessage:(NSString *)message {
      NSLog(@"[Radar] %@", message);
  }
  ```
</CodeGroup>

**Parameters:**

* `message`: The debug log message string

<Note>
  Enable log messages by setting the log level: `Radar.setLogLevel(.debug)`
</Note>

## Event Types

The `RadarEventType` enum includes the following event types:

<Accordion title="Geofence Events">
  * `userEnteredGeofence` - User entered a geofence
  * `userExitedGeofence` - User exited a geofence
  * `userDwelledInGeofence` - User dwelled in a geofence
</Accordion>

<Accordion title="Place Events">
  * `userEnteredPlace` - User entered a place
  * `userExitedPlace` - User exited a place
  * `userNearbyPlaceChain` - User is near a place chain
</Accordion>

<Accordion title="Region Events">
  * `userEnteredRegionCountry` - User entered a country
  * `userExitedRegionCountry` - User exited a country
  * `userEnteredRegionState` - User entered a state
  * `userExitedRegionState` - User exited a state
  * `userEnteredRegionDMA` - User entered a DMA
  * `userExitedRegionDMA` - User exited a DMA
  * `userEnteredRegionPostalCode` - User entered a postal code
  * `userExitedRegionPostalCode` - User exited a postal code
</Accordion>

<Accordion title="Trip Events">
  * `userStartedTrip` - User started a trip
  * `userUpdatedTrip` - User's trip was updated
  * `userApproachingTripDestination` - User is approaching trip destination
  * `userArrivedAtTripDestination` - User arrived at trip destination
  * `userArrivedAtWrongTripDestination` - User arrived at wrong destination
  * `userStoppedTrip` - User stopped a trip
  * `userFiredTripOrders` - Trip orders were fired
</Accordion>

<Accordion title="Beacon Events">
  * `userEnteredBeacon` - User entered a beacon region
  * `userExitedBeacon` - User exited a beacon region
</Accordion>

<Accordion title="Other Events">
  * `userFailedFraud` - User failed fraud verification
  * `conversion` - Conversion event (from `Radar.logConversion()`)
</Accordion>

## Complete Example

Here's a complete example of implementing `RadarDelegate`:

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

  class AppDelegate: UIResponder, UIApplicationDelegate, RadarDelegate {
      
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
          // Initialize Radar
          Radar.initialize(publishableKey: "prj_test_pk_...")
          Radar.setDelegate(self)
          Radar.setLogLevel(.debug)
          
          return true
      }
      
      // MARK: - RadarDelegate
      
      func didReceiveEvents(_ events: [RadarEvent], user: RadarUser?) {
          for event in events {
              // Handle specific event types
              if event.type == .userEnteredGeofence {
                  if let geofence = event.geofence {
                      showNotification(title: "Welcome!", body: "You entered \(geofence.tag ?? "area")")
                  }
              }
          }
      }
      
      func didUpdateLocation(_ location: CLLocation, user: RadarUser) {
          // Update UI with location
          NotificationCenter.default.post(
              name: NSNotification.Name("RadarLocationUpdated"),
              object: nil,
              userInfo: ["location": location, "user": user]
          )
      }
      
      func didUpdateClientLocation(_ location: CLLocation, stopped: Bool, source: RadarLocationSource) {
          // Log for debugging
          print("Location: \(location.coordinate), stopped: \(stopped)")
      }
      
      func didFail(status: RadarStatus) {
          if status == .errorPermissions {
              // Show permission request
              print("Location permission required")
          }
      }
      
      func didLog(message: String) {
          print("[Radar SDK] \(message)")
      }
      
      // MARK: - Helper
      
      func showNotification(title: String, body: String) {
          let content = UNMutableNotificationContent()
          content.title = title
          content.body = body
          content.sound = .default
          
          let request = UNNotificationRequest(
              identifier: UUID().uuidString,
              content: content,
              trigger: nil
          )
          
          UNUserNotificationCenter.current().add(request)
      }
  }
  ```

  ```objective-c Objective-C theme={null}
  #import "AppDelegate.h"
  #import <RadarSDK/RadarSDK.h>
  #import <UserNotifications/UserNotifications.h>

  @interface AppDelegate () <RadarDelegate>
  @end

  @implementation AppDelegate

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

  #pragma mark - RadarDelegate

  - (void)didReceiveEvents:(NSArray<RadarEvent *> *)events user:(RadarUser *)user {
      for (RadarEvent *event in events) {
          if (event.type == RadarEventTypeUserEnteredGeofence) {
              if (event.geofence) {
                  [self showNotificationWithTitle:@"Welcome!"
                                             body:[NSString stringWithFormat:@"You entered %@", event.geofence.tag]];
              }
          }
      }
  }

  - (void)didUpdateLocation:(CLLocation *)location user:(RadarUser *)user {
      [[NSNotificationCenter defaultCenter] postNotificationName:@"RadarLocationUpdated"
                                                          object:nil
                                                        userInfo:@{@"location": location, @"user": user}];
  }

  - (void)didUpdateClientLocation:(CLLocation *)location stopped:(BOOL)stopped source:(RadarLocationSource)source {
      NSLog(@"Location: %@, stopped: %@", location, stopped ? @"YES" : @"NO");
  }

  - (void)didFailWithStatus:(RadarStatus)status {
      if (status == RadarStatusErrorPermissions) {
          NSLog(@"Location permission required");
      }
  }

  - (void)didLogMessage:(NSString *)message {
      NSLog(@"[Radar SDK] %@", message);
  }

  #pragma mark - Helper

  - (void)showNotificationWithTitle:(NSString *)title body:(NSString *)body {
      UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
      content.title = title;
      content.body = body;
      content.sound = [UNNotificationSound defaultSound];
      
      UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:[[NSUUID UUID] UUIDString]
                                                                            content:content
                                                                            trigger:nil];
      
      [[UNUserNotificationCenter currentCenter] addNotificationRequest:request withCompletionHandler:nil];
  }

  @end
  ```
</CodeGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Keep delegate methods lightweight" icon="gauge">
    Delegate methods are called on the main thread. Avoid heavy processing or blocking operations.
  </Card>

  <Card title="Handle all error cases" icon="triangle-exclamation">
    Implement `didFail` to gracefully handle errors like permission denials or network issues.
  </Card>

  <Card title="Use appropriate callback" icon="map-location-dot">
    Use `didUpdateLocation` for server-synced updates, `didUpdateClientLocation` for all location changes.
  </Card>

  <Card title="Filter events in the delegate" icon="filter">
    Filter events by type in `didReceiveEvents` rather than processing all events.
  </Card>
</CardGroup>

## Related Resources

* [Events & Context](/api/radar-event)
* [Background Tracking](/features/tracking)
* [Anonymous Tracking](/configuration/anonymous-tracking)
