Skip to main content

Overview

Anonymous tracking mode allows you to track location events without creating identifiable user records on the Radar server. When enabled, Radar avoids sending stable device IDs, user IDs, and user metadata to the server, providing enhanced privacy for users.

When to Use Anonymous Tracking

Use anonymous tracking when:
  • You need to comply with strict privacy regulations (GDPR, CCPA, etc.)
  • You want to track aggregate location patterns without identifying individual users
  • Users have opted out of personalized tracking
  • You’re building a privacy-first application
  • You need location features without user identification
Anonymous tracking has limitations. You cannot:
  • Identify specific users or devices across sessions
  • Access user history or profiles
  • Use features that require persistent user identity
  • Retrieve user state from RadarUser objects in delegate callbacks

Enabling Anonymous Tracking

Enable anonymous tracking before starting location tracking:
import RadarSDK

// Enable anonymous tracking
Radar.setAnonymousTrackingEnabled(true)

// Start tracking
Radar.startTracking(trackingOptions: .presetResponsive)
Anonymous tracking can be enabled or disabled at any time. Changes take effect immediately for subsequent location updates.

How It Works

When anonymous tracking is enabled:
1

No user records created

Radar does not create or update user records on the server. Each location update is treated independently.
2

Device ID is anonymized

Instead of sending the actual device ID (IDFV), Radar sends a generic "anonymous" identifier.
3

User metadata is excluded

User IDs, descriptions, metadata, and tags are not sent to the server.
4

Events still fire

Geofence, place, and region events still fire normally, but without persistent user context.

What Still Works

With anonymous tracking enabled, you can still use:

Location tracking

trackOnce(), startTracking(), and all tracking methods work normally.

Geofencing

Geofence entry and exit events are detected and delivered.

Place detection

Place entry, exit, and nearby events work as expected.

Region detection

Country, state, DMA, and postal code events are generated.

Client-side events

Delegate callbacks like didReceiveEvents are called with event data.

Context API

getContext() provides location context without user identification.

What Doesn’t Work

With anonymous tracking enabled, these features are not available:
You cannot call setUserId(), setDescription(), setMetadata(), or setTags(). These values will not be sent to the server.
// These have no effect in anonymous mode
Radar.setUserId("user_123")  // Not sent
Radar.setMetadata(["key": "value"])  // Not sent
The server does not maintain a history of locations or events for anonymous users. Each request is independent.
In RadarDelegate callbacks, the user parameter will be nil when anonymous tracking is enabled.
func didReceiveEvents(_ events: [RadarEvent], user: RadarUser?) {
    // user will be nil in anonymous mode
    if user == nil {
        print("Anonymous mode - no user state available")
    }
}

func didUpdateLocation(_ location: CLLocation, user: RadarUser) {
    // This method is NOT called in anonymous mode
    // Use didUpdateClientLocation instead
}
Trip tracking requires persistent user identity and is not available in anonymous mode.
// Trip tracking will fail in anonymous mode
Radar.startTrip(options: tripOptions)  // Not supported
logConversion() requires user identity and will not work properly in anonymous mode.

Disabling Anonymous Tracking

You can disable anonymous tracking at any time:
// Disable anonymous tracking
Radar.setAnonymousTrackingEnabled(false)

// Now you can identify users
Radar.setUserId("user_123")
When you disable anonymous tracking, the next location update will create or update a user record with the device ID.

Privacy Compliance Example

Here’s an example of using anonymous tracking based on user consent:
import RadarSDK

class LocationManager {
    
    func updateTrackingConsent(hasConsent: Bool) {
        if hasConsent {
            // User consented to personalized tracking
            Radar.setAnonymousTrackingEnabled(false)
            Radar.setUserId("user_123")
            Radar.setMetadata(["plan": "premium"])
            
        } else {
            // User declined - use anonymous mode
            Radar.setAnonymousTrackingEnabled(true)
            
            // Clear any previously set user data
            Radar.setUserId(nil)
            Radar.setMetadata(nil)
        }
        
        // Start tracking with appropriate mode
        Radar.startTracking(trackingOptions: .presetResponsive)
    }
    
    func showConsentDialog() {
        let alert = UIAlertController(
            title: "Location Tracking",
            message: "Would you like to enable personalized location features? You can choose anonymous tracking for privacy.",
            preferredStyle: .alert
        )
        
        alert.addAction(UIAlertAction(title: "Enable Personalized", style: .default) { _ in
            self.updateTrackingConsent(hasConsent: true)
        })
        
        alert.addAction(UIAlertAction(title: "Use Anonymous Mode", style: .default) { _ in
            self.updateTrackingConsent(hasConsent: false)
        })
        
        // Present alert...
    }
}

Server-Side Behavior

When anonymous tracking is enabled, Radar API requests will:
  • Include "anonymous": true in the request payload
  • Use "deviceId": "anonymous" instead of the actual device ID
  • Not create or update user records in the database
  • Still return events and context in the response
  • Not persist any user state on the server

Delegate Considerations

When implementing RadarDelegate with anonymous tracking:
class AppDelegate: UIResponder, UIApplicationDelegate, RadarDelegate {
    
    func didReceiveEvents(_ events: [RadarEvent], user: RadarUser?) {
        // user will be nil in anonymous mode
        for event in events {
            print("Event: \(event.type)")
            
            // Event properties like geofence, place are still available
            if let geofence = event.geofence {
                print("Geofence: \(geofence.tag ?? "unknown")")
            }
        }
        
        if user == nil {
            print("Running in anonymous mode")
        }
    }
    
    func didUpdateLocation(_ location: CLLocation, user: RadarUser) {
        // This method is NOT called in anonymous mode
    }
    
    func didUpdateClientLocation(_ location: CLLocation, stopped: Bool, source: RadarLocationSource) {
        // This method IS called in anonymous mode
        print("Anonymous location update: \(location.coordinate)")
    }
}
Use didUpdateClientLocation instead of didUpdateLocation when implementing anonymous tracking, as didUpdateLocation requires user state.

Best Practices

1

Respect user choice

Always give users control over whether they want personalized or anonymous tracking.
2

Set before tracking starts

Enable anonymous mode before calling startTracking() or trackOnce() to ensure no user data is sent.
3

Clear user data when enabling

When switching to anonymous mode, clear any previously set user IDs and metadata.
4

Document the limitations

Make sure your app gracefully handles the limitations of anonymous mode (no user state, no history).
5

Use appropriate delegate methods

Implement didUpdateClientLocation and check for nil user in didReceiveEvents.

Testing Anonymous Mode

To test anonymous tracking:
// Enable anonymous mode
Radar.setAnonymousTrackingEnabled(true)

// Track location
Radar.trackOnce { status, location, events, user in
    print("Status: \(status)")
    print("User: \(user == nil ? "nil (anonymous)" : "not nil")")
    
    // Events will still be returned
    if let events = events {
        for event in events {
            print("Event: \(event.type)")
        }
    }
}

Build docs developers (and LLMs) love