How do I tell iOS to download a file from iCloud D

2019-02-07 04:28发布

问题:

I am using the UIDocumentPicker to select a file but if it's large it can take a while to open and that is not a particularly good experience for users.

I have looked at the iCloud programming guide from Apple and I cannot seem to figure out how to actually download a file and get some progress feedback, the documentation is just too vague. I know I am supposed to do something with NSMetadataItems, but there isn't much explaining actually how to get one and use it.

Can someone please explain it to me?

P.S. can someone with higher rep than me tag this post with UIDocumentPicker and iCloudDrive?

回答1:

To my knowledge, you can only retrieve progress feedback using the Ubiquitous Item Downloading Status Constants which provides only 3 states:

  • NSURLUbiquitousItemDownloadingStatusCurrent
  • NSURLUbiquitousItemDownloadingStatusDownloaded
  • NSURLUbiquitousItemDownloadingStatusNotDownloaded

So you can't have a quantified progress feedback, only partial aka downloaded or not.

To do so, you need to prepare and start your NSMetadataQuery, add some observers and check the downloading status of your NSMetadataItem using the NSURLUbiquitousItemDownloadingStatusKey key.

self.query = [NSMetadataQuery new];
self.query.searchScopes = @[ NSMetadataQueryUbiquitousDocumentsScope ];
self.query.predicate = [NSPredicate predicateWithFormat:@"%K like '*.yourextension'", NSMetadataItemFSNameKey];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidUpdate:) name:NSMetadataQueryDidUpdateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:nil];

[self.query startQuery];

Then,

- (void)queryDidUpdate:(NSNotification *)notification
{
    [self.query disableUpdates];

    for (NSMetadataItem *item in [self.query results]) {
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
        NSError *error = nil;
        NSString *downloadingStatus = nil;

        if ([url getResourceValue:&downloadingStatus forKey:NSURLUbiquitousItemDownloadingStatusKey error:&error] == YES) {
            if ([downloadingStatus isEqualToString:NSURLUbiquitousItemDownloadingStatusNotDownloaded] == YES) {
                // Download
            }
            // etc.
        }
    }

    [self.query enableUpdates];
}


回答2:

Progress feedback from an NSMetadataQuery happens through notifications. The update frequency is once per second per default (can be changed by setting notificationBatchingInterval). The updated objects are encapsulated in the userInfo dict of the notification as arrays of NSMetadataItem. Download feedback is encapsultaed in the key NSMetadataUbiquitousItemPercentDownloadedKey of each item. Since the arrays are internally mutable, we need to tell NSMetadataQuery to disable updates while we enumerate the results. This is important, otherwise strange crashes will occur.

A typical implementation might look like this:

- (void) queryDidUpdate:(NSNotification *)notification {

[self.mdQuery disableUpdates];// we don't want to receive a new update while we still process the old one

NSArray *addedItems     = notification.userInfo[NSMetadataQueryUpdateAddedItemsKey];
NSArray *remItems       = notification.userInfo[NSMetadataQueryUpdateRemovedItemsKey];
NSArray *changedItems   = notification.userInfo[NSMetadataQueryUpdateChangedItemsKey];

// add
for (NSMetadataItem *mdItem in addedItems) {
    NSURL *url          = [mdItem valueForKey:NSMetadataUbiquitousItemURLInLocalContainerKey];
    // do something...
}
// remove
for (NSMetadataItem *mdItem in remItems) {
    NSURL *url          = [mdItem valueForKey:NSMetadataUbiquitousItemURLInLocalContainerKey];
    // do something...
}
// change
for (NSMetadataItem *mdItem in changedItems) {
    NSURL *url          = [mdItem valueForKey:NSMetadataUbiquitousItemURLInLocalContainerKey];
        // uploading
        BOOL uploading  = [(NSNumber *)[mdItem valueForKey:NSMetadataUbiquitousItemIsUploadingKey] boolValue];
        if (uploading) {
            NSNumber *percent   = [mdItem valueForKey:NSMetadataUbiquitousItemPercentUploadedKey];
            cell.progressView.progress = percent.floatValue;
            // do something...
        }
        // downloading
        BOOL downloading    = [(NSNumber *)[mdItem valueForKey:NSMetadataUbiquitousItemIsDownloadingKey] boolValue];
        if (downloading) {
            NSNumber *percent   = [mdItem valueForKey:NSMetadataUbiquitousItemPercentDownloadedKey];
            cell.progressView.progress = percent.floatValue;
            // do something...
        }
    }
[self.mdQuery enableUpdates];
}