Skip to content

[Help Wanted]: iOS didExitRegion fires 30 min to 3 h late after onAppTerminate despite device clearly exiting stationary geofence #2580

@romaindeveloppeur-dotcom

Description

Required Reading

  • Confirmed

Plugin Version

5.1.1 (TSLocationManager 4.1.3)

Mobile operating-system(s)

  • iOS
  • Android

Device Manufacturer(s) and Model(s)

iPhone 13 pro

Device operating-systems(s)

iOS 18.4

React Native / Expo version

React Native 0.81.5 / Expo SDK 54

What happened?

Context

Mileage-tracking app for French professionals (stopOnTerminate: false, Always permission).We understand that on iOS, after onAppTerminate, the SDK relies on the "stationary geofence" (typically requiring ~200 m of movement) to re-awaken the app via didExitRegion.

Observed behavior

On the same user, two reproductions in dense urban Paris:

  • 2026-05-05, onAppTerminate at point A (15 rue Soufflot). User then drove ~17:50 to point B (11 rue Quatrefages, 1.7 km away), then back to A around 18:15 (1.2 km). Both legs entirely missed. didExitRegion only fired at 19:04:48 , 2 h 53 min after onAppTerminate, ~45 min after the user returned to A.
  • 2026-05-06, Same pattern. onAppTerminate at point A. User drove ~14:50 to point C (9 rue de Pontoise, 1.5 km away). didExitRegion fired at 14:59:05 , 31 min after `onAppTerminate*, after the user already arrived at C. Outbound trip lost. Return trip C → A (1.4 km) at ~15:10 also missed under the same pattern.

In both cases, the device clearly exited the documented ~200 m stationary-geofence threshold (1.7 km on day 1, 1.5 km on day 2). The SDK setup on terminate looks correct (matching Philosophy of Operation):

2026-05-05 16:11:38.129  onEnterBackground
2026-05-05 16:11:39.954  onAppTerminate  stopOnTerminate? 0
2026-05-05 16:11:39.955  startMonitoringStationaryRegion: Radius: 150
2026-05-05 16:11:39.956  stopUpdatingLocation
2026-05-05 16:11:39.956  startMonitoringSignificantLocationChanges
[ silence  user does A → B (1.7 km) → A (1.2 km) ]
2026-05-05 19:04:48      KVO wiring (fresh ready())
2026-05-05 19:04:48.510  locationManager:didExitRegion  Exit stationary region
2026-05-05 19:04:48.511  📍 +48.85050,+2.34298  (382 m from region center, on foot)

The SDK behaves exactly as documented once it's awakened. The issue is purely the latency between the geofence boundary crossing and didExitRegion firing when the app has been terminated.

Question

Is this kind of latency (30 min to ~3 h between onAppTerminate and didExitRegion, despite ≫ 200 m of movement) something you've observed before for short urban round-trips that return near the original location?

Is this behavior didExitRegion firing minutes-to-hours after the device has clearly exited the geofence something you've encountered for terminated apps? Any recommendation on config or monitoring strategy to get earlier wake-ups in this scenario?

Thanks for your work on this SDK. 🙏

Plugin Code and/or Config

### Plugin Code and/or Config


const TRACKING_CONFIG: Config = {
  reset: true,
  geolocation: {
    desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
    locationUpdateInterval: 5000,
    allowIdenticalLocations: true,
    stationaryRadius: 25,
    stopTimeout: 5,
    elasticityMultiplier: 1.2,
    activityType: BackgroundGeolocation.ActivityType.Other,
    showsBackgroundLocationIndicator: true,
    disableLocationAuthorizationAlert: true,
    locationAuthorizationRequest: 'Always',
  },
  activity: {
    activityRecognitionInterval: 10000,
    minimumActivityRecognitionConfidence: 50,
    stopOnStationary: false,
  },
  app: {
    stopOnTerminate: false,
    startOnBoot: true,
    enableHeadless: true,
    heartbeatInterval: 60,
    preventSuspend: true,
  },
  http: {
    url: '<API_URL>/locations',
    headers: { 'X-App-Key': '<REDACTED>' },
    rootProperty: 'locations',
    autoSync: true,
    batchSync: true,
    maxBatchSize: 50,
    autoSyncThreshold: 10,
  },
  persistence: { maxDaysToPersist: 60 },
  logger: { debug: false, logLevel: BackgroundGeolocation.LogLevel.Verbose },
};

// index.js
BackgroundGeolocation.ready(TRACKING_CONFIG).then(() => {
  BackgroundGeolocation.start();
});

BackgroundGeolocation.registerHeadlessTask(async (event) => { /* logs only */ });
BackgroundFetch.registerHeadlessTask(async (event) => {
  if (event.taskId === 'react-native-background-fetch') {
    try {
      await BackgroundGeolocation.getCurrentPosition({ samples: 1 });
    } catch (e) {}
  }
  BackgroundFetch.finish(event.taskId);
});

Relevant log output

### Relevant log output

Full iOS log (TSLocationManager Verbose, ~86 400 lines, 2026-05-05 / 06 over ~36 h):
👉 https://gist.github.com/romaindeveloppeur-dotcom/906dc2ffb34a17d18777f51cf7d67753

Two annotated extracts (May 5 and May 6) in the timeline above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions