Skip to main content
This guide helps you diagnose and resolve common issues with the Radar iOS SDK.

Quick Diagnostics

Before diving into specific issues, verify these basics:
Ensure you’re calling Radar.initialize() in your AppDelegate’s didFinishLaunchingWithOptions method before any other Radar methods.
AppDelegate.swift
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    Radar.initialize(publishableKey: "prj_test_pk_...")
    return true
}
Verify your publishable key is correct:
  • Starts with prj_test_pk_ (test) or prj_live_pk_ (live)
  • No extra spaces or characters
  • Copied from your Radar dashboard
// Check if initialized
if Radar.isInitialized {
    print("SDK version: \(Radar.sdkVersion)")
}
Check that your app has location permissions:
import CoreLocation

let status = CLLocationManager.authorizationStatus()
switch status {
case .authorizedWhenInUse:
    print("When in use authorization granted")
case .authorizedAlways:
    print("Always authorization granted")
case .denied, .restricted:
    print("Location permission denied or restricted")
case .notDetermined:
    print("Location permission not requested yet")
@unknown default:
    print("Unknown authorization status")
}
Ensure these keys are in your Info.plist:
  • NSLocationWhenInUseUsageDescription
  • NSLocationAlwaysAndWhenInUseUsageDescription (if using background tracking)
  • Optionally: UIBackgroundModes with location for background tracking

Common Issues

Location Tracking

Location Updates Not Received

1

Enable debug logging

Enable verbose logging to see what the SDK is doing:
// Enable before calling Radar.initialize()
let options = RadarInitializeOptions()
options.logLevel = .debug
Radar.initialize(publishableKey: "prj_test_pk_...", options: options)
2

Implement delegate

Make sure you’ve set a delegate to receive updates:
class MyRadarDelegate: NSObject, RadarDelegate {
    func didUpdateClientLocation(
        _ location: CLLocation,
        stopped: Bool,
        source: RadarLocationSource
    ) {
        print("Location: \(location.coordinate), stopped: \(stopped)")
    }
    
    func didFail(status: RadarStatus) {
        print("Error: \(Radar.stringForStatus(status))")
    }
}

// Set delegate
Radar.setDelegate(MyRadarDelegate())
3

Check tracking status

Verify tracking is actually started:
if Radar.isTracking() {
    let options = Radar.getTrackingOptions()
    print("Tracking with options: \(options)")
} else {
    print("Tracking not started")
    Radar.startTracking(trackingOptions: .presetResponsive)
}
4

Verify location permissions

Background tracking requires “Always” authorization:
let status = CLLocationManager.authorizationStatus()
if status != .authorizedAlways {
    print("Need 'Always' authorization for background tracking")
    // Request always authorization
    let locationManager = CLLocationManager()
    locationManager.requestAlwaysAuthorization()
}
Location updates may be delayed by iOS Low Power Mode, poor connectivity, low battery, or disabled Wi-Fi.

Location Updates Too Infrequent

Symptoms: Location updates happen less often than expected. Solutions:
// Most frequent updates (~30 seconds)
Radar.startTracking(trackingOptions: .presetContinuous)

// Moderate frequency (~2.5 minutes when moving)
Radar.startTracking(trackingOptions: .presetResponsive)

// Least frequent (only on visits)
Radar.startTracking(trackingOptions: .presetEfficient)
let options = RadarTrackingOptions.presetResponsive
options.desiredStoppedUpdateInterval = 120  // 2 minutes when stopped
options.desiredMovingUpdateInterval = 60    // 1 minute when moving
options.desiredAccuracy = .high             // Use best accuracy
Radar.startTracking(trackingOptions: options)
iOS may throttle location updates if:
  • Device is in Low Power Mode
  • Battery is low (< 20%)
  • Poor network connectivity
  • App is in background for extended period
  • Location services are restricted systemwide
These are iOS limitations and cannot be overridden by the SDK.

Tracking Stops When App Backgrounded

Symptoms: Location updates stop when app goes to background. Solutions:
1

Add background location capability

In Xcode, go to your target’s Signing & Capabilities tab:
  1. Click + Capability
  2. Add Background Modes
  3. Enable Location updates
2

Request Always authorization

Background tracking requires “Always” permission:
let locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
3

Add Info.plist key

Add NSLocationAlwaysAndWhenInUseUsageDescription to your Info.plist with a clear explanation of why you need background location access.
4

