How can I change the filename in a SwiftUI Document-based app?

1.1k views Asked by At

I've been using the new template for a document-based SwiftUI app. While you get a lot of file-management "for free" in the new template, as it stands in the iOS version users have to back out of the file to the file browser to change the filename. I want to create an opportunity for the user to rename the file while it is open.

Here's a sample project focused on the issue: https://github.com/stevepvc/DocumentRenamer

In the code, I've added to the template code a simple UI with a textfield for the user to enter a new name. When the user hits the "rename" button, the app checks to see if the URL with that name component is available, appending a suffix if necessary to create a target url.

func getTargetURL() -> URL {
    let baseURL  =  self.fileurl.deletingLastPathComponent()
    
    print("filename: \(self.filename)")
    print("fileURL: \(self.fileurl)")
    print("BaseURL: \(baseURL)")
    var target = URL(fileURLWithPath: baseURL.path + "/\(filename).exampletext")

    var nameSuffix = 1
    
    while (target as NSURL).checkPromisedItemIsReachableAndReturnError(nil) {
        
        target = URL(fileURLWithPath: baseURL.path + "/\(filename)-\(nameSuffix).sermon")
        print("Checking: \(target)")
        nameSuffix += 1
   }
    print("Available Target: \(target)")

    return target
}

It then attempts to rename the file, and this is when I am stuck. I have tried several methods, most recently the following:

func changeFilename(){

    let target  = getTargetURL()
    var rv = URLResourceValues()
    let newFileName = target.deletingPathExtension().lastPathComponent
    rv.name = newFileName
    do {
        if fileurl.startAccessingSecurityScopedResource(){
            try fileurl.setResourceValues(rv)
            fileurl.stopAccessingSecurityScopedResource()
        }
    } catch {
        print("Error:\(error)")
    }
}

But I keep getting the following error:

Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “Untitled” in the folder “DocumentRenamer”."

I have also tried this without the startAccessingSecurityScopedResource() check, and alternatively have tried creating a helper class as follows:

class FileMover: NSObject {
func moveFile(originalURL: URL, updatedURL:URL) -> Bool {
    let coordinator = NSFileCoordinator(filePresenter: nil)
    var writingError: NSError? = nil
    var success : Bool = true
    print("moving file")
    coordinator.coordinate(writingItemAt: originalURL, options: NSFileCoordinator.WritingOptions.forMoving, error: &writingError, byAccessor: { (coordinatedURL) in
        do {
            try FileManager.default.moveItem(at: coordinatedURL, to: updatedURL)
            success = true
            print("file moved")
            
        } catch {
            print(error)
            success = false
        }
    })
return success
    
}
}

But using this method locks up the app entirely. It's possible that there is something about iCloud permissions going on there, but I think I've have those set up appropriately.

It appears to work fine in the simulator, but not when run on a device.

What is the correct method for renaming a file in the app's container?

0

There are 0 answers