Downloading files from iCloud

I'm trying to safely perform the apparently complex task for a cloud storage API, namely "downloading files", but it seems like iCloud APIs are comically broken beyond repair:

  1. -[NSFileCoordinator coordinateAccessWithIntents:queue:byAccessor:] calls the accessor block before all files have finished downloading.
  2. The same API will also return success (called the block with error == nil) even if the download fails (e.g. the phone is in airplane mode). I both cases, the files requested by the intents will not exist.
  3. -[NSFileManager startDownloadingUbiquitousItemAtURL:error:] does not have a completion block (Why?!?!)
  4. Similarly, this API will return success even if it fails (e.g. airplane mode)
  5. Manually checking NSURLUbiquitousItemIsDownloadingKey is broken as well, failed downloads (e.g. Airplane mode again) will retain their "Downloading" status, and NSURLUbiquitousItemDownloadingErrorKey is never updated.

How can one safely download a file from iCloud if all of the APIs are broken?

Maybe we can start with #1 and #2 – Would you mind to share the details of how you configure the intents and how you observe the issues?

In the simplest case, if I use coordinate(readingItemAt:options:error:byAccessor:) to coordinated read a file, I always see that the API downloads the file before calling my accessor.

Applying a coordinated read on a folder, however, doesn't necessarily download the folder recursively, and I'm curious if that is the case where you see the issues...

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I first obtain the directory listing with NSMetadataQuery, which works fine. Once the query is done, I great an array of intents like this:

         for (NSMetadataItem *item in query.results) {
            NSURL *itemURL = [item valueForAttribute:NSMetadataItemURLKey];
            
            if (![allowedExtensions containsObject:itemURL.pathExtension.lowercaseString]) {
                continue;
            }
            NSFileAccessIntent *intent = [NSFileAccessIntent writingIntentWithURL:itemURL
                                                                          options:0];
            [intents addObject:intent];
        }

Then, after stoping the query and removing the notification observer for it, I create a coordinator with the intents array I created and use it:

        NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
        
        [coordinator coordinateAccessWithIntents:intents
                                           queue:queue
                                      byAccessor:^(NSError *error) {
            // This gets called with error = nil in Airplane Mode
            for (NSFileAccessIntent *intent in intents) {
                // Sometimes, the file at intent.URL does not exist!
            }
        }];

Note that I use a writing intent because I sometimes need to update some of these files, but using a reading intent instead does not solve the issue.

Any update on this? I can't add iCloud support to my app without being able to safely download and sync files.

OK, I saw that the issue (that coordinateAccessWithIntents:queue:byAccessor: doesn't force a download of an iCloud Documents file) had been reported, which strongly indicates that it is a system bug. I suggest that you file your own feedback report to hopefully get more attentions – If you do so, please share your report ID here.

For a workaround, I am curious if using coordinateWritingItemAtURL:options:error:byAccessor: (or other variants) to do coordinated read or write one by one works. Did you try that? The API is synchronous, so you might want to call it from a global queue.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Unfortunately it seems like coordinateWritingItemAtURL:options:error:byAccessor: suffers from the same issue and calls, but coordinateReadingItemAtURL:options:error:byAccessor: does actually work. It's a bit annoying because it complicates both updating files and syncing multiple files (e.g. bundle-based formats), but it is something I can work around. It's very unfortunate that it's indeed a bug and not some API-misuse, because it seems to affect at least iOS 15 and up, and I'm targeting iOS 13-18.

Thanks for your help!

Downloading files from iCloud
 
 
Q