Location
Tag profile updates and events with lat / lng — and the design choice that means the SDK never asks for location permission itself.
The Swan SDK does not acquire device location itself. The host app
supplies coordinates via Swan.updateLocation(...) (on Android / iOS / Web)
or Swan.updateLocation() (on React Native, which integrates with
react-native's Geolocation under the hood).
This guide explains why and how.
1. Why the host app owns acquisition
If the SDK acquired location, it would:
- Force every host app to depend on the platform location framework (Google Play Services Location, Core Location, navigator.geolocation) — including apps that don't need location at all
- Force every host app to declare the corresponding manifest / Info.plist permission strings — which causes App Store reviewer flags and lower opt-in rates
- Create ambiguity about which permission prompt the user is responding to — the host's "we use your location for nearby stores" or the SDK's?
By making the host app supply coordinates, the SDK stays small (no location-framework dependency) and the host app keeps full control over when and why it asks for permission.
The SDK reads SwanConfig.location.enabled to surface a documented
opt-in flag — useful for host apps that want a single switch to gate their
own acquisition logic.
2. Enable in config
import cx.swan.sdk.Swan
import cx.swan.sdk.SwanConfig
import cx.swan.sdk.LocationConfig
Swan.init(
applicationContext,
appId = "your-app-id",
config = SwanConfig(
location = LocationConfig(enabled = true),
),
)Swan.shared.initialize(
appId: "your-app-id",
config: SwanConfig(location: .init(enabled: true))
)import SwanSDK from '@loyalytics/swan-react-native-sdk';
SwanSDK.getInstance('your-app-id', {
isProduction: true,
location: { enabled: true },
});Swan.init({
appId: 'your-app-id',
location: { enabled: true },
});3. Push coordinates
// Without accuracy
Swan.updateLocation(latitude = 25.2048, longitude = 55.2708)
// With accuracy in meters
Swan.updateLocation(
latitude = 25.2048,
longitude = 55.2708,
accuracy = 12.0,
)Coordinates are WGS-84 decimal degrees (the standard everywhere). Accuracy
is in meters at 68% confidence (matches Location.getAccuracy() on
Google Play Services).
The SDK timestamps the call (server-side timestamp on a recent SDK clock)
and persists it as part of the device blob — surfaced via
Swan.getDeviceInfo().location.
Wiring to FusedLocationProvider
A typical Google Play Services setup:
val fused = LocationServices.getFusedLocationProviderClient(this)
fused.lastLocation.addOnSuccessListener { loc ->
loc?.let { Swan.updateLocation(it.latitude, it.longitude, it.accuracy.toDouble()) }
}import CoreLocation
let mgr = CLLocationManager()
mgr.requestWhenInUseAuthorization()
class Delegate: NSObject, CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
let loc = locations.last!
Swan.shared.updateLocation(
latitude: loc.coordinate.latitude,
longitude: loc.coordinate.longitude,
accuracy: loc.horizontalAccuracy
)
}
}You don't pass coordinates to the RN SDK — it acquires them internally via the platform's geolocation API after you've ensured the host app has been granted location permission.
import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
// Make sure permission is granted (host-app concern), then:
await sdk?.updateLocation();If location is disabled in config (location.enabled: false),
updateLocation() no-ops with a warning.
navigator.geolocation.getCurrentPosition((pos) => {
Swan.updateLocation({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
accuracy: pos.coords.accuracy,
});
});4. Reading the stored location
val info = Swan.getDeviceInfo()
val loc = info.location // SwanLocation? — null until first updateLocation
loc?.let {
Log.d("loc", "lat=${it.latitude}, lng=${it.longitude}, t=${it.timestamp}")
}SwanLocation shape: { latitude: Double, longitude: Double, accuracy: Double?, timestamp: Long }.
let loc = Swan.shared.getDeviceInfo().locationimport SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
const info = await sdk?.getDeviceInfo();
// { deviceId, generatedCDID, currentCDID, identifier?, ... }The RN SDK persists location server-side as part of the device blob.
getDeviceInfo() returns the credentials blob — identifiers and
registration timestamps. Location flows server-side with the next
event ingest, where the campaigns engine reads it for targeting.
const info = await Swan.getDeviceInfo();
const loc = info.location;5. Gate your acquisition on isLocationEnabled
If you build your own "should I prompt for location?" logic, branch on
Swan.isLocationEnabled() to respect the SDK's config opt-in:
if (Swan.isLocationEnabled()) {
// Run your fused-location flow; eventually call Swan.updateLocation(...)
}if Swan.shared.isLocationEnabled() { /* prompt + acquire */ }import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
if (sdk?.isLocationEnabled()) {
// your prompt + acquisition flow
await sdk?.updateLocation();
}if (Swan.isLocationEnabled()) { /* prompt + acquire */ }6. Usage patterns
| Pattern | Call frequency | Why |
|---|---|---|
| On every app foreground | each time | low-cost when nothing changed — the SDK persists locally and only ships with the next batch |
| On profile enrichment moments | at sign-in / first checkout | tag the profile with shipping locale for segmentation |
| On geofence-tripped event | on enter / exit | host app owns the geofence framework; SDK just tags the moment |
| On screen view for store-locator | per screen view | useful for "show offer based on closest branch" campaigns |
Avoid:
- Continuous location streaming — the SDK isn't designed for trail-style tracking. Push the latest sample on meaningful events instead.
- Calling without permission — on Android 14+ / iOS Always, supplying background-fetched coords without explicit permission is grounds for store removal.
7. Privacy / compliance notes
The SDK persists the most recent location only — there's no history buffer. The location is part of the device blob and is sent on subsequent event ingest as a device attribute, not a separate event.
If your app is subject to GDPR / CCPA / similar:
- Treat
enabled = trueas a user-consented state — flip it off when the user revokes - Clear by calling
Swan.updateLocation(0.0, 0.0)is not a clear; there is no public clear API in v1. If you need explicit deletion, callSwan.logout()to rotate the device or wait for the next major release - The Swan backend honors deletion-of-personal-data requests via the customer ID — not via the SDK
Reference
- Concepts → Identity (CDID) — location attaches to the device blob, not the event
- Android:
Swan.updateLocation,Swan.isLocationEnabled,Swan.getDeviceInfo,SwanLocation