luyx2412
2/9/2018 - 4:03 PM

React native sync icloud documents

React native sync icloud documents

//
//  RCTICloudDocuments.m
//
//  Created by DangTX on 18/01/01.
//  Copyright © 2018 Facebook. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "React/RCTBridgeModule.h"
#import "React/RCTEventDispatcher.h"
#import "React/RCTUtils.h"

@interface RCTICloudDocuments : NSObject <RCTBridgeModule>

//@property (nonatomic, retain) NSMetadataQuery *query;

@end

@implementation RCTICloudDocuments


RCT_EXPORT_MODULE();


/*
 let Documents = React.NativeModules.ICloudDocuments;
 Documents.attributesOfItemAtPath(path,(err,attrs)=>{});
 Documents.contentsAtPath(manifest,(err,contents)=>{});
 Documents.contentsOfDirectoryAtPath(path, (err, files)=>{});
 Documents.copyFileToICloud(manifest,(err,attrs)=>{});
 Documents.documentPath((path)=>{});
 Documents.iCloudDocumentPath((path)=>{});
 Documents.getICloudDocumentURLByLocalPath(localPath,(iCloudPath)=>{});
 Documents.getICloudToken((token)=>{});
 Documents.moveFileToICloud(localPath,(err, result)=>{});
 Documents.removeICloudFile(iCloudPath,(err, isSuccess)=>{});
 
 */


#pragma mark - remove / replace file to or from iCloud


RCT_EXPORT_METHOD(removeICloudFile:(NSString *)path :(RCTResponseSenderBlock)callback){
  [[self fileManager] setUbiquitous:NO itemAtURL:[NSURL fileURLWithPath:path] destinationURL:[NSURL fileURLWithPath:path] error:nil];
  [[self fileManager] removeItemAtPath:path error:nil];
}
RCT_EXPORT_METHOD(replaceFileToICloud:(NSString *)localPath :(RCTResponseSenderBlock)callback)
{
  NSURL *sourceURL = [NSURL fileURLWithPath:localPath];
  NSURL *destinationURL = [self getICloudDocumentURLByLocalPath:localPath];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self createDirectionOfFileURL:destinationURL];
    
    NSURL *tmpFileURL = [NSURL fileURLWithPath: [self createTempFile:sourceURL]];
    NSError *err;
    
    dispatch_sync(dispatch_get_main_queue(), ^{
      
      NSMetadataQuery *query = [self metaQueryWithPath:[destinationURL path]];
      
//      [[NSNotificationCenter defaultCenter] addObserverForName:NSMetadataQueryDidFinishGatheringNotification object:query queue:nil usingBlock:^(NSNotification * _Nonnull note) {
//        NSDictionary *res =[self uploadProgress:query type:@"finish"];
//        callback(@[@NO, res]);
//      }];
      [[NSNotificationCenter defaultCenter] addObserverForName:NSMetadataQueryDidUpdateNotification object:query queue:nil usingBlock:^(NSNotification * _Nonnull note) {
//        NSDictionary *res =[self uploadProgress:query type:@"finish"];
        NSDictionary *result = [[self parseMetaQueryResult:query] objectAtIndex:0];
//        NSLog(@"replaceToICloud meta update %@",result);
        if([[result objectForKey:@"isUploaded"] boolValue]){
//          NSLog([result objectForKey:@"isUploaded"]);
          callback(@[@NO, result]);
          [query disableUpdates];
        }
//
      }];
      [query startQuery];
    });
    if([[self fileManager] fileExistsAtPath:[destinationURL path]]){
      [[self fileManager] removeItemAtURL:destinationURL error:nil];
      
    }
    [[self fileManager] setUbiquitous:YES itemAtURL:tmpFileURL destinationURL:destinationURL error:&err];

    if(err){
      callback(@[[err localizedDescription]]);
    }else {
//      callback(@[@NO, [destinationURL absoluteString]]);
    }
  });
}
- (NSArray *)parseMetaQueryResult: (NSMetadataQuery *)query {
  NSMutableArray *res = [NSMutableArray array];
  [[query results] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    NSNumber *isDownloaded = [obj valueForAttribute:NSMetadataUbiquitousItemDownloadingStatusDownloaded];
    if(!isDownloaded)isDownloaded = [NSNumber numberWithInt:-1];
    NSNumber *isUploaded = [obj valueForAttribute:NSMetadataUbiquitousItemIsUploadedKey];
    if(!isUploaded)isUploaded = [NSNumber numberWithInt:-1];
    
//    NSLog(@"download current key %@", NSMetadataUbiquitousItemDownloadingStatusCurrent);
    [res addObject:@{
                     @"path": [obj valueForAttribute:NSMetadataItemPathKey],
                     @"filename": [obj valueForAttribute:NSMetadataItemFSNameKey],
                     @"isUploaded": isUploaded,
                     @"isDownloaded": isDownloaded,
                     @"downloadStatus": [obj valueForAttribute:NSMetadataUbiquitousItemDownloadingStatusKey]
                     }];
  }];
  return res;
}
- (NSDictionary *)uploadProgress: (id)query type:(NSString *)type{
  NSMutableArray *arr = [NSMutableArray array];
  [[query results] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  [arr addObject:@{
                   @"path": [obj valueForAttribute:NSMetadataItemPathKey],
                   @"filename": [obj valueForAttribute:NSMetadataItemFSNameKey],
                   @"percentUploaded": [obj valueForAttribute:NSMetadataUbiquitousItemPercentUploadedKey]
                   }];
  }];
  return @{@"type":@"finish", @"result":arr};
}