Don't use continuous with blue bar disabled

If you disable the blue bar indicator:
let options = RadarTrackingOptions.presetContinuous
options.showBlueBar = false  // ⚠️ Not recommended
iOS may still throttle updates. The blue bar is required for guaranteed continuous tracking.

Location Permissions

Permission Prompt Not Showing

Symptoms: Location permission dialog never appears. Solutions:
Ensure you have the correct keys in your Info.plist:
Info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to provide nearby recommendations</string>

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We track your location to provide real-time updates and alerts</string>
The description text cannot be empty or the prompt won’t show.
Permission requests must be made on the main thread:
DispatchQueue.main.async {
    let locationManager = CLLocationManager()
    locationManager.requestWhenInUseAuthorization()
}
If the user previously denied permission, the prompt won’t show again:
let status = CLLocationManager.authorizationStatus()
if status == .denied || status == .restricted {
    // Direct user to Settings app
    if let url = URL(string: UIApplication.openSettingsURLString) {
        UIApplication.shared.open(url)
    }
}

Can’t Upgrade from When In Use to Always

Symptoms: Already have “When In Use” permission but can’t get “Always” permission. Solutions:
1

Request in correct order

Request “When In Use” first, then “Always” after the app has been used:
let locationManager = CLLocationManager()

// First request
locationManager.requestWhenInUseAuthorization()

// Later, request Always
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    locationManager.requestAlwaysAuthorization()
}
2

Add both Info.plist keys

You must have both keys in Info.plist to upgrade permissions:
  • NSLocationWhenInUseUsageDescription
  • NSLocationAlwaysAndWhenInUseUsageDescription
3

Timing matters

iOS may not show the Always prompt immediately. Request it:
  • After the user has used the app a few times
  • When they’re performing an action that benefits from background tracking
  • Not immediately on first launch

Trip Tracking

Trip Not Starting

Symptoms: startTrip() call returns an error or trip doesn’t track. Solutions:
let tripOptions = RadarTripOptions(
    externalId: "trip-123",  // Must be unique
    destinationGeofenceTag: "store",
    destinationGeofenceExternalId: "store-456"
)
tripOptions.mode = .car

Radar.startTrip(options: tripOptions) { (status, trip, events) in
    if status == .success {
        print("Trip started: \(trip?._id ?? "")")
    } else {
        print("Failed to start trip: \(Radar.stringForStatus(status))")
    }
}
The destination geofence must exist in your Radar dashboard:
  1. Go to Geofences
  2. Search for your destinationGeofenceTag or destinationGeofenceExternalId
  3. Ensure the geofence is enabled
Trip tracking requires location tracking to be active:
if !Radar.isTracking() {
    print("Need to start tracking first")
    Radar.startTracking(trackingOptions: .presetResponsive)
}

// Or use automatic tracking
let tripOptions = RadarTripOptions(...)
tripOptions.startTracking = true  // Auto-start tracking
Radar.startTrip(options: tripOptions)

No Trip Events Generated

Symptoms: Trip starts but no approaching/arrival events received. Solutions:
1

Check approaching threshold

Adjust the approaching threshold if events aren’t triggered at the right distance:
let tripOptions = RadarTripOptions(...)
tripOptions.approachingThreshold = 500  // Meters from destination
2

Verify geofence radius

Ensure your destination geofence is large enough. Geofences smaller than 100 meters may have delayed entry detection.
3

Test with manual location

Simulate location updates to test events:
let origin = CLLocation(latitude: 40.7128, longitude: -74.0060)
let destination = CLLocation(latitude: 40.7589, longitude: -73.9851)

Radar.mockTracking(
    origin: origin,
    destination: destination,
    mode: .car,
    steps: 10,
    interval: 3
) { (status, location, events, user) in
    print("Mock update: \(events?.count ?? 0) events")
}

Events and Webhooks

Events Not Received in Delegate

Symptoms: Location updates work but event delegate methods aren’t called. Solutions:
class MyRadarDelegate: NSObject, RadarDelegate {
    func didReceiveEvents(_ events: [RadarEvent], user: RadarUser?) {
        for event in events {
            print("Event: \(RadarEvent.stringForType(event.type) ?? "unknown")")
            if let geofence = event.geofence {
                print("Geofence: \(geofence.tag)")
            }
        }
    }
    
    func didUpdateLocation(_ location: CLLocation, user: RadarUser) {
        print("Location updated, user: \(user._id)")
    }
    
