@amazon-devices/expo-file-system 2.0.1758683737
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -0
- package/CHANGELOG.md +317 -0
- package/CMakeLists.txt +52 -0
- package/LICENSE +7 -0
- package/README.kepler.md +260 -0
- package/README.md +46 -0
- package/THIRD_PARTY_NOTICES.txt +24 -0
- package/android/build.gradle +118 -0
- package/android/src/main/AndroidManifest.xml +17 -0
- package/android/src/main/java/expo/modules/filesystem/AppDirectoriesModule.kt +26 -0
- package/android/src/main/java/expo/modules/filesystem/CountingRequestBody.kt +52 -0
- package/android/src/main/java/expo/modules/filesystem/FilePermissionModule.kt +48 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemExceptions.kt +48 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemFileProvider.kt +5 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt +1110 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemPackage.kt +10 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemRecords.kt +92 -0
- package/android/src/main/res/xml/file_system_provider_paths.xml +5 -0
- package/app.plugin.js +1 -0
- package/build/ExponentFileSystem.d.ts +4 -0
- package/build/ExponentFileSystem.d.ts.map +1 -0
- package/build/ExponentFileSystem.js +5 -0
- package/build/ExponentFileSystem.js.map +1 -0
- package/build/ExponentFileSystem.kepler.d.ts +4 -0
- package/build/ExponentFileSystem.kepler.d.ts.map +1 -0
- package/build/ExponentFileSystem.kepler.js +112 -0
- package/build/ExponentFileSystem.kepler.js.map +1 -0
- package/build/ExponentFileSystem.web.d.ts +3 -0
- package/build/ExponentFileSystem.web.d.ts.map +1 -0
- package/build/ExponentFileSystem.web.js +3 -0
- package/build/ExponentFileSystem.web.js.map +1 -0
- package/build/ExponentFileSystemShim.d.ts +4 -0
- package/build/ExponentFileSystemShim.d.ts.map +1 -0
- package/build/ExponentFileSystemShim.js +21 -0
- package/build/ExponentFileSystemShim.js.map +1 -0
- package/build/FileSystem.d.ts +361 -0
- package/build/FileSystem.d.ts.map +1 -0
- package/build/FileSystem.js +616 -0
- package/build/FileSystem.js.map +1 -0
- package/build/FileSystem.types.d.ts +304 -0
- package/build/FileSystem.types.d.ts.map +1 -0
- package/build/FileSystem.types.js +47 -0
- package/build/FileSystem.types.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +35 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/expo-module.config.json +11 -0
- package/ios/EXFilePermissionModule.h +16 -0
- package/ios/EXFilePermissionModule.m +68 -0
- package/ios/EXFileSystem.h +25 -0
- package/ios/EXFileSystem.m +1084 -0
- package/ios/EXFileSystemAssetLibraryHandler.h +6 -0
- package/ios/EXFileSystemAssetLibraryHandler.m +142 -0
- package/ios/EXFileSystemHandler.h +17 -0
- package/ios/EXFileSystemLocalFileHandler.h +7 -0
- package/ios/EXFileSystemLocalFileHandler.m +79 -0
- package/ios/EXSessionTasks/EXSessionCancelableUploadTaskDelegate.h +16 -0
- package/ios/EXSessionTasks/EXSessionCancelableUploadTaskDelegate.m +55 -0
- package/ios/EXSessionTasks/EXSessionDownloadTaskDelegate.h +13 -0
- package/ios/EXSessionTasks/EXSessionDownloadTaskDelegate.m +64 -0
- package/ios/EXSessionTasks/EXSessionHandler.h +19 -0
- package/ios/EXSessionTasks/EXSessionHandler.m +49 -0
- package/ios/EXSessionTasks/EXSessionResumableDownloadTaskDelegate.h +18 -0
- package/ios/EXSessionTasks/EXSessionResumableDownloadTaskDelegate.m +66 -0
- package/ios/EXSessionTasks/EXSessionTaskDelegate.h +32 -0
- package/ios/EXSessionTasks/EXSessionTaskDelegate.m +58 -0
- package/ios/EXSessionTasks/EXSessionTaskDispatcher.h +19 -0
- package/ios/EXSessionTasks/EXSessionTaskDispatcher.m +96 -0
- package/ios/EXSessionTasks/EXSessionUploadTaskDelegate.h +8 -0
- package/ios/EXSessionTasks/EXSessionUploadTaskDelegate.m +52 -0
- package/ios/EXSessionTasks/EXTaskHandlersManager.h +21 -0
- package/ios/EXSessionTasks/EXTaskHandlersManager.m +56 -0
- package/ios/Encoding.swift +88 -0
- package/ios/ExpoFileSystem.h +1 -0
- package/ios/ExpoFileSystem.podspec +32 -0
- package/ios/FileSystemBackgroundSessionHandler.swift +25 -0
- package/ios/FileSystemExceptions.swift +69 -0
- package/ios/FileSystemHelpers.swift +66 -0
- package/ios/FileSystemModule.swift +263 -0
- package/ios/FileSystemRecords.swift +74 -0
- package/ios/NSData+EXFileSystem.h +9 -0
- package/ios/NSData+EXFileSystem.m +19 -0
- package/ios/NetworkingHelpers.swift +84 -0
- package/ios/Tests/EXFileSystemSpec.swift +55 -0
- package/jest.config.js +3 -0
- package/kepler/ExponentFileSystem.cpp +498 -0
- package/kepler/ExponentFileSystem.h +60 -0
- package/kepler/KeplerInit.cpp +28 -0
- package/kepler/NetworkingRequestHandler.cpp +47 -0
- package/kepler/test/FileSystem.cpp +425 -0
- package/package.json +1 -0
- package/plugin/build/withFileSystem.d.ts +3 -0
- package/plugin/build/withFileSystem.js +12 -0
- package/plugin/src/withFileSystem.ts +13 -0
- package/plugin/tsconfig.json +9 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/react-native.config.js +21 -0
- package/src/ExponentFileSystem.kepler.ts +167 -0
- package/src/ExponentFileSystem.ts +7 -0
- package/src/ExponentFileSystem.web.ts +2 -0
- package/src/ExponentFileSystemShim.ts +23 -0
- package/src/FileSystem.ts +756 -0
- package/src/FileSystem.types.ts +339 -0
- package/src/index.ts +2 -0
- package/src/types.ts +34 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
|
|
2
|
+
#import <ExpoFileSystem/EXFileSystemAssetLibraryHandler.h>
|
|
3
|
+
#import <ExpoFileSystem/NSData+EXFileSystem.h>
|
|
4
|
+
|
|
5
|
+
#import <Photos/Photos.h>
|
|
6
|
+
|
|
7
|
+
@implementation EXFileSystemAssetLibraryHandler
|
|
8
|
+
|
|
9
|
+
+ (void)getInfoForFile:(NSURL *)fileUri
|
|
10
|
+
withOptions:(NSDictionary *)options
|
|
11
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
12
|
+
rejecter:(EXPromiseRejectBlock)reject
|
|
13
|
+
{
|
|
14
|
+
NSError *error;
|
|
15
|
+
PHFetchResult<PHAsset *> *fetchResult = [self fetchResultForUri:fileUri error:&error];
|
|
16
|
+
if (error) {
|
|
17
|
+
reject(@"E_UNSUPPORTED_ARG", error.description, error);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (fetchResult.count > 0) {
|
|
21
|
+
PHAsset *asset = fetchResult[0];
|
|
22
|
+
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
|
23
|
+
result[@"exists"] = @(YES);
|
|
24
|
+
result[@"isDirectory"] = @(NO);
|
|
25
|
+
result[@"uri"] = fileUri;
|
|
26
|
+
result[@"modificationTime"] = @(asset.modificationDate.timeIntervalSince1970);
|
|
27
|
+
if (options[@"md5"] || options[@"size"]) {
|
|
28
|
+
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
|
|
29
|
+
result[@"size"] = @(imageData.length);
|
|
30
|
+
if (options[@"md5"]) {
|
|
31
|
+
result[@"md5"] = [imageData md5String];
|
|
32
|
+
}
|
|
33
|
+
resolve(result);
|
|
34
|
+
}];
|
|
35
|
+
} else {
|
|
36
|
+
resolve(result);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
resolve(@{@"exists": @(NO), @"isDirectory": @(NO)});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
+ (void)copyFrom:(NSURL *)from
|
|
44
|
+
to:(NSURL *)to
|
|
45
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
46
|
+
rejecter:(EXPromiseRejectBlock)reject
|
|
47
|
+
{
|
|
48
|
+
NSString *toPath = [to.path stringByStandardizingPath];
|
|
49
|
+
|
|
50
|
+
// NOTE: The destination-delete and the copy should happen atomically, but we hope for the best for now
|
|
51
|
+
NSError *error;
|
|
52
|
+
if ([[NSFileManager defaultManager] fileExistsAtPath:toPath]) {
|
|
53
|
+
if (![[NSFileManager defaultManager] removeItemAtPath:toPath error:&error]) {
|
|
54
|
+
reject(@"E_FILE_NOT_COPIED",
|
|
55
|
+
[NSString stringWithFormat:@"File '%@' could not be copied to '%@' because a file already exists at "
|
|
56
|
+
"the destination and could not be deleted.", from, to],
|
|
57
|
+
error);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
PHFetchResult<PHAsset *> *fetchResult = [self fetchResultForUri:from error:&error];
|
|
63
|
+
if (error) {
|
|
64
|
+
reject(@"E_UNSUPPORTED_ARG", error.description, error);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (fetchResult.count > 0) {
|
|
69
|
+
PHAsset *asset = fetchResult[0];
|
|
70
|
+
if (asset.mediaType == PHAssetMediaTypeVideo) {
|
|
71
|
+
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
|
|
72
|
+
if (![asset isKindOfClass:[AVURLAsset class]]) {
|
|
73
|
+
reject(@"ERR_INCORRECT_ASSET_TYPE",
|
|
74
|
+
[NSString stringWithFormat:@"File '%@' has incorrect asset type.", from],
|
|
75
|
+
nil);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
AVURLAsset* urlAsset = (AVURLAsset*)asset;
|
|
80
|
+
NSNumber *size;
|
|
81
|
+
[urlAsset.URL getResourceValue:&size forKey:NSURLFileSizeKey error:nil];
|
|
82
|
+
NSData *data = [NSData dataWithContentsOfURL:urlAsset.URL];
|
|
83
|
+
|
|
84
|
+
[EXFileSystemAssetLibraryHandler copyData:data toPath:toPath resolver:resolve rejecter:reject];
|
|
85
|
+
}];
|
|
86
|
+
} else {
|
|
87
|
+
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
|
|
88
|
+
[EXFileSystemAssetLibraryHandler copyData:imageData toPath:toPath resolver:resolve rejecter:reject];
|
|
89
|
+
}];
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
reject(@"E_FILE_NOT_COPIED",
|
|
93
|
+
[NSString stringWithFormat:@"File '%@' could not be found.", from],
|
|
94
|
+
error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// adapted from RCTImageLoader.m
|
|
99
|
+
+ (PHFetchResult<PHAsset *> *)fetchResultForUri:(NSURL *)url error:(NSError **)error
|
|
100
|
+
{
|
|
101
|
+
if ([url.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame) {
|
|
102
|
+
// Fetch assets using PHAsset localIdentifier (recommended)
|
|
103
|
+
NSString *const localIdentifier = [url.absoluteString substringFromIndex:@"ph://".length];
|
|
104
|
+
return [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil];
|
|
105
|
+
} else if ([url.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame) {
|
|
106
|
+
#if TARGET_OS_MACCATALYST
|
|
107
|
+
static BOOL hasWarned = NO;
|
|
108
|
+
if (!hasWarned) {
|
|
109
|
+
NSLog(@"assets-library:// URLs have been deprecated and cannot be accessed in macOS Catalyst. Returning nil (future warnings will be suppressed).");
|
|
110
|
+
hasWarned = YES;
|
|
111
|
+
}
|
|
112
|
+
return nil;
|
|
113
|
+
#else
|
|
114
|
+
// This is the older, deprecated way of fetching assets from assets-library
|
|
115
|
+
// using the "assets-library://" protocol
|
|
116
|
+
return [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil];
|
|
117
|
+
#endif
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
NSString *description = [NSString stringWithFormat:@"Invalid URL provided, expected scheme to be either 'ph' or 'assets-library', was '%@'.", url.scheme];
|
|
121
|
+
*error = [[NSError alloc] initWithDomain:NSURLErrorDomain
|
|
122
|
+
code:NSURLErrorUnsupportedURL
|
|
123
|
+
userInfo:@{NSLocalizedDescriptionKey: description}];
|
|
124
|
+
return nil;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
+ (void)copyData:(NSData *)data
|
|
129
|
+
toPath:(NSString *)path
|
|
130
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
131
|
+
rejecter:(EXPromiseRejectBlock)reject
|
|
132
|
+
{
|
|
133
|
+
if ([data writeToFile:path atomically:YES]) {
|
|
134
|
+
resolve(nil);
|
|
135
|
+
} else {
|
|
136
|
+
reject(@"E_FILE_NOT_COPIED",
|
|
137
|
+
[NSString stringWithFormat:@"File could not be copied to '%@'.", path],
|
|
138
|
+
nil);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Copyright 2023-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoModulesCore/ExpoModulesCore.h>
|
|
4
|
+
|
|
5
|
+
@protocol EXFileSystemHandler
|
|
6
|
+
|
|
7
|
+
+ (void)getInfoForFile:(NSURL *)fileUri
|
|
8
|
+
withOptions:(NSDictionary *)optionxs
|
|
9
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
10
|
+
rejecter:(EXPromiseRejectBlock)reject;
|
|
11
|
+
|
|
12
|
+
+ (void)copyFrom:(NSURL *)from
|
|
13
|
+
to:(NSURL *)to
|
|
14
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
15
|
+
rejecter:(EXPromiseRejectBlock)reject;
|
|
16
|
+
|
|
17
|
+
@end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
#import <ExpoFileSystem/EXFileSystemLocalFileHandler.h>
|
|
3
|
+
#import <ExpoFileSystem/NSData+EXFileSystem.h>
|
|
4
|
+
|
|
5
|
+
@implementation EXFileSystemLocalFileHandler
|
|
6
|
+
|
|
7
|
+
+ (void)getInfoForFile:(NSURL *)fileUri
|
|
8
|
+
withOptions:(NSDictionary *)options
|
|
9
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
10
|
+
rejecter:(EXPromiseRejectBlock)reject
|
|
11
|
+
{
|
|
12
|
+
NSString *path = fileUri.path;
|
|
13
|
+
BOOL isDirectory;
|
|
14
|
+
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) {
|
|
15
|
+
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
|
|
16
|
+
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
|
17
|
+
result[@"exists"] = @(YES);
|
|
18
|
+
result[@"isDirectory"] = @(isDirectory);
|
|
19
|
+
result[@"uri"] = [NSURL fileURLWithPath:path].absoluteString;
|
|
20
|
+
if (options[@"md5"]) {
|
|
21
|
+
result[@"md5"] = [[NSData dataWithContentsOfFile:path] md5String];
|
|
22
|
+
}
|
|
23
|
+
result[@"size"] = @([EXFileSystemLocalFileHandler getFileSize:path attributes:attributes]);
|
|
24
|
+
result[@"modificationTime"] = @(attributes.fileModificationDate.timeIntervalSince1970);
|
|
25
|
+
resolve(result);
|
|
26
|
+
} else {
|
|
27
|
+
resolve(@{@"exists": @(NO), @"isDirectory": @(NO)});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
+ (unsigned long long)getFileSize:(NSString *)path attributes:(NSDictionary<NSFileAttributeKey, id> *)attributes
|
|
32
|
+
{
|
|
33
|
+
if (attributes.fileType != NSFileTypeDirectory) {
|
|
34
|
+
return attributes.fileSize;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The path is pointing to the folder
|
|
38
|
+
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
|
|
39
|
+
NSEnumerator *contentsEnumurator = [contents objectEnumerator];
|
|
40
|
+
NSString *file;
|
|
41
|
+
unsigned long long folderSize = 0;
|
|
42
|
+
while (file = [contentsEnumurator nextObject]) {
|
|
43
|
+
NSString *filePath = [path stringByAppendingPathComponent:file];
|
|
44
|
+
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
|
|
45
|
+
folderSize += [EXFileSystemLocalFileHandler getFileSize:filePath attributes:fileAttributes];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return folderSize;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
+ (void)copyFrom:(NSURL *)from
|
|
52
|
+
to:(NSURL *)to
|
|
53
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
54
|
+
rejecter:(EXPromiseRejectBlock)reject
|
|
55
|
+
{
|
|
56
|
+
NSString *fromPath = [from.path stringByStandardizingPath];
|
|
57
|
+
NSString *toPath = [to.path stringByStandardizingPath];
|
|
58
|
+
|
|
59
|
+
NSError *error;
|
|
60
|
+
if ([[NSFileManager defaultManager] fileExistsAtPath:toPath]) {
|
|
61
|
+
if (![[NSFileManager defaultManager] removeItemAtPath:toPath error:&error]) {
|
|
62
|
+
reject(@"E_FILE_NOT_COPIED",
|
|
63
|
+
[NSString stringWithFormat:@"File '%@' could not be copied to '%@' because a file already exists at "
|
|
64
|
+
"the destination and could not be deleted.", from, to],
|
|
65
|
+
error);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if ([[NSFileManager defaultManager] copyItemAtPath:fromPath toPath:toPath error:&error]) {
|
|
71
|
+
resolve(nil);
|
|
72
|
+
} else {
|
|
73
|
+
reject(@"E_FILE_NOT_COPIED",
|
|
74
|
+
[NSString stringWithFormat:@"File '%@' could not be copied to '%@'.", from, to],
|
|
75
|
+
error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionUploadTaskDelegate.h>
|
|
4
|
+
#import <ExpoFileSystem/EXTaskHandlersManager.h>
|
|
5
|
+
|
|
6
|
+
typedef void (^EXUploadDelegateOnSendCallback)(NSURLSessionUploadTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
|
|
7
|
+
|
|
8
|
+
@interface EXSessionCancelableUploadTaskDelegate : EXSessionUploadTaskDelegate
|
|
9
|
+
|
|
10
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
11
|
+
reject:(EXPromiseRejectBlock)reject
|
|
12
|
+
onSendCallback:(EXUploadDelegateOnSendCallback)onSendCallback
|
|
13
|
+
resumableManager:(EXTaskHandlersManager *)manager
|
|
14
|
+
uuid:(NSString *)uuid;
|
|
15
|
+
|
|
16
|
+
@end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionCancelableUploadTaskDelegate.h>
|
|
4
|
+
|
|
5
|
+
@interface EXSessionCancelableUploadTaskDelegate ()
|
|
6
|
+
|
|
7
|
+
@property (strong, nonatomic, readonly) EXUploadDelegateOnSendCallback onSendCallback;
|
|
8
|
+
@property (weak, nonatomic) EXTaskHandlersManager *manager;
|
|
9
|
+
@property (strong, nonatomic) NSString *uuid;
|
|
10
|
+
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
@implementation EXSessionCancelableUploadTaskDelegate
|
|
14
|
+
|
|
15
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
16
|
+
reject:(EXPromiseRejectBlock)reject
|
|
17
|
+
onSendCallback:(EXUploadDelegateOnSendCallback)onSendCallback
|
|
18
|
+
resumableManager:(EXTaskHandlersManager *)manager
|
|
19
|
+
uuid:(NSString *)uuid;
|
|
20
|
+
{
|
|
21
|
+
if (self = [super initWithResolve:resolve
|
|
22
|
+
reject:reject]) {
|
|
23
|
+
_onSendCallback = onSendCallback;
|
|
24
|
+
_manager = manager;
|
|
25
|
+
_uuid = uuid;
|
|
26
|
+
}
|
|
27
|
+
return self;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
31
|
+
{
|
|
32
|
+
if (error) {
|
|
33
|
+
// The task was paused by us. So, we shouldn't throw.
|
|
34
|
+
if (error.code == NSURLErrorCancelled) {
|
|
35
|
+
self.resolve([NSNull null]);
|
|
36
|
+
[_manager unregisterTask:_uuid];
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[super URLSession:session task:task didCompleteWithError:error];
|
|
42
|
+
[_manager unregisterTask:_uuid];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
|
46
|
+
didSendBodyData:(int64_t)bytesSent
|
|
47
|
+
totalBytesSent:(int64_t)totalBytesSent
|
|
48
|
+
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
|
|
49
|
+
{
|
|
50
|
+
if (_onSendCallback && bytesSent > 0) {
|
|
51
|
+
_onSendCallback(task, bytesSent, totalBytesSent, totalBytesExpectedToSend);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionTaskDelegate.h>
|
|
4
|
+
|
|
5
|
+
@interface EXSessionDownloadTaskDelegate : EXSessionTaskDelegate
|
|
6
|
+
|
|
7
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
8
|
+
reject:(EXPromiseRejectBlock)reject
|
|
9
|
+
localUrl:(NSURL *)localUrl
|
|
10
|
+
shouldCalculateMd5:(BOOL)shouldCalculateMd5;
|
|
11
|
+
|
|
12
|
+
@end
|
|
13
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionDownloadTaskDelegate.h>
|
|
4
|
+
#import <ExpoFileSystem/NSData+EXFileSystem.h>
|
|
5
|
+
|
|
6
|
+
@interface EXSessionDownloadTaskDelegate ()
|
|
7
|
+
|
|
8
|
+
@property (strong, nonatomic) NSURL *localUrl;
|
|
9
|
+
@property (nonatomic) BOOL shouldCalculateMd5;
|
|
10
|
+
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
@implementation EXSessionDownloadTaskDelegate
|
|
14
|
+
|
|
15
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
16
|
+
reject:(EXPromiseRejectBlock)reject
|
|
17
|
+
localUrl:(NSURL *)localUrl
|
|
18
|
+
shouldCalculateMd5:(BOOL)shouldCalculateMd5
|
|
19
|
+
{
|
|
20
|
+
if (self = [super initWithResolve:resolve reject:reject])
|
|
21
|
+
{
|
|
22
|
+
_localUrl = localUrl;
|
|
23
|
+
_shouldCalculateMd5 = shouldCalculateMd5;
|
|
24
|
+
}
|
|
25
|
+
return self;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
|
|
29
|
+
{
|
|
30
|
+
NSError *error;
|
|
31
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
32
|
+
if ([fileManager fileExistsAtPath:_localUrl.path]) {
|
|
33
|
+
[fileManager removeItemAtURL:_localUrl error:&error];
|
|
34
|
+
if (error) {
|
|
35
|
+
self.reject(@"ERR_FILESYSTEM_CANNOT_REMOVE",
|
|
36
|
+
[NSString stringWithFormat:@"Unable to remove file from local URI: '%@'", error.description],
|
|
37
|
+
error);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[fileManager moveItemAtURL:location toURL:_localUrl error:&error];
|
|
43
|
+
if (error) {
|
|
44
|
+
self.reject(@"ERR_FILESYSTEM_CANNOT_SAVE",
|
|
45
|
+
[NSString stringWithFormat:@"Unable to save file to local URI: '%@'", error.description],
|
|
46
|
+
error);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
self.resolve([self parseServerResponse:downloadTask.response]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
- (NSDictionary *)parseServerResponse:(NSURLResponse *)response
|
|
54
|
+
{
|
|
55
|
+
NSMutableDictionary *result = [[super parseServerResponse:response] mutableCopy];
|
|
56
|
+
result[@"uri"] = _localUrl.absoluteString;
|
|
57
|
+
if (_shouldCalculateMd5) {
|
|
58
|
+
NSData *data = [NSData dataWithContentsOfURL:_localUrl];
|
|
59
|
+
result[@"md5"] = [data md5String];
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
#import <ExpoModulesCore/EXSingletonModule.h>
|
|
5
|
+
#import <UIKit/UIKit.h>
|
|
6
|
+
|
|
7
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
8
|
+
|
|
9
|
+
@protocol EXSessionHandler
|
|
10
|
+
|
|
11
|
+
- (void)invokeCompletionHandlerForSessionIdentifier:(NSString *)identifier;
|
|
12
|
+
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
@interface EXSessionHandler : EXSingletonModule <UIApplicationDelegate, EXSessionHandler>
|
|
16
|
+
|
|
17
|
+
@end
|
|
18
|
+
|
|
19
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionHandler.h>
|
|
4
|
+
|
|
5
|
+
#import <ExpoModulesCore/EXDefines.h>
|
|
6
|
+
|
|
7
|
+
@interface EXSessionHandler ()
|
|
8
|
+
|
|
9
|
+
@property (nonatomic, strong) NSMutableDictionary<NSString *, void (^)(void)> *completionHandlers;
|
|
10
|
+
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
@implementation EXSessionHandler
|
|
14
|
+
|
|
15
|
+
EX_REGISTER_SINGLETON_MODULE(SessionHandler);
|
|
16
|
+
|
|
17
|
+
- (instancetype)init
|
|
18
|
+
{
|
|
19
|
+
if (self = [super init]) {
|
|
20
|
+
_completionHandlers = [NSMutableDictionary dictionary];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return self;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
- (void)invokeCompletionHandlerForSessionIdentifier:(NSString *)identifier
|
|
27
|
+
{
|
|
28
|
+
if (!identifier) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void (^completionHandler)(void) = _completionHandlers[identifier];
|
|
33
|
+
if (completionHandler) {
|
|
34
|
+
// We need to run completionHandler explicite on the main thread because is's part of UIKit
|
|
35
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
36
|
+
completionHandler();
|
|
37
|
+
});
|
|
38
|
+
[_completionHandlers removeObjectForKey:identifier];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#pragma mark - AppDelegate
|
|
43
|
+
|
|
44
|
+
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
|
|
45
|
+
{
|
|
46
|
+
_completionHandlers[identifier] = completionHandler;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionDownloadTaskDelegate.h>
|
|
4
|
+
#import <ExpoFileSystem/EXTaskHandlersManager.h>
|
|
5
|
+
|
|
6
|
+
typedef void (^EXDownloadDelegateOnWriteCallback)(NSURLSessionDownloadTask *task, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
|
|
7
|
+
|
|
8
|
+
@interface EXSessionResumableDownloadTaskDelegate : EXSessionDownloadTaskDelegate
|
|
9
|
+
|
|
10
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
11
|
+
reject:(EXPromiseRejectBlock)reject
|
|
12
|
+
localUrl:(NSURL *)localUrl
|
|
13
|
+
shouldCalculateMd5:(BOOL)shouldCalculateMd5
|
|
14
|
+
onWriteCallback:(EXDownloadDelegateOnWriteCallback)onWriteCallback
|
|
15
|
+
resumableManager:(EXTaskHandlersManager *)manager
|
|
16
|
+
uuid:(NSString *)uuid;
|
|
17
|
+
|
|
18
|
+
@end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionResumableDownloadTaskDelegate.h>
|
|
4
|
+
|
|
5
|
+
@interface EXSessionResumableDownloadTaskDelegate ()
|
|
6
|
+
|
|
7
|
+
@property (strong, nonatomic, readonly) EXDownloadDelegateOnWriteCallback onWriteCallback;
|
|
8
|
+
@property (weak, nonatomic) EXTaskHandlersManager *manager;
|
|
9
|
+
@property (strong, nonatomic) NSString *uuid;
|
|
10
|
+
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
@implementation EXSessionResumableDownloadTaskDelegate
|
|
14
|
+
|
|
15
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
16
|
+
reject:(EXPromiseRejectBlock)reject
|
|
17
|
+
localUrl:(NSURL *)localUrl
|
|
18
|
+
shouldCalculateMd5:(BOOL)shouldCalculateMd5
|
|
19
|
+
onWriteCallback:(EXDownloadDelegateOnWriteCallback)onWriteCallback
|
|
20
|
+
resumableManager:(EXTaskHandlersManager *)manager
|
|
21
|
+
uuid:(NSString *)uuid;
|
|
22
|
+
{
|
|
23
|
+
if (self = [super initWithResolve:resolve
|
|
24
|
+
reject:reject
|
|
25
|
+
localUrl:localUrl
|
|
26
|
+
shouldCalculateMd5:shouldCalculateMd5]) {
|
|
27
|
+
_onWriteCallback = onWriteCallback;
|
|
28
|
+
_manager = manager;
|
|
29
|
+
_uuid = uuid;
|
|
30
|
+
}
|
|
31
|
+
return self;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
|
|
35
|
+
{
|
|
36
|
+
[super URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
|
|
37
|
+
[_manager unregisterTask:_uuid];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
41
|
+
{
|
|
42
|
+
if (error) {
|
|
43
|
+
// The task was paused by us. So, we shouldn't throw.
|
|
44
|
+
if (error.code == NSURLErrorCancelled) {
|
|
45
|
+
self.resolve([NSNull null]);
|
|
46
|
+
} else {
|
|
47
|
+
self.reject(@"ERR_FILESYSTEM_CANNOT_DOWNLOAD",
|
|
48
|
+
[NSString stringWithFormat:@"Unable to download file: %@", error.description],
|
|
49
|
+
error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
[_manager unregisterTask:_uuid];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
|
57
|
+
didWriteData:(int64_t)bytesWritten
|
|
58
|
+
totalBytesWritten:(int64_t)totalBytesWritten
|
|
59
|
+
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
|
|
60
|
+
{
|
|
61
|
+
if (_onWriteCallback && bytesWritten > 0) {
|
|
62
|
+
_onWriteCallback(downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
#import <ExpoModulesCore/EXDefines.h>
|
|
5
|
+
|
|
6
|
+
@interface EXSessionTaskDelegate : NSObject
|
|
7
|
+
|
|
8
|
+
@property (nonatomic, strong, readonly) EXPromiseResolveBlock resolve;
|
|
9
|
+
@property (nonatomic, strong, readonly) EXPromiseRejectBlock reject;
|
|
10
|
+
|
|
11
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
12
|
+
reject:(EXPromiseRejectBlock)reject;
|
|
13
|
+
|
|
14
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
|
|
15
|
+
|
|
16
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;
|
|
17
|
+
|
|
18
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
|
19
|
+
didWriteData:(int64_t)bytesWritten
|
|
20
|
+
totalBytesWritten:(int64_t)totalBytesWritten
|
|
21
|
+
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
|
|
22
|
+
|
|
23
|
+
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;
|
|
24
|
+
|
|
25
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
|
26
|
+
didSendBodyData:(int64_t)bytesSent
|
|
27
|
+
totalBytesSent:(int64_t)totalBytesSent
|
|
28
|
+
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
|
|
29
|
+
|
|
30
|
+
- (NSDictionary *)parseServerResponse:(NSURLResponse *)response;
|
|
31
|
+
|
|
32
|
+
@end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoFileSystem/EXSessionTaskDelegate.h>
|
|
4
|
+
|
|
5
|
+
@implementation EXSessionTaskDelegate
|
|
6
|
+
|
|
7
|
+
- (nonnull instancetype)initWithResolve:(EXPromiseResolveBlock)resolve
|
|
8
|
+
reject:(EXPromiseRejectBlock)reject
|
|
9
|
+
{
|
|
10
|
+
if (self = [super init]) {
|
|
11
|
+
_resolve = resolve;
|
|
12
|
+
_reject = reject;
|
|
13
|
+
}
|
|
14
|
+
return self;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
18
|
+
{
|
|
19
|
+
if (error) {
|
|
20
|
+
self.reject(@"ERR_FILESYSTEM_CANNOT_DOWNLOAD",
|
|
21
|
+
[NSString stringWithFormat:@"Unable to download file: %@", error.description],
|
|
22
|
+
error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
- (NSDictionary *)parseServerResponse:(NSURLResponse *)response
|
|
27
|
+
{
|
|
28
|
+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
|
29
|
+
return @{
|
|
30
|
+
@"status": @([httpResponse statusCode]),
|
|
31
|
+
@"headers": [httpResponse allHeaderFields],
|
|
32
|
+
@"mimeType": EXNullIfNil([httpResponse MIMEType])
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
|
|
37
|
+
{
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
|
41
|
+
didWriteData:(int64_t)bytesWritten
|
|
42
|
+
totalBytesWritten:(int64_t)totalBytesWritten
|
|
43
|
+
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
|
|
44
|
+
{
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
|
|
48
|
+
{
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
|
52
|
+
didSendBodyData:(int64_t)bytesSent
|
|
53
|
+
totalBytesSent:(int64_t)totalBytesSent
|
|
54
|
+
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
|
|
55
|
+
{
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
#import <ExpoFileSystem/EXSessionTaskDelegate.h>
|
|
5
|
+
#import <ExpoFileSystem/EXSessionHandler.h>
|
|
6
|
+
|
|
7
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
8
|
+
|
|
9
|
+
@interface EXSessionTaskDispatcher : NSObject <NSURLSessionDelegate>
|
|
10
|
+
|
|
11
|
+
- (instancetype)initWithSessionHandler:(nullable id<EXSessionHandler>)sessionHandler;
|
|
12
|
+
|
|
13
|
+
- (void)registerTaskDelegate:(EXSessionTaskDelegate *)delegate forTask:(NSURLSessionTask *)task;
|
|
14
|
+
|
|
15
|
+
- (void)deactivate;
|
|
16
|
+
|
|
17
|
+
@end
|
|
18
|
+
|
|
19
|
+
NS_ASSUME_NONNULL_END
|