Skip to main content

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:
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
    }
}
All delegate methods are optional. Implement only the methods you need for your use case.

Delegate Methods

didReceiveEvents

Called when events are generated by Radar, such as geofence entries/exits, place visits, or trip updates.
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)")
    }
}
Parameters:
  • events: Array of RadarEvent objects representing the events that were generated
  • user: The updated RadarUser object, or nil if anonymous tracking is enabled
Use this method to update your UI, trigger notifications, or update your app’s state based on location events.

didUpdateLocation

Called when the user’s location is updated and successfully synced to the server.
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)
}
Parameters:
  • location: The CLLocation object representing the user’s current location
  • user: The updated RadarUser object with current context and state
This method is only called for location updates that are successfully synced to the server. Use didUpdateClientLocation if you need all location updates.

didUpdateClientLocation

Called when the device’s location is updated, even if not synced to the server. Use this for more frequent location updates.
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")
    }
}
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
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.

didFail

Called when a Radar request fails.
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)")
    }
}
Parameters:
  • status: The RadarStatus error code indicating what went wrong

didLog

Called when the SDK generates debug log messages. Useful for debugging and troubleshooting.
func didLog(message: String) {
    print("[Radar] \(message)")
    
    // You can also log to your analytics service
    // Analytics.log("radar_sdk", message: message)
}
Parameters:
  • message: The debug log message string
Enable log messages by setting the log level: Radar.setLogLevel(.debug)

Event Types

The RadarEventType enum includes the following event types:
  • userEnteredGeofence - User entered a geofence
  • userExitedGeofence - User exited a geofence
  • userDwelledInGeofence - User dwelled in a geofence
  • userEnteredPlace - User entered a place
  • userExitedPlace - User exited a place
  • userNearbyPlaceChain - User is near a place chain
  • 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
  • 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
  • userEnteredBeacon - User entered a beacon region
  • userExitedBeacon - User exited a beacon region
  • userFailedFraud - User failed fraud verification
  • conversion - Conversion event (from Radar.logConversion())

Complete Example

Here’s a complete example of implementing RadarDelegate:
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)
    }
}

Best Practices

Keep delegate methods lightweight

Delegate methods are called on the main thread. Avoid heavy processing or blocking operations.

Handle all error cases

Implement didFail to gracefully handle errors like permission denials or network issues.

Use appropriate callback

Use didUpdateLocation for server-synced updates, didUpdateClientLocation for all location changes.

Filter events in the delegate

Filter events by type in didReceiveEvents rather than processing all events.

Build docs developers (and LLMs) love