Piero Piero - 6 months ago 54
iOS Question

NSMetadataQueryDidUpdateNotification called several time

i enabled iCloud

UIDocument
in my app, and i store document on the cloud, the document are packaged, for receive notification of update of the iCloud Document i'm doing this:

- (void)startQuery {

[self stopQuery];

NSLog(@"Starting to watch iCloud dir...");

_query = [self documentQuery];

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

[_query startQuery];
}

- (void)stopQuery {

if (_query) {

NSLog(@"No longer watching iCloud dir...");

[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidUpdateNotification object:nil];
[_query stopQuery];
_query = nil;
}

}

- (NSMetadataQuery *)documentQuery {

NSMetadataQuery * query = [[NSMetadataQuery alloc] init];
if (query) {

[query setSearchScopes:[NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope]];

NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K == %@",
NSMetadataItemFSNameKey,
kFILENAME];
[query setPredicate:pred];

}
return query;

}

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

[_query disableUpdates];

if ([_query resultCount] == 1) {
NSMetadataItem *item = [_query resultAtIndex:0];
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];

if (!self.folder) {
self.folder = [[MyDocument alloc] initWithFileURL:url];
}
[self.folder openWithCompletionHandler:^ (BOOL success) {
if (success) {
NSLog(@"loadData - doc opened from cloud %i", self.folder.count);
[self.folder closeWithCompletionHandler:^(BOOL success) {
NSLog(@"doc closed");
}
];
} else {
NSLog(@"failed to open");
} }];

}
}


to start all i call
[self startQeury]
, the problem is that
NSMetadataQueryDidUpdateNotification
called a notification several time if i make a change in the document, how you can see the log:

2013-10-12 01:43:54.933 Starting to watch iCloud dir...
2013-10-12 01:43:56.119 loadData - doc opened from cloud 56
2013-10-12 01:43:56.120 doc closed
2013-10-12 01:44:01.552 item deleted


as you can see here i make a change, and here below you can see that spawn 4 notification:

2013-10-12 01:44:08.110 loadData - doc opened from cloud 55
2013-10-12 01:44:08.111 doc closed
2013-10-12 01:44:11.942 loadData - doc opened from cloud 55
2013-10-12 01:44:11.943 doc closed
2013-10-12 01:44:13.198 loadData - doc opened from cloud 55
2013-10-12 01:44:13.199 doc closed
2013-10-12 01:44:14.925 loadData - doc opened from cloud 55
2013-10-12 01:44:14.926 doc closed


why is called several time? in my app i need to handle the change when the update is finish, so i need only one notification...how i can do?

EDIT:
i have try what @rmaddy suggest in the answer, i have try this:

if ([[item valueForAttribute:NSMetadataUbiquitousItemIsDownloadedKey] boolValue]) {

if (!self.folder) {
self.tvfilesFolder = [[MyDocument alloc] initWithFileURL:url];
}
[self.folder openWithCompletionHandler:^ (BOOL success) {
if (success) {
NSLog(@"loadData - doc opened from cloud %i", self.folder.count);
[self.folder closeWithCompletionHandler:^(BOOL success) {
NSLog(@"doc closed");
}
];
} else {
NSLog(@"failed to open");
} }];
}


but again i have the problem this is the log:

2013-10-12 10:35:26.084 Starting to watch iCloud dir...
2013-10-12 10:35:30.306 loadData - doc opened from cloud 54
2013-10-12 10:35:30.307 doc closed


then i have edited a document and this is the log:

2013-10-12 10:35:53.233 loadData - doc opened from cloud 54
2013-10-12 10:35:53.235 doc closed
2013-10-12 10:35:56.703 loadData - doc opened from cloud 54
2013-10-12 10:35:56.704 doc closed
2013-10-12 10:35:58.489 loadData - doc opened from cloud 54
2013-10-12 10:35:58.490 doc closed


how i can do?

EDIT 2:

i have try with this:

if ([[item valueForAttribute:NSMetadataUbiquitousItemIsDownloadedKey] boolValue] && [[item valueForAttribute:NSMetadataUbiquitousItemIsUploadedKey] boolValue]) {
....
}


and seems work, what you think? i can handle all time there is a change, or i can miss in this way some notification?

Answer

The notification is called for lots of reasons. When the file is first uploaded to iCloud and when the file is downloaded from iCloud. You also get updates about the percentage of upload and download. You can see all of these if you look at the various properties of the file.

I use a little debugging method to log each metadata item:

- (void)echoMetadataItem:(NSMetadataItem *)item {
    NSString *path = [item valueForAttribute:NSMetadataItemPathKey];
    NSString *display = [item valueForAttribute:NSMetadataItemDisplayNameKey];
    NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
    NSString *name = [item valueForAttribute:NSMetadataItemFSNameKey];
    NSString *downloadStatus = [item valueForAttribute:NSMetadataUbiquitousItemDownloadingStatusKey];
    NSNumber *downloading = [item valueForAttribute:NSMetadataUbiquitousItemIsDownloadingKey];
    NSNumber *uploaded = [item valueForAttribute:NSMetadataUbiquitousItemIsUploadedKey];
    NSNumber *uploading = [item valueForAttribute:NSMetadataUbiquitousItemIsUploadingKey];
    NSDate *createData = [item valueForAttribute:NSMetadataItemFSCreationDateKey];
    NSDate *updateDate = [item valueForAttribute:NSMetadataItemFSContentChangeDateKey];
    NSNumber *hasConflicts = [item valueForAttribute:NSMetadataUbiquitousItemHasUnresolvedConflictsKey];

    RMLogInfo(@"Metadata item name: %@, display: %@, path: %@, url: %@, dstat: %@, ding: %@, ued:%@, uing: %@, created: %@, updated: %@, conflicts: %@", name, display, path, url, downloadStatus, downloading, uploaded, uploading, createData, updateDate, hasConflicts);
}

There are also keys to get the percentage of upload and download.

If you want to handle the notification once, you can check that the file is downloaded.