Skip to main content
This guide will walk you through implementing location tracking with the Radar iOS SDK, from installation through your first tracking call.

What You’ll Build

In this quickstart, you’ll:
  1. Install and initialize the Radar SDK
  2. Request location permissions from the user
  3. Track the user’s location once (foreground tracking)
  4. Set up a delegate to receive events
  5. Start background tracking

Step 1: Installation & Setup

1

Install the SDK

First, install the Radar SDK using CocoaPods or Swift Package Manager. See the installation guide for detailed instructions.For CocoaPods, add to your Podfile:
pod 'RadarSDK', '~> 3.27.0'
Then run:
pod install
2

Configure Info.plist

Add location permission descriptions to your Info.plist:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We use your location to provide location-based features.</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location while you're using the app.</string>
3

Initialize the SDK

In your AppDelegate.swift, initialize Radar with your publishable key:
import UIKit
import RadarSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Initialize Radar
        Radar.initialize(publishableKey: "prj_live_pk_...")
        
        return true
    }
}
Replace prj_live_pk_... with your actual publishable API key from the Radar dashboard.

Step 2: Request Location Permissions

Create a location manager to request permissions:
import UIKit
import CoreLocation
import RadarSDK

class ViewController: UIViewController, CLLocationManagerDelegate {
    let locationManager = CLLocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        locationManager.delegate = self
        requestLocationPermissions()
    }
    
    func requestLocationPermissions() {
        let status: CLAuthorizationStatus
        
        if #available(iOS 14.0, *) {
            status = locationManager.authorizationStatus
        } else {
            status = CLLocationManager.authorizationStatus()
        }
        
        if #available(iOS 13.4, *) {
            // On iOS 13.4+, request foreground first
            if status == .notDetermined {
                locationManager.requestWhenInUseAuthorization()
            } else if status == .authorizedWhenInUse {
                // Then request background
                locationManager.requestAlwaysAuthorization()
            }
        } else {
            // Before iOS 13.4, request background directly
            locationManager.requestAlwaysAuthorization()
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        // Continue requesting permissions if needed
        requestLocationPermissions()
    }
}
iOS 13.4+ requires a two-step permission flow: first request “When In Use” permission, then request “Always” permission for background tracking.

Step 3: Track Location Once

Now let’s track the user’s location once (foreground tracking):
import RadarSDK

class ViewController: UIViewController {
    
    @IBAction func trackLocationTapped(_ sender: Any) {
        Radar.trackOnce { (status, location, events, user) in
            switch status {
            case .success:
                print("✅ Location tracked successfully")
                print("📍 Location: \(location?.coordinate ?? CLLocationCoordinate2D())")
                print("👤 User ID: \(user?._id ?? "unknown")")
                
                // Check for events
                if let events = events, !events.isEmpty {
                    print("🎉 Events received: \(events.count)")
                    for event in events {
                        print("  - \(event.type)")
                    }
                }
                
            case .errorPermissions:
                print("❌ Location permissions not granted")
                
            case .errorLocation:
                print("❌ Could not get location")
                
            case .errorNetwork:
                print("❌ Network error")
                
            default:
                print("❌ Error: \(Radar.string(for: status))")
            }
        }
    }
}

Understanding the Response

The trackOnce completion handler receives:
  • status: A RadarStatus enum indicating success or failure
  • location: A CLLocation object with the user’s coordinates
  • events: An array of RadarEvent objects (geofence entries/exits, place events, etc.)
  • user: A RadarUser object with the current user state
  • .success - Location tracked successfully
  • .errorPublishableKey - SDK not initialized
  • .errorPermissions - Location permissions not granted
  • .errorLocation - Location services error
  • .errorNetwork - Network error
  • .errorBadRequest - Invalid request parameters
  • .errorUnauthorized - Invalid API key
  • .errorRateLimit - Rate limit exceeded
  • .errorServer - Server error
  • .errorUnknown - Unknown error

Step 4: Set Up a Delegate

Implement RadarDelegate to receive events and location updates:
import RadarSDK

class MyRadarDelegate: NSObject, RadarDelegate {
    
    func didReceiveEvents(_ events: [RadarEvent], user: RadarUser?) {
        print("📬 Received \(events.count) event(s)")
        
        for event in events {
            switch event.type {
            case .userEnteredGeofence:
                if let geofence = event.geofence {
                    print("🎯 Entered geofence: \(geofence.description)")
                }
                
            case .userExitedGeofence:
                if let geofence = event.geofence {
                    print("👋 Exited geofence: \(geofence.description)")
                }
                
            case .userEnteredPlace:
                if let place = event.place {
                    print("🏪 Entered place: \(place.name)")
                }
                
            default:
                print("📍 Event: \(event.type)")
            }
        }
    }
    
