Search code examples
ioscocoa-touchicloudnsfilemanager

How can I delete a file from iCloud without downloading it to the device first?


I have a basic iOS app that shows a list of documents. I'm trying to delete a document, but noticed that the code below fails with "No such file or directory" if the document has not yet been downloaded from iCloud to the device.

Documents can be quite large (40MB) and I'd like to avoid downloading the document only to delete it (this takes time and bandwidth out of the user's data plan). Is this possible at all?

[[[NSFileCoordinator alloc] initWithFilePresenter:nil]
coordinateWritingItemAtURL:documentURL
                  options:NSFileCoordinatorWritingForDeleting
         writingItemAtURL:previewURL
                  options:NSFileCoordinatorWritingForDeleting
                    error:&error
               byAccessor:^(NSURL *newDocumentURL, NSURL *newPreviewURL){

    // Fails with "No such file" error if not yet downloaded from iCloud:
    [[NSFileManager defaultManager] removeItemAtURL:newDocumentURL error:&error];
    [[NSFileManager defaultManager] removeItemAtURL:newPreviewURL  error:&error];
}];

The full error:

Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed. 
(Cocoa error 4.)" UserInfo=0x14e82930 {NSUnderlyingError=0x14e69220 
"The operation couldn’t be completed. No such file or directory",

Solution

  • If you just need to delete a file, use the other NSFileCoordinator coordinateWritingItemAtURL ( the one with a single newURL parameter in the accessor block ).

    If you need to batch delete, then create an array of NSFileAccessIntent and use NSFileCoordinator's coordinateAccessWithIntents.

    Example:

    - ( void )deleteItemsAtURLs: ( NSArray * )urls queue: ( NSOperationQueue * )queue
    {
        //assuming urls is an array of urls to be deleted
        NSFileCoordinator   * coordinator;
        NSMutableArray      * writingIntents;
        NSURL               * url;
    
        writingIntents = [ NSMutableArray arrayWithCapacity: urls.count ];
    
        for( url in urls )
        {
            [ writingIntents addObject: [ NSFileAccessIntent writingIntentWithURL: url options: NSFileCoordinatorWritingForDeleting ] ];
        }
    
        coordinator = [ [ NSFileCoordinator alloc ] initWithFilePresenter: nil ];
    
        [ coordinator coordinateAccessWithIntents: writingIntents
                                            queue: queue
                                       byAccessor: ^( NSError * error )
         {
             if( error )
             {
                 //handle
                 return;
             }
             NSFileAccessIntent * intent;
    
             error = nil;
    
             for( intent in writingIntents )
             {
                 [ [ NSFileManager defaultManager ] removeItemAtURL: intent.URL error: &error ];
                 if( error )
                 {
                     //handle
                 }
             }
         }];
    }