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?
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];
}
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];
}