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:
[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?
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).