Lost data while moving file out of iCloud that was not yet completely downloaded (no error reported)

StackOverflow https://stackoverflow.com/questions/22638379

Domanda

I have a Mac app with iCloud integration. It's not based on NSDocument and I handle moving files in and out of iCloud myself via [NSFileManager setUbiquitous:…]. Here's what I ran into:

  1. I added a large (15 MB) document to my app while connected to iCloud
  2. Waited for document to be completely uploaded to iCloud
  3. Now I signed out of my iCloud account on my Mac → the file was removed from the Mac
  4. I opened my app, signed back in to iCloud, the file appeared on disk
  5. Quickly, through my app I moved all documents out of iCloud to the local disk (internally using [NSFileManager setUbiquitous:NO…]

The large document was not copied to the local disk (I suspect because it was not yet downloaded 100% from iCloud), but it also disappeared from iCloud. No way to recover the data. There was no error reported by NSFileManager.

Here is the relevant code:

NSArray *files = [fileManager contentsOfDirectoryAtURL:iCloudDataFolderURL
                            includingPropertiesForKeys:nil options:0 error:&error];

for (NSURL *fileURL in files) {
    // figure out URLs […]
    if (![fileManager setUbiquitous:NO 
                          itemAtURL:iCloudFileURL 
                     destinationURL:localDocumentURL 
                              error:&error]) {
        hadError = YES;
        NSLog(@"Error moving file from iCloud: %@ to local storage: %@ Error: %@",
               iCloudFileURL, localDocumentURL, error);
    }
}

I would have expected the call to [NSFileManager setUbiquitous:NO…] to either block or fail if the file is not completely on the local disk. Instead I end up with a file wrapper that shows a file size of 15 MB in Finder, but is actually empty.

What is a safe way to move documents out of iCloud to the local disk?

È stato utile?

Soluzione

You can just use NSFileCoordinator and NSFileManager methods to move the files in and out. I don't know if it is safer, but it should give you more control over error conditions and what should happen when something goes wrong.

For an example, take a look at the iCloud backend of the Ensembles framework here (Disclosure: it is my project). Search for uploadLocalFile:... and downloadFromPath:.... They are particularly advanced methods, with timeouts, but the idea is the same.

First, create a file coordinator.

NSFileCoordinator *coordinator = 
     [[NSFileCoordinator alloc] initWithFilePresenter:nil];

Then copy or move the file.

    [coordinator coordinateReadingItemAtURL:fromURL options:0   
        writingItemAtURL:toURL 
        options:NSFileCoordinatorWritingForReplacing 
        error:&fileCoordinatorError 
        byAccessor:^(NSURL *newReadingURL, NSURL *newWritingURL) {
        [fileManager removeItemAtPath:newWritingURL.path error:NULL];
        [fileManager copyItemAtPath:newReadingURL.path 
            toPath:newWritingURL.path error:&fileManagerError];
    }];

If you don't want to program this all yourself, I split off a class that may help (iCloudAccess).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top