    func didUpdateClientLocation(
        _ location: CLLocation,
        stopped: Bool,
        source: RadarLocationSource
    ) {
        print("Client location updated")
    }
    
    func didFail(status: RadarStatus) {
        print("Error: \(Radar.stringForStatus(status))")
    }
    
    func didLog(message: String) {
        print("Radar log: \(message)")
    }
}
Make sure your delegate isn’t being deallocated:
// ❌ Delegate will be deallocated
Radar.setDelegate(MyRadarDelegate())

// ✅ Keep a strong reference
class MyViewController: UIViewController {
    let radarDelegate = MyRadarDelegate()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        Radar.setDelegate(radarDelegate)
    }
}

Webhooks Not Firing

Symptoms: Events generated but webhooks not received on your server. Solutions:
1

Check webhook configuration

In your Radar dashboard:
  1. Go to SettingsWebhooks
  2. Verify URL is correct and reachable
  3. Ensure event types are enabled
  4. Check webhook signature verification
2

Test endpoint manually

Use the Radar dashboard’s webhook tester to send a test event to your endpoint.
3

Check server logs

Look for incoming requests on your server. Radar webhooks:
  • Use POST method
  • Have Content-Type: application/json
  • Include X-Radar-Signature header
4

Verify event conditions

Some events have conditions:
  • Geofence events require matching tag or metadata
  • Trip events require active trip
  • Dwell events require minimum dwell time

Verified Location

trackVerified() Fails

Symptoms: trackVerified() returns error status. Solutions:
SSL pinning must be configured before calling trackVerified():
// Configure SSL pinning in initialize options
let options = RadarInitializeOptions()
// Add your SSL pinning configuration here
Radar.initialize(publishableKey: "prj_test_pk_...", options: options)

// Then call trackVerified
Radar.trackVerified { (status, token) in
    if let token = token, token.passed {
        print("Location verified: \(token.token)")
    }
}
Location verification requires:
  • SSL pinning configured
  • Valid location with good accuracy
  • Network connectivity
  • Fraud detection enabled in dashboard
Radar.trackVerified(beacons: false, desiredAccuracy: .high) { (status, token) in
    if status != .success {
        print("Verification failed: \(Radar.stringForStatus(status))")
    } else if let token = token {
        print("Passed: \(token.passed)")
        print("Expires: \(token.expiresAt ?? Date())")
    }
}
A failed verification doesn’t mean an error - it might indicate:
  • Location spoofing detected
  • Proxy/VPN usage
  • Location mismatch with expected jurisdiction
Radar.trackVerified { (status, token) in
    guard status == .success else {
        // Handle API error
        return
    }
    
    if token?.passed == true {
        // Verification passed
        sendTokenToServer(token?.token)
    } else {
        // Verification failed - may indicate fraud
        showErrorMessage("Location verification failed")
    }
}

Performance Issues

High Battery Usage

Symptoms: App drains battery faster than expected. Solutions:
// ⚡ Highest battery usage
Radar.startTracking(trackingOptions: .presetContinuous)

// ⚡⚡ Moderate battery usage (recommended)
Radar.startTracking(trackingOptions: .presetResponsive)

// ⚡⚡⚡ Lowest battery usage
Radar.startTracking(trackingOptions: .presetEfficient)
let options = RadarTrackingOptions.presetResponsive
options.desiredMovingUpdateInterval = 300  // 5 minutes
options.desiredStoppedUpdateInterval = 0   // Shut down when stopped
options.desiredAccuracy = .low             // Use lower accuracy
Radar.startTracking(trackingOptions: options)
let options = RadarTrackingOptions.presetResponsive
options.beacons = false          // Disable beacon ranging
options.useIndoorScan = false    // Disable indoor positioning
options.syncGeofences = false    // Disable geofence sync
Radar.startTracking(trackingOptions: options)

App Launch Time Increased

Symptoms: App takes longer to launch after adding Radar SDK. Solutions:
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // Initialize Radar first (required)
    Radar.initialize(publishableKey: "prj_test_pk_...")
    
    // Defer tracking start until after UI loads
    DispatchQueue.main.async {
        if CLLocationManager.authorizationStatus() == .authorizedAlways {
            Radar.startTracking(trackingOptions: .presetResponsive)
        }
    }
    
    return true
}
Disable features you don’t need:
let options = RadarInitializeOptions()
options.autoLogNotificationConversions = false  // If not using notifications
options.autoHandleNotificationDeepLinks = false
Radar.initialize(publishableKey: "prj_test_pk_...", options: options)