RCT_EXPORT_METHOD(replaceFileFromICloud:(NSString *)iCloudPath :(RCTResponseSenderBlock)callback)
{
  //  NSURL *docUrl = [self getICloudDocumentURL];
  NSURL *sourceURL = [NSURL fileURLWithPath:iCloudPath];
  NSURL *destinationURL = [self getDocumentURLByICloudPath:iCloudPath];
  if(![[[NSFileManager alloc] init] fileExistsAtPath:iCloudPath]){
    callback(@[@"file not exists, probably not downloaded yet"]);
    return;
  }
//  return;
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self createDirectionOfFileURL:destinationURL];
    
    [self createTempFile:sourceURL];
    NSString *tmpPath = [self createTempFile:sourceURL];
    if(!tmpPath) {
      NSString *msg =[NSString stringWithFormat:@"create tmp file fail: %@", sourceURL];
      callback(@[ msg ]);
    }else {
      NSURL *tmpFileURL = [NSURL fileURLWithPath:tmpPath];
      NSURL *resultingURL;
      NSError *err;
//      NSLog(@"replace item to url %@", destinationURL);
      [[self fileManager] replaceItemAtURL:destinationURL withItemAtURL:tmpFileURL backupItemName:nil options:0 resultingItemURL:&resultingURL error:&err];
      //    NSURL *resultingURL = [self replaceItemFrom:tmpFileURL to:destinationURL];
      if(err){
//        NSLog(@"replacing err %@", err);
        callback(@[[err localizedDescription]]);
        //      callback(@[@"URL unexpectly changed during replacing", [resultingURL absoluteString]]);
      }else {
        callback(@[@NO, [resultingURL absoluteString]]);
      }
    }
  });
}


#pragma mark - copy file to or from iCloud
RCT_EXPORT_METHOD(copyFileToICloud:(NSString *)localPath :(RCTResponseSenderBlock)callback)
{
  //  NSURL *docUrl = [self getICloudDocumentURL];
  NSURL *sourceURL = [NSURL fileURLWithPath:localPath];
  NSURL *destinationURL = [self getICloudDocumentURLByLocalPath:localPath];
  
  
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSFileManager *fileManager = [[NSFileManager alloc] init];
    [self createDirectionOfFileURL:destinationURL];
    NSError *err = nil;
    [fileManager copyItemAtURL:sourceURL toURL:destinationURL error:&err];
    
    if(err==nil) {
      callback(@[@NO, [destinationURL absoluteString]]);
    }else {
      callback(@[ [err localizedDescription] ]);
    }
  });
}
RCT_EXPORT_METHOD(copyFileFromICloud:(NSString *)path :(RCTResponseSenderBlock)callback)
{
  NSURL *destinationURL = [self getDocumentURLByICloudPath:path];
  NSURL *sourceURL = [NSURL fileURLWithPath:path];
  
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSFileManager *fileManager = [[NSFileManager alloc] init];
    [self createDirectionOfFileURL:destinationURL];
    NSError *err = nil;
    [fileManager copyItemAtURL:sourceURL toURL:destinationURL error:&err];
    
    if(err==nil) {
      callback(@[@NO, [destinationURL absoluteString]]);
    }else {
      callback(@[ [err localizedDescription] ]);
    }
  });
}
//- (void)copyItemFrom:(NSURL *)urlFrom to:(NSURL *)urlTo error:(NSError **)error {
//  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//    NSFileManager *fileManager = [[NSFileManager alloc] init];
//    [self createDirectionOfFileURL:urlTo];
//    [fileManager copyItemAtURL:urlFrom toURL:urlTo error:error];
//  });
//}

