@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,1084 @@
|
|
|
1
|
+
// Copyright 2016-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#import <ExpoModulesCore/EXModuleRegistry.h>
|
|
4
|
+
|
|
5
|
+
#import <ExpoFileSystem/EXFileSystem.h>
|
|
6
|
+
|
|
7
|
+
#import <CommonCrypto/CommonDigest.h>
|
|
8
|
+
#import <MobileCoreServices/MobileCoreServices.h>
|
|
9
|
+
|
|
10
|
+
#import <ExpoFileSystem/EXFileSystemLocalFileHandler.h>
|
|
11
|
+
#import <ExpoFileSystem/EXFileSystemAssetLibraryHandler.h>
|
|
12
|
+
|
|
13
|
+
#import <ExpoModulesCore/EXFileSystemInterface.h>
|
|
14
|
+
#import <ExpoModulesCore/EXFilePermissionModuleInterface.h>
|
|
15
|
+
|
|
16
|
+
#import <ExpoModulesCore/EXEventEmitterService.h>
|
|
17
|
+
|
|
18
|
+
#import <ExpoFileSystem/EXTaskHandlersManager.h>
|
|
19
|
+
#import <ExpoFileSystem/EXSessionTaskDispatcher.h>
|
|
20
|
+
#import <ExpoFileSystem/EXSessionDownloadTaskDelegate.h>
|
|
21
|
+
#import <ExpoFileSystem/EXSessionResumableDownloadTaskDelegate.h>
|
|
22
|
+
#import <ExpoFileSystem/EXSessionUploadTaskDelegate.h>
|
|
23
|
+
#import <ExpoFileSystem/EXSessionCancelableUploadTaskDelegate.h>
|
|
24
|
+
|
|
25
|
+
NSString * const EXDownloadProgressEventName = @"expo-file-system.downloadProgress";
|
|
26
|
+
NSString * const EXUploadProgressEventName = @"expo-file-system.uploadProgress";
|
|
27
|
+
|
|
28
|
+
typedef NS_ENUM(NSInteger, EXFileSystemSessionType) {
|
|
29
|
+
EXFileSystemBackgroundSession = 0,
|
|
30
|
+
EXFileSystemForegroundSession = 1,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
typedef NS_ENUM(NSInteger, EXFileSystemUploadType) {
|
|
34
|
+
EXFileSystemInvalidType = -1,
|
|
35
|
+
EXFileSystemBinaryContent = 0,
|
|
36
|
+
EXFileSystemMultipart = 1,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
@interface EXFileSystem ()
|
|
40
|
+
|
|
41
|
+
@property (nonatomic, strong) NSURLSession *backgroundSession;
|
|
42
|
+
@property (nonatomic, strong) NSURLSession *foregroundSession;
|
|
43
|
+
@property (nonatomic, strong) EXSessionTaskDispatcher *sessionTaskDispatcher;
|
|
44
|
+
@property (nonatomic, strong) EXTaskHandlersManager *taskHandlersManager;
|
|
45
|
+
@property (nonatomic, weak) EXModuleRegistry *moduleRegistry;
|
|
46
|
+
@property (nonatomic, weak) id<EXEventEmitterService> eventEmitter;
|
|
47
|
+
@property (nonatomic, strong) NSString *documentDirectory;
|
|
48
|
+
@property (nonatomic, strong) NSString *cachesDirectory;
|
|
49
|
+
@property (nonatomic, strong) NSString *bundleDirectory;
|
|
50
|
+
|
|
51
|
+
@end
|
|
52
|
+
|
|
53
|
+
@implementation EXFileSystem
|
|
54
|
+
|
|
55
|
+
EX_REGISTER_MODULE();
|
|
56
|
+
|
|
57
|
+
+ (const NSString *)exportedModuleName
|
|
58
|
+
{
|
|
59
|
+
return @"ExponentFileSystem";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
+ (const NSArray<Protocol *> *)exportedInterfaces
|
|
63
|
+
{
|
|
64
|
+
return @[@protocol(EXFileSystemInterface)];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
- (instancetype)initWithDocumentDirectory:(NSString *)documentDirectory cachesDirectory:(NSString *)cachesDirectory bundleDirectory:(NSString *)bundleDirectory
|
|
68
|
+
{
|
|
69
|
+
if (self = [super init]) {
|
|
70
|
+
_documentDirectory = documentDirectory;
|
|
71
|
+
_cachesDirectory = cachesDirectory;
|
|
72
|
+
_bundleDirectory = bundleDirectory;
|
|
73
|
+
|
|
74
|
+
_taskHandlersManager = [EXTaskHandlersManager new];
|
|
75
|
+
|
|
76
|
+
[EXFileSystem ensureDirExistsWithPath:_documentDirectory];
|
|
77
|
+
[EXFileSystem ensureDirExistsWithPath:_cachesDirectory];
|
|
78
|
+
}
|
|
79
|
+
return self;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
- (instancetype)init
|
|
83
|
+
{
|
|
84
|
+
NSArray<NSString *> *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
85
|
+
NSString *documentDirectory = [documentPaths objectAtIndex:0];
|
|
86
|
+
|
|
87
|
+
NSArray<NSString *> *cachesPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
|
88
|
+
NSString *cacheDirectory = [cachesPaths objectAtIndex:0];
|
|
89
|
+
|
|
90
|
+
return [self initWithDocumentDirectory:documentDirectory
|
|
91
|
+
cachesDirectory:cacheDirectory
|
|
92
|
+
bundleDirectory:[NSBundle mainBundle].bundlePath];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry
|
|
96
|
+
{
|
|
97
|
+
_moduleRegistry = moduleRegistry;
|
|
98
|
+
_eventEmitter = [_moduleRegistry getModuleImplementingProtocol:@protocol(EXEventEmitterService)];
|
|
99
|
+
|
|
100
|
+
_sessionTaskDispatcher = [[EXSessionTaskDispatcher alloc] initWithSessionHandler:[moduleRegistry getSingletonModuleForName:@"SessionHandler"]];
|
|
101
|
+
_backgroundSession = [self _createSession:EXFileSystemBackgroundSession delegate:_sessionTaskDispatcher];
|
|
102
|
+
_foregroundSession = [self _createSession:EXFileSystemForegroundSession delegate:_sessionTaskDispatcher];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (NSDictionary *)constantsToExport
|
|
106
|
+
{
|
|
107
|
+
return @{
|
|
108
|
+
@"documentDirectory": _documentDirectory ? [NSURL fileURLWithPath:_documentDirectory].absoluteString : [NSNull null],
|
|
109
|
+
@"cacheDirectory": _cachesDirectory ? [NSURL fileURLWithPath:_cachesDirectory].absoluteString : [NSNull null],
|
|
110
|
+
@"bundleDirectory": _bundleDirectory ? [NSURL fileURLWithPath:_bundleDirectory].absoluteString : [NSNull null]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
- (NSArray<NSString *> *)supportedEvents
|
|
115
|
+
{
|
|
116
|
+
return @[EXDownloadProgressEventName, EXUploadProgressEventName];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
- (void)startObserving {
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
- (void)stopObserving {
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
- (void)dealloc
|
|
129
|
+
{
|
|
130
|
+
[_sessionTaskDispatcher deactivate];
|
|
131
|
+
[_backgroundSession invalidateAndCancel];
|
|
132
|
+
[_foregroundSession invalidateAndCancel];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
- (NSDictionary *)encodingMap
|
|
136
|
+
{
|
|
137
|
+
/*
|
|
138
|
+
TODO:Bacon: match node.js fs
|
|
139
|
+
https://github.com/nodejs/node/blob/master/lib/buffer.js
|
|
140
|
+
ascii
|
|
141
|
+
base64
|
|
142
|
+
binary
|
|
143
|
+
hex
|
|
144
|
+
ucs2/ucs-2
|
|
145
|
+
utf16le/utf-16le
|
|
146
|
+
utf8/utf-8
|
|
147
|
+
latin1 (ISO8859-1, only in node 6.4.0+)
|
|
148
|
+
*/
|
|
149
|
+
return @{
|
|
150
|
+
@"ascii": @(NSASCIIStringEncoding),
|
|
151
|
+
@"nextstep": @(NSNEXTSTEPStringEncoding),
|
|
152
|
+
@"japaneseeuc": @(NSJapaneseEUCStringEncoding),
|
|
153
|
+
@"utf8": @(NSUTF8StringEncoding),
|
|
154
|
+
@"isolatin1": @(NSISOLatin1StringEncoding),
|
|
155
|
+
@"symbol": @(NSSymbolStringEncoding),
|
|
156
|
+
@"nonlossyascii": @(NSNonLossyASCIIStringEncoding),
|
|
157
|
+
@"shiftjis": @(NSShiftJISStringEncoding),
|
|
158
|
+
@"isolatin2": @(NSISOLatin2StringEncoding),
|
|
159
|
+
@"unicode": @(NSUnicodeStringEncoding),
|
|
160
|
+
@"windowscp1251": @(NSWindowsCP1251StringEncoding),
|
|
161
|
+
@"windowscp1252": @(NSWindowsCP1252StringEncoding),
|
|
162
|
+
@"windowscp1253": @(NSWindowsCP1253StringEncoding),
|
|
163
|
+
@"windowscp1254": @(NSWindowsCP1254StringEncoding),
|
|
164
|
+
@"windowscp1250": @(NSWindowsCP1250StringEncoding),
|
|
165
|
+
@"iso2022jp": @(NSISO2022JPStringEncoding),
|
|
166
|
+
@"macosroman": @(NSMacOSRomanStringEncoding),
|
|
167
|
+
@"utf16": @(NSUTF16StringEncoding),
|
|
168
|
+
@"utf16bigendian": @(NSUTF16BigEndianStringEncoding),
|
|
169
|
+
@"utf16littleendian": @(NSUTF16LittleEndianStringEncoding),
|
|
170
|
+
@"utf32": @(NSUTF32StringEncoding),
|
|
171
|
+
@"utf32bigendian": @(NSUTF32BigEndianStringEncoding),
|
|
172
|
+
@"utf32littleendian": @(NSUTF32LittleEndianStringEncoding),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
EX_EXPORT_METHOD_AS(getInfoAsync,
|
|
177
|
+
getInfoAsyncWithURI:(NSString *)uriString
|
|
178
|
+
withOptions:(NSDictionary *)options
|
|
179
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
180
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
181
|
+
{
|
|
182
|
+
NSURL *uri = [self percentEncodedURLFromURIString:uriString];
|
|
183
|
+
// no scheme provided in uri, handle as a local path and add 'file://' scheme
|
|
184
|
+
if (!uri.scheme) {
|
|
185
|
+
uri = [NSURL fileURLWithPath:uriString isDirectory:false];
|
|
186
|
+
}
|
|
187
|
+
if (!([self permissionsForURI:uri] & EXFileSystemPermissionRead)) {
|
|
188
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
189
|
+
[NSString stringWithFormat:@"File '%@' isn't readable.", uri],
|
|
190
|
+
nil);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if ([uri.scheme isEqualToString:@"file"]) {
|
|
195
|
+
[EXFileSystemLocalFileHandler getInfoForFile:uri withOptions:options resolver:resolve rejecter:reject];
|
|
196
|
+
} else if ([uri.scheme isEqualToString:@"assets-library"] || [uri.scheme isEqualToString:@"ph"]) {
|
|
197
|
+
[EXFileSystemAssetLibraryHandler getInfoForFile:uri withOptions:options resolver:resolve rejecter:reject];
|
|
198
|
+
} else {
|
|
199
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
200
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", uri],
|
|
201
|
+
nil);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
EX_EXPORT_METHOD_AS(readAsStringAsync,
|
|
206
|
+
readAsStringAsyncWithURI:(NSString *)uriString
|
|
207
|
+
withOptions:(NSDictionary *)options
|
|
208
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
209
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
210
|
+
{
|
|
211
|
+
NSURL *uri = [self percentEncodedURLFromURIString:uriString];
|
|
212
|
+
// no scheme provided in uri, handle as a local path and add 'file://' scheme
|
|
213
|
+
if (!uri.scheme) {
|
|
214
|
+
uri = [NSURL fileURLWithPath:uriString isDirectory:false];
|
|
215
|
+
}
|
|
216
|
+
if (!([self permissionsForURI:uri] & EXFileSystemPermissionRead)) {
|
|
217
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
218
|
+
[NSString stringWithFormat:@"File '%@' isn't readable.", uri],
|
|
219
|
+
nil);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if ([uri.scheme isEqualToString:@"file"]) {
|
|
224
|
+
NSString *encodingType = @"utf8";
|
|
225
|
+
if (options[@"encoding"] && [options[@"encoding"] isKindOfClass:[NSString class]]) {
|
|
226
|
+
encodingType = [options[@"encoding"] lowercaseString];
|
|
227
|
+
}
|
|
228
|
+
if ([encodingType isEqualToString:@"base64"]) {
|
|
229
|
+
NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:uri.path];
|
|
230
|
+
if (file == nil) {
|
|
231
|
+
reject(@"ERR_FILESYSTEM_CANNOT_READ_FILE",
|
|
232
|
+
[NSString stringWithFormat:@"File '%@' could not be read.", uri.path],
|
|
233
|
+
nil);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// position and length are used as a cursor/paging system.
|
|
237
|
+
if ([options[@"position"] isKindOfClass:[NSNumber class]]) {
|
|
238
|
+
[file seekToFileOffset:[options[@"position"] intValue]];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
NSData *data;
|
|
242
|
+
if ([options[@"length"] isKindOfClass:[NSNumber class]]) {
|
|
243
|
+
data = [file readDataOfLength:[options[@"length"] intValue]];
|
|
244
|
+
} else {
|
|
245
|
+
data = [file readDataToEndOfFile];
|
|
246
|
+
}
|
|
247
|
+
resolve([data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]);
|
|
248
|
+
} else {
|
|
249
|
+
NSUInteger encoding = NSUTF8StringEncoding;
|
|
250
|
+
id possibleEncoding = [[self encodingMap] valueForKey:encodingType];
|
|
251
|
+
if (possibleEncoding != nil) {
|
|
252
|
+
encoding = [possibleEncoding integerValue];
|
|
253
|
+
}
|
|
254
|
+
NSError *error;
|
|
255
|
+
NSString *string = [NSString stringWithContentsOfFile:uri.path encoding:encoding error:&error];
|
|
256
|
+
if (string) {
|
|
257
|
+
resolve(string);
|
|
258
|
+
} else {
|
|
259
|
+
reject(@"ERR_FILESYSTEM_CANNOT_READ_FILE",
|
|
260
|
+
[NSString stringWithFormat:@"File '%@' could not be read.", uri],
|
|
261
|
+
error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
266
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", uri],
|
|
267
|
+
nil);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
EX_EXPORT_METHOD_AS(writeAsStringAsync,
|
|
272
|
+
writeAsStringAsyncWithURI:(NSString *)uriString
|
|
273
|
+
withString:(NSString *)string
|
|
274
|
+
withOptions:(NSDictionary *)options
|
|
275
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
276
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
277
|
+
{
|
|
278
|
+
NSURL *uri = [self percentEncodedURLFromURIString:uriString];
|
|
279
|
+
if (!([self permissionsForURI:uri] & EXFileSystemPermissionWrite)) {
|
|
280
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
281
|
+
[NSString stringWithFormat:@"File '%@' isn't writable.", uri],
|
|
282
|
+
nil);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if ([uri.scheme isEqualToString:@"file"]) {
|
|
287
|
+
NSString *encodingType = @"utf8";
|
|
288
|
+
if ([options[@"encoding"] isKindOfClass:[NSString class]]) {
|
|
289
|
+
encodingType = [options[@"encoding"] lowercaseString];
|
|
290
|
+
}
|
|
291
|
+
if ([encodingType isEqualToString:@"base64"]) {
|
|
292
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
293
|
+
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:string options:NSDataBase64DecodingIgnoreUnknownCharacters];
|
|
294
|
+
if (imageData) {
|
|
295
|
+
// TODO:Bacon: Should we surface `attributes`?
|
|
296
|
+
if ([[NSFileManager defaultManager] createFileAtPath:uri.path contents:imageData attributes:nil]) {
|
|
297
|
+
resolve([NSNull null]);
|
|
298
|
+
} else {
|
|
299
|
+
return reject(@"ERR_FILESYSTEM_UNKNOWN_FILE",
|
|
300
|
+
[NSString stringWithFormat:@"No such file or directory '%@'", uri.path],
|
|
301
|
+
nil);
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
reject(@"ERR_FILESYSTEM_INVALID_FORMAT",
|
|
305
|
+
@"Failed to parse base64 string.",
|
|
306
|
+
nil);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
} else {
|
|
310
|
+
NSUInteger encoding = NSUTF8StringEncoding;
|
|
311
|
+
id possibleEncoding = [[self encodingMap] valueForKey:encodingType];
|
|
312
|
+
if (possibleEncoding != nil) {
|
|
313
|
+
encoding = [possibleEncoding integerValue];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
NSError *error;
|
|
317
|
+
if ([string writeToFile:uri.path atomically:YES encoding:encoding error:&error]) {
|
|
318
|
+
resolve([NSNull null]);
|
|
319
|
+
} else {
|
|
320
|
+
reject(@"ERR_FILESYSTEM_CANNOT_WRITE_TO_FILE",
|
|
321
|
+
[NSString stringWithFormat:@"File '%@' could not be written.", uri],
|
|
322
|
+
error);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
327
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", uri],
|
|
328
|
+
nil);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
EX_EXPORT_METHOD_AS(deleteAsync,
|
|
333
|
+
deleteAsyncWithURI:(NSString *)uriString
|
|
334
|
+
withOptions:(NSDictionary *)options
|
|
335
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
336
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
337
|
+
{
|
|
338
|
+
NSURL *uri = [self percentEncodedURLFromURIString:uriString];
|
|
339
|
+
if (!([self permissionsForURI:[uri URLByAppendingPathComponent:@".."]] & EXFileSystemPermissionWrite)) {
|
|
340
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
341
|
+
[NSString stringWithFormat:@"Location '%@' isn't deletable.", uri],
|
|
342
|
+
nil);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if ([uri.scheme isEqualToString:@"file"]) {
|
|
347
|
+
NSString *path = uri.path;
|
|
348
|
+
if ([self _checkIfFileExists:path]) {
|
|
349
|
+
NSError *error;
|
|
350
|
+
if ([[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
|
|
351
|
+
resolve([NSNull null]);
|
|
352
|
+
} else {
|
|
353
|
+
reject(@"ERR_FILESYSTEM_CANNOT_DELETE_FILE",
|
|
354
|
+
[NSString stringWithFormat:@"File '%@' could not be deleted.", uri],
|
|
355
|
+
error);
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
if (options[@"idempotent"]) {
|
|
359
|
+
resolve([NSNull null]);
|
|
360
|
+
} else {
|
|
361
|
+
reject(@"ERR_FILESYSTEM_CANNOT_FIND_FILE",
|
|
362
|
+
[NSString stringWithFormat:@"File '%@' could not be deleted because it could not be found.", uri],
|
|
363
|
+
nil);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
368
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", uri],
|
|
369
|
+
nil);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
EX_EXPORT_METHOD_AS(moveAsync,
|
|
374
|
+
moveAsyncWithOptions:(NSDictionary *)options
|
|
375
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
376
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
377
|
+
{
|
|
378
|
+
NSURL *from = [NSURL URLWithString:options[@"from"]];
|
|
379
|
+
if (!from) {
|
|
380
|
+
reject(@"ERR_FILESYSTEM_MISSING_PARAMETER", @"Need a `from` location.", nil);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (!([self permissionsForURI:[from URLByAppendingPathComponent:@".."]] & EXFileSystemPermissionWrite)) {
|
|
384
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
385
|
+
[NSString stringWithFormat:@"Location '%@' isn't movable.", from],
|
|
386
|
+
nil);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
NSURL *to = [NSURL URLWithString:options[@"to"]];
|
|
390
|
+
if (!to) {
|
|
391
|
+
reject(@"ERR_FILESYSTEM_MISSING_PARAMETER", @"Need a `to` location.", nil);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!([self permissionsForURI:to] & EXFileSystemPermissionWrite)) {
|
|
395
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
396
|
+
[NSString stringWithFormat:@"File '%@' isn't writable.", to],
|
|
397
|
+
nil);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// NOTE: The destination-delete and the move should happen atomically, but we hope for the best for now
|
|
402
|
+
if ([from.scheme isEqualToString:@"file"]) {
|
|
403
|
+
NSString *fromPath = [from.path stringByStandardizingPath];
|
|
404
|
+
NSString *toPath = [to.path stringByStandardizingPath];
|
|
405
|
+
NSError *error;
|
|
406
|
+
if ([self _checkIfFileExists:toPath]) {
|
|
407
|
+
if (![[NSFileManager defaultManager] removeItemAtPath:toPath error:&error]) {
|
|
408
|
+
reject(@"ERR_FILESYSTEM_CANNOT_MOVE_FILE",
|
|
409
|
+
[NSString stringWithFormat:@"File '%@' could not be moved to '%@' because a file already exists at "
|
|
410
|
+
"the destination and could not be deleted.", from, to],
|
|
411
|
+
error);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if ([[NSFileManager defaultManager] moveItemAtPath:fromPath toPath:toPath error:&error]) {
|
|
416
|
+
resolve([NSNull null]);
|
|
417
|
+
} else {
|
|
418
|
+
reject(@"ERR_FILESYSTEM_CANNOT_MOVE_FILE",
|
|
419
|
+
[NSString stringWithFormat:@"File '%@' could not be moved to '%@'.", from, to],
|
|
420
|
+
error);
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
424
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", from],
|
|
425
|
+
nil);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
EX_EXPORT_METHOD_AS(copyAsync,
|
|
430
|
+
copyAsyncWithOptions:(NSDictionary *)options
|
|
431
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
432
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
433
|
+
{
|
|
434
|
+
NSURL *from = [NSURL URLWithString:options[@"from"]];
|
|
435
|
+
if (!from) {
|
|
436
|
+
reject(@"ERR_FILESYSTEM_MISSING_PARAMETER", @"Need a `from` location.", nil);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (!([self permissionsForURI:from] & EXFileSystemPermissionRead)) {
|
|
440
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
441
|
+
[NSString stringWithFormat:@"File '%@' isn't readable.", from],
|
|
442
|
+
nil);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
NSURL *to = [NSURL URLWithString:options[@"to"]];
|
|
446
|
+
if (!to) {
|
|
447
|
+
reject(@"ERR_FILESYSTEM_MISSING_PARAMETER", @"Need a `to` location.", nil);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (!([self permissionsForURI:to] & EXFileSystemPermissionWrite)) {
|
|
451
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
452
|
+
[NSString stringWithFormat:@"File '%@' isn't writable.", to],
|
|
453
|
+
nil);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if ([from.scheme isEqualToString:@"file"]) {
|
|
458
|
+
[EXFileSystemLocalFileHandler copyFrom:from to:to resolver:resolve rejecter:reject];
|
|
459
|
+
} else if ([from.scheme isEqualToString:@"assets-library"] || [from.scheme isEqualToString:@"ph"]) {
|
|
460
|
+
[EXFileSystemAssetLibraryHandler copyFrom:from to:to resolver:resolve rejecter:reject];
|
|
461
|
+
} else {
|
|
462
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
463
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", from],
|
|
464
|
+
nil);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
EX_EXPORT_METHOD_AS(makeDirectoryAsync,
|
|
469
|
+
makeDirectoryAsyncWithURI:(NSString *)uriString
|
|
470
|
+
withOptions:(NSDictionary *)options
|
|
471
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
472
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
473
|
+
{
|
|
474
|
+
|
|
475
|
+
NSURL *uri = [self percentEncodedURLFromURIString:uriString];
|
|
476
|
+
if (!([self permissionsForURI:uri] & EXFileSystemPermissionWrite)) {
|
|
477
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
478
|
+
[NSString stringWithFormat:@"Directory '%@' could not be created because the location isn't writable.", uri],
|
|
479
|
+
nil);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if ([uri.scheme isEqualToString:@"file"]) {
|
|
484
|
+
NSError *error;
|
|
485
|
+
if ([[NSFileManager defaultManager] createDirectoryAtPath:uri.path
|
|
486
|
+
withIntermediateDirectories:[options[@"intermediates"] boolValue]
|
|
487
|
+
attributes:nil
|
|
488
|
+
error:&error]) {
|
|
489
|
+
resolve([NSNull null]);
|
|
490
|
+
} else {
|
|
491
|
+
reject(@"ERR_FILESYSTEM_CANNOT_CREATE_DIRECTORY",
|
|
492
|
+
[NSString stringWithFormat:@"Directory '%@' could not be created.", uri],
|
|
493
|
+
error);
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
497
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", uri],
|
|
498
|
+
nil);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
EX_EXPORT_METHOD_AS(readDirectoryAsync,
|
|
503
|
+
readDirectoryAsyncWithURI:(NSString *)uriString
|
|
504
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
505
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
506
|
+
{
|
|
507
|
+
NSURL *uri = [self percentEncodedURLFromURIString:uriString];
|
|
508
|
+
if (!([self permissionsForURI:uri] & EXFileSystemPermissionRead)) {
|
|
509
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
510
|
+
[NSString stringWithFormat:@"Location '%@' isn't readable.", uri],
|
|
511
|
+
nil);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if ([uri.scheme isEqualToString:@"file"]) {
|
|
516
|
+
NSError *error;
|
|
517
|
+
NSArray<NSString *> *children = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:uri.path error:&error];
|
|
518
|
+
if (children) {
|
|
519
|
+
resolve(children);
|
|
520
|
+
} else {
|
|
521
|
+
reject(@"ERR_FILESYSTEM_CANNOT_READ_DIRECTORY",
|
|
522
|
+
[NSString stringWithFormat:@"Directory '%@' could not be read.", uri],
|
|
523
|
+
error);
|
|
524
|
+
}
|
|
525
|
+
} else {
|
|
526
|
+
reject(@"ERR_FILESYSTEM_INVALID_URI",
|
|
527
|
+
[NSString stringWithFormat:@"Unsupported URI scheme for '%@'", uri],
|
|
528
|
+
nil);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
EX_EXPORT_METHOD_AS(downloadAsync,
|
|
533
|
+
downloadAsyncWithUrl:(NSString *)urlString
|
|
534
|
+
localURI:(NSString *)localUriString
|
|
535
|
+
options:(NSDictionary *)options
|
|
536
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
537
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
538
|
+
{
|
|
539
|
+
NSURL *url = [NSURL URLWithString:urlString];
|
|
540
|
+
NSURL *localUri = [self percentEncodedURLFromURIString:localUriString];
|
|
541
|
+
if (!([self checkIfFileDirExists:localUri.path])) {
|
|
542
|
+
reject(@"ERR_FILESYSTEM_WRONG_DESTINATION",
|
|
543
|
+
[NSString stringWithFormat:@"Directory for '%@' doesn't exist. Please make sure directory '%@' exists before calling downloadAsync.", localUriString, [localUri.path stringByDeletingLastPathComponent]],
|
|
544
|
+
nil);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (!([self permissionsForURI:localUri] & EXFileSystemPermissionWrite)) {
|
|
548
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
549
|
+
[NSString stringWithFormat:@"File '%@' isn't writable.", localUri],
|
|
550
|
+
nil);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (![self _checkHeadersDictionary:options[@"headers"]]) {
|
|
554
|
+
reject(@"ERR_FILESYSTEM_INVALID_HEADERS",
|
|
555
|
+
@"Invalid headers dictionary. Keys and values should be strings.",
|
|
556
|
+
nil);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
NSURLSession *session = [self _sessionForType:[options[@"sessionType"] intValue]];
|
|
561
|
+
if (!session) {
|
|
562
|
+
reject(@"ERR_FILESYSTEM_INVALID_SESSION_TYPE",
|
|
563
|
+
[NSString stringWithFormat:@"Invalid session type: '%@'", options[@"sessionType"]],
|
|
564
|
+
nil);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
NSURLRequest *request = [self _createRequest:url headers:options[@"headers"]];
|
|
569
|
+
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request];
|
|
570
|
+
EXSessionTaskDelegate *taskDelegate = [[EXSessionDownloadTaskDelegate alloc] initWithResolve:resolve
|
|
571
|
+
reject:reject
|
|
572
|
+
localUrl:localUri
|
|
573
|
+
shouldCalculateMd5:[options[@"md5"] boolValue]];
|
|
574
|
+
[_sessionTaskDispatcher registerTaskDelegate:taskDelegate forTask:task];
|
|
575
|
+
[task resume];
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
EX_EXPORT_METHOD_AS(uploadAsync,
|
|
579
|
+
uploadAsync:(NSString *)urlString
|
|
580
|
+
localURI:(NSString *)fileUriString
|
|
581
|
+
options:(NSDictionary *)options
|
|
582
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
583
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
584
|
+
{
|
|
585
|
+
NSURLSessionUploadTask *task = [self createUploadTask:urlString localURI:fileUriString options:options rejecter:reject];
|
|
586
|
+
if (!task) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
EXSessionTaskDelegate *taskDelegate = [[EXSessionUploadTaskDelegate alloc] initWithResolve:resolve reject:reject];
|
|
591
|
+
[_sessionTaskDispatcher registerTaskDelegate:taskDelegate forTask:task];
|
|
592
|
+
[task resume];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
EX_EXPORT_METHOD_AS(uploadTaskStartAsync,
|
|
596
|
+
uploadTaskStartAsync:(NSString *)urlString
|
|
597
|
+
localURI:(NSString *)fileUriString
|
|
598
|
+
uuid:(NSString *)uuid
|
|
599
|
+
options:(NSDictionary *)options
|
|
600
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
601
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
602
|
+
{
|
|
603
|
+
NSURLSessionUploadTask *task = [self createUploadTask:urlString localURI:fileUriString options:options rejecter:reject];
|
|
604
|
+
if (!task) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
EX_WEAKIFY(self);
|
|
609
|
+
EXUploadDelegateOnSendCallback onSend = ^(NSURLSessionUploadTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
|
|
610
|
+
EX_ENSURE_STRONGIFY(self);
|
|
611
|
+
[self sendEventWithName:EXUploadProgressEventName
|
|
612
|
+
body:@{
|
|
613
|
+
@"uuid": uuid,
|
|
614
|
+
@"data": @{
|
|
615
|
+
@"totalBytesSent": @(totalBytesSent),
|
|
616
|
+
@"totalBytesExpectedToSend": @(totalBytesExpectedToSend),
|
|
617
|
+
},
|
|
618
|
+
}];
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
EXSessionTaskDelegate *taskDelegate = [[EXSessionCancelableUploadTaskDelegate alloc] initWithResolve:resolve
|
|
622
|
+
reject:reject
|
|
623
|
+
onSendCallback:onSend
|
|
624
|
+
resumableManager:_taskHandlersManager
|
|
625
|
+
uuid:uuid];
|
|
626
|
+
|
|
627
|
+
[_sessionTaskDispatcher registerTaskDelegate:taskDelegate forTask:task];
|
|
628
|
+
[_taskHandlersManager registerTask:task uuid:uuid];
|
|
629
|
+
[task resume];
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
- (NSURLSessionUploadTask * _Nullable)createUploadTask:(NSString *)urlString
|
|
633
|
+
localURI:(NSString *)fileUriString
|
|
634
|
+
options:(NSDictionary *)options
|
|
635
|
+
rejecter:(EXPromiseRejectBlock)reject
|
|
636
|
+
{
|
|
637
|
+
NSURL *fileUri = [self percentEncodedURLFromURIString:fileUriString];
|
|
638
|
+
NSString *httpMethod = options[@"httpMethod"];
|
|
639
|
+
EXFileSystemUploadType type = [self _getUploadTypeFrom:options[@"uploadType"]];
|
|
640
|
+
if (![fileUri.scheme isEqualToString:@"file"]) {
|
|
641
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
642
|
+
[NSString stringWithFormat:@"Cannot upload file '%@'. Only 'file://' URI are supported.", fileUri],
|
|
643
|
+
nil);
|
|
644
|
+
return nil;
|
|
645
|
+
}
|
|
646
|
+
if (!([self _checkIfFileExists:fileUri.path])) {
|
|
647
|
+
reject(@"ERR_FILE_NOT_EXISTS",
|
|
648
|
+
[NSString stringWithFormat:@"File '%@' does not exist.", fileUri],
|
|
649
|
+
nil);
|
|
650
|
+
return nil;
|
|
651
|
+
}
|
|
652
|
+
if (![self _checkHeadersDictionary:options[@"headers"]]) {
|
|
653
|
+
reject(@"ERR_FILESYSTEM_INVALID_HEADERS_DICTIONARY",
|
|
654
|
+
@"Invalid headers dictionary. Keys and values should be strings.",
|
|
655
|
+
nil);
|
|
656
|
+
return nil;
|
|
657
|
+
}
|
|
658
|
+
if (!httpMethod) {
|
|
659
|
+
reject(@"ERR_FILESYSTEM_MISSING_HTTP_METHOD", @"Missing HTTP method.", nil);
|
|
660
|
+
return nil;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
NSMutableURLRequest *request = [self _createRequest:[NSURL URLWithString:urlString] headers:options[@"headers"]];
|
|
664
|
+
[request setHTTPMethod:httpMethod];
|
|
665
|
+
NSURLSession *session = [self _sessionForType:[options[@"sessionType"] intValue]];
|
|
666
|
+
if (!session) {
|
|
667
|
+
reject(@"ERR_FILESYSTEM_INVALID_SESSION_TYPE",
|
|
668
|
+
[NSString stringWithFormat:@"Invalid session type: '%@'", options[@"sessionType"]],
|
|
669
|
+
nil);
|
|
670
|
+
return nil;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
NSURLSessionUploadTask *task;
|
|
674
|
+
if (type == EXFileSystemBinaryContent) {
|
|
675
|
+
task = [session uploadTaskWithRequest:request fromFile:fileUri];
|
|
676
|
+
} else if (type == EXFileSystemMultipart) {
|
|
677
|
+
NSString *boundaryString = [[NSUUID UUID] UUIDString];
|
|
678
|
+
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundaryString] forHTTPHeaderField:@"Content-Type"];
|
|
679
|
+
NSData *httpBody = [self _createBodyWithBoundary:boundaryString
|
|
680
|
+
fileUri:fileUri
|
|
681
|
+
parameters:options[@"parameters"]
|
|
682
|
+
fieldName:options[@"fieldName"]
|
|
683
|
+
mimeType:options[@"mimeType"]];
|
|
684
|
+
[request setHTTPBody:httpBody];
|
|
685
|
+
task = [session uploadTaskWithStreamedRequest:request];
|
|
686
|
+
} else {
|
|
687
|
+
reject(@"ERR_FILESYSTEM_INVALID_UPLOAD_TYPE",
|
|
688
|
+
[NSString stringWithFormat:@"Invalid upload type: '%@'.", options[@"uploadType"]],
|
|
689
|
+
nil);
|
|
690
|
+
}
|
|
691
|
+
return task;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
EX_EXPORT_METHOD_AS(downloadResumableStartAsync,
|
|
695
|
+
downloadResumableStartAsyncWithUrl:(NSString *)urlString
|
|
696
|
+
fileURI:(NSString *)fileUri
|
|
697
|
+
uuid:(NSString *)uuid
|
|
698
|
+
options:(NSDictionary *)options
|
|
699
|
+
resumeData:(NSString *)data
|
|
700
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
701
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
702
|
+
{
|
|
703
|
+
NSURL *url = [NSURL URLWithString:urlString];
|
|
704
|
+
NSURL *localUrl = [self percentEncodedURLFromURIString:fileUri];
|
|
705
|
+
if (!([self checkIfFileDirExists:localUrl.path])) {
|
|
706
|
+
reject(@"ERR_FILESYSTEM_WRONG_DESTINATION",
|
|
707
|
+
[NSString stringWithFormat:@"Directory for '%@' doesn't exist. Please make sure directory '%@' exists before calling downloadAsync.", fileUri, [localUrl.path stringByDeletingLastPathComponent]],
|
|
708
|
+
nil);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (![localUrl.scheme isEqualToString:@"file"]) {
|
|
712
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
713
|
+
[NSString stringWithFormat:@"Cannot download to '%@': only 'file://' URI destinations are supported.", fileUri],
|
|
714
|
+
nil);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
NSString *path = localUrl.path;
|
|
719
|
+
if (!([self _permissionsForPath:path] & EXFileSystemPermissionWrite)) {
|
|
720
|
+
reject(@"ERR_FILESYSTEM_NO_PERMISSIONS",
|
|
721
|
+
[NSString stringWithFormat:@"File '%@' isn't writable.", fileUri],
|
|
722
|
+
nil);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (![self _checkHeadersDictionary:options[@"headers"]]) {
|
|
727
|
+
reject(@"ERR_FILESYSTEM_INVALID_HEADERS_DICTIONARY",
|
|
728
|
+
@"Invalid headers dictionary. Keys and values should be strings.",
|
|
729
|
+
nil);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
NSData *resumeData = data ? [[NSData alloc] initWithBase64EncodedString:data options:0] : nil;
|
|
734
|
+
[self _downloadResumableCreateSessionWithUrl:url
|
|
735
|
+
fileUrl:localUrl
|
|
736
|
+
uuid:uuid
|
|
737
|
+
optins:options
|
|
738
|
+
resumeData:resumeData
|
|
739
|
+
resolve:resolve
|
|
740
|
+
reject:reject];
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
EX_EXPORT_METHOD_AS(downloadResumablePauseAsync,
|
|
744
|
+
downloadResumablePauseAsyncWithUUID:(NSString *)uuid
|
|
745
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
746
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
747
|
+
{
|
|
748
|
+
NSURLSessionDownloadTask *task = [_taskHandlersManager downloadTaskForId:uuid];
|
|
749
|
+
if (!task) {
|
|
750
|
+
reject(@"ERR_FILESYSTEM_CANNOT_FIND_TASK",
|
|
751
|
+
[NSString stringWithFormat:@"There is no download object with UUID: %@", uuid],
|
|
752
|
+
nil);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
EX_WEAKIFY(self);
|
|
757
|
+
[task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
|
|
758
|
+
EX_ENSURE_STRONGIFY(self);
|
|
759
|
+
resolve(@{ @"resumeData": EXNullIfNil([resumeData base64EncodedStringWithOptions:0]) });
|
|
760
|
+
}];
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
EX_EXPORT_METHOD_AS(networkTaskCancelAsync,
|
|
764
|
+
networkTaskCancelAsyncWithUUID:(NSString *)uuid
|
|
765
|
+
resolver:(EXPromiseResolveBlock)resolve
|
|
766
|
+
rejecter:(EXPromiseRejectBlock)reject)
|
|
767
|
+
{
|
|
768
|
+
NSURLSessionTask *task = [_taskHandlersManager taskForId:uuid];
|
|
769
|
+
if (task) {
|
|
770
|
+
[task cancel];
|
|
771
|
+
}
|
|
772
|
+
resolve([NSNull null]);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
EX_EXPORT_METHOD_AS(getFreeDiskStorageAsync, getFreeDiskStorageAsyncWithResolver:(EXPromiseResolveBlock)resolve rejecter:(EXPromiseRejectBlock)reject)
|
|
776
|
+
{
|
|
777
|
+
NSError *error = nil;
|
|
778
|
+
NSNumber *freeDiskStorage = [self freeDiskStorageWithError:&error];
|
|
779
|
+
|
|
780
|
+
if(!freeDiskStorage || error) {
|
|
781
|
+
reject(@"ERR_FILESYSTEM_CANNOT_DETERMINE_DISK_CAPACITY", @"Unable to determine free disk storage capacity", error);
|
|
782
|
+
} else {
|
|
783
|
+
resolve(freeDiskStorage);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
EX_EXPORT_METHOD_AS(getTotalDiskCapacityAsync, getTotalDiskCapacityAsyncWithResolver:(EXPromiseResolveBlock)resolve rejecter:(EXPromiseRejectBlock)reject)
|
|
788
|
+
{
|
|
789
|
+
NSError *error = nil;
|
|
790
|
+
NSNumber *diskCapacity = [self totalDiskCapacityWithError:&error];
|
|
791
|
+
|
|
792
|
+
if (!diskCapacity || error) {
|
|
793
|
+
reject(@"ERR_FILESYSTEM_CANNOT_DETERMINE_DISK_CAPACITY", @"Unable to determine total disk capacity", error);
|
|
794
|
+
} else {
|
|
795
|
+
resolve(diskCapacity);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
#pragma mark - Internal methods
|
|
800
|
+
|
|
801
|
+
- (EXFileSystemUploadType)_getUploadTypeFrom:(NSNumber * _Nullable)type
|
|
802
|
+
{
|
|
803
|
+
switch ([type intValue]) {
|
|
804
|
+
case EXFileSystemBinaryContent:
|
|
805
|
+
case EXFileSystemMultipart:
|
|
806
|
+
return [type intValue];
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return EXFileSystemInvalidType;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Borrowed from http://stackoverflow.com/questions/2439020/wheres-the-iphone-mime-type-database
|
|
813
|
+
- (NSString *)_guessMIMETypeFromPath:(NSString *)path
|
|
814
|
+
{
|
|
815
|
+
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL);
|
|
816
|
+
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
|
|
817
|
+
if (UTI) {
|
|
818
|
+
CFRelease(UTI);
|
|
819
|
+
}
|
|
820
|
+
if (!MIMEType) {
|
|
821
|
+
return @"application/octet-stream";
|
|
822
|
+
}
|
|
823
|
+
return (__bridge NSString *)(MIMEType);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
- (NSData *)_createBodyWithBoundary:(NSString *)boundary
|
|
827
|
+
fileUri:(NSURL *)fileUri
|
|
828
|
+
parameters:(NSDictionary * _Nullable)parameters
|
|
829
|
+
fieldName:(NSString * _Nullable)fieldName
|
|
830
|
+
mimeType:(NSString * _Nullable)mimetype
|
|
831
|
+
{
|
|
832
|
+
|
|
833
|
+
NSMutableData *body = [NSMutableData data];
|
|
834
|
+
NSData *data = [NSData dataWithContentsOfURL:fileUri];
|
|
835
|
+
NSString *filename = [[fileUri path] lastPathComponent];
|
|
836
|
+
|
|
837
|
+
if (!mimetype) {
|
|
838
|
+
mimetype = [self _guessMIMETypeFromPath:[fileUri path]];
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
|
|
842
|
+
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
843
|
+
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", parameterKey] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
844
|
+
[body appendData:[[NSString stringWithFormat:@"%@\r\n", parameterValue] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
845
|
+
}];
|
|
846
|
+
|
|
847
|
+
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
848
|
+
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName ?: filename, filename] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
849
|
+
[body appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimetype] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
850
|
+
[body appendData:data];
|
|
851
|
+
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
|
852
|
+
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
853
|
+
|
|
854
|
+
return body;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
- (NSMutableURLRequest *)_createRequest:(NSURL *)url headers:(NSDictionary * _Nullable)headers
|
|
858
|
+
{
|
|
859
|
+
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
|
|
860
|
+
if (headers != nil) {
|
|
861
|
+
for (NSString *headerKey in headers) {
|
|
862
|
+
[request setValue:[headers valueForKey:headerKey] forHTTPHeaderField:headerKey];
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return request;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
- (NSURLSession *)_sessionForType:(EXFileSystemSessionType)type
|
|
870
|
+
{
|
|
871
|
+
switch (type) {
|
|
872
|
+
case EXFileSystemBackgroundSession:
|
|
873
|
+
return _backgroundSession;
|
|
874
|
+
case EXFileSystemForegroundSession:
|
|
875
|
+
return _foregroundSession;
|
|
876
|
+
}
|
|
877
|
+
return nil;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
- (BOOL)_checkHeadersDictionary:(NSDictionary * _Nullable)headers
|
|
881
|
+
{
|
|
882
|
+
for (id key in [headers allKeys]) {
|
|
883
|
+
if (![key isKindOfClass:[NSString class]] || ![headers[key] isKindOfClass:[NSString class]]) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
- (NSURLSession *)_createSession:(EXFileSystemSessionType)type delegate:(id<NSURLSessionDelegate>)delegate
|
|
892
|
+
{
|
|
893
|
+
NSURLSessionConfiguration *sessionConfiguration;
|
|
894
|
+
if (type == EXFileSystemForegroundSession) {
|
|
895
|
+
sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
896
|
+
} else {
|
|
897
|
+
sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[[NSUUID UUID] UUIDString]];
|
|
898
|
+
}
|
|
899
|
+
sessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
|
900
|
+
sessionConfiguration.URLCache = nil;
|
|
901
|
+
return [NSURLSession sessionWithConfiguration:sessionConfiguration
|
|
902
|
+
delegate:delegate
|
|
903
|
+
delegateQueue:nil];
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
- (BOOL)_checkIfFileExists:(NSString *)path
|
|
907
|
+
{
|
|
908
|
+
return [[NSFileManager defaultManager] fileExistsAtPath:path];
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
- (void)_downloadResumableCreateSessionWithUrl:(NSURL *)url
|
|
912
|
+
fileUrl:(NSURL *)fileUrl
|
|
913
|
+
uuid:(NSString *)uuid
|
|
914
|
+
optins:(NSDictionary *)options
|
|
915
|
+
resumeData:(NSData * _Nullable)resumeData
|
|
916
|
+
resolve:(EXPromiseResolveBlock)resolve
|
|
917
|
+
reject:(EXPromiseRejectBlock)reject
|
|
918
|
+
{
|
|
919
|
+
EX_WEAKIFY(self);
|
|
920
|
+
EXDownloadDelegateOnWriteCallback onWrite = ^(NSURLSessionDownloadTask *task, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
|
|
921
|
+
EX_ENSURE_STRONGIFY(self);
|
|
922
|
+
[self sendEventWithName:EXDownloadProgressEventName
|
|
923
|
+
body:@{
|
|
924
|
+
@"uuid": uuid,
|
|
925
|
+
@"data": @{
|
|
926
|
+
@"totalBytesWritten": @(totalBytesWritten),
|
|
927
|
+
@"totalBytesExpectedToWrite": @(totalBytesExpectedToWrite),
|
|
928
|
+
},
|
|
929
|
+
}];
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
NSURLSessionDownloadTask *downloadTask;
|
|
933
|
+
NSURLSession *session = [self _sessionForType:[options[@"sessionType"] intValue]];
|
|
934
|
+
if (!session) {
|
|
935
|
+
reject(@"ERR_FILESYSTEM_INVALID_SESSION_TYPE",
|
|
936
|
+
[NSString stringWithFormat:@"Invalid session type: '%@'", options[@"sessionType"]],
|
|
937
|
+
nil);
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (resumeData) {
|
|
942
|
+
downloadTask = [session downloadTaskWithResumeData:resumeData];
|
|
943
|
+
} else {
|
|
944
|
+
NSURLRequest *request = [self _createRequest:url headers:options[@"headers"]];
|
|
945
|
+
downloadTask = [session downloadTaskWithRequest:request];
|
|
946
|
+
}
|
|
947
|
+
EXSessionTaskDelegate *taskDelegate = [[EXSessionResumableDownloadTaskDelegate alloc] initWithResolve:resolve
|
|
948
|
+
reject:reject
|
|
949
|
+
localUrl:fileUrl
|
|
950
|
+
shouldCalculateMd5:[options[@"md5"] boolValue]
|
|
951
|
+
onWriteCallback:onWrite
|
|
952
|
+
resumableManager:_taskHandlersManager
|
|
953
|
+
uuid:uuid];
|
|
954
|
+
[_sessionTaskDispatcher registerTaskDelegate:taskDelegate forTask:downloadTask];
|
|
955
|
+
[_taskHandlersManager registerTask:downloadTask uuid:uuid];
|
|
956
|
+
[downloadTask resume];
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
- (EXFileSystemPermissionFlags)_permissionsForPath:(NSString *)path
|
|
960
|
+
{
|
|
961
|
+
return [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXFilePermissionModuleInterface)] getPathPermissions:(NSString *)path];
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
- (void)sendEventWithName:(NSString *)eventName body:(id)body
|
|
965
|
+
{
|
|
966
|
+
if (_eventEmitter != nil) {
|
|
967
|
+
[_eventEmitter sendEventWithName:eventName body:body];
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
- (NSDictionary<NSURLResourceKey, id> *)documentFileResourcesForKeys:(NSArray<NSURLResourceKey> *)keys
|
|
972
|
+
error:(out NSError * __autoreleasing *)error
|
|
973
|
+
{
|
|
974
|
+
if (!keys.count) {
|
|
975
|
+
return @{};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
NSURL *documentDirectoryUrl = [NSURL fileURLWithPath:_documentDirectory];
|
|
979
|
+
NSDictionary *results = [documentDirectoryUrl resourceValuesForKeys:keys
|
|
980
|
+
error:error];
|
|
981
|
+
|
|
982
|
+
if (!results) {
|
|
983
|
+
return @{};
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return results;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
#pragma mark - Public utils
|
|
990
|
+
|
|
991
|
+
- (EXFileSystemPermissionFlags)permissionsForURI:(NSURL *)uri
|
|
992
|
+
{
|
|
993
|
+
NSArray *validSchemas = @[
|
|
994
|
+
@"assets-library",
|
|
995
|
+
@"http",
|
|
996
|
+
@"https",
|
|
997
|
+
@"ph",
|
|
998
|
+
];
|
|
999
|
+
if ([validSchemas containsObject:uri.scheme]) {
|
|
1000
|
+
return EXFileSystemPermissionRead;
|
|
1001
|
+
}
|
|
1002
|
+
if ([uri.scheme isEqualToString:@"file"]) {
|
|
1003
|
+
return [self _permissionsForPath:uri.path];
|
|
1004
|
+
}
|
|
1005
|
+
return EXFileSystemPermissionNone;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
- (BOOL)checkIfFileDirExists:(NSString *)path
|
|
1009
|
+
{
|
|
1010
|
+
NSString *dir = [path stringByDeletingLastPathComponent];
|
|
1011
|
+
return [self _checkIfFileExists:dir];
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
Given an URI string, returns a percent-encoded URL as an NSURL object.
|
|
1016
|
+
Only encodes characters that are outside the set of allowed characters defined by RFC3986.
|
|
1017
|
+
*/
|
|
1018
|
+
- (nullable NSURL *)percentEncodedURLFromURIString:(nonnull NSString *)uri
|
|
1019
|
+
{
|
|
1020
|
+
NSMutableCharacterSet *allowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet];
|
|
1021
|
+
[allowedCharacterSet addCharactersInString:@"-._~:/?#[]@!$&'()*+,;="];
|
|
1022
|
+
NSString *encodedUriString = [uri stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
|
|
1023
|
+
|
|
1024
|
+
return [NSURL URLWithString:[encodedUriString stringByReplacingOccurrencesOfString:@"%25" withString:@"%"]];
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
#pragma mark - Class methods
|
|
1028
|
+
|
|
1029
|
+
- (BOOL)ensureDirExistsWithPath:(NSString *)path
|
|
1030
|
+
{
|
|
1031
|
+
return [EXFileSystem ensureDirExistsWithPath:path];
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
+ (BOOL)ensureDirExistsWithPath:(NSString *)path
|
|
1035
|
+
{
|
|
1036
|
+
BOOL isDir = NO;
|
|
1037
|
+
NSError *error;
|
|
1038
|
+
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
|
|
1039
|
+
if (!(exists && isDir)) {
|
|
1040
|
+
[[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
|
|
1041
|
+
if (error) {
|
|
1042
|
+
return NO;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
return YES;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
- (NSString *)generatePathInDirectory:(NSString *)directory withExtension:(NSString *)extension
|
|
1049
|
+
{
|
|
1050
|
+
return [EXFileSystem generatePathInDirectory:directory withExtension:extension];
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
+ (NSString *)generatePathInDirectory:(NSString *)directory withExtension:(NSString *)extension
|
|
1054
|
+
{
|
|
1055
|
+
NSString *fileName = [[[NSUUID UUID] UUIDString] stringByAppendingString:extension];
|
|
1056
|
+
[EXFileSystem ensureDirExistsWithPath:directory];
|
|
1057
|
+
return [directory stringByAppendingPathComponent:fileName];
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// '<ARCType> *__autoreleasing*' problem solution: https://stackoverflow.com/a/8862061/4337317
|
|
1061
|
+
- (NSNumber *)totalDiskCapacityWithError:(out NSError * __autoreleasing *)error
|
|
1062
|
+
{
|
|
1063
|
+
NSDictionary *results = [self documentFileResourcesForKeys:@[NSURLVolumeTotalCapacityKey]
|
|
1064
|
+
error:error];
|
|
1065
|
+
|
|
1066
|
+
return results[NSURLVolumeTotalCapacityKey];
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// '<ARCType> *__autoreleasing*' problem solution: https://stackoverflow.com/a/8862061/4337317
|
|
1070
|
+
- (NSNumber *)freeDiskStorageWithError:(out NSError * __autoreleasing *)error
|
|
1071
|
+
{
|
|
1072
|
+
#if TARGET_OS_TV
|
|
1073
|
+
NSURLResourceKey key = NSURLVolumeAvailableCapacityKey;
|
|
1074
|
+
#else
|
|
1075
|
+
NSURLResourceKey key = NSURLVolumeAvailableCapacityForImportantUsageKey;
|
|
1076
|
+
#endif
|
|
1077
|
+
|
|
1078
|
+
NSDictionary *results = [self documentFileResourcesForKeys:@[key]
|
|
1079
|
+
error:error];
|
|
1080
|
+
|
|
1081
|
+
return results[key];
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
@end
|