better background downloads
This commit is contained in:
parent
dcbd64a59e
commit
f58f726d8e
|
@ -24,7 +24,7 @@ typedef void (^ResumableCallback)();
|
||||||
|
|
||||||
@interface RNFSDownloader : NSObject <NSURLSessionDelegate, NSURLSessionDownloadDelegate>
|
@interface RNFSDownloader : NSObject <NSURLSessionDelegate, NSURLSessionDownloadDelegate>
|
||||||
|
|
||||||
- (void)downloadFile:(RNFSDownloadParams*)params;
|
- (NSString *)downloadFile:(RNFSDownloadParams*)params;
|
||||||
- (void)stopDownload;
|
- (void)stopDownload;
|
||||||
- (void)resumeDownload;
|
- (void)resumeDownload;
|
||||||
- (BOOL)isResumable;
|
- (BOOL)isResumable;
|
||||||
|
|
13
Downloader.m
13
Downloader.m
|
@ -22,9 +22,11 @@
|
||||||
|
|
||||||
@implementation RNFSDownloader
|
@implementation RNFSDownloader
|
||||||
|
|
||||||
- (void)downloadFile:(RNFSDownloadParams*)params
|
- (NSString *)downloadFile:(RNFSDownloadParams*)params
|
||||||
{
|
{
|
||||||
_params = params;
|
NSString *uuid = nil;
|
||||||
|
|
||||||
|
_params = params;
|
||||||
|
|
||||||
_bytesWritten = 0;
|
_bytesWritten = 0;
|
||||||
|
|
||||||
|
@ -37,14 +39,15 @@
|
||||||
NSError* error = [NSError errorWithDomain:@"Downloader" code:NSURLErrorFileDoesNotExist
|
NSError* error = [NSError errorWithDomain:@"Downloader" code:NSURLErrorFileDoesNotExist
|
||||||
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to create target file at path: %@", _params.toFile]}];
|
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to create target file at path: %@", _params.toFile]}];
|
||||||
|
|
||||||
return _params.errorCallback(error);
|
_params.errorCallback(error);
|
||||||
|
return nil;
|
||||||
} else {
|
} else {
|
||||||
[_fileHandle closeFile];
|
[_fileHandle closeFile];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSURLSessionConfiguration *config;
|
NSURLSessionConfiguration *config;
|
||||||
if (_params.background) {
|
if (_params.background) {
|
||||||
NSString *uuid = [[NSUUID UUID] UUIDString];
|
uuid = [[NSUUID UUID] UUIDString];
|
||||||
config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:uuid];
|
config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:uuid];
|
||||||
} else {
|
} else {
|
||||||
config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
@ -55,6 +58,8 @@
|
||||||
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
||||||
_task = [_session downloadTaskWithURL:url];
|
_task = [_session downloadTaskWithURL:url];
|
||||||
[_task resume];
|
[_task resume];
|
||||||
|
|
||||||
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
|
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
|
||||||
|
|
|
@ -224,6 +224,10 @@ var RNFS = {
|
||||||
RNFSManager.stopUpload(jobId);
|
RNFSManager.stopUpload(jobId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
completeHandlerIOS(jobId: number): void {
|
||||||
|
RNFSManager.completeHandlerIOS(jobId);
|
||||||
|
},
|
||||||
|
|
||||||
readDir(dirpath: string): Promise<ReadDirItem[]> {
|
readDir(dirpath: string): Promise<ReadDirItem[]> {
|
||||||
return readDirGeneric(dirpath, RNFSManager.readDir);
|
return readDirGeneric(dirpath, RNFSManager.readDir);
|
||||||
},
|
},
|
||||||
|
|
41
README.md
41
README.md
|
@ -503,11 +503,7 @@ Use it for performance issues.
|
||||||
If `progressDivider` = 0, you will receive all `progressCallback` calls, default value is 0.
|
If `progressDivider` = 0, you will receive all `progressCallback` calls, default value is 0.
|
||||||
|
|
||||||
(IOS only): `options.background` (`Boolean`) - Whether to continue downloads when the app is not focused (default: `false`)
|
(IOS only): `options.background` (`Boolean`) - Whether to continue downloads when the app is not focused (default: `false`)
|
||||||
This option is currently only available for iOS, and you must [enable
|
This option is currently only available for iOS, see the [Background Downloads Tutorial (iOS)](#background-downloads-tutorial-ios) section.
|
||||||
background fetch](https://www.objc.io/issues/5-ios7/multitasking/#background-fetch<Paste>)
|
|
||||||
for your project in XCode. You only need to enable background fetch in `Info.plist` and set
|
|
||||||
the fetch interval in `didFinishLaunchingWithOptions`. The `performFetchWithCompletionHandler`
|
|
||||||
callback is handled by RNFS.
|
|
||||||
|
|
||||||
(IOS only): If `options.resumable` is provided, it will be invoked when the download has stopped and and can be resumed using `resumeDownload()`.
|
(IOS only): If `options.resumable` is provided, it will be invoked when the download has stopped and and can be resumed using `resumeDownload()`.
|
||||||
|
|
||||||
|
@ -531,6 +527,12 @@ if (await RNFS.isResumable(jobId) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### (iOS only) `completeHandlerIOS(jobId: number): void`
|
||||||
|
|
||||||
|
For use when using background downloads, tell iOS you are done handling a completed download.
|
||||||
|
|
||||||
|
Read more about background donwloads in the [Background Downloads Tutorial (iOS)](#background-downloads-tutorial-ios) section.
|
||||||
|
|
||||||
### (iOS only) `uploadFiles(options: UploadFileOptions): { jobId: number, promise: Promise<UploadResult> }`
|
### (iOS only) `uploadFiles(options: UploadFileOptions): { jobId: number, promise: Promise<UploadResult> }`
|
||||||
|
|
||||||
`options` (`Object`) - An object containing named parameters
|
`options` (`Object`) - An object containing named parameters
|
||||||
|
@ -613,6 +615,35 @@ Invalid group identifier will cause a rejection.
|
||||||
|
|
||||||
For more information read the [Adding an App to an App Group](https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW19) section.
|
For more information read the [Adding an App to an App Group](https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW19) section.
|
||||||
|
|
||||||
|
## Background Downloads Tutorial (iOS)
|
||||||
|
|
||||||
|
Background downloads in iOS require a bit of a setup.
|
||||||
|
|
||||||
|
First, in your `AppDelegate.m` file add the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
#import <RNFSManager.h>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
|
||||||
|
{
|
||||||
|
[RNFSManager setCompletionHandlerForIdentifier:identifier completionHandler:completionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The `handleEventsForBackgroundURLSession` method is called when a background download is done and your app is not in the foreground.
|
||||||
|
|
||||||
|
We need to pass the `completionHandler` to RNFS along with its `identifier`.
|
||||||
|
|
||||||
|
The JavaScript will continue to work as usual when the download is done but now you must call `RNFS.completeHandlerIOS(jobId)` when you're done handling the download (show a notification etc.)
|
||||||
|
|
||||||
|
**BE AWARE!** iOS will give about 30 sec. to run your code after `handleEventsForBackgroundURLSession` is called and until `completionHandler`
|
||||||
|
is triggered so don't do anything that might take a long time (like unzipping), you will be able to do it after the user re-launces the app,
|
||||||
|
otherwide iOS will terminate your app.
|
||||||
|
|
||||||
|
|
||||||
## Test / Demo app
|
## Test / Demo app
|
||||||
|
|
||||||
Test app to demostrate the use of the module. Useful for testing and developing the module:
|
Test app to demostrate the use of the module. Useful for testing and developing the module:
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
#import <React/RCTLog.h>
|
#import <React/RCTLog.h>
|
||||||
|
|
||||||
|
typedef void (^CompletionHandler)();
|
||||||
|
|
||||||
@interface RNFSManager : NSObject <RCTBridgeModule>
|
@interface RNFSManager : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
|
+(void)setCompletionHandlerForIdentifier: (NSString *)identifier completionHandler: (CompletionHandler)completionHandler;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -23,12 +23,15 @@
|
||||||
@interface RNFSManager()
|
@interface RNFSManager()
|
||||||
|
|
||||||
@property (retain) NSMutableDictionary* downloaders;
|
@property (retain) NSMutableDictionary* downloaders;
|
||||||
|
@property (retain) NSMutableDictionary* uuids;
|
||||||
@property (retain) NSMutableDictionary* uploaders;
|
@property (retain) NSMutableDictionary* uploaders;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RNFSManager
|
@implementation RNFSManager
|
||||||
|
|
||||||
|
static NSMutableDictionary *completionHandlers;
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
RCT_EXPORT_MODULE();
|
RCT_EXPORT_MODULE();
|
||||||
|
@ -463,9 +466,14 @@ RCT_EXPORT_METHOD(downloadFile:(NSDictionary *)options
|
||||||
|
|
||||||
RNFSDownloader* downloader = [RNFSDownloader alloc];
|
RNFSDownloader* downloader = [RNFSDownloader alloc];
|
||||||
|
|
||||||
[downloader downloadFile:params];
|
NSString *uuid = [downloader downloadFile:params];
|
||||||
|
|
||||||
[self.downloaders setValue:downloader forKey:[jobId stringValue]];
|
[self.downloaders setValue:downloader forKey:[jobId stringValue]];
|
||||||
|
if (uuid) {
|
||||||
|
NSLog(@"setting uuid: %@", uuid);
|
||||||
|
if (!self.uuids) self.uuids = [[NSMutableDictionary alloc] init];
|
||||||
|
[self.uuids setValue:uuid forKey:[jobId stringValue]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(stopDownload:(nonnull NSNumber *)jobId)
|
RCT_EXPORT_METHOD(stopDownload:(nonnull NSNumber *)jobId)
|
||||||
|
@ -500,6 +508,19 @@ RCT_EXPORT_METHOD(isResumable:(nonnull NSNumber *)jobId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(completeHandlerIOS:(nonnull NSNumber *)jobId)
|
||||||
|
{
|
||||||
|
if (self.uuids) {
|
||||||
|
NSString *uuid = [self.uuids objectForKey:[jobId stringValue]];
|
||||||
|
CompletionHandler completionHandler = [completionHandlers objectForKey:uuid];
|
||||||
|
if (completionHandler) {
|
||||||
|
NSLog(@"Calling completion handler on: %@", uuid);
|
||||||
|
completionHandler();
|
||||||
|
[completionHandlers removeObjectForKey:uuid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(uploadFiles:(NSDictionary *)options
|
RCT_EXPORT_METHOD(uploadFiles:(NSDictionary *)options
|
||||||
resolver:(RCTPromiseResolveBlock)resolve
|
resolver:(RCTPromiseResolveBlock)resolve
|
||||||
rejecter:(RCTPromiseRejectBlock)reject)
|
rejecter:(RCTPromiseRejectBlock)reject)
|
||||||
|
@ -773,4 +794,10 @@ RCT_EXPORT_METHOD(touch:(NSString*)filepath
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+(void)setCompletionHandlerForIdentifier: (NSString *)identifier completionHandler: (CompletionHandler)completionHandler
|
||||||
|
{
|
||||||
|
if (!completionHandlers) completionHandlers = [[NSMutableDictionary alloc] init];
|
||||||
|
[completionHandlers setValue:completionHandler forKey:identifier];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue