5

I'm struggling to figure out how to rename an instance of a UIDocument subclass in an iCloud folder. I've tried saving the document with the new URL…

func renameDocument(to name: String) {

    let targetURL = document.fileURL.deletingLastPathComponent().appendingPathComponent(name)
        .appendingPathExtension("<extension>")

    document.save(to: targetURL, for: .forCreating) { success in
        guard success else {
            // This always fails
            return
        }
        // Success
    }
}

…but this fails with…

Error Domain=NSCocoaErrorDomain Code=513 "“<new-file-name>” couldn’t be moved because you don’t have permission to access “<folder>”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Data/Application/1A9ACC2B-81EF-4EC9-940E-1C129BDB1914/tmp/(A Document Being Saved By My App)/<new-file-name>, NSUserStringVariant=( Move ), NSDestinationFilePath=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/<folder>/<new-file-name>, NSFilePath=/private/var/mobile/Containers/Data/Application/1A9ACC2B-81EF-4EC9-940E-1C129BDB1914/tmp/(A Document Being Saved By My App)/<new-file-name>, NSUnderlyingError=0x1c4e54280 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}

…and just a simple move…

func renameDocument(to name: String) {
    let targetURL = document.fileURL.deletingLastPathComponent().appendingPathComponent(name)
        .appendingPathExtension("<extension>")

    do {
        try FileManager.default.moveItem(at: document.fileURL, to: targetURL)
    } catch {
        // This always fails
    }        
    // Success
}

…which fails with…

Error Domain=NSCocoaErrorDomain Code=513 "“<old-file-name>” couldn’t be moved because you don’t have permission to access “<folder>”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/<folder>/<old-file-name>, NSUserStringVariant=( Move ), NSDestinationFilePath=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/<folder>/<new-file-name>, NSFilePath=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/<folder>/<old-file-name>, NSUnderlyingError=0x1c4c4d8c0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}

Both of these work fine for local files, and renaming iCloud files works OK in the UIDocumentBrowserViewController root view controller.

My guess is that there's some permission missing somewhere that allows the app to write to iCloud folders.

For info, the info.plist contains all the following keys…

  • LSSupportsOpeningDocumentsInPlace
  • NSExtensionFileProviderSupportsEnumeration
  • UISupportsDocumentBrowser
Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
  • Is the document open when you are trying to rename it? – theMikeSwan Oct 04 '17 at 16:29
  • It doesn't seem to make any difference whether it's open or closed. – Ashley Mills Oct 04 '17 at 16:31
  • Have a look at https://developer.apple.com/documentation/foundation/filemanager/1413989-setubiquitous see if that helps. I'm pretty sure some of the normal file manager methods don't work in the iCloud container (though iOS 11 may have changed things…) – theMikeSwan Oct 04 '17 at 16:43
  • I'll take another look at that. I had a previous iOS 10 version that worked using that method, but I was under the impression that things had changed in iOS 11, since you can save a file to any location, in which case how do you know whether it's an iCloud or local folder… or some other location? – Ashley Mills Oct 04 '17 at 16:46
  • This may be one of those spots Apple missed assuming that any renaming would happen in their shiny new document browser. I've experimented with the browser a little but not enough to have found the rough edges yet (other than the need to get the info.plist stuff _exactly_ right for it to work). – theMikeSwan Oct 04 '17 at 16:49

2 Answers2

2

Are you doing this in the context of NSFileCoordinator? This is required. You shouldn't need any info.plist settings apart from NSUbiquitousContainers.

Here is my code for renaming iCloud documents:

///
/// move cloudFile within same store - show errors
///
- (void)moveCloudFile:(NSURL *)url toUrl:(NSURL *)newURL
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{
        NSError *coordinationError;
        NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];

        [coordinator coordinateWritingItemAtURL:url    options:NSFileCoordinatorWritingForMoving
                               writingItemAtURL:newURL options:NSFileCoordinatorWritingForReplacing
                                                       error:&coordinationError
                                     byAccessor:
        ^(NSURL *readingURL, NSURL  *writingURL){
            if ([self moveFile:readingURL toUrl:writingURL])
                 [coordinator itemAtURL:readingURL didMoveToURL:writingURL];
        }];
        if (coordinationError) {
            MRLOG(@"Coordination error: %@", coordinationError);
            [(SSApplication *)SSApplication.sharedApplication fileErrorAlert:coordinationError];
        }
    });
}

///
///
/// move file within same store - show errors
///
- (BOOL)moveFile:(NSURL *)url toUrl:(NSURL *)newURL
{
    NSFileManager *manager = NSFileManager.defaultManager;
    NSError *error;

    if ([manager fileExistsAtPath:newURL.path])
        [self removeFile:newURL];

    if ([manager moveItemAtURL:url toURL:newURL error:&error] == NO) {
        MRLOG(@"Move failed: %@", error);
        [(SSApplication *)SSApplication.sharedApplication fileErrorAlert:error];
        return NO;
    }
    return YES;
}
MichaelR
  • 1,681
  • 15
  • 28
  • 1
    Nice to see the which options someone else used to do this. Much better than what I was using. But I thought you were not to use the default FileManager in a Coordinator. – Sojourner9 Nov 04 '17 at 16:08
1

NSDestinationFilePath=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/<folder>/<new-file-name>

You're trying to save the document outside of your sandbox. You can only write files inside your own container, so there's no way to do what you want. Please file a bug at bugreport.apple.com if you want an API to rename the current file.

Thomas Deniau
  • 2,488
  • 1
  • 15
  • 15