Debugging Tools

Logging

Enable different log levels to see SDK activity:
let options = RadarInitializeOptions()
options.logLevel = .debug  // .none, .error, .warning, .info, or .debug

Radar.initialize(publishableKey: "prj_test_pk_...", options: options)
Log Levels:
  • .none - No logs
  • .error - Only errors
  • .warning - Warnings and errors
  • .info - Info, warnings, and errors (default)
  • .debug - Verbose logging including all SDK operations
Implement log delegate:
class MyRadarDelegate: NSObject, RadarDelegate {
    func didLog(message: String) {
        print("[Radar] \(message)")
        // Send to your analytics service
    }
}

Testing with Mock Locations

Test tracking without moving:
let sanFrancisco = CLLocation(latitude: 37.7749, longitude: -122.4194)
let newYork = CLLocation(latitude: 40.7128, longitude: -74.0060)

Radar.mockTracking(
    origin: sanFrancisco,
    destination: newYork,
    mode: .car,
    steps: 20,       // Number of location updates
    interval: 2      // Seconds between updates
) { (status, location, events, user) in
    print("Mock location: \(location?.coordinate ?? CLLocationCoordinate2D())")
    print("Events: \(events?.count ?? 0)")
}

Manual Tracking

Test with a manual location:
let testLocation = CLLocation(
    coordinate: CLLocationCoordinate2D(latitude: 40.7589, longitude: -73.9851),
    altitude: 10,
    horizontalAccuracy: 5,
    verticalAccuracy: 5,
    timestamp: Date()
)

Radar.trackOnce(location: testLocation) { (status, location, events, user) in
    print("Status: \(Radar.stringForStatus(status))")
    print("Events: \(events?.count ?? 0)")
    print("User: \(user?._id ?? "")")
}

Check SDK State

Get current SDK configuration:
// Version and initialization
print("SDK Version: \(Radar.sdkVersion)")
print("Is Initialized: \(Radar.isInitialized)")

// Tracking status
print("Is Tracking: \(Radar.isTracking())")
if Radar.isTracking() {
    let options = Radar.getTrackingOptions()
    print("Tracking Options: \(options.dictionaryValue)")
}

// Verified tracking
print("Is Tracking Verified: \(Radar.isTrackingVerified())")

// User info
if let userId = Radar.getUserId() {
    print("User ID: \(userId)")
}
if let metadata = Radar.getMetadata() {
    print("Metadata: \(metadata)")
}

// Trip info
if let trip = Radar.getTrip() {
    print("Active Trip: \(trip._id)")
    print("Status: \(trip.status)")
}

Common Error Codes

Understand error status codes:
switch status {
case .success:
    print("Operation succeeded")
    
case .errorPublishableKey:
    print("SDK not initialized or invalid API key")
    
case .errorPermissions:
    print("Location permissions not granted")
    
case .errorLocation:
    print("Location services error or timeout (20 seconds)")
    
case .errorBluetooth:
    print("Beacon ranging error or timeout (5 seconds)")
    
case .errorNetwork:
    print("Network error or timeout (10 seconds)")
    
case .errorBadRequest:
    print("Bad request - check parameters")
    
case .errorUnauthorized:
    print("Invalid API key")
    
case .errorPaymentRequired:
    print("Organization disabled or usage exceeded")
    
case .errorForbidden:
    print("Insufficient permissions or no beta access")
    
case .errorNotFound:
    print("Resource not found")
    
case .errorRateLimit:
    print("Too many requests - rate limit exceeded")
    
case .errorServer:
    print("Internal server error")
    
case .errorUnknown:
    print("Unknown error")
    
default:
    print("Unhandled error: \(status.rawValue)")
}

Still Having Issues?

If you’re still experiencing problems:

Contact Support

Email [email protected] with:
  • SDK version
  • iOS version
  • Device model
  • Detailed description
  • Relevant logs

GitHub Issues

Search existing issues or create a new one with:
  • Steps to reproduce
  • Expected behavior
  • Actual behavior
  • Code samples

Documentation

Review the complete documentation for detailed API references and guides

Dashboard

Check your Radar dashboard for:
  • Event logs
  • Webhook logs
  • API usage
  • Configuration

Build docs developers (and LLMs) love