Why Background Sync Is Trickier Than It Seems
Imagine: a user opens your delivery service, but the order list is empty — data hasn't updated since morning. Result: a negative review and a lost customer. Background Fetch solves this, but implementing it requires careful handling of platform constraints, battery drain, and app lifecycle. We set up Background Fetch turnkey in 3–5 days per platform, with guaranteed stable operation. Over 5 years we've been developing mobile apps with background sync for iOS and Android. Our experience: 50+ projects, including for banking, retail, and logistics.
Problems We Solve
- Stale data: orders, messages, or inventory not updated in the background.
- Battery drain: poorly configured sync can consume up to 5% per session.
- Platform restrictions: iOS may skip tasks if battery is low; Android's Doze Mode limits execution.
- Inconsistent intervals: relying on
setMinimumBackgroundFetchIntervalon iOS is outdated and unreliable.
How We Do It
iOS Background Fetch with BGTask Framework
Modern iOS uses the BackgroundTasks framework. The old UIApplication.setMinimumBackgroundFetchInterval is deprecated. Registering a task:
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.myapp.refresh",
using: nil
) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
The identifier must be listed in Info.plist under BGTaskSchedulerPermittedIdentifiers. Without this, the task won't register — no error, just silence. This is a common pitfall. Scheduling the next run happens at the end of the current execution via BGAppRefreshTaskRequest. The minimum interval is set with earliestBeginDate, but iOS decides when to actually run the task based on battery and usage patterns. No guarantee of a specific time. Crucially, a background task has a CPU time limit. If the task doesn't finish on time, the system calls task.expirationHandler. You must save progress and call task.setTaskCompleted(success: false). When using incremental sync, data volume drops by 60%, saving up to 25% battery compared to full loads.
Android Background Sync with WorkManager
WorkManager is the proper tool for periodic tasks on Android. It is 10 times more reliable than AlarmManager in Doze Mode and survives reboots. It replaces JobScheduler, AlarmManager, and SyncAdapter in most cases.
val refreshRequest = PeriodicWorkRequestBuilder<DataRefreshWorker>(
repeatInterval = 1,
repeatIntervalTimeUnit = TimeUnit.HOURS,
flexTimeInterval = 15,
flexTimeIntervalUnit = TimeUnit.MINUTES
)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"data_refresh",
ExistingPeriodicWorkPolicy.KEEP,
refreshRequest
)
ExistingPeriodicWorkPolicy.KEEP prevents overwriting the existing task if enqueue is called again (important on every app launch). The minimum interval for PeriodicWorkRequest is 15 minutes — the system won't allow less. If you don't set flexTimeInterval, the system may run the task immediately after the interval end, increasing battery consumption by 15–20%.
Comparison: iOS Background Fetch vs Android WorkManager
| Parameter | iOS (BGAppRefreshTask) | Android (WorkManager) |
|---|---|---|
| Periodicity | Minimum interval set but not guaranteed | Minimum 15 minutes, guaranteed when conditions met |
| Battery management | Automatic, based on usage patterns | Configurable constraints: battery level, charging, network |
| Registration required | Yes, in Info.plist | No, automatic via configuration |
| Task cancellation | cancel(taskRequestWithIdentifier:) |
enqueueUniquePeriodicWork with policy CANCEL_AND_REENQUEUE |
| Error handling | expirationHandler |
Result.retry(), Result.failure() |
Real-World Case: Incremental Sync in a Food Delivery App
We worked on a food delivery service where the app downloaded the entire order list (average 15 MB) every 15 minutes. This caused severe battery drain and frequent timeouts. We redesigned the sync to use timestamps: now the app only transfers changes from the last hour (about 200 KB), and the background task completes in 2 seconds. Battery consumption dropped by 40%, and sync failures fell from 25% to 3%.
Choosing Between Full and Incremental Sync
| Criterion | Full Sync | Incremental Sync |
|---|---|---|
| Data volume | Entire dataset (5–50 MB) | Only changes (0.1–1 MB) |
| Execution time | 10–30 seconds | 1–3 seconds |
| Battery consumption | 2–5% per session | 0.2–1% per session |
| Timeout risk | High (up to 30% failures) | Low (under 5% failures) |
Incremental sync is 4 times faster than full sync and uses 10 times less traffic. We recommend an incremental approach for apps with frequent background updates. It is 4 times faster and 10 times more traffic-efficient. Our turnkey background sync setup starts at $1,500 per platform. Request a consultation — we'll help you choose the optimal sync strategy for your project.
What to Do in a Background Task
A background task should be minimal: request only what changed (incremental sync), save to local database (Room / Core Data), and send a local notification if there are important updates. Do avoid heavy computations, large HTTP requests without timeouts, or synchronous UI operations.
How to Debug Background Sync: Step-by-Step
-
iOS: Use Xcode with the
BGTaskScheduler.shared.registerflag and enable logging viaOSSignposter. Trigger the task usingsimulateBackgroundFetchin the simulator. -
Android: Add
WorkManager.getWorkInfosByTag("data_refresh")to your code and print the status. For a forced run, useadb shell am broadcast -a android.intent.action.BOOT_COMPLETED. - Ensure the task doesn't exceed the CPU limit: on iOS — 30 seconds, on Android — 10 minutes (considering Doze Mode restrictions).
- On Android, do rely on
WorkManager— it correctly handles battery constraints, unlikeAlarmManagerwhich won't fire in Doze Mode.
React Native and Flutter
In React Native, background tasks are implemented via native modules. The library react-native-background-fetch wraps BGAppRefreshTask on iOS and JobScheduler/WorkManager on Android into a unified JS API. In Flutter, use the workmanager plugin for Android and background_fetch for iOS. The same platform constraints apply — the abstraction does not remove them. Implementation timeline: 3–5 days for one platform, 1–1.5 weeks for iOS + Android with testing of edge cases (battery, no network, Doze Mode on Android).
Deliverables
- Audit of current background task implementation
- Architecture design for sync (incremental, caching)
- Integration of BGTask / WorkManager following platform best practices
- Configuration of push notifications for change alerts
- Battery consumption optimization using profilers — up to 30% reduction
- Testing on 10+ devices with different OS versions
- Documentation and recommendations for production monitoring
- Access to source code and project documentation
- Training session for your development team
- 1-month free support after deployment
Why background sync may fail?
The two most common reasons: the system terminates the app due to memory pressure, or the task exceeds its CPU time limit. Solutions include incremental sync and efficient data caching.Contact us to discuss your project. Get a consultation on background sync implementation today.