#pragma mark - move file to or from icloud
RCT_EXPORT_METHOD(moveFileToICloud:(NSString *)pathToUpload :(RCTResponseSenderBlock)callback)
{
  //  NSURL *docUrl = [self getICloudDocumentURL];
  
  NSURL *sourceURL = [NSURL fileURLWithPath:pathToUpload];
  NSURL *destinationURL = [self getICloudDocumentURLByLocalPath:pathToUpload];
  
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSFileManager *fileManager = [[NSFileManager alloc] init];
    
    [self createDirectionOfFileURL:destinationURL];
    NSError *err = nil;
    BOOL success = [fileManager setUbiquitous:YES itemAtURL:sourceURL destinationURL:destinationURL error:&err];
    dispatch_async(dispatch_get_main_queue(), ^{
      if(success) {
        callback(@[@NO, [destinationURL absoluteString]]);
      }else {
        callback(@[ [err localizedDescription] ]);
      }
    });
    
  });
}
RCT_EXPORT_METHOD(moveFileFromICloud:(NSString *)iCloudPath :(RCTResponseSenderBlock)callback)
{
  //  NSURL *docUrl = [self getICloudDocumentURL];
  
  NSURL *sourceURL = [NSURL fileURLWithPath:iCloudPath];
  NSURL *destinationURL = [self getDocumentURLByICloudPath:iCloudPath];
  
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSFileManager *fileManager = [[NSFileManager alloc] init];
    
    [self createDirectionOfFileURL:destinationURL];
    NSError *err = nil;
    BOOL success = [fileManager setUbiquitous:NO itemAtURL:sourceURL destinationURL:destinationURL error:&err];
    dispatch_async(dispatch_get_main_queue(), ^{
      if(success) {
        callback(@[@NO, [destinationURL absoluteString]]);
      }else {
        callback(@[ [err localizedDescription] ]);
      }
    });
    
  });
}

- (NSFileManager *)fileManager {
  return [[NSFileManager alloc] init];
}
- (NSString *)encodePath: (NSString*)path {
  return [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
# pragma mark - downloadUbiquitousPath
RCT_EXPORT_METHOD(downloadUbiquitousPath:(NSString *)path :(RCTResponseSenderBlock)callback){
  
  dispatch_sync(dispatch_get_main_queue(), ^{
    
    NSMetadataQuery *query = [self metaQueryWithPath:path];
//    NSLog(@"try get download update status %@", path);
    
    [[NSNotificationCenter defaultCenter] addObserverForName:NSMetadataQueryDidUpdateNotification object:query queue:nil usingBlock:^(NSNotification * _Nonnull note) {
      [[query results] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSString *filename =[obj valueForAttribute:NSMetadataItemFSNameKey];
        if (![[path lastPathComponent] isEqualToString:filename]) { return; }
        
        NSDictionary *res = [[self parseMetaQueryResult:query] objectAtIndex:idx];
        if([res valueForKey:@"isDownloaded"]){
          callback(@[ @NO, res ]); [query disableUpdates];
        }
        //      NSDictionary *result = [[self parseMetaQueryResult:query] objectAtIndex:0];
        //      NSLog(@"download update %@", result);
        
      }];
    }];
     
      
    [[NSNotificationCenter defaultCenter] addObserverForName:NSMetadataQueryDidFinishGatheringNotification object:query queue:nil usingBlock:^(NSNotification * _Nonnull note) {
      [[query results] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSString *filename =[obj valueForAttribute:NSMetadataItemFSNameKey];
        if (![[path lastPathComponent] isEqualToString:filename]) {
          NSLog(@"path not same %@, %@" ,[path lastPathComponent], filename);
          return; }
        //        NSDictionary *obj = [[query results] objectAtIndex:0];
        if ([[obj valueForKey:NSMetadataUbiquitousItemDownloadingStatusKey]
             isEqualToString:NSMetadataUbiquitousItemDownloadingStatusCurrent]) { //file is updated
          NSDictionary *res = [[self parseMetaQueryResult:query] objectAtIndex:idx];
          callback(@[@NO, res ]); [query disableUpdates];
        }
        
      }];
      
    }];
      
    NSError *err;
    [[[NSFileManager alloc] init] startDownloadingUbiquitousItemAtURL:[NSURL fileURLWithPath:path] error:&err];
    [query startQuery];
    NSLog(@"something");
    if(err){
      NSLog(@"download err %@",err);
      callback(@[[err localizedDescription]]);
    }

  });
}

