diff --git a/ios/RNCAssetsLibraryRequestHandler.m b/ios/RNCAssetsLibraryRequestHandler.m index c1f1778c2..e205ed1ec 100644 --- a/ios/RNCAssetsLibraryRequestHandler.m +++ b/ios/RNCAssetsLibraryRequestHandler.m @@ -15,13 +15,14 @@ #import #import +#import #import @implementation RNCAssetsLibraryRequestHandler RCT_EXPORT_MODULE() -#pragma mark - RCTURLRequestHandler +#pragma mark - RNCURLRequestHandler - (BOOL)canHandleRequest:(NSURLRequest *)request { @@ -30,7 +31,8 @@ RCT_EXPORT_MODULE() } return [request.URL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame - || [request.URL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame; + || [request.URL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame + || [request.URL.scheme caseInsensitiveCompare:RCTNetworkingPHUploadHackScheme] == NSOrderedSame; } - (id)sendRequest:(NSURLRequest *)request @@ -40,8 +42,14 @@ RCT_EXPORT_MODULE() void (^cancellationBlock)(void) = ^{ atomic_store(&cancelled, YES); }; + + NSURL *requestURL = request.URL; + BOOL isPHUpload = [requestURL.scheme caseInsensitiveCompare:RCTNetworkingPHUploadHackScheme] == NSOrderedSame; + if (isPHUpload) { + requestURL = [NSURL URLWithString:[@"ph" stringByAppendingString:[requestURL.absoluteString substringFromIndex:RCTNetworkingPHUploadHackScheme.length]]]; + } - if (!request.URL) { + if (!requestURL) { NSString *const msg = [NSString stringWithFormat:@"Cannot send request without URL"]; [delegate URLRequest:cancellationBlock didCompleteWithError:RCTErrorWithMessage(msg)]; return cancellationBlock; @@ -49,23 +57,23 @@ RCT_EXPORT_MODULE() PHFetchResult *fetchResult; - if ([request.URL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame) { + if ([requestURL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame) { // Fetch assets using PHAsset localIdentifier (recommended) - NSString *const localIdentifier = [request.URL.absoluteString substringFromIndex:@"ph://".length]; + NSString *const localIdentifier = [requestURL.absoluteString substringFromIndex:@"ph://".length]; fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil]; - } else if ([request.URL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame) { + } else if ([requestURL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame) { // This is the older, deprecated way of fetching assets from assets-library // using the "assets-library://" protocol - fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[request.URL] options:nil]; + fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[requestURL] options:nil]; } else { - NSString *const msg = [NSString stringWithFormat:@"Cannot send request with unknown protocol: %@", request.URL]; + NSString *const msg = [NSString stringWithFormat:@"Cannot send request with unknown protocol: %@", requestURL]; [delegate URLRequest:cancellationBlock didCompleteWithError:RCTErrorWithMessage(msg)]; return cancellationBlock; } if (![fetchResult firstObject]) { NSString *errorMessage = [NSString stringWithFormat:@"Failed to load asset" - " at URL %@ with no error message.", request.URL]; + " at URL %@ with no error message.", requestURL]; NSError *error = RCTErrorWithMessage(errorMessage); [delegate URLRequest:cancellationBlock didCompleteWithError:error]; return cancellationBlock; @@ -77,37 +85,70 @@ RCT_EXPORT_MODULE() PHAsset *const _Nonnull asset = [fetchResult firstObject]; - // By default, allow downloading images from iCloud - PHImageRequestOptions *const requestOptions = [PHImageRequestOptions new]; - requestOptions.networkAccessAllowed = YES; - - [[PHImageManager defaultManager] requestImageDataForAsset:asset - options:requestOptions - resultHandler:^(NSData * _Nullable imageData, - NSString * _Nullable dataUTI, - UIImageOrientation orientation, - NSDictionary * _Nullable info) { - NSError *const error = [info objectForKey:PHImageErrorKey]; - if (error) { + // When we're uploading a video, provide the full data but in any other case, + // provide only the thumbnail of the video. + if (asset.mediaType == PHAssetMediaTypeVideo && isPHUpload) { + PHVideoRequestOptions *const requestOptions = [PHVideoRequestOptions new]; + requestOptions.networkAccessAllowed = YES; + [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:requestOptions resultHandler:^(AVAsset * _Nullable avAsset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { + NSError *error = [info objectForKey:PHImageErrorKey]; + if (error) { + [delegate URLRequest:cancellationBlock didCompleteWithError:error]; + return; + } + + if (![avAsset isKindOfClass:[AVURLAsset class]]) { + error = [NSError errorWithDomain:RCTErrorDomain code:0 userInfo: + @{ + NSLocalizedDescriptionKey: @"Unable to load AVURLAsset", + }]; + [delegate URLRequest:cancellationBlock didCompleteWithError:error]; + return; + } + + NSData *data = [NSData dataWithContentsOfURL:((AVURLAsset *)avAsset).URL + options:(NSDataReadingOptions)0 + error:&error]; + if (data) { + NSURLResponse *const response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:nil expectedContentLength:data.length textEncodingName:nil]; + [delegate URLRequest:cancellationBlock didReceiveResponse:response]; + [delegate URLRequest:cancellationBlock didReceiveData:data]; + } [delegate URLRequest:cancellationBlock didCompleteWithError:error]; - return; - } + }]; + } else { + // By default, allow downloading images from iCloud + PHImageRequestOptions *const requestOptions = [PHImageRequestOptions new]; + requestOptions.networkAccessAllowed = YES; - NSInteger const length = [imageData length]; - CFStringRef const dataUTIStringRef = (__bridge CFStringRef _Nonnull)(dataUTI); - CFStringRef const mimeType = UTTypeCopyPreferredTagWithClass(dataUTIStringRef, kUTTagClassMIMEType); + [[PHImageManager defaultManager] requestImageDataForAsset:asset + options:requestOptions + resultHandler:^(NSData * _Nullable imageData, + NSString * _Nullable dataUTI, + UIImageOrientation orientation, + NSDictionary * _Nullable info) { + NSError *const error = [info objectForKey:PHImageErrorKey]; + if (error) { + [delegate URLRequest:cancellationBlock didCompleteWithError:error]; + return; + } - NSURLResponse *const response = [[NSURLResponse alloc] initWithURL:request.URL - MIMEType:(__bridge NSString *)(mimeType) - expectedContentLength:length - textEncodingName:nil]; - if(mimeType) CFRelease(mimeType); - - [delegate URLRequest:cancellationBlock didReceiveResponse:response]; - - [delegate URLRequest:cancellationBlock didReceiveData:imageData]; - [delegate URLRequest:cancellationBlock didCompleteWithError:nil]; - }]; + NSInteger const length = [imageData length]; + CFStringRef const dataUTIStringRef = (__bridge CFStringRef _Nonnull)(dataUTI); + CFStringRef const mimeType = UTTypeCopyPreferredTagWithClass(dataUTIStringRef, kUTTagClassMIMEType); + + NSURLResponse *const response = [[NSURLResponse alloc] initWithURL:request.URL + MIMEType:(__bridge NSString *)(mimeType) + expectedContentLength:length + textEncodingName:nil]; + CFRelease(mimeType); + + [delegate URLRequest:cancellationBlock didReceiveResponse:response]; + + [delegate URLRequest:cancellationBlock didReceiveData:imageData]; + [delegate URLRequest:cancellationBlock didCompleteWithError:nil]; + }]; + } return cancellationBlock; }