fix(ios): resume interrupted downloads instead of hanging, and show download progress - #45
Draft
BMichaelJ wants to merge 2 commits into
Conversation
On iOS, react-native-fs never settles downloadFile() when a transfer stops and iOS can produce resume data. It only calls the optional `resumable` callback, which we never passed. Azure Blob always supports resuming (ETag and byte ranges), so a dropped connection, a request timeout or a stopDownload left the Packs screen spinning forever. Android settles every transfer, which is why it never showed there. - Pass `resumable` and resume the transfer in place. After five interruptions the attempt fails, so the retry loop still takes over. - Wait for the foreground before starting a transfer or the pack lookup. Tokens are stored WHEN_UNLOCKED, and iOS treats transfers started in the background as discretionary. - Hand iOS its background-session completion handler back after each transfer. - Report 100% once the transfer completes, before the SHA-256 pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
"Download Latest Pack" fetches a 194.6 MB model and then a 217.5 MB pack behind a single spinner, so a slow link and a real stall looked the same. Show the stage, the percentage and megabytes, and a hint to keep the app open. The stage line is the live region, so screen readers hear stage changes rather than a new percentage every second. The screen is shared, so Android shows the same text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #42: please review and merge that first. This PR's base is #42's branch, and it retargets to
wildlife-reidwhen #42 merges.#42 bounds the iOS download with a watchdog and a background session, and notes that the download fix was not yet confirmed end to end. This PR finds why the iOS transfer itself hangs, fixes it, and makes the long download visible.
Root cause: react-native-fs never settles an interrupted iOS transfer
In react-native-fs 2.20.0,
Downloader.mhandles a stopped transfer inURLSession:task:didCompleteWithError:andstopDownload. When iOS can produce resume data, both only call the optionalresumablecallback. They never resolve or reject thedownloadFile()promise, and anNSURLErrorCancellederror is dropped entirely.We never passed
resumable. Azure Blob always supports resuming: I checked the pack blob, which returns a strong ETag andAccept-Ranges: bytes. So practically any interruption left the promise pending forever, and that is the endless spinner in the report. Interruptions include a request timeout, a dropped connection, the app being suspended, andstopDownload. It is the same class of bug as itinance/react-native-fs#568, open since 2018. Android's implementation always settles, which is why Android never showed it.With #42 alone, the watchdog turns the hang into a 60 s timeout and a restart from byte zero, up to three times, with no progress shown.
Changes
resumableand callRNFS.resumeDownload(jobId), so an interrupted transfer continues from where it stopped. After 5 interruptions in one attempt, the attempt fails into the existing retry loop. A transfer stopped on purpose (watchdog or abort) is never resumed.WHEN_UNLOCKEDand the call reports "Not signed in". A transfer started in the background is also discretionary. On iOS only,waitForForeground()holds the next transfer and the pack lookup until the app is active.RNFS.completeHandlerIOS(jobId)runs after each transfer, so iOS gets back the handler thatAppDelegatepasses to RNFS.fileDownloadService/nativeDownload.ts.PacksScreen's status and button-title ternaries became lookup tables with the same strings.Type of Change
Screenshots / Screen Recordings
Android (Pixel 9a, Android 17)
iOS
Not captured yet. It needs a device run (see the test plan below).
Checklist
General
Testing
npm test)Android: Pixel 9a, Android 17, debug build of this branch. From a fresh install, I signed in and tapped Download Latest Pack. The model went from 0 to 100%, then the pack, and it installed with "Up to date" in about 87 s. I also tested the update path with airplane mode switched on mid-pack; see the Android note below.
The JS suite passes (70 suites, 1,067 tests), along with
tsc --noEmitand eslint. The Android and iOS native suites thatnpm testalso chains were not run. Only dark mode was checked. No iOS device or Mac was available on this host.New tests cover:
React Native Specific
SPACING/TYPOGRAPHYconstants from the themeuseThemedStylespattern (not inline or staticStyleSheet.create)The rest of this section does not apply.
Performance & Models
/vs\\)Security
iOS test plan (@blclo)
This is a JS-only change. Check out the branch (
gh pr checkout <this PR> -R WildMeOrg/off-grid-mobile) and reload Metro on your dev build; no native rebuild orpod installis needed. A release build is the better final check.RNFS downloadwhile a download hangs. Expected:didCompleteWithError … NSURLSessionDownloadTaskResumeData, and nothing after it.Please report the device and iOS version, the network, pass/fail for each step, and the Console
RNFSlines for any failure.Related Issues
Stacked on #42. Android counterpart: #44, a failed download crashes the Android app.
Additional Notes
CFNetworkDownload_*.tmpfiles are not cleaned up.org.ganesha.elebookscheme. "Just once" on the top suggestion goes to the release app; pick "EleBook (Debug)".