8.7 KiB
Download System Refactor - Summary
Overview
The download system has been completely refactored to use a new native iOS module for background downloads, replacing the third-party library with a custom, streamlined solution.
What Was Done
1. Created New Native Module (BackgroundDownloader)
Location: modules/background-downloader/
A complete Expo native module for iOS background file downloads:
-
Swift Implementation:
BackgroundDownloaderModule.swift- Uses
NSURLSessionwith background configuration - Implements
URLSessionDownloadDelegatefor progress tracking - Handles iOS background session events via AppDelegate subscriber
- Session ID:
com.fredrikburmester.streamyfin.backgrounddownloader
- Uses
-
TypeScript Interface: Full type-safe API with events
startDownload(url, destinationPath?)- Start a downloadcancelDownload(taskId)- Cancel a specific downloadcancelAllDownloads()- Cancel all downloadsgetActiveDownloads()- List active downloads
-
Events:
onDownloadProgress- Progress updates with bytes/percentageonDownloadComplete- Completion with file pathonDownloadError- Error handlingonDownloadStarted- Download initiation confirmation
-
Documentation: Complete README with usage examples
2. Rewrote DownloadProvider
Location: providers/DownloadProvider.tsx
A simplified, focused implementation:
Features Included:
- ✅ Video file downloads with progress tracking
- ✅ Background download support
- ✅ Database persistence (same format as before)
- ✅ Movies and TV episodes support
- ✅ Download notifications (success/error)
- ✅ File deletion and management
- ✅ Size calculation
- ✅ Same context API for backward compatibility
Features Removed (for simplicity):
- ❌ Trickplay image downloads
- ❌ Subtitle downloads
- ❌ Queue management with concurrent limits
- ❌ Pause/Resume (can be added back easily)
- ❌ Download speed/ETA calculations
- ❌ Cache directory management
Key Improvements:
- Event-driven architecture (no polling)
- Better background handling via native module
- Cleaner, more maintainable code
- Proper TypeScript typing throughout
- Simplified state management
3. Preserved Old Implementation
Location: providers/DownloadProvider.deprecated.tsx
The old implementation has been preserved for reference but should not be used.
4. Documentation
Created comprehensive documentation:
-
Module README:
modules/background-downloader/README.md- API reference
- Usage examples
- Implementation details
-
Migration Guide:
providers/Downloads/MIGRATION.md- What changed
- API compatibility matrix
- Migration steps
- Troubleshooting guide
-
System README:
providers/Downloads/README.md- Architecture overview
- Type definitions
- Usage examples
- File storage details
Technical Details
Background Download Implementation
The native module uses iOS's recommended approach:
- Background URLSession: Persistent identifier ensures downloads continue
- Delegate Pattern: Progress and completion via delegate callbacks
- AppDelegate Integration: Handles system wake-ups for download events
- Completion Handler: Properly signals iOS when background work is done
State Management
// Active downloads tracked in Jotai atom
const processesAtom = atom<JobStatus[]>([]);
// Task ID to Process ID mapping for event correlation
const taskMap = Map<number, string>();
// Persistent database in MMKV
storage.set('downloads.v2.json', JSON.stringify(database));
Download Flow
User initiates download
↓
Create JobStatus & add to processes
↓
Generate destination path
↓
Start native BackgroundDownloader
↓
Map taskId ↔ processId
↓
Receive progress events → Update UI
↓
Receive completion event
↓
Move file to permanent location
↓
Save to database
↓
Send notification
↓
Clean up process
Configuration
iOS Background Modes
Already configured in app.json:
{
"ios": {
"infoPlist": {
"UIBackgroundModes": ["audio", "fetch"]
}
}
}
The "fetch" mode enables background URLSession downloads.
Module Integration
The module is:
- ✅ Auto-linked via Expo
- ✅ Exported from
modules/index.ts - ✅ Type-safe with full TypeScript support
- ✅ Registered as AppDelegate subscriber
Breaking Changes
None for Normal Usage
The public API remains the same for the most common operations:
const {
startBackgroundDownload,
cancelDownload,
getDownloadedItems,
deleteFile,
processes
} = useDownload();
No-op Methods
These methods exist but do nothing (for compatibility):
pauseDownload()resumeDownload()startDownload()(usestartBackgroundDownloadinstead)deleteFileByType()cleanCacheDirectory()updateDownloadedItem()dumpDownloadDiagnostics()
Testing Checklist
Before deployment, test:
- Download a movie
- Download a TV episode
- Download multiple items simultaneously
- Cancel an active download
- Delete a downloaded item
- View list of downloads
- Background the app during download
- Force quit and restart (download should be cancelled)
- Verify notifications appear
- Check file integrity and playback
- Verify database persistence
- Check storage calculations
Next Steps
Immediate
-
Rebuild iOS app:
npx expo prebuild -p ios cd ios && pod install && cd .. npx expo run:ios -
Test thoroughly: Use the testing checklist above
-
Monitor for issues: Check console logs and user reports
Future Enhancements
Priority features to add back:
-
Pause/Resume (High Priority)
- Easy to implement with NSURLSession
- User-requested feature
-
Queue Management (Medium Priority)
- Concurrent download limits
- Automatic queue processing
-
Progress Persistence (Medium Priority)
- Resume interrupted downloads after app restart
- Save download state to database
-
Trickplay & Subtitles (Low Priority)
- Re-add auxiliary file downloads
- Integrate with video playback
-
Download Analytics (Low Priority)
- Speed calculation
- ETA estimation
- Failure rate tracking
Known Limitations
- iOS Only: Android support not yet implemented
- No Pause: Cannot pause/resume downloads yet
- No Queue: All downloads start immediately
- Force Quit: Downloads cancelled if app is force-quit (iOS limitation)
- No Persistence: Downloads lost if app crashes or is terminated
Performance Improvements
Over the old system:
- Better Background Support: True iOS background sessions
- Event-Driven: No polling, immediate updates
- Lower Overhead: Removed unused features
- Native Integration: Tighter iOS system integration
- Cleaner Code: Easier to maintain and extend
Dependencies Changed
Removed
@kesha-antonov/react-native-background-downloader
Added
- Custom
BackgroundDownloadernative module (local)
No external dependencies added, reducing maintenance burden.
File Changes Summary
New Files
modules/background-downloader/(entire module)providers/Downloads/MIGRATION.mdproviders/Downloads/README.mdDOWNLOAD_SYSTEM_REFACTOR.md(this file)
Modified Files
modules/index.ts(exported new module)providers/DownloadProvider.tsx(complete rewrite)
Renamed Files
providers/DownloadProvider.tsx→providers/DownloadProvider.deprecated.tsx
Unchanged
providers/Downloads/types.ts(types remain the same)- Database format and storage location
- Public API for most common operations
Rollback Plan
If issues arise:
- Rename
DownloadProvider.deprecated.tsxback toDownloadProvider.tsx - Remove the
background-downloadermodule export frommodules/index.ts - Re-install
@kesha-antonov/react-native-background-downloader - Rebuild the app
Note: The database format is unchanged, so existing downloads will work with either version.
Success Metrics
The refactor is successful if:
- ✅ Downloads work in foreground
- ✅ Downloads continue in background
- ✅ Progress updates are accurate
- ✅ Notifications work correctly
- ✅ Files are saved and playable
- ✅ No crashes or memory leaks
- ✅ Performance is equal or better
- ✅ Code is cleaner and more maintainable
Conclusion
This refactor provides a solid foundation for the download system moving forward. The native module approach gives us full control over the download experience and makes it easier to add features in the future.
The simplified DownloadProvider focuses on core functionality while maintaining API compatibility, making this a low-risk, high-reward change.