Search code examples
swifturlfile-renamedocumentsdirectory

Modern way to change the name of a file the documents directory in Swift


What is the modern Swift way to change a filename in the documents directory when you need to replace an existing file of the target name if one exists?

(For this reason, I am not using moveItem... but rather replaceItem

There are a lot of questions on this going back many years such as here but I cannot get any to work for me.

For example:

let oldpicname = "22_contactpic.png"
let newpicname = "9213_contactpic.png"
    do {
    let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    let documentDirectory = URL(fileURLWithPath: path)
    let originPath = documentDirectory.appendingPathComponent(oldpicname)
                                    let destinationPath = documentDirectory.appendingPathComponent(newpicname)
    try
    print("try to replace file")
    FileManager.default.replaceItemAt(originPath, withItemAt: destinationPath)
    } catch {
    print("FIRST TRY FAILED TO RENAME FILE")
    print(error)
    }

Compiles and does not throw an error but when I check for the file afterwards it does not exist.

Matt in the above link suggests the following:

var rv = URLResourceValues()
rv.name = newname
try? url.setResourceValues(rv)

This gives a number of errors I can not resolve including you cannot use mutating member on immutable value.

Note I am able to do this in Objective-C with the following code:

- (void)renameWithReplaceFileWithName:(NSString *)beforeName toName:(NSString *)afterName
{       
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *filePathBefore = [documentsDirectory stringByAppendingPathComponent:beforeName];
    
    NSString *filePathAfter = [documentsDirectory stringByAppendingPathComponent:afterName];
   
    NSLog(@"filepath after is%@. This will be the new name of this file",filePathAfter);
    NSFileManager *manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:filePathBefore]) {
        NSError *error = nil;
        NSURL *previousItemUrl = [NSURL fileURLWithPath: filePathBefore];/
        NSURL *currentItemUrl = [NSURL fileURLWithPath:filePathAfter]; 
        [[NSFileManager defaultManager] replaceItemAtURL:previousItemUrl withItemAtURL:currentItemUrl backupItemName:nil options:0 resultingItemURL:nil error:&error];
        if (error) {
            // handle error
        }
    }
}

Thank you for any suggestions.


Solution

  • This works for me, to change the name of a file, using the original code with minor change.

    func replace() {
        let oldpicname = "22_contactpic.png"
        let newpicname = "9213_contactpic.png"
        do {
            let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            let documentDirectory = URL(fileURLWithPath: path)
            let originPath = documentDirectory.appendingPathComponent(oldpicname)
            let destinationPath = documentDirectory.appendingPathComponent(newpicname)
    
            // -- here, note the change
            let results = try FileManager.default.replaceItemAt(destinationPath, withItemAt: originPath)
            
            print("----> results: \(results)")
        } catch {
            print("FIRST TRY FAILED TO RENAME FILE")
            print(error)
        }
    }
    

    Alternatively, without NSSearchPathForDirectoriesInDomains, checking that the file exists and using moveItem(...):

    func renameFile(from oldName: String, to newName: String) {
        let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let oldFilePath = docDir.appendingPathComponent(oldName)
        let newFilePath = docDir.appendingPathComponent(newName)
        // check that the file exist first
        if FileManager.default.fileExists(atPath: oldFilePath.path) {
            do {
                try FileManager.default.moveItem(at: oldFilePath, to: newFilePath)
                print("---> File renamed \n from:  \(oldFilePath) \n to:    \(newFilePath)")
            } catch {
                print("---> Error renaming file: \(error)")
            }
        } else {
            print("----> \(oldFilePath) does not exist")
        }
    }