This guide helps you diagnose and resolve common issues with the Radar iOS SDK.
Quick Diagnostics
Before diving into specific issues, verify these basics:
SDK initialized correctly
Ensure you’re calling Radar.initialize() in your AppDelegate’s didFinishLaunchingWithOptions method before any other Radar methods. 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 ) " )
}
Location permissions granted
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" )
}
Common Issues
Location Tracking
Location Updates Not Received
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)
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 ())
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 )
}
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:
Use a more aggressive preset
// 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 )
Customize tracking options
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:
Add background location capability
In Xcode, go to your target’s Signing & Capabilities tab:
Click + Capability
Add Background Modes
Enable Location updates
Request Always authorization
Background tracking requires “Always” permission: let locationManager = CLLocationManager ()
locationManager. requestAlwaysAuthorization ()
Add Info.plist key
Add NSLocationAlwaysAndWhenInUseUsageDescription to your Info.plist with a clear explanation of why you need background location access.
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: < 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 ()
}
Check if already determined
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:
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 ()
}
Add both Info.plist keys
You must have both keys in Info.plist to upgrade permissions:
NSLocationWhenInUseUsageDescription
NSLocationAlwaysAndWhenInUseUsageDescription
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) ) " )
}
}
Verify destination geofence exists
The destination geofence must exist in your Radar dashboard:
Go to Geofences
Search for your destinationGeofenceTag or destinationGeofenceExternalId
Ensure the geofence is enabled
Check tracking 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:
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
Verify geofence radius
Ensure your destination geofence is large enough. Geofences smaller than 100 meters may have delayed entry detection.
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:
Implement all delegate methods
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 ) " )
}
}
Retain delegate reference
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:
Check webhook configuration
In your Radar dashboard :
Go to Settings → Webhooks
Verify URL is correct and reachable
Ensure event types are enabled
Check webhook signature verification
Test endpoint manually
Use the Radar dashboard’s webhook tester to send a test event to your endpoint.
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
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:
Check verification requirements
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 () ) " )
}
}
Handle verification failures
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" )
}
}
High Battery Usage
Symptoms: App drains battery faster than expected.
Solutions:
Use appropriate tracking preset
// ⚡ 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)
Disable unnecessary features
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:
Defer non-critical initialization
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
}
Optimize tracking options
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)
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 support@radar.com 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