Skip to content

Uploading large video files causes Jetsam to kill the app #386

@ericberman

Description

@ericberman

From CoPilot - should switch to using background URL Session
Background URLSession is one of the few parts of iOS that behaves like a different universe compared to normal networking. And for your use case—huge video uploads—it’s exactly the universe you want to live in.

Let’s unpack it in a way that makes sense for someone who’s been hand‑rolling MIME posts.


🚚 What makes a background URLSession special

A background session (URLSessionConfiguration.background) is not just “a URLSession that works in the background.” It’s a system‑managed upload engine with these superpowers:

1. Upload continues even if your app is killed

  • Jetsam kills
  • User swipes up
  • Device reboots
  • App crashes

The upload keeps going because the system process, not your app, owns the socket and the file I/O.

This alone would have prevented your entire restart‑resume‑Jetsam loop.


2. No file‑backed dirtying in your process

This is the big one.

When you stream a huge MIME upload manually, you dirty file‑backed pages continuously:

  • reading chunks
  • encoding boundaries
  • writing to buffers
  • retrying
  • re‑seeking

iOS sees this as “your app is slowly dirtying memory for hours” → Jetsam.

A background session avoids this because:

  • The system reads the file directly from disk
  • Your app never touches the bytes
  • No dirty pages accumulate in your process

This is why Apple pushes background sessions so hard for large uploads.


3. The system handles retries, backoff, and network transitions

You get:

  • automatic exponential backoff
  • automatic resume after network loss
  • automatic resume after reboot
  • automatic handling of Wi‑Fi → cellular transitions

All without you writing retry logic.


4. You don’t stream the body—you give the system a file

This is the biggest architectural shift for you.

Instead of:

  • building MIME boundaries
  • chunking
  • streaming
  • retrying
  • resuming

You hand iOS a file URL and say:

“Upload this file to that endpoint.”

iOS handles the rest.


🧱 What your code looks like with background upload

1. Create a background session

let config = URLSessionConfiguration.background(withIdentifier: "com.yourapp.uploads")
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)

2. Create a request

var request = URLRequest(url: uploadURL)
request.httpMethod = "POST"
request.setValue("video/mp4", forHTTPHeaderField: "Content-Type")

3. Provide a file URL

let task = session.uploadTask(with: request, fromFile: localVideoURL)
task.resume()

That’s it.

No MIME boundaries.
No chunking.
No streaming.
No retry loops.
No dirtying memory.
No Jetsam.


🧩 “But I need a multipart MIME upload”

You have two options:

Option A: Pre‑build the multipart file on disk

You generate the MIME body once, write it to a temporary file, and hand that file to the background session.

This is the most common pattern.

Option B: Switch your server to accept raw uploads

If you control the server, this is even better.

Let the client upload the raw video file, and send metadata separately.

This is how:

  • iCloud
  • Google Photos
  • Dropbox
  • OneDrive

…all do it.

Multipart uploads are rarely worth the pain for large files.


🧨 Why your current approach caused Jetsam

Your hand‑rolled MIME upload likely did this:

  • read a chunk
  • encode it
  • write to a buffer
  • send it
  • repeat for hours

Each chunk dirtied a few KB of file‑backed memory.
Over 20 hours, that added up to 1.1 GB of dirty pages.

iOS has a rolling 24‑hour dirty‑page budget.
You exceeded it.
Jetsam killed you.
Your app restarted and resumed the upload.
Jetsam killed you again.
Repeat 10 times.

Background sessions avoid this entire class of failure.


🎯 The short version

If you’re uploading anything bigger than a few MB, background URLSession is the correct tool. It:

  • moves the upload out of your process
  • prevents dirty‑memory Jetsam kills
  • handles retries and resumes
  • survives crashes and reboots
  • eliminates the need for manual MIME streaming

It’s the only way to make large uploads truly resilient on iOS.


If you want, you can tell me how your current MIME upload is structured—streaming? chunked? single giant Data?—and I can show you the cleanest migration path to a background session.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions