Search code examples
objective-cmacoscocoaunzip

Unable to rename the file while moving from temporary directorary


I am developing a zip extractor app i followed the algorithm that CRD explained @Here but i stuck at third step i am unable to rename the unzipped file which is at temporary directorary. here is my code

  NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
        NSError *error;

        NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];
        if (error) {
            return ;
        }
        tmpDirectory = [tmpDirectory URLByAppendingPathComponent:@"extracts"];
        NSLog(@"temp dir %@",tmpDirectory);
        NSLog(@"temp path %@",tmpDirectory.path);


        [SSZipArchive unzipFileAtPath:zipFilePath toDestination:tmpDirectory.path];

        NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectory.path error:nil];
        NSLog(@"dir file %@",dirFiles);
        for (NSString *string in dirFiles) {

            NSArray *dirDestinationFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:destinationPath error:nil];
            NSLog(@"dir destination file %@",dirDestinationFiles);
            [dirDestinationFiles enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSFileManager *fm = [NSFileManager defaultManager];
                NSError *error;


                if ([string isEqualToString:obj]) {


                    NSLog(@"Already present");

                    BOOL isMoved = [fm moveItemAtPath:tmpDirectory.path  toPath:[destinationPath stringByAppendingString:[NSString stringWithFormat:@"/%@-1",string]] error:&error];

                    if (isMoved) {
                        NSLog(@"Moved");
                    }else{
                        NSLog(@"errorL %@", error);
                        NSLog(@"Not moved");
                    }

                    [fm removeItemAtPath:tmpDirectory.path error:&error];

                    [self moveFileToTrash:zipFilePath];
                    [self openExtractedFolderWithZipPath:zipFilePath toDestinationPath:destinationPath];

                }

            }];


        }

Any Suggestions.. Thanks in Advance !


Solution

  • Let's just review your code to hopefully help you on your way.

    It may seem minor, but pick good variable names:

    NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
    NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];
    

    Two names which are semantically similar for different things, that is just confusing. How about, say, destinationURL instead of tempDir?

    Next, when constructing/pulling apart/etc. pathnames or URLs you will be better off being consistent. Both NSURL and NSString provide similar methods for these operations, in one place you use them:

    tmpDirectory = [tmpDirectory URLByAppendingPathComponent:@"extracts"];
    

    but then restort to direct string manipulation using a path separator which may, or may not, be correct:

    [destinationPath stringByAppendingString:[NSString stringWithFormat:@"/%@-1",string]]
    

    The routines provided by NSURL and NSString abstract away from the details of path separators and how to, say, find the extension on the last path component (which you might find useful when renaming to avoid clashes).

    Going back to:

    tmpDirectory = [tmpDirectory URLByAppendingPathComponent:@"extracts"];
    

    There is no reason for you to do this. The temporary directory is created for you and you should delete it after using it. So there is no need to create a subdirectory extracts within it, and by reassigning to the same variable you've lost the URL you need to delete the temporary directory.

    Now something less obvious, in my comment above I wrote:

    To move each item you must handle name clashes, to do this try the move and if you get an error indicating a name clash modify the destination name however you like and re-try the move, repeating until you succeed or you until reach some limit of tries (determined by you).

    I didn't explain why you should do it this way and you have tackled the problem a different way: for each item you are going to move you check for names clashes before attempting the move by iterating over the names in the destination directory.

    If you read Apple's documentation on the file system you will find they often recommend you try an operation and then examine any error returned instead of trying to predict whether an error will occur and avoid it. The reason for this is the file system is dynamic, other processes can be modifying it, so if you try to avoid an error you may still get one. In pseudocode you are better of doing something like:

    moveDone = false
    attemptCount = 0
    while not moveDone and attemptCount < MAX_ATTEMPTS
       move object
       if object exists error
          modify destination URL
          increment attemptCount
       else
          moveDone = true
       end
    end
    if not moveDone then handle error
    

    Following this outline and using a simple count and the NSString/NSURL path routines will produce you a much simpler and more reliable solution than the one you have now posted as an answer.

    HTH