Search code examples
iosnsfilemanager

The replaceItemAtURL: method fails to remove original moved files in some cases


I am using NSFileManager's replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: method to move an sqlite file in the case where it fails to move using the replacePersistentStoreAtURL:destinationOptions:withPersistentStoreFromURL:sourceOptions:storeType:error: method. The file has three component files - one that ends with .sqlite, one that ends with .sqlite-wal and one that ends with .sqlite-shm. All files correctly replace their existing counterparts using the replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: method; however, only the moved .sqlite file is actually removed from its original location. The .sqlite-wal and .sqlite-shm files do replace as desired, but it seems that they are actually copied rather than moved, as those two original files are still in place at the end of their respectful successful replacePersistentStoreAtURL:destinationOptions:withPersistentStoreFromURL:sourceOptions:storeType:error: operation. Everything is happening within the same volume, so there seems to be no reason why copies would be made. Can someone help me to make sense of why this could be happening?

Here is the code. The status message, when later logged, reads:

Successfully replaced SQLITE file. SQLITE file does NOT still exist in original location. Successfully replaced WAL file. WAL file DOES still exist in original location. Successfully replaced SHM file. SHM file DOES still exist in original location.

- (void)moveSqliteFileFromMigrateStorePathToFinalStorePathWithCompletionHandler:(void(^)(NSURL * migrateStoreURL,NSString * statusMessage,BOOL actuallyMovedFiles,BOOL movingHudIsRunning))handler {

    __block NSString *statusMessage = nil;
    __block BOOL actuallyMovedFiles = NO;
    __block BOOL hudIsRunning = NO;

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *applicationDocumentsDirectory = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    NSString *migrateStorePath = [applicationDocumentsDirectory stringByAppendingPathComponent:@"MyAppData.sqlite"];
    NSURL *migrateStoreURL = [NSURL fileURLWithPath:migrateStorePath];
    NSURL *finalStoreURL = [CoreDataController desiredFinalStoreURL];
    NSString *finalStorePath = finalStoreURL.path;
    NSString *fromWalPath = [migrateStorePath stringByAppendingString:@"-wal"];
    NSString *fromShmPath = [migrateStorePath stringByAppendingString:@"-shm"];
    BOOL walFileExists = [NSFileManager.defaultManager fileExistsAtPath:fromWalPath];
    BOOL shmFileExists = [NSFileManager.defaultManager fileExistsAtPath:fromShmPath];
    BOOL sqliteFileExists = [NSFileManager.defaultManager fileExistsAtPath:migrateStorePath];

    if (sqliteFileExists || shmFileExists || walFileExists) {

        [SVProgressHUD setForegroundColor:CPS_DARK_BLUE_COLOR];
        [SVProgressHUD showWithStatus:NSLocalizedString(@"My App is updating. This one-time operation may take several minutes.",@"")];

        hudIsRunning = YES;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            if (sqliteFileExists) {
                BOOL finalStorePathFileExists = [NSFileManager.defaultManager fileExistsAtPath:finalStorePath];
                NSError * sqliteMoveError = nil;
                BOOL successfulSqliteMove = NO;
                BOOL replacingSqliteFile = NO;
                if (finalStorePathFileExists) {
                    replacingSqliteFile = YES;
                    NSURL *migrateStoreURL = [NSURL fileURLWithPath:migrateStorePath];
                    NSURL *finalStoreURL = [NSURL fileURLWithPath:finalStorePath];
                    successfulSqliteMove = [[NSFileManager defaultManager] replaceItemAtURL:finalStoreURL withItemAtURL:migrateStoreURL backupItemName:@"sqliteBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&sqliteMoveError];//NSFileManagerItemReplacementUsingNewMetadataOnly
                }
                else {
                    successfulSqliteMove = [[NSFileManager defaultManager] moveItemAtPath:migrateStorePath toPath:finalStorePath error:&sqliteMoveError];
                }

                if (sqliteMoveError) {
                    DLog(@"The error for the SQLITE move: %@",sqliteMoveError.localizedDescription);
                }

                if (successfulSqliteMove) {
                    actuallyMovedFiles = YES;
                    if([NSFileManager.defaultManager fileExistsAtPath:migrateStorePath]) {
                        statusMessage = replacingSqliteFile?NSLocalizedString(@"Successfully replaced SQLITE file. SQLITE file DOES still exist in original location.", @""):NSLocalizedString(@"Successfully moved SQLITE file. SQLITE file DOES still exist in original location.", @"");
                    }
                    else {
                        statusMessage = replacingSqliteFile?NSLocalizedString(@"Successfully replaced SQLITE file. SQLITE file does NOT still exist in original location.", @""):NSLocalizedString(@"Successfully moved SQLITE file. SQLITE file does NOT still exist in original location.", @"");
                    }
                }
                else {
                    statusMessage = replacingSqliteFile?[NSString stringWithFormat:@"%@ (%@). ",NSLocalizedString(@"Failed to replace SQLITE file", @""),sqliteMoveError.localizedDescription]:[NSString stringWithFormat:@"%@ (%@). ",NSLocalizedString(@"Failed to move SQLITE file", @""),sqliteMoveError.localizedDescription];
                }
            }
            else {
                statusMessage = NSLocalizedString(@"No SQLITE file to move.", @"");
            }

            if (walFileExists) {
                NSString *toWalPath = [finalStorePath stringByAppendingString:@"-wal"];
                BOOL toWalFileExists = [NSFileManager.defaultManager fileExistsAtPath:toWalPath];
                NSError * walMoveError = nil;
                BOOL successfulWalMove = NO;
                BOOL replacingWalFile = NO;
                if (toWalFileExists) {
                    replacingWalFile = YES;
                    NSURL *fromWalURL = [NSURL fileURLWithPath:fromWalPath];
                    NSURL *toWalURL = [NSURL fileURLWithPath:toWalPath];
                    //successfulWalMove = [[NSFileManager defaultManager] replaceItemAtURL:fromWalURL withItemAtURL:toWalURL backupItemName:@"walBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&walMoveError];
                    //THE ABOVE CODE WAS WRONG, WHICH WAS WHAT WAS CAUSING THE ISSUE
                    successfulWalMove = [[NSFileManager defaultManager] replaceItemAtURL:toWalURL withItemAtURL:fromWalURL backupItemName:@"walBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&walMoveError];
                }
                else {
                    successfulWalMove = [[NSFileManager defaultManager] moveItemAtPath:fromWalPath toPath:toWalPath error:&walMoveError];
                }

                if (walMoveError) {
                    DLog(@"The error for the WAL move: %@",walMoveError.localizedDescription);
                }

                if (successfulWalMove) {
                    actuallyMovedFiles = YES;

                    if([NSFileManager.defaultManager fileExistsAtPath:fromWalPath]) {
                        statusMessage = replacingWalFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced WAL file. WAL file DOES still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved WAL file. WAL file DOES still exist in original location.", @"")];
                    }
                    else {
                        statusMessage = replacingWalFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced WAL file. WAL file does NOT still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved WAL file. WAL file does NOT still exist in original location.", @"")];
                    }
                }
                else {
                    statusMessage = replacingWalFile?[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to replace WAL file", @""),walMoveError.localizedDescription]:[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to move WAL file", @""),walMoveError.localizedDescription];
                }
            }
            else {
                statusMessage = NSLocalizedString(@"No WAL file to move.", @"");
            }

            if (shmFileExists) {
                NSString *toShmPath = [finalStorePath stringByAppendingString:@"-shm"];
                BOOL toShmFileExists = [NSFileManager.defaultManager fileExistsAtPath:toShmPath];
                NSError * shmMoveError = nil;
                BOOL successfulShmMove = NO;
                BOOL replacingShmFile = NO;
                if (toShmFileExists) {
                    replacingShmFile = YES;
                    NSURL *fromShmURL = [NSURL fileURLWithPath:fromShmPath];
                    NSURL *toShmURL = [NSURL fileURLWithPath:toShmPath];
                    //successfulShmMove = [[NSFileManager defaultManager] replaceItemAtURL:fromShmURL withItemAtURL:toShmURL backupItemName:@"shmBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&shmMoveError];
                    //THE ABOVE CODE WAS WRONG, WHICH WAS WHAT WAS CAUSING THE ISSUE
                    successfulShmMove = [[NSFileManager defaultManager] replaceItemAtURL:toShmURL withItemAtURL:fromShmURL backupItemName:@"shmBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&shmMoveError];
                }
                else {
                    successfulShmMove = [[NSFileManager defaultManager] moveItemAtPath:fromShmPath toPath:toShmPath error:&shmMoveError];
                }

                if (shmMoveError) {
                    DLog(@"The error for the SHM move: %@",shmMoveError.localizedDescription);
                }

                if (successfulShmMove) {
                    actuallyMovedFiles = YES;

                    if([NSFileManager.defaultManager fileExistsAtPath:fromWalPath]) {
                        statusMessage = replacingShmFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced SHM file. SHM file DOES still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved SHM file. SHM file DOES still exist in original location.", @"")];
                    }
                    else {
                        statusMessage = replacingShmFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced SHM file. SHM file does NOT still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved SHM file. SHM file does NOT still exist in original location.", @"")];
                    }
                }
                else {
                    statusMessage = replacingShmFile?[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to replace SHM file", @""),shmMoveError.localizedDescription]:[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to move SHM file", @""),shmMoveError.localizedDescription];
                }
            }
            else {
                statusMessage = NSLocalizedString(@"No SHM file to move.", @"");
            }

            if (handler) {
                handler(migrateStoreURL,statusMessage,actuallyMovedFiles,hudIsRunning);
            }
        });
    }
    else {
        if (handler) {
            actuallyMovedFiles = NO;
            hudIsRunning = NO;
            statusMessage = NSLocalizedString(@"No SQLITE files to move.", @"");
            handler(migrateStoreURL,statusMessage,actuallyMovedFiles,hudIsRunning);
        }
    }
}

EDIT: Thanks to @matt, the problem is solved - for the shm and wal files, the 'to' and 'from' were mixed up. I can't believe I missed that. So now, as I expected from researching online - even though it is not written in the documentation - each file is actually moved, and not copied, when the replace method is successful. With my revised and fixed code, this is the message I now get:

Successfully replaced SQLITE file. SQLITE file does NOT still exist in original location. Successfully replaced WAL file. WAL file does NOT still exist in original location. Successfully replaced SHM file. SHM file does NOT still exist in original location.


Solution

  • I may be wrong, but at a glance it seems to me that you are using the notions "from" and "to" in the opposite of the way I would use them. You may be confused about where you are moving the files from and where you want to move them to.