From 5fbeb83a66c6c76acce6d9ff1eac6063aff22c28 Mon Sep 17 00:00:00 2001 From: Chris Dell Date: Thu, 19 Nov 2015 00:20:09 +0000 Subject: [PATCH] iOS downloadFile implementation now async --- Downloader.h | 10 +++++++ Downloader.m | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ RNFSManager.m | 21 ++++++------- 3 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 Downloader.h create mode 100644 Downloader.m diff --git a/Downloader.h b/Downloader.h new file mode 100644 index 0000000..4289508 --- /dev/null +++ b/Downloader.h @@ -0,0 +1,10 @@ +#import + +typedef void (^ErrorCallback)(NSError*); +typedef void (^DownloaderCallback)(NSNumber*, NSNumber*, NSNumber*); + +@interface Downloader : NSObject + +- (void)downloadFile:(NSString*)urlStr toFile:(NSString*)downloadPath callback:(DownloaderCallback)callback errorCallback:(ErrorCallback)errorCallback progressCallback:(DownloaderCallback)progressCallback; + +@end diff --git a/Downloader.m b/Downloader.m new file mode 100644 index 0000000..4c25cd1 --- /dev/null +++ b/Downloader.m @@ -0,0 +1,83 @@ +#import "Downloader.h" + +@interface Downloader() + +@property (copy) DownloaderCallback callback; +@property (copy) ErrorCallback errorCallback; +@property (copy) DownloaderCallback progressCallback; + +@property (retain) NSNumber* statusCode; +@property (retain) NSNumber* contentLength; +@property (retain) NSNumber* bytesWritten; + +@property (retain) NSFileHandle* fileHandle; + +@end + +@implementation Downloader + +- (void)downloadFile:(NSString*)urlStr toFile:(NSString*)downloadPath callback:(DownloaderCallback)callback errorCallback:(ErrorCallback)errorCallback progressCallback:(DownloaderCallback)progressCallback +{ + _callback = callback; + _errorCallback = errorCallback; + _progressCallback = progressCallback; + + _bytesWritten = 0; + + NSURL* url = [NSURL URLWithString:urlStr]; + + NSMutableURLRequest* downloadRequest = [NSMutableURLRequest requestWithURL:url + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:30]; + + [[NSFileManager defaultManager] createFileAtPath:downloadPath contents:nil attributes:nil]; + + _fileHandle = [NSFileHandle fileHandleForWritingAtPath:downloadPath]; + + if (!_fileHandle) { + NSError* error = [NSError errorWithDomain:@"Downloader" code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to create target file at path: %@", downloadPath]}]; + + return _errorCallback(error); + } + + NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self startImmediately:NO]; + + [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + + [connection start]; +} + +- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error +{ + [_fileHandle closeFile]; + + return _errorCallback(error); +} + +- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response +{ + NSHTTPURLResponse* httpUrlResponse = (NSHTTPURLResponse*)response; + + _statusCode = [NSNumber numberWithLong:httpUrlResponse.statusCode]; + _contentLength = [NSNumber numberWithLong: httpUrlResponse.expectedContentLength]; +} + +- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data +{ + if ([_statusCode isEqualToNumber:[NSNumber numberWithInt:200]]) { + [_fileHandle writeData:data]; + + _bytesWritten = [NSNumber numberWithUnsignedInteger:[_bytesWritten unsignedIntegerValue] + data.length]; + + return _progressCallback(_statusCode, _contentLength, _bytesWritten); + } +} + +- (void)connectionDidFinishLoading:(NSURLConnection*)connection +{ + [_fileHandle closeFile]; + + return _callback(_statusCode, _contentLength, _bytesWritten); +} + +@end diff --git a/RNFSManager.m b/RNFSManager.m index 3846702..5bfc069 100644 --- a/RNFSManager.m +++ b/RNFSManager.m @@ -9,6 +9,7 @@ #import "RNFSManager.h" #import "RCTBridge.h" #import "NSArray+Map.h" +#import "Downloader.h" @implementation RNFSManager @@ -142,22 +143,22 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *)urlStr filepath:(NSString *)filepath callback:(RCTResponseSenderBlock)callback) { - NSError *error = nil; - NSURL *url = [NSURL URLWithString:urlStr]; - NSData *urlData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error]; + DownloaderCallback downloaderSuccessCallback = ^(NSNumber* statusCode, NSNumber* contentLength, NSNumber* bytesWritten) { + return callback(@[[NSNull null], [NSNumber numberWithBool:YES], filepath]); + }; - if (error) { + ErrorCallback downloaderErrorCallback = ^(NSError* error) { return callback([self makeErrorPayload:error]); - } + }; - BOOL success = [urlData writeToFile:filepath atomically:YES]; + DownloaderCallback downloaderProgressCallback = ^(NSNumber* statusCode, NSNumber* contentLength, NSNumber* bytesWritten) { - if (!success) { - return callback(@[[NSString stringWithFormat:@"Could not write downloaded data to file at path %@", filepath]]); - } + }; - callback(@[[NSNull null], [NSNumber numberWithBool:success], filepath]); + Downloader* downloader = [Downloader alloc]; + + [downloader downloadFile:urlStr toFile:filepath callback:downloaderSuccessCallback errorCallback:downloaderErrorCallback progressCallback:downloaderProgressCallback]; } RCT_EXPORT_METHOD(pathForBundle:(NSString *)bundleNamed