1 2#import <ABI47_0_0EXFileSystem/ABI47_0_0EXFileSystemAssetLibraryHandler.h> 3 4#import <Photos/Photos.h> 5 6@implementation ABI47_0_0EXFileSystemAssetLibraryHandler 7 8+ (void)getInfoForFile:(NSURL *)fileUri 9 withOptions:(NSDictionary *)options 10 resolver:(ABI47_0_0EXPromiseResolveBlock)resolve 11 rejecter:(ABI47_0_0EXPromiseRejectBlock)reject 12{ 13 NSError *error; 14 PHFetchResult<PHAsset *> *fetchResult = [self fetchResultForUri:fileUri error:&error]; 15 if (error) { 16 reject(@"E_UNSUPPORTED_ARG", error.description, error); 17 return; 18 } 19 if (fetchResult.count > 0) { 20 PHAsset *asset = fetchResult[0]; 21 NSMutableDictionary *result = [NSMutableDictionary dictionary]; 22 result[@"exists"] = @(YES); 23 result[@"isDirectory"] = @(NO); 24 result[@"uri"] = fileUri; 25 result[@"modificationTime"] = @(asset.modificationDate.timeIntervalSince1970); 26 if (options[@"md5"] || options[@"size"]) { 27 [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { 28 result[@"size"] = @(imageData.length); 29 if (options[@"md5"]) { 30 result[@"md5"] = [imageData md5String]; 31 } 32 resolve(result); 33 }]; 34 } else { 35 resolve(result); 36 } 37 } else { 38 resolve(@{@"exists": @(NO), @"isDirectory": @(NO)}); 39 } 40} 41 42+ (void)copyFrom:(NSURL *)from 43 to:(NSURL *)to 44 resolver:(ABI47_0_0EXPromiseResolveBlock)resolve 45 rejecter:(ABI47_0_0EXPromiseRejectBlock)reject 46{ 47 NSString *toPath = [to.path stringByStandardizingPath]; 48 49 // NOTE: The destination-delete and the copy should happen atomically, but we hope for the best for now 50 NSError *error; 51 if ([[NSFileManager defaultManager] fileExistsAtPath:toPath]) { 52 if (![[NSFileManager defaultManager] removeItemAtPath:toPath error:&error]) { 53 reject(@"E_FILE_NOT_COPIED", 54 [NSString stringWithFormat:@"File '%@' could not be copied to '%@' because a file already exists at " 55 "the destination and could not be deleted.", from, to], 56 error); 57 return; 58 } 59 } 60 61 PHFetchResult<PHAsset *> *fetchResult = [self fetchResultForUri:from error:&error]; 62 if (error) { 63 reject(@"E_UNSUPPORTED_ARG", error.description, error); 64 return; 65 } 66 67 if (fetchResult.count > 0) { 68 PHAsset *asset = fetchResult[0]; 69 if (asset.mediaType == PHAssetMediaTypeVideo) { 70 [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) { 71 if (![asset isKindOfClass:[AVURLAsset class]]) { 72 reject(@"ERR_INCORRECT_ASSET_TYPE", 73 [NSString stringWithFormat:@"File '%@' has incorrect asset type.", from], 74 nil); 75 return; 76 } 77 78 AVURLAsset* urlAsset = (AVURLAsset*)asset; 79 NSNumber *size; 80 [urlAsset.URL getResourceValue:&size forKey:NSURLFileSizeKey error:nil]; 81 NSData *data = [NSData dataWithContentsOfURL:urlAsset.URL]; 82 83 [ABI47_0_0EXFileSystemAssetLibraryHandler copyData:data toPath:toPath resolver:resolve rejecter:reject]; 84 }]; 85 } else { 86 [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { 87 [ABI47_0_0EXFileSystemAssetLibraryHandler copyData:imageData toPath:toPath resolver:resolve rejecter:reject]; 88 }]; 89 } 90 } else { 91 reject(@"E_FILE_NOT_COPIED", 92 [NSString stringWithFormat:@"File '%@' could not be found.", from], 93 error); 94 } 95} 96 97// adapted from ABI47_0_0RCTImageLoader.m 98+ (PHFetchResult<PHAsset *> *)fetchResultForUri:(NSURL *)url error:(NSError **)error 99{ 100 if ([url.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame) { 101 // Fetch assets using PHAsset localIdentifier (recommended) 102 NSString *const localIdentifier = [url.absoluteString substringFromIndex:@"ph://".length]; 103 return [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil]; 104 } else if ([url.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame) { 105#if TARGET_OS_MACCATALYST 106 static BOOL hasWarned = NO; 107 if (!hasWarned) { 108 NSLog(@"assets-library:// URLs have been deprecated and cannot be accessed in macOS Catalyst. Returning nil (future warnings will be suppressed)."); 109 hasWarned = YES; 110 } 111 return nil; 112#else 113 // This is the older, deprecated way of fetching assets from assets-library 114 // using the "assets-library://" protocol 115 return [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil]; 116#endif 117 } 118 119 NSString *description = [NSString stringWithFormat:@"Invalid URL provided, expected scheme to be either 'ph' or 'assets-library', was '%@'.", url.scheme]; 120 *error = [[NSError alloc] initWithDomain:NSURLErrorDomain 121 code:NSURLErrorUnsupportedURL 122 userInfo:@{NSLocalizedDescriptionKey: description}]; 123 return nil; 124} 125 126 127+ (void)copyData:(NSData *)data 128 toPath:(NSString *)path 129 resolver:(ABI47_0_0EXPromiseResolveBlock)resolve 130 rejecter:(ABI47_0_0EXPromiseRejectBlock)reject 131{ 132 if ([data writeToFile:path atomically:YES]) { 133 resolve(nil); 134 } else { 135 reject(@"E_FILE_NOT_COPIED", 136 [NSString stringWithFormat:@"File could not be copied to '%@'.", path], 137 nil); 138 } 139} 140 141@end 142