When to use this
- You're about to ship a mobile release that uploads media, downloads assets, or syncs — and your users are on metered or prepaid data.
- You've had reports of unexpected data usage, out-of-bundle charges, or the app being killed mid-upload.
Prerequisites
- A test device on a genuinely metered or throttled connection (not office Wi-Fi).
- A network proxy (Charles/Proxyman) to see every byte the app and its SDKs send.
- Telemetry on bytes transferred per session.
Procedure
- 1
Stream files — never read the whole thing into memory
Loading a large file into a single buffer allocates its full size in RAM and risks an out-of-memory kill. Hand the networking layer a file URL and let it read and transmit in bounded chunks, for a flat memory profile at any file size.
Stream from disk, not from a Data blob // Flat memory regardless of file size — no whole-file allocation. let task = URLSession.shared.uploadTask(with: request, fromFile: fileURL) task.resume() - 2
Default large transfers to Wi-Fi; make cellular an explicit choice
Don't spend a user's data bundle without asking. Default large uploads/downloads to Wi-Fi, and if cellular is genuinely needed, prompt with the size so it's the user's informed decision — not a silent charge.
Wi-Fi by default; cellular is opt-in let config = URLSessionConfiguration.background(withIdentifier: "uploads") config.allowsCellularAccess = false // large media: Wi-Fi only by default config.isDiscretionary = true // let the OS pick a good moment // If cellular is unavoidable, make it the user's call: if path.usesInterfaceType(.cellular), fileSize > largeThreshold { // Prompt: "This is a 480 MB upload on mobile data. Continue?" } - 3
Use resumable, background transfers
A transfer that fails at 99% must not restart from zero — that re-reads and re-sends everything, multiplying data cost and crash risk. Use a resumable protocol (tus, multipart, ranged append) over a background session so a failure costs only the remainder and survives the app being suspended.
- 4
Audit third-party SDKs and CDN hosts for background traffic
The bytes you didn't write are the ones that surprise you. Proxy the app and watch what analytics, ads, and media SDKs fetch in the background. Pin asset/CDN traffic to a known host whitelist so a mis-scoped SDK can't route a client's data through an expensive path.
- 5
Add a data-budget guard and per-session telemetry
Instrument bytes-per-session and set a soft budget that warns (or defers to Wi-Fi) when a session is about to spend a lot on cellular. You can't fix what you can't see; make data spend a first-class metric.
Verify it worked
- A large upload on the metered device prompts for cellular, streams with flat memory, and resumes after the connection drops.
- The proxy shows no unexpected background traffic from SDKs on cellular.
- Per-session data telemetry is emitted and a budget breach triggers the warning path.
If it goes wrong — rollback
- Feature-flag uploads/large downloads off and ship a hotfix that disables cellular transfers entirely while you fix the root cause.
- Fall back to Wi-Fi-only for the affected feature until the streaming/resumable path is verified.