Skip to content
scroll to zoom · drag to pan

Mobile — React Native and Expo

The mobile path is not “the web path in a WebView”. Two subpaths exist because a phone has constraints a browser does not.

import { NitidaClient } from "@nitida/sdk/server";
import { createExpoUploader } from "@nitida/sdk/expo";
const task = createExpoUploader(client, {
uri: asset.uri, // from expo-image-picker
fileName: "photo.jpg",
partSize: 8 * 1024 * 1024,
concurrency: 2,
});

createExpoUploader inherits the client’s endpoint, key and tenant, so the call site only says what is being uploaded and how hard to push.

The transfer is delegated to a native background session — URLSession on iOS, WorkManager on Android. That matters because the upload then survives:

  • the user backgrounding the app,
  • the screen locking,
  • the OS suspending your JS runtime.

A JS-driven upload survives none of those. On a 100 MB video over cellular, that is the difference between a feature and a support ticket.

import { compressImage } from "@nitida/sdk/native";

A thin pass-through to the native compressor, with the same JS API as the web one, so call sites are identical across web and React Native. That is the whole design goal: shared upload code, different engine underneath.

The concurrency default is 1, and leave it there

Section titled “The concurrency default is 1, and leave it there”

Mobile batches sequentially. A phone running four decode-resize-encode pipelines at once does not finish sooner — low-end Android runs out of memory and the OS kills the process. Desktop defaults to 4 for the same reason inverted.

iPhones produce HEIC by default. It decodes natively in ~136 ms where the engine supports it, falling back to a 341 kB WASM decoder only where it does not — so most users never fetch the decoder.

Worth confirming on a real device rather than a simulator: the simulator’s engine is not the phone’s.