- (NSMetadataQuery *)metaQueryWithPath: (NSString *)path {
  
  NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
  [query setSearchScopes:@[NSMetadataQueryUbiquitousDocumentsScope]];
  [query setPredicate:[NSPredicate predicateWithFormat:@"%K BEGINSWITH %@", NSMetadataItemPathKey, path]];
  return query;
}

#pragma mark - itemAttrsOfDirectoryAtICloud
RCT_EXPORT_METHOD(itemAttrsOfDirectoryAtICloud:(NSString *)path :(RCTResponseSenderBlock)callback){
  
  dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"try get attrs of icloud %@", path);
    NSMetadataQuery *query = [self metaQueryWithPath:path];
//    query.delegate = self;
//    query = [[NSMetadataQuery alloc] init];
//      [query setSearchScopes:@[NSMetadataQueryUbiquitousDocumentsScope]];
//    [query setPredicate:[NSPredicate predicateWithFormat:@"%K BEGINSWITH %@", NSMetadataItemPathKey, path]];
//    
    
    [[NSNotificationCenter defaultCenter] addObserverForName:NSMetadataQueryDidUpdateNotification object:query queue:nil usingBlock:^(NSNotification * _Nonnull note) {
      NSLog(@"attrs of icloud %@", [self parseMetaQueryResult:query]);
//      NSArray *res = [self queryDidFinishGathering:query];
//      callback(@[@NO, res]);
    }];
    [[NSNotificationCenter defaultCenter] addObserverForName:NSMetadataQueryDidFinishGatheringNotification object:query queue:nil usingBlock:^(NSNotification * _Nonnull note) {
      NSArray *res = [self queryDidFinishGathering:query];
      callback(@[@NO, res]);
    }];
    [query startQuery];
  });

}

//@synthesize query;

- (NSArray *)queryDidFinishGathering:(NSMetadataQuery *)query {
  NSMutableArray *res = [NSMutableArray array];
  [query enumerateResultsUsingBlock:^(id  _Nonnull result, NSUInteger idx, BOOL * _Nonnull stop) {
    NSNumber *createAt = [self getDateNumber:[result valueForAttribute:NSMetadataItemFSCreationDateKey]];
    NSNumber *modifyAt = [self getDateNumber:[result valueForAttribute:NSMetadataItemFSContentChangeDateKey]];
    NSNumber *size = [result valueForAttribute:NSMetadataItemFSSizeKey];
    if(!size) size = [NSNumber numberWithInt:-1];
    NSString *path = [result valueForAttribute:NSMetadataItemPathKey];
    NSNumber *exists = [NSNumber numberWithBool:[[[NSFileManager alloc] init] fileExistsAtPath:path]] ;
    
    NSDictionary *item = @{
                           @"path": path,
                           @"name": [result valueForAttribute:NSMetadataItemFSNameKey],
                           @"createAt": createAt,
                           @"modifyAt": modifyAt,
                           @"size": size,
                           @"downloaded": exists
//                           @"downloaded": [result valueForAttribute:NSMetadataUbiquitousItemPercentDownloadedKey]
                           };
    [res addObject:item];
//    NSLog(@"meta item %@", result);
  }];
  return res;
//  [[query results] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//    
//  }];
//  NSLog(@"did finish observe %@", res);
  
//  [query disableUpdates];
//  [query stopQuery];
  
}

