2016-01-20 19:03:22 +00:00
|
|
|
/**
|
2018-09-11 22:27:47 +00:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2016-01-20 19:03:22 +00:00
|
|
|
*
|
2018-02-17 02:24:55 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-01-20 19:03:22 +00:00
|
|
|
*/
|
|
|
|
|
2019-02-24 04:29:24 +00:00
|
|
|
#import "RNCAssetsLibraryRequestHandler.h"
|
2016-01-20 19:03:22 +00:00
|
|
|
|
2017-07-31 10:17:39 +00:00
|
|
|
#import <stdatomic.h>
|
2018-06-19 20:18:51 +00:00
|
|
|
#import <dlfcn.h>
|
|
|
|
#import <objc/runtime.h>
|
2016-11-23 15:47:52 +00:00
|
|
|
|
2019-01-08 00:11:52 +00:00
|
|
|
#import <Photos/Photos.h>
|
2016-05-03 15:51:58 +00:00
|
|
|
#import <MobileCoreServices/MobileCoreServices.h>
|
2016-01-20 19:03:22 +00:00
|
|
|
|
2016-11-23 15:47:52 +00:00
|
|
|
#import <React/RCTBridge.h>
|
|
|
|
#import <React/RCTUtils.h>
|
2016-01-20 19:03:22 +00:00
|
|
|
|
2019-02-24 04:29:24 +00:00
|
|
|
@implementation RNCAssetsLibraryRequestHandler
|
2016-01-20 19:03:22 +00:00
|
|
|
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
|
|
|
#pragma mark - RCTURLRequestHandler
|
|
|
|
|
|
|
|
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
|
|
|
{
|
2019-01-08 00:11:52 +00:00
|
|
|
if (![PHAsset class]) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return [request.URL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame
|
|
|
|
|| [request.URL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame;
|
2016-01-20 19:03:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id)sendRequest:(NSURLRequest *)request
|
|
|
|
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
|
|
|
{
|
2017-07-31 10:17:39 +00:00
|
|
|
__block atomic_bool cancelled = ATOMIC_VAR_INIT(NO);
|
2016-01-20 19:03:22 +00:00
|
|
|
void (^cancellationBlock)(void) = ^{
|
2017-07-31 10:17:39 +00:00
|
|
|
atomic_store(&cancelled, YES);
|
2016-01-20 19:03:22 +00:00
|
|
|
};
|
2019-01-08 00:11:52 +00:00
|
|
|
|
|
|
|
if (!request.URL) {
|
|
|
|
NSString *const msg = [NSString stringWithFormat:@"Cannot send request without URL"];
|
|
|
|
[delegate URLRequest:cancellationBlock didCompleteWithError:RCTErrorWithMessage(msg)];
|
|
|
|
return cancellationBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
PHFetchResult<PHAsset *> *fetchResult;
|
|
|
|
|
|
|
|
if ([request.URL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame) {
|
|
|
|
// Fetch assets using PHAsset localIdentifier (recommended)
|
|
|
|
NSString *const localIdentifier = [request.URL.absoluteString substringFromIndex:@"ph://".length];
|
|
|
|
fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil];
|
|
|
|
} else if ([request.URL.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];
|
|
|
|
} else {
|
|
|
|
NSString *const msg = [NSString stringWithFormat:@"Cannot send request with unknown protocol: %@", request.URL];
|
|
|
|
[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];
|
|
|
|
NSError *error = RCTErrorWithMessage(errorMessage);
|
|
|
|
[delegate URLRequest:cancellationBlock didCompleteWithError:error];
|
|
|
|
return cancellationBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (atomic_load(&cancelled)) {
|
|
|
|
return cancellationBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2018-12-22 08:16:33 +00:00
|
|
|
[delegate URLRequest:cancellationBlock didCompleteWithError:error];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-08 00:11:52 +00:00
|
|
|
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];
|
2019-04-18 02:19:31 +00:00
|
|
|
if(mimeType) CFRelease(mimeType);
|
2019-01-08 00:11:52 +00:00
|
|
|
|
|
|
|
[delegate URLRequest:cancellationBlock didReceiveResponse:response];
|
|
|
|
|
|
|
|
[delegate URLRequest:cancellationBlock didReceiveData:imageData];
|
|
|
|
[delegate URLRequest:cancellationBlock didCompleteWithError:nil];
|
|
|
|
}];
|
|
|
|
|
2016-01-20 19:03:22 +00:00
|
|
|
return cancellationBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)cancelRequest:(id)requestToken
|
|
|
|
{
|
|
|
|
((void (^)(void))requestToken)();
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|