@onekeyfe/react-native-cloud-fs 2.6.0

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.
@@ -0,0 +1,467 @@
1
+
2
+ #import "RNCloudFs.h"
3
+ #import <UIKit/UIKit.h>
4
+ #if __has_include(<React/RCTBridgeModule.h>)
5
+ #import <React/RCTBridgeModule.h>
6
+ #else
7
+ #import "RCTBridgeModule.h"
8
+ #endif
9
+ #import "RCTEventDispatcher.h"
10
+ #import "RCTUtils.h"
11
+ #import <AssetsLibrary/AssetsLibrary.h>
12
+ #import "RCTLog.h"
13
+
14
+ @implementation RNCloudFs
15
+
16
+
17
+
18
+ - (dispatch_queue_t)methodQueue
19
+ {
20
+ return dispatch_queue_create("RNCloudFs.queue", DISPATCH_QUEUE_SERIAL);
21
+ }
22
+
23
+ RCT_EXPORT_MODULE()
24
+
25
+ //see https://developer.apple.com/library/content/documentation/General/Conceptual/iCloudDesignGuide/Chapters/iCloudFundametals.html
26
+
27
+ RCT_EXPORT_METHOD(isAvailable:(RCTPromiseResolveBlock)resolve
28
+ rejecter:(RCTPromiseRejectBlock)reject) {
29
+
30
+ NSURL *ubiquityURL = [self icloudDirectory];
31
+ if(ubiquityURL != nil){
32
+ return resolve(@YES);
33
+ }
34
+ return resolve(@NO);
35
+ }
36
+
37
+ RCT_EXPORT_METHOD(createFile:(NSDictionary *) options
38
+ resolver:(RCTPromiseResolveBlock)resolve
39
+ rejecter:(RCTPromiseRejectBlock)reject) {
40
+
41
+ NSString *destinationPath = [options objectForKey:@"targetPath"];
42
+ NSString *content = [options objectForKey:@"content"];
43
+ NSString *scope = [options objectForKey:@"scope"];
44
+ bool documentsFolder = !scope || [scope caseInsensitiveCompare:@"visible"] == NSOrderedSame;
45
+
46
+ NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
47
+
48
+ NSError *error;
49
+ [content writeToFile:tempFile atomically:YES encoding:NSUTF8StringEncoding error:&error];
50
+ if(error) {
51
+ return reject(@"error", error.description, nil);
52
+ }
53
+
54
+ [self moveToICloudDirectory:documentsFolder :tempFile :destinationPath :resolve :reject];
55
+ }
56
+
57
+ RCT_EXPORT_METHOD(fileExists:(NSDictionary *)options
58
+ resolver:(RCTPromiseResolveBlock)resolve
59
+ rejecter:(RCTPromiseRejectBlock)reject) {
60
+
61
+ NSString *destinationPath = [options objectForKey:@"targetPath"];
62
+ NSString *scope = [options objectForKey:@"scope"];
63
+ bool documentsFolder = !scope || [scope caseInsensitiveCompare:@"visible"] == NSOrderedSame;
64
+
65
+ NSFileManager* fileManager = [NSFileManager defaultManager];
66
+
67
+ NSURL *ubiquityURL = documentsFolder ? [self icloudDocumentsDirectory] : [self icloudDirectory];
68
+
69
+ if (ubiquityURL) {
70
+ NSURL* dir = [ubiquityURL URLByAppendingPathComponent:destinationPath];
71
+ NSString* dirPath = [dir.path stringByStandardizingPath];
72
+
73
+ bool exists = [fileManager fileExistsAtPath:dirPath];
74
+
75
+ return resolve(@(exists));
76
+ } else {
77
+ RCTLogTrace(@"Could not retrieve a ubiquityURL");
78
+ return reject(@"error", [NSString stringWithFormat:@"could access iCloud drive '%@'", destinationPath], nil);
79
+ }
80
+ }
81
+
82
+ RCT_EXPORT_METHOD(listFiles:(NSDictionary *)options
83
+ resolver:(RCTPromiseResolveBlock)resolve
84
+ rejecter:(RCTPromiseRejectBlock)reject) {
85
+
86
+ NSString *destinationPath = [options objectForKey:@"targetPath"];
87
+ NSString *scope = [options objectForKey:@"scope"];
88
+ bool documentsFolder = !scope || [scope caseInsensitiveCompare:@"visible"] == NSOrderedSame;
89
+
90
+ NSFileManager* fileManager = [NSFileManager defaultManager];
91
+
92
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
93
+ [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZ"];
94
+
95
+ NSURL *ubiquityURL = documentsFolder ? [self icloudDocumentsDirectory] : [self icloudDirectory];
96
+
97
+ if (ubiquityURL) {
98
+ NSURL* target = [ubiquityURL URLByAppendingPathComponent:destinationPath];
99
+
100
+ NSMutableArray<NSDictionary *> *fileData = [NSMutableArray new];
101
+
102
+ NSError *error = nil;
103
+
104
+ BOOL isDirectory;
105
+ [fileManager fileExistsAtPath:[target path] isDirectory:&isDirectory];
106
+
107
+ NSURL *dirPath;
108
+ NSArray *contents;
109
+ if(isDirectory) {
110
+ contents = [fileManager contentsOfDirectoryAtPath:[target path] error:&error];
111
+ dirPath = target;
112
+ } else {
113
+ contents = @[[target lastPathComponent]];
114
+ dirPath = [target URLByDeletingLastPathComponent];
115
+ }
116
+
117
+ if(error) {
118
+ return reject(@"error", error.description, nil);
119
+ }
120
+
121
+ [contents enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
122
+ NSURL *fileUrl = [dirPath URLByAppendingPathComponent:object];
123
+
124
+ NSError *error;
125
+ NSDictionary *attributes = [fileManager attributesOfItemAtPath:[fileUrl path] error:&error];
126
+ if(error) {
127
+ RCTLogTrace(@"problem getting attributes for %@", [fileUrl path]);
128
+ //skip this one
129
+ return;
130
+ }
131
+
132
+ NSFileAttributeType type = [attributes objectForKey:NSFileType];
133
+
134
+ bool isDir = type == NSFileTypeDirectory;
135
+ bool isFile = type == NSFileTypeRegular;
136
+
137
+ if(!isDir && !isFile)
138
+ return;
139
+
140
+ NSDate* modDate = [attributes objectForKey:NSFileModificationDate];
141
+
142
+ NSURL *shareUrl = [fileManager URLForPublishingUbiquitousItemAtURL:fileUrl expirationDate:nil error:&error];
143
+
144
+ [fileData addObject:@{
145
+ @"name": object,
146
+ @"path": [fileUrl path],
147
+ @"uri": shareUrl ? [shareUrl absoluteString] : [NSNull null],
148
+ @"size": [attributes objectForKey:NSFileSize],
149
+ @"lastModified": [dateFormatter stringFromDate:modDate],
150
+ @"isDirectory": @(isDir),
151
+ @"isFile": @(isFile)
152
+ }];
153
+ }];
154
+
155
+ if (error) {
156
+ return reject(@"error", [NSString stringWithFormat:@"could not copy to iCloud drive '%@'", destinationPath], error);
157
+ }
158
+
159
+ NSString *relativePath = [[dirPath path] stringByReplacingOccurrencesOfString:[ubiquityURL path] withString:@"."];
160
+
161
+ return resolve(@{
162
+ @"files": fileData,
163
+ @"path": relativePath
164
+ });
165
+
166
+ } else {
167
+ NSLog(@"Could not retrieve a ubiquityURL");
168
+ return reject(@"error", [NSString stringWithFormat:@"could not copy to iCloud drive '%@'", destinationPath], nil);
169
+ }
170
+ }
171
+
172
+
173
+ RCT_EXPORT_METHOD(getIcloudDocument:(NSString *)filename
174
+ resolver:(RCTPromiseResolveBlock)resolver
175
+ rejecter:(RCTPromiseRejectBlock)rejecter) {
176
+ __block bool resolved = NO;
177
+ _query = [[NSMetadataQuery alloc] init];
178
+
179
+ [_query setSearchScopes:@[NSMetadataQueryUbiquitousDocumentsScope, NSMetadataQueryUbiquitousDataScope]];
180
+
181
+ NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, filename];
182
+ [_query setPredicate:pred];
183
+
184
+
185
+ [[NSNotificationCenter defaultCenter] addObserverForName:
186
+ NSMetadataQueryDidFinishGatheringNotification
187
+ object:_query queue:[NSOperationQueue currentQueue]
188
+ usingBlock:^(NSNotification __strong *notification)
189
+ {
190
+ NSMetadataQuery *query = [notification object];
191
+ [query disableUpdates];
192
+ [query stopQuery];
193
+ for (NSMetadataItem *item in query.results) {
194
+ if([[item valueForAttribute:NSMetadataItemFSNameKey] isEqualToString:filename]){
195
+ resolved = YES;
196
+ NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
197
+ bool fileIsReady = [self downloadFileIfNotAvailable: item];
198
+ if(fileIsReady){
199
+ NSData *data = [NSData dataWithContentsOfURL: url];
200
+ NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
201
+ return resolver(content);
202
+ } else {
203
+ // Call itself until the file it's ready
204
+ [self getIcloudDocument:filename resolver:resolver rejecter:rejecter];
205
+ }
206
+ }
207
+ }
208
+ if(!resolved){
209
+ return rejecter(@"error", [NSString stringWithFormat:@"item not found '%@'", filename], nil);
210
+ }
211
+ }];
212
+
213
+ dispatch_async(dispatch_get_main_queue(), ^{
214
+ [self->_query startQuery];
215
+ });
216
+
217
+ }
218
+
219
+ RCT_EXPORT_METHOD(deleteFromCloud:(NSDictionary *)item
220
+ resolver:(RCTPromiseResolveBlock)resolver
221
+ rejecter:(RCTPromiseRejectBlock)rejecter) {
222
+ NSError *error;
223
+
224
+ NSFileManager* fileManager = [NSFileManager defaultManager];
225
+ [fileManager removeItemAtPath:item[@"path"] error:&error];
226
+ if(error) {
227
+ return rejecter(@"error", error.description, nil);
228
+ }
229
+ bool result = YES;
230
+ return resolver(@(result));
231
+
232
+ }
233
+
234
+
235
+ RCT_EXPORT_METHOD(copyToCloud:(NSDictionary *)options
236
+ resolver:(RCTPromiseResolveBlock)resolve
237
+ rejecter:(RCTPromiseRejectBlock)reject) {
238
+
239
+ // mimeType is ignored for iOS
240
+ NSDictionary *source = [options objectForKey:@"sourcePath"];
241
+ NSString *destinationPath = [options objectForKey:@"targetPath"];
242
+ NSString *scope = [options objectForKey:@"scope"];
243
+ bool documentsFolder = !scope || [scope caseInsensitiveCompare:@"visible"] == NSOrderedSame;
244
+
245
+ NSFileManager* fileManager = [NSFileManager defaultManager];
246
+
247
+ NSString *sourceUri = [source objectForKey:@"uri"];
248
+ if(!sourceUri) {
249
+ sourceUri = [source objectForKey:@"path"];
250
+ }
251
+
252
+ if([sourceUri hasPrefix:@"assets-library"]){
253
+ ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
254
+
255
+ [library assetForURL:[NSURL URLWithString:sourceUri] resultBlock:^(ALAsset *asset) {
256
+
257
+ ALAssetRepresentation *rep = [asset defaultRepresentation];
258
+
259
+ Byte *buffer = (Byte*)malloc(rep.size);
260
+ NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
261
+ NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
262
+
263
+ if (data) {
264
+ NSString *filename = [sourceUri lastPathComponent];
265
+ NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
266
+ [data writeToFile:tempFile atomically:YES];
267
+ [self moveToICloudDirectory:documentsFolder :tempFile :destinationPath :resolve :reject];
268
+ } else {
269
+ RCTLogTrace(@"source file does not exist %@", sourceUri);
270
+ return reject(@"error", [NSString stringWithFormat:@"failed to copy asset '%@'", sourceUri], nil);
271
+ }
272
+ } failureBlock:^(NSError *error) {
273
+ RCTLogTrace(@"source file does not exist %@", sourceUri);
274
+ return reject(@"error", error.description, nil);
275
+ }];
276
+ } else if ([sourceUri hasPrefix:@"file:/"] || [sourceUri hasPrefix:@"/"]) {
277
+ NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^file:/+" options:NSRegularExpressionCaseInsensitive error:nil];
278
+ NSString *modifiedSourceUri = [regex stringByReplacingMatchesInString:sourceUri options:0 range:NSMakeRange(0, [sourceUri length]) withTemplate:@"/"];
279
+
280
+ if ([fileManager fileExistsAtPath:modifiedSourceUri isDirectory:nil]) {
281
+ NSURL *sourceURL = [NSURL fileURLWithPath:modifiedSourceUri];
282
+
283
+ // todo: figure out how to *copy* to icloud drive
284
+ // ...setUbiquitous will move the file instead of copying it, so as a work around lets copy it to a tmp file first
285
+ NSString *filename = [sourceUri lastPathComponent];
286
+ NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
287
+
288
+ NSError *error;
289
+ if([fileManager fileExistsAtPath:tempFile]){
290
+ [fileManager removeItemAtPath:tempFile error:&error];
291
+ if(error) {
292
+ return reject(@"error", error.description, nil);
293
+ }
294
+ }
295
+
296
+ [fileManager copyItemAtPath:[sourceURL path] toPath:tempFile error:&error];
297
+ if(error) {
298
+ return reject(@"error", error.description, nil);
299
+ }
300
+
301
+ [self moveToICloudDirectory:documentsFolder :tempFile :destinationPath :resolve :reject];
302
+ } else {
303
+ NSLog(@"source file does not exist %@", sourceUri);
304
+ return reject(@"error", [NSString stringWithFormat:@"no such file or directory, open '%@'", sourceUri], nil);
305
+ }
306
+ } else {
307
+ NSURL *url = [NSURL URLWithString:sourceUri];
308
+ NSData *urlData = [NSData dataWithContentsOfURL:url];
309
+
310
+ if (urlData) {
311
+ NSString *filename = [sourceUri lastPathComponent];
312
+ NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
313
+ [urlData writeToFile:tempFile atomically:YES];
314
+ [self moveToICloudDirectory:documentsFolder :tempFile :destinationPath :resolve :reject];
315
+ } else {
316
+ RCTLogTrace(@"source file does not exist %@", sourceUri);
317
+ return reject(@"error", [NSString stringWithFormat:@"cannot download '%@'", sourceUri], nil);
318
+ }
319
+ }
320
+ }
321
+
322
+ - (void) moveToICloudDirectory:(bool) documentsFolder :(NSString *)tempFile :(NSString *)destinationPath
323
+ :(RCTPromiseResolveBlock)resolver
324
+ :(RCTPromiseRejectBlock)rejecter {
325
+
326
+ if(documentsFolder) {
327
+ NSURL *ubiquityURL = [self icloudDocumentsDirectory];
328
+ [self moveToICloud:ubiquityURL :tempFile :destinationPath :resolver :rejecter];
329
+ } else {
330
+ NSURL *ubiquityURL = [self icloudDirectory];
331
+ [self moveToICloud:ubiquityURL :tempFile :destinationPath :resolver :rejecter];
332
+ }
333
+ }
334
+
335
+ - (void) moveToICloud:(NSURL *)ubiquityURL :(NSString *)tempFile :(NSString *)destinationPath
336
+ :(RCTPromiseResolveBlock)resolver
337
+ :(RCTPromiseRejectBlock)rejecter {
338
+
339
+
340
+ NSString * destPath = destinationPath;
341
+ while ([destPath hasPrefix:@"/"]) {
342
+ destPath = [destPath substringFromIndex:1];
343
+ }
344
+
345
+ RCTLogTrace(@"Moving file %@ to %@", tempFile, destPath);
346
+
347
+ NSFileManager* fileManager = [NSFileManager defaultManager];
348
+
349
+ if (ubiquityURL) {
350
+
351
+ NSURL* targetFile = [ubiquityURL URLByAppendingPathComponent:destPath];
352
+ NSURL *dir = [targetFile URLByDeletingLastPathComponent];
353
+
354
+ NSURL* uniqueFile = targetFile;
355
+
356
+ if([fileManager fileExistsAtPath:uniqueFile.path]){
357
+ NSError *error;
358
+ [fileManager removeItemAtPath:uniqueFile.path error:&error];
359
+ if(error) {
360
+ return rejecter(@"error", error.description, nil);
361
+ }
362
+ }
363
+
364
+ RCTLogTrace(@"Target file: %@", uniqueFile.path);
365
+
366
+ if (![fileManager fileExistsAtPath:dir.path]) {
367
+ [fileManager createDirectoryAtURL:dir withIntermediateDirectories:YES attributes:nil error:nil];
368
+ }
369
+
370
+ NSError *error;
371
+ [fileManager setUbiquitous:YES itemAtURL:[NSURL fileURLWithPath:tempFile] destinationURL:uniqueFile error:&error];
372
+ if(error) {
373
+ return rejecter(@"error", error.description, nil);
374
+ }
375
+
376
+ [fileManager removeItemAtPath:tempFile error:&error];
377
+
378
+ return resolver(uniqueFile.path);
379
+ } else {
380
+ NSError *error;
381
+ [fileManager removeItemAtPath:tempFile error:&error];
382
+
383
+ return rejecter(@"error", [NSString stringWithFormat:@"could not copy '%@' to iCloud drive", tempFile], nil);
384
+ }
385
+ }
386
+
387
+ - (NSURL *)icloudDocumentsDirectory {
388
+ NSFileManager* fileManager = [NSFileManager defaultManager];
389
+ NSURL *rootDirectory = [[self icloudDirectory] URLByAppendingPathComponent:@"Documents"];
390
+
391
+ if (rootDirectory) {
392
+ if (![fileManager fileExistsAtPath:rootDirectory.path isDirectory:nil]) {
393
+ RCTLogTrace(@"Creating documents directory: %@", rootDirectory.path);
394
+ [fileManager createDirectoryAtURL:rootDirectory withIntermediateDirectories:YES attributes:nil error:nil];
395
+ }
396
+ }
397
+
398
+ return rootDirectory;
399
+ }
400
+
401
+ - (NSURL *)icloudDirectory {
402
+ NSFileManager* fileManager = [NSFileManager defaultManager];
403
+ NSURL *rootDirectory = [fileManager URLForUbiquityContainerIdentifier:nil];
404
+ return rootDirectory;
405
+ }
406
+
407
+ - (NSURL *)localPathForResource:(NSString *)resource ofType:(NSString *)type {
408
+ NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
409
+ NSString *resourcePath = [[documentsDirectory stringByAppendingPathComponent:resource] stringByAppendingPathExtension:type];
410
+ return [NSURL fileURLWithPath:resourcePath];
411
+ }
412
+
413
+
414
+
415
+ - (BOOL)downloadFileIfNotAvailable:(NSMetadataItem*)item {
416
+ if ([[item valueForAttribute:NSMetadataUbiquitousItemDownloadingStatusKey] isEqualToString:NSMetadataUbiquitousItemDownloadingStatusCurrent]){
417
+ NSLog(@"File is ready!");
418
+ return YES;
419
+ }
420
+ // Download the file.
421
+ NSFileManager* fm = [NSFileManager defaultManager];
422
+ NSError *downloadError = nil;
423
+ [fm startDownloadingUbiquitousItemAtURL:[item valueForAttribute:NSMetadataItemURLKey] error:&downloadError];
424
+ if (downloadError) {
425
+ NSLog(@"Error occurred starting download: %@", downloadError);
426
+ }
427
+ NSLog(@"Waiting before retrying...");
428
+ [NSThread sleepForTimeInterval:0.3];
429
+ return NO;
430
+
431
+ }
432
+
433
+
434
+ RCT_EXPORT_METHOD(syncCloud:(RCTPromiseResolveBlock)resolve
435
+ rejecter:(RCTPromiseRejectBlock)reject) {
436
+
437
+ _query = [[NSMetadataQuery alloc] init];
438
+ [_query setSearchScopes:@[NSMetadataQueryUbiquitousDocumentsScope, NSMetadataQueryUbiquitousDataScope]];
439
+ [_query setPredicate:[NSPredicate predicateWithFormat: @"%K LIKE '*'", NSMetadataItemFSNameKey]];
440
+
441
+
442
+ dispatch_async(dispatch_get_main_queue(), ^{
443
+
444
+ BOOL startedQuery = [self->_query startQuery];
445
+ if (!startedQuery)
446
+ {
447
+ reject(@"error", @"Failed to start query.\n", nil);
448
+ }
449
+ });
450
+
451
+ [[NSNotificationCenter defaultCenter] addObserverForName:
452
+ NSMetadataQueryDidFinishGatheringNotification
453
+ object:_query queue:[NSOperationQueue currentQueue]
454
+ usingBlock:^(NSNotification __strong *notification)
455
+ {
456
+ NSMetadataQuery *query = [notification object];
457
+ [query disableUpdates];
458
+ [query stopQuery];
459
+ for (NSMetadataItem *item in query.results) {
460
+ [self downloadFileIfNotAvailable: item];
461
+ }
462
+ return resolve(@YES);
463
+ }];
464
+
465
+ }
466
+
467
+ @end