#pragma mark - get file attributes with path
RCT_EXPORT_METHOD(attributesOfItemAtPath:(NSString *)path :(RCTResponseSenderBlock)callback){
  NSFileManager *fileManager = [[NSFileManager alloc] init];
  NSDictionary *attrs;
  BOOL isDir;
  BOOL isExists = [fileManager fileExistsAtPath:path isDirectory:&isDir];
  if (!isExists) {
    callback(@[@"path not exists"]);
    return;
  }else if(isDir){
    attrs = [fileManager attributesOfItemAtPath:path error:nil];
  }else {
    attrs = [fileManager attributesOfItemAtPath:path error:nil];
  }
  
//  NSNumber *createAt = [NSNumber numberWithDouble: [[attrs objectForKey:@"NSFileCreationDate"] timeIntervalSince1970] ];
  NSNumber *createAt = [self getDateNumber:[attrs objectForKey:@"NSFileCreationDate"]];
//  NSNumber *modifyAt = [NSNumber numberWithDouble: [[attrs objectForKey:@"NSFileModificationDate"] timeIntervalSince1970] ];
  NSNumber *modifyAt = [self getDateNumber:[attrs objectForKey:@"NSFileModificationDate"]];
//  NSLog(@"attrs %@ at %@", attrs, path);
  NSMutableDictionary *ret = [NSMutableDictionary dictionaryWithDictionary: @{
            @"path": path,
            @"createAt": createAt,
            @"modifyAt": modifyAt,
            @"isDirectory": [NSNumber numberWithBool:isDir]
            }];
  if(!isDir) {
    [ret setValue:[attrs objectForKey:@"NSFileSize"] forKey:@"size"];
  }
  //  NSLog(@"attrs %@", attrs);
  callback(@[@NO, ret]);
}
- (NSNumber *)getDateNumber: (id)obj{
  return [NSNumber numberWithDouble: [ obj timeIntervalSince1970] ];
}

#pragma mark - contents at item or directory
RCT_EXPORT_METHOD(contentsAtPath:(NSString *)path :(RCTResponseSenderBlock)callback){
  NSFileManager *fileManager = [[NSFileManager alloc] init];
  
  NSData *data = [fileManager contentsAtPath:path];
  NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  //  NSLog(@"datastr %@", dataStr);
  callback(@[@NO, dataStr]);
}
RCT_EXPORT_METHOD(contentsOfDirectoryAtPath:(NSString *)path :(RCTResponseSenderBlock)callback){
  NSFileManager *fileManager = [[NSFileManager alloc] init];
  NSError *err;
  NSArray *array = [fileManager contentsOfDirectoryAtPath:path error:&err];
//  NSLog(@"contentsOfDirectoryAtPath err %@", [err description]);
  if(err){
    callback(@[[err localizedDescription]]);
  }else {
    callback(@[@NO, array]);
  }
}

#pragma mark - remove file with path
RCT_EXPORT_METHOD(removeFileAtPath:(NSString *)pathToRemove :(RCTResponseSenderBlock)callback){
  NSURL *destinationURL = [NSURL URLWithString:pathToRemove];
  
  NSFileManager *fileManager = [[NSFileManager alloc] init];
  BOOL fileExists = [fileManager fileExistsAtPath:pathToRemove];
  if(!fileExists){
    callback(@[@"file not exists"]);
    return;
  }
  
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSError *error = nil;
    NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
    [fileCoordinator coordinateWritingItemAtURL:destinationURL options:NSFileCoordinatorWritingForDeleting error:&error byAccessor:^(NSURL * _Nonnull newURL) {
      NSError *err = nil;
      [fileManager removeItemAtURL:destinationURL error:&err];
      if (err) {
        callback(@[ [err localizedDescription] ]);
      }else {
        callback(@[ @FALSE, @TRUE ]);
      }
    }];
  });
  
}

#pragma mark - get iCloud token
RCT_EXPORT_METHOD(getICloudToken:(RCTResponseSenderBlock)callback){
  id token = [[[NSFileManager alloc] init] ubiquityIdentityToken];
  //  DumpObjcMethods(object_getClass(token));
  //  NSLog(@"token %@", token);
  if (token == nil) {
    callback(@[@"Token Nil", [NSNull null]]);
  }else {
    
    NSData *tokenData = [NSKeyedArchiver archivedDataWithRootObject:token];
    NSLog(@"parsed token NSData %@", tokenData);
    //    NSString *tokenDataString = [[NSString alloc] initWithData:tokenData encoding:NSUTF8StringEncoding];
    NSString *tokenDataString = [tokenData description];
    NSLog(@"try parse data to string %@", tokenDataString);
    callback(@[@NO, tokenDataString]);
    //    NSData *tokenData = [token serializedRepresentation];
    //    NSString *tokenStr = [[NSString alloc] initWithData:tokenData encoding:NSUTF8StringEncoding];
    //    callback(@[tokenStr]);
  }
}


