Skip to content

[RNE Rewrite] Integrate resource fetcher (react-native-blob-util) - #1328

Open
msluszniak wants to merge 5 commits into
rne-rewritefrom
@ms/resource-fetcher
Open

[RNE Rewrite] Integrate resource fetcher (react-native-blob-util)#1328
msluszniak wants to merge 5 commits into
rne-rewritefrom
@ms/resource-fetcher

Conversation

@msluszniak

@msluszniak msluszniak commented Jul 24, 2026

Copy link
Copy Markdown
Member

Description

Adds resource fetching to the rewrite as a single react-native-blob-util-backed fetcher inside react-native-executorch (no separate expo/bare packages). Public download(source | source[], { onProgress, signal }) returns local path(s); local paths pass through untouched. Every create<Task> factory now resolves remote model URLs internally, so create<Task>(models.X) works directly and the use<Task> hooks (which pre-download for progress) hit an instant no-op. HF download-counter + download-event telemetry are bundled and fired once per genuine fetch. Replaces the temporary react-native-fs hook.

Backend is split by platform: Android uses the system DownloadManager (reliable for files >2 GB, continues in the background, OS-managed resume), iOS streams via NSURLSession with .partial/HTTP-Range resume.

Introduces a breaking change?

  • Yes
  • No

Type of change

  • Bug fix (change which fixes an issue)
  • New feature (change which adds functionality)
  • Documentation update (improves or adds clarity to existing documentation)
  • Other (chores, tests, code style improvements etc.)

Tested on

  • iOS
  • Android

Testing instructions

  1. Run the computer-vision example app.
  2. Open Classification, pick an XNNPACK model — it downloads with progress, then loads and runs.
  3. Reopen the screen — the model is served from cache instantly (no re-download).
  4. Imperative: createClassifier(models.classification.EFFICIENTNET_V2_S.XNNPACK_FP32) resolves the URL to a cached path internally.

Screenshots

Related issues

Closes #1253

Checklist

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly
  • My changes generate no new warnings

Additional notes

Replace the temporary react-native-fs download hook with a single
blob-util-backed fetcher living in react-native-executorch (no separate
expo/bare packages).

- src/fetcher: imperative `download(source | source[], { onProgress, signal })`
  returning local path(s); persistent DocumentDir cache keyed by URL hash.
- HTTP-Range auto-resume of interrupted downloads via a .partial file, with a
  safe fallback to a fresh full download if partial assembly fails.
- Byte-weighted unified progress across multiple files; AbortSignal cancel that
  preserves bytes for later resume.
- Bundled download telemetry (HF download counter + anonymous download event),
  fired once per genuine, non-cached fetch.
- Rewire useResourceDownload + inspectModel onto the new fetcher; swap the
  react-native-fs dependency for react-native-blob-util in the lib and example
  apps.

Closes #1253
…tories

Every create<Task> factory now runs its model/tokenizer path fields through
the fetcher's download() before loading, so remote URLs are transparently
downloaded and cached. Because download() passes local paths through
untouched, the use<Task> hooks (which still pre-download for progress) hit an
instant no-op — no double download, single telemetry event.

- Wrap each loadModel/loadTokenizer arg with download() across all 12 factories
  (single modelPath, plus tokenizerPath and nested vadModel for the multi-file
  tasks). Whisper delegates VAD resolution to the FSMN factory.
- Add a tuple-preserving download() overload so download([a, b]) resolves to
  [string, string] under noUncheckedIndexedAccess.
- Imperative create<Task>(models.X) now works without an explicit download().
…large downloads

On-device testing revealed react-native-blob-util's in-process streaming
download is broken on modern Android (a 0.24.10 regression, upstream #475):
it aborts after 8 KB with "Download interrupted", so every model download
failed. curl and the system DownloadManager fetch the same URLs fine.

Split the fetcher backend by platform:
- Android: route through blob-util's system DownloadManager. It reliably
  handles files >2 GB (the whole point — LLM .pte files exceed the OkHttp
  2 GB in-process limit), continues in the background / across app kill, and
  resumes transient network drops itself. Downloads stage in the app-private
  external files dir so DownloadManager can write there and the move into the
  cache stays on one volume (no multi-GB cross-filesystem copy).
- iOS: keep the NSURLSession streaming path with .partial/HTTP-Range resume
  (unaffected by the Android regression, no 2 GB limit).

Verified on a physical Android device: byte-weighted progress, cache-hit
short-circuit, and create<Task>(models.X) URL resolution all pass end to end.
@msluszniak
msluszniak marked this pull request as draft July 24, 2026 16:12
@msluszniak msluszniak self-assigned this Jul 25, 2026
@msluszniak msluszniak added refactoring feature PRs that implement a new feature labels Jul 25, 2026
@msluszniak msluszniak linked an issue Jul 25, 2026 that may be closed by this pull request
@msluszniak
msluszniak marked this pull request as ready for review July 25, 2026 09:58
Analytics stay enabled by default; setTelemetryEnabled(false) opts out of the
anonymous download-event POST to Software Mansion. The Hugging Face download
counter is unaffected and always fires.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature PRs that implement a new feature refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RNE Rewrite] Integrate ResourceFetcher with refactor

1 participant