# Apple App Store Review — Background Task Compliance
> **TL;DR.** The library lets you dispatch many different workers behind a single
> `BGTaskScheduler` identifier declared in `Info.plist`. That power is
> double-edged: misused, it triggers Apple's §2.5.4 review (which can reject or
> retroactively ban your app). This doc tells you which patterns are safe and
> which are review bait.
## The relevant App Store guidelines
| § | Guideline | What it means for this library |
|---|---|---|
| 2.5.4 | "Multitasking apps may only use background services for their intended purposes." | Every worker dispatched through a `BGTaskScheduler` identifier must serve a function the user clearly understands. |
| 2.5.1 | "Apps should use APIs and frameworks for their intended purposes." | Don't use `BGTaskScheduler` to keep your app alive for analytics, ad refresh, or to "pre-cache things just in case." |
| 4.1 | "Don't impersonate or mislead." | If `Info.plist` declares a single identifier called `com.acme.refresh`, the user-visible name and description must cover everything that actually runs under it. |
| 5.1.1 (iii) | "If your app collects user or usage data, you must secure user consent." | Background uploads of analytics / telemetry need the same consent UX as foreground uploads. |
Apple's automated review pipeline cannot inspect your worker chain — they
can't see what a chain step actually does. But **human reviewers do read your
app**, and **TestFlight & post-release auditors do strace your app**. If your
background usage doesn't match the description you provided at submission, you
will get a "Rejected — Guideline 2.5.4" notice with no further detail.
---
## Patterns that are safe
The library's dynamic-task-dispatch pattern (one `Info.plist` identifier,
many worker classes) is safe **when all workers fall under a single
user-understandable function**. Concrete examples:
### ✅ Photo backup chain
`Info.plist` identifier: `com.acme.photo-backup`
Workers dispatched under it:
- `CompressPhotoWorker`
- `ChecksumWorker`
- `HttpUploadWorker`
- `MarkUploadedWorker`
Why this is fine: every worker is part of "back up the user's photo to their
cloud account." A reviewer reading your privacy disclosure understands all
four. App Store description mentions "photo backup runs in the background".
### ✅ Offline sync chain
`Info.plist` identifier: `com.acme.offline-sync`
Workers:
- `FetchPendingMutationsWorker`
- `HttpRequestWorker`
- `ConflictResolutionWorker`
Why this is fine: all workers are part of "sync user's offline edits when
connectivity returns." The user opted into offline mode in settings.
### ✅ Maintenance chain
`Info.plist` identifier: `com.acme.maintenance` (preferably as a
`BGProcessingTaskRequest`, not App Refresh)
Workers:
- `LogRotationWorker`
- `CacheEvictionWorker`
- `DbVacuumWorker`
Why this is fine: housekeeping work that has no privacy impact, runs only
when device is charging + on Wi-Fi (constraint), runs once per day at most.
---
## Patterns that get you rejected
### 🚫 Cross-purpose dispatch under one identifier
`Info.plist` identifier: `com.acme.background` (generic name 🚩)
Workers dispatched under it:
- `PhotoBackupWorker` (uploads user content)
- `AdMetricsWorker` (sends ad impressions)
- `LocationLoggerWorker` (records GPS even when feature isn't in use)
- `CompetitorPriceCheckerWorker` (scrapes a third-party URL)
This is a **§2.5.4 + §5.1.1 + privacy-policy-mismatch** trifecta. Even if your
`Info.plist` "only declares one identifier" the actual behaviour is multi-purpose
and undisclosed.
Fix:
1. Split into multiple `BGTaskScheduler` identifiers, each scoped to one
user-understandable function.
2. List each identifier + its purpose in the app's App Store description.
3. Update your privacy policy to disclose the data flow for each.
### 🚫 Long-tail background work to keep DAU stats up
Dispatching a worker every 6 hours that just `URLSession.dataTask` to your
analytics endpoint, with the side effect of "marking the user as active
today." This violates §2.5.4 (the worker has no user-facing purpose) and
§4.5.4 (push notifications cannot be used purely for marketing — same logic
applies here).
### 🚫 Background fetch of content that's never shown
Pre-fetching content "just in case" when the worker has no signal that the
user will look at it. Apple cracks down on this because it wastes battery for
no user benefit. If the user opens the app once a month, pre-fetching daily
is a §2.5.4 violation.
### 🚫 Hidden cryptocurrency / proof-of-work
Self-explanatory, but worth saying: any background CPU work that isn't
directly providing a service the user requested is review bait. Even
non-crypto "ML training on user device while idle" is subject to §3.1.2
(consent + disclosure).
---
## Practical compliance checklist
Before submitting an app that uses this library:
1. **Audit your `Info.plist`.**
```xml
BGTaskSchedulerPermittedIdentifiers
com.acme.photo-backup
com.acme.offline-sync
com.acme.maintenance
```
Each identifier should name **a specific feature**, not a generic
"background" bucket.
2. **Audit every worker class.** Open `@Worker(name = …)` annotations and
group by which `BGTaskScheduler` identifier they get dispatched under.
Each group should map to one user-understandable feature.
3. **Audit your App Store description.** Search for "background" in the
description and screenshots. If your app does background uploads, say so.
"App backs up your photos automatically when you're on Wi-Fi" is the kind
of one-liner that resolves §2.5.4 questions.
4. **Audit your privacy policy.** Every endpoint your workers hit must be
listed. CDN, analytics, third-party SDK callbacks — all of it.
5. **Don't share identifiers across unrelated features.** Two unrelated
workers under one identifier is the single most common §2.5.4 trigger.
6. **Cellular vs Wi-Fi.** Use `Constraints.requiresUnmeteredNetwork = true`
for any worker that uploads user content > 1 MB. Reviewers regularly check
"does the app eat my cellular data without warning."
7. **Background fetch interval.** Don't request more than once per hour from
`BGAppRefreshTaskRequest.earliestBeginDate`. iOS will silently throttle
you, and the review team treats high-frequency requests as a red flag.
---
## What the library can and cannot do for you
| Concern | Library | You (the host app) |
|---|---|---|
| Disk persistence + retry | ✅ Handles it | — |
| Worker dispatch | ✅ Dispatches multiple worker classes per identifier | — |
| Tagging worker → identifier | ❌ Not enforced at compile time | Audit yourself per §2.5.4 |
| Privacy disclosure | ❌ No automated check | Update policy + Info.plist `NSUsageDescription`s |
| App Store description match | ❌ Cannot know | Review submission text |
| Cellular constraint | ✅ `Constraints.requiresUnmeteredNetwork` | Set it on user-content uploads |
| BGTask interval rate-limit | ⚠️ Library will submit whatever you request | Cap to ≥ 1 hour for App Refresh |
The library is a power tool. Apple's review process treats power tools as the
*developer's responsibility* — there is no §2.5.4 carve-out for "but the
library let me do it."
---
## If you get rejected
A §2.5.4 rejection typically reads something like:
> Guideline 2.5.4 — Performance — Software Requirements
>
> Your app declares support for background modes in its Info.plist but you have
> not provided features that require persistent background execution. Apps that
> declare support for background modes for their intended purposes only.
Resolution path:
1. Reply to the reviewer in App Store Connect explaining which
`BGTaskScheduler` identifier serves which user-visible feature.
2. Provide a screenshot / screen recording demonstrating the feature.
3. If they push back, remove the identifier that doesn't map to a clear
feature — even if you "wanted to use it eventually."
A §2.5.4 rejection is **resolvable** if you act fast and provide concrete
mapping evidence. Repeat rejections on the same guideline escalate to
account-level review which is much harder to recover from.
---
## See also
- [`docs/IOS_BGTASK_LIMITS.md`](./IOS_BGTASK_LIMITS.md) — the OS-level physics
(opportunistic scheduling, time budget) that constrain what's even possible.
- [`docs/ios-best-practices.md`](./ios-best-practices.md) — engineering-level
best practices for BGTask integration.
- [Apple — App Review Guideline 2.5.4](https://developer.apple.com/app-store/review/guidelines/#2.5.4)
- [Apple — BGTaskScheduler documentation](https://developer.apple.com/documentation/backgroundtasks)