#pragma mark - get iCloud or local doc root
RCT_EXPORT_METHOD(iCloudDocumentPath:(RCTResponseSenderBlock)callback)
{
  callback(@[@NO, [[self getICloudDocumentURL] path] ]);
}
RCT_EXPORT_METHOD(documentPath:(RCTResponseSenderBlock)callback)
{
  callback(@[@NO,  [self _documentPath] ]);
}
RCT_EXPORT_METHOD(getICloudDocumentURLByLocalPath:(NSString *)localPath :(RCTResponseSenderBlock)callback){
  NSURL *url = [self getICloudDocumentURLByLocalPath:localPath];
  callback(@[@NO,  [url absoluteString] ]);
}


#pragma mark - native methods
- (NSString *)createTempFile:(NSURL *)url {
  NSFileManager *fileManager = [[NSFileManager alloc] init];
//  NSLog(@"create tmp file %@, %i", url, [fileManager fileExistsAtPath:[url path]]);
  if(![fileManager fileExistsAtPath:[url path]]){
    NSLog(@"create tmp fail, source file not exists: %@", url);
    return nil;
  }
  NSString *filename = [url lastPathComponent];
  NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
  [fileManager createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:nil];
  
  if ( [fileManager fileExistsAtPath:tempPath] ) {
//    NSLog(@"remove existed: %@", tempPath);
    NSError *removeErr;
    [fileManager removeItemAtPath:tempPath error:&removeErr];
    if (removeErr) {
      NSLog(@"remove item err %@", [removeErr localizedDescription]);
    }
  }
  
  NSError *err;
  BOOL tempCopied = [[self fileManager] copyItemAtPath:[url path] toPath:tempPath error:&err];
  if(err)NSLog(@"create tmp err,%@, %@", tempPath, err);
  if (tempCopied) {
    return tempPath;
  }else {
    return nil;
  }
  //  return tempCopied;
}
- (void)createDirectionOfFileURL:(NSURL *)url {
  NSURL *dir = [url URLByDeletingLastPathComponent];
  [[[NSFileManager alloc] init] createDirectoryAtURL:dir withIntermediateDirectories:YES attributes:nil error:nil];
}

- (NSString *)_documentPath {
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  return [paths objectAtIndex:0];
}
- (NSString*)getRelativePath:(NSString *)path {
  //  NSString *docPath = [self _documentPath];
  NSRange range = [path rangeOfString:@"/Documents/"];
  NSString *relativePath = [path substringFromIndex:range.location+range.length];
//  NSLog(@"relative path input:%@ out:%@", path, relativePath);
  return relativePath;
}
- (NSURL *)getDocumentURLByICloudPath: (NSString *)iCloudPath {
//  NSLog(@"rctmd5 of diary-item-2015-10-29, %@",RCTMD5Hash(@"diary-item-2015-10-29"));
//  NSString *relativePath = [self getRelativePath:iCloudPath];
//  NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
//  NSString *icloudID = [NSString stringWithFormat:@"iCloud.%@", bundleIdentifier];
  NSString *basePath = [[self getICloudDocumentURL] path];
  NSRange range = [iCloudPath rangeOfString:basePath];
  NSString *relativePath = [iCloudPath substringFromIndex:range.location+range.length+1];
//  NSLog(@"icloud basePath %@, relative: %@", basePath, relativePath);
  
  
  NSURL *localURL = [[NSURL fileURLWithPath:[self _documentPath]] URLByAppendingPathComponent:relativePath];
//  NSLog(@"getDocumentURLByICloudPath \nrelative:%@\n\nsource:\n%@\n\ndest:\n%@ \n\n\n", relativePath, iCloudPath, localURL);
  return localURL;
}

- (NSURL *)getICloudDocumentURLByLocalPath: (NSString *)localPath {
  NSURL *docUrl = [self getICloudDocumentURL];
  //  localPath = [self getRelativePathOfDocuments:localPath];
  //  NSString *filename = [localPath lastPathComponent];
  NSString *filename = [self getRelativePath:localPath];
  NSURL *destinationURL = [docUrl URLByAppendingPathComponent:filename];
  return destinationURL;
}

- (NSURL *)getICloudDocumentURL {
  // get icloud docURL with default bundleID
  NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
  NSString *icloudID = [NSString stringWithFormat:@"iCloud.%@", bundleIdentifier];
  //  [NSFileManager URLForUbiquityContainerIdentifier:nil].path
  NSURL *containerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:icloudID];
  //  NSURL *containerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
  NSURL *docUrl = [containerURL URLByAppendingPathComponent:@"Documents"];
  
  return docUrl;
}

@end