    func didUpdateLocation(_ location: CLLocation, user: RadarUser) {
        print("📍 Location updated: \(location.coordinate)")
        print("👤 User state updated")
        
        // Check if user is in any geofences
        if let geofences = user.geofences, !geofences.isEmpty {
            print("   In \(geofences.count) geofence(s)")
        }
        
        // Check if user is at a place
        if let place = user.place {
            print("   At: \(place.name)")
        }
    }
    
    func didUpdateClientLocation(_ location: CLLocation, stopped: Bool, source: RadarLocationSource) {
        print("📱 Client location: \(location.coordinate) (stopped: \(stopped))")
    }
    
    func didFail(status: RadarStatus) {
        print("❌ Radar error: \(Radar.string(for: status))")
    }
    
    func didLog(message: String) {
        print("🔍 Radar log: \(message)")
    }
}
Set the delegate in your AppDelegate:
import RadarSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    let radarDelegate = MyRadarDelegate()
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Initialize Radar
        Radar.initialize(publishableKey: "prj_live_pk_...")
        
        // Set delegate
        Radar.setDelegate(radarDelegate)
        
        return true
    }
}
Keep a strong reference to your delegate (as shown above) to ensure it receives callbacks.

Step 5: Start Background Tracking

Enable continuous background tracking with one of the preset tracking modes:
import RadarSDK

// Start tracking with the responsive preset
Radar.startTracking(trackingOptions: .presetResponsive)

Tracking Presets

Radar provides three preset tracking modes optimized for different use cases:
Radar.startTracking(trackingOptions: .presetResponsive)
Best for: Most apps
  • Updates every ~2.5 minutes when moving
  • Shuts down when stopped to save battery
  • Low battery usage
  • Requires location background mode

Custom Tracking Options

You can also create custom tracking options:
let trackingOptions = RadarTrackingOptions()
trackingOptions.desiredStoppedUpdateInterval = 180  // 3 minutes when stopped
trackingOptions.desiredMovingUpdateInterval = 60    // 1 minute when moving
trackingOptions.desiredSyncInterval = 50            // Sync every 50 seconds
trackingOptions.desiredAccuracy = .medium
trackingOptions.stopDuration = 140                  // Consider stopped after 140s
trackingOptions.stopDistance = 70                   // Within 70 meters
trackingOptions.syncGeofences = true                // Sync nearby geofences
trackingOptions.useVisits = false
trackingOptions.useSignificantLocationChanges = false
trackingOptions.beacons = false

Radar.startTracking(trackingOptions: trackingOptions)

Step 6: Identify Users

Optionally identify users with a custom ID:
// Set user ID (e.g., your internal user ID)
Radar.setUserId("user_12345")

// Set user description
Radar.setDescription("Premium User")

// Set custom metadata
Radar.setMetadata([
    "plan": "premium",
    "signupDate": "2024-01-15"
])
If you don’t set a user ID, Radar automatically identifies users by device ID (IDFV).

Complete Example

Here’s a complete working example:
import UIKit
import RadarSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    let radarDelegate = MyRadarDelegate()
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Initialize Radar
        Radar.initialize(publishableKey: "prj_live_pk_...")
        
        // Set delegate
        Radar.setDelegate(radarDelegate)
        
        // Identify user
        Radar.setUserId("user_12345")
        Radar.setMetadata(["plan": "premium"])
        
        return true
    }
}

Testing Your Integration

To test your integration:
1

Run the app

Build and run your app on a physical device (simulator location is less accurate).
2

Grant permissions

Accept the location permission prompts when they appear.
3

Track location

Tap your “Track Once” button to send a location update to Radar.
4

View in dashboard

Open the Radar dashboard and go to the Users page to see your location update.
Enable debug logs to see detailed SDK activity:
Radar.setLogLevel(.debug)

Next Steps

Now that you have basic tracking working, explore more features:

Create Geofences

Set up geofences in the Radar dashboard

Trip Tracking

Track multi-stop trips with live ETAs

Search Places

Search for nearby places and chains

API Reference

View the complete SDK reference

Common Issues

Make sure you’ve added the required NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription keys to your Info.plist.On iOS 13.4+, you must request “When In Use” permission before requesting “Always” permission.
Verify that:
  • You’ve enabled the Location updates background mode in Signing & Capabilities
  • The user granted “Always” location permission (not just “When In Use”)
  • You called Radar.startTracking() with valid tracking options
Make sure:
  • You’ve set a delegate with Radar.setDelegate()
  • You’re keeping a strong reference to your delegate object
  • You’ve created geofences in the Radar dashboard
The trackOnce() method is subject to rate limits. For continuous tracking, use startTracking() instead, which handles rate limiting automatically.

Get Help

Have questions? We’re here to help!

Build docs developers (and LLMs) love