- Remove outdated feature requests from .github/CONTRIBUTING.md (GitLab support already exists) and add commit message format section - Improve PR template with type-of-change checkboxes and checklist - Update commit guideline links in README and CONTRIBUTING.md to reference the project's own format instead of external angular.js docs - Add "only edit en.json" rule to TRANSLATING.md and clarify workflow - Update add-new-integration.md provider list to match codebase (add Trello, ClickUp, Linear, Azure DevOps, Nextcloud Deck; note GitHub plugin migration; fix type name to BuiltInIssueProviderKey) - Add cross-references between mac certificate docs and remove 240-line duplicate section from update-mac-certificates.md - Clean up update-android-app.md (specify npm version args, collapse deprecated workflow, translate German UI labels to English) - Add context to howto-refresh-snap-credentials.md - Fix fine-grained token note in github-access-token-instructions.md - Fix absolute URL to relative path in gitlab-access-token-instructions.md - Fix grammar in i18n-script-usage.md - Add status headers to all 19 long-term plan files (Planned, Completed, Archived with reason, Investigation Complete) - Fix broken relative link in hybrid-manifest-architecture.md - Delete supersync-scenarios-simplified.md (duplicate of supersync-scenarios.md; known issues already covered there) - Rename vector-clock-pruning-research.md to vector-clock-history-and-alternatives.md for clarity
6.3 KiB
iOS Dropbox Sync Reliability
Status: Investigation Complete — Fixes Pending
Issue: #6333 Severity: High — Dropbox sync is completely broken for some iOS users
Problem
Dropbox sync consistently fails on iOS with -1005 "The network connection was lost", while the web app works fine on the same device. Multiple users have confirmed the issue across different iOS versions and SP releases.
Root Cause Analysis (confidence: 60-65%)
Primary suspect: Capacitor uses URLSession.shared
Capacitor's iOS HTTP handler (CapacitorUrlRequest.swift) routes all requests through URLSession.shared — a singleton that:
- Cannot be invalidated or reconfigured
- Holds persistent HTTP/2 connections that can go stale
- Does not auto-retry POST requests (Dropbox uses POST for all API calls)
When connections go stale (from app backgrounding, server-side connection resets, or HTTP/2 lifecycle), URLSession.shared tries to reuse the dead connections, causing -1005.
Evidence from logs: First download succeeds, second download 19 seconds later fails with -1005 — classic stale connection reuse pattern.
Why web works: Browser fetch() runs through WKWebView's own networking stack, which handles reconnection more gracefully.
Known triggers
- App backgrounding (most common): User goes to Safari to get Dropbox auth code, iOS reclaims sockets while app is suspended (Apple TN2277)
- HTTP/2 connection lifecycle: Server closes connection between requests,
URLSession.sharedreuses it - Retry delays too short: Current 1s/2s delays don't give the connection pool time to flush
What we haven't confirmed
- No network-level packet capture during the failure
- Haven't reproduced on a test device
- Haven't verified that ephemeral sessions fix it
- Could be a different/additional cause
Sources
Apple official
- TN2277: Networking and Multitasking — socket reclamation during suspension
- QA1941: Handling "The network connection was lost" — official -1005 guidance
Apple Developer Forums
- Thread 777999: "URLSession.shared fails with -1005... things do work with an ephemeral session"
- Thread 84656: Quinn (Apple DTS) recommends retry as primary strategy
Capacitor issues (same bug)
- #6733: "iOS app switching causes network lost error"
- #7974: Recommends ephemeral session workaround
- #6789:
[NSURLSession sharedSession] may not be invalidated
Other frameworks
- Alamofire #872: Same -1005 after device lock — fix was recreating sessions
- AWS SDK iOS #4281: Same -1005 with S3 uploads
Dropbox-specific
- SwiftyDropbox SDK uses custom URLSession instances, never
URLSession.shared - Dropbox Forum: Same error reported with official SDK
Proposed Fixes
Fix 1: Patch Capacitor to use ephemeral sessions (highest impact)
Use patch-package to modify CapacitorUrlRequest.swift:
// Before (Capacitor 7.4.3+)
open func getUrlSession(_ call: CAPPluginCall) -> URLSession {
let disableRedirects = call.getBool("disableRedirects") ?? false
if !disableRedirects {
return URLSession.shared
}
return URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
}
// After
open func getUrlSession(_ call: CAPPluginCall) -> URLSession {
let disableRedirects = call.getBool("disableRedirects") ?? false
let config = URLSessionConfiguration.ephemeral
if !disableRedirects {
return URLSession(configuration: config)
}
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}
Tradeoffs:
- Cookies won't persist between sessions (not relevant for Dropbox — we manage tokens ourselves)
- Must re-apply patch on Capacitor upgrades
- Recommended by Capacitor community for #7974
Note: Capacitor 8 does NOT fix this — URLSession.shared usage is identical in v8.
Fix 2: Fix timeout bug (quick win)
Capacitor's Swift code: let timeout = (connectTimeout ?? readTimeout ?? 600000.0) / 1000.0
Our code passes both connectTimeout: 30000 and readTimeout: 120000. Since connectTimeout is set, readTimeout is silently ignored. All data transfers get a 30s timeout instead of the intended 120s.
Fix: Stop passing connectTimeout from native-http-retry.ts, or pass a single combined timeout.
Fix 3: Increase retry delays (quick win)
Current delays: 1s, 2s. Stale connections need more time to flush.
Fix: Increase to 3s, 6s (or similar). Consider whether MAX_RETRIES should also increase.
Fix 4: Add foreground-resume delay (medium effort)
Use Capacitor's App.addListener('appStateChange') to detect foreground resume and add a brief delay (500ms-1s) before allowing sync requests.
Fix 5: Background task assertion (medium effort, iOS-specific)
Use @capawesome/capacitor-background-task to request ~30s of background execution time during sync, preventing iOS from suspending the app mid-operation.
Implementation Order
- Fixes 2 + 3 — Quick wins, low risk, can ship immediately
- Fix 1 — Highest impact but requires
patch-packagesetup for iOS native code - Fix 4 — Good defense-in-depth
- Fix 5 — Nice-to-have, prevents a different failure mode (suspension during active sync)
Validation
- Reproduce the issue on a physical iOS device
- Apply fixes and verify sync works after app backgrounding
- Test the full Dropbox setup flow (Safari auth → paste code → sync)
- Verify no regressions on Android or web
- Ask issue reporters to test a beta build