Setting up NSMetadataQueryDidUpdateNotification for a simple response

I’m trying to get up and running with an NSMetadataQueryDidUpdateNotification on an OS X app, to alert me when a file in my iCloud ubiquity container is updated. I’ve been doing a lot of research (including reading other Stack answers like this, this, this, and this), but I still don’t have it quite right, it seems.

I’ve got a “CloudDocument” object subclassed from NSDocument, which includes this code in the H:

  • Class Constants
  • Could not change executable permissions on the application
  • Setting up a git repository in XCode for a pre-existing project
  • Set default localization of iOS app
  • How to install iOS 7 and onwards simulators in Xcode 7 Beta 5?
  • Finished running <App> on iPhone 6.1 Simulator
  • @property (nonatomic, strong) NSMetadataQuery *alertQuery;
    

    and this is the M file:

    @synthesize alertQuery;
    
    -(id)init {
        self = [super init];
        if (self) {
            if (alertQuery) {
                [alertQuery stopQuery];
            } else {
                alertQuery = [[NSMetadataQuery alloc]init];
            }
    
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidUpdate:) name:NSMetadataQueryDidUpdateNotification object:nil];
            NSLog(@"Notification created");
    
            [alertQuery startQuery];
        }
        return self;
    }
    
    -(void)queryDidUpdate:(NSNotification *)notification {
        NSLog(@"Something changed!!!");
    }
    

    According to my best understanding, that should stop a pre-existing query if one is running, set up a notification for changes to the ubiquity container, and then start the query so it will monitor changes from here on out.

    Except, clearly that’s not the case because I get Notification created in the log on launch but never Something changed!!! when I change the iCloud document.

    Can anyone tell me what I’m missing? And if you’re extra-super-sauce awesome, you’ll help me out with some code samples and/or tutorials?

    Edit: If it matters/helps, there is only one file in my ubiquity container being synced around. It’s called “notes”, so I access it using the URL result from:

    +(NSURL *)notesURL {
        NSURL *url = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
        return [url URLByAppendingPathComponent:kAllNotes];
    }
    

    where “kAllNotes” is set with #define kAllNotes @"notes".

    EDIT #2: There have been a lot of updates to my code through my conversation with Daij-Djan, so here is my updated code:

    @synthesize alertQuery;
    
    -(id)init {
        self = [super init];
        if (self) {
            alertQuery = [[NSMetadataQuery alloc] init];
            if (alertQuery) {
                [alertQuery setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
    
                NSString *STEDocFilenameExtension = @"*";
                NSString* filePattern = [NSString stringWithFormat:@"*.%@", STEDocFilenameExtension];
                [alertQuery setPredicate:[NSPredicate predicateWithFormat:@"%K LIKE %@", NSMetadataItemFSNameKey, filePattern]];
            }
    
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidUpdate:) name:NSMetadataQueryDidFinishGatheringNotification object:nil];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidUpdate:) name:NSMetadataQueryDidUpdateNotification object:nil];
    
            NSLog(@"Notification created");
    
            [alertQuery startQuery];
        }
        return self;
    }
    
    -(void)queryDidUpdate:(NSNotification *)notification {
        [alertQuery disableUpdates];
    
        NSLog(@"Something changed!!!");
    
        [alertQuery enableUpdates];
    }
    

    2 Solutions Collect From Internet About “Setting up NSMetadataQueryDidUpdateNotification for a simple response”

    How do you save your document – what url do you give it? Unless you give it an extension yourself, it won’t automatically be given one – so your *.* pattern will never match a file that does not have an extension. Try * as the pattern and see what happens.

    Also, it helps to log what is happening within queryDidUpdate, until you’ve worked out exactly what’s going on :

    Try something like:

    -(void)queryDidUpdate:(NSNotification *)notification {
        [alertQuery disableUpdates];
    
        NSLog(@"Something changed!!!");
    
        // Look at each element returned by the search
        // - note it returns the entire list each time this method is called, NOT just the changes
        int resultCount = [alertQuery resultCount];
        for (int i = 0; i < resultCount; i++) {
            NSMetadataItem *item = [alertQuery resultAtIndex:i];
            [self logAllCloudStorageKeysForMetadataItem:item];
        }
    
        [alertQuery enableUpdates];
    }
    
    - (void)logAllCloudStorageKeysForMetadataItem:(NSMetadataItem *)item
    {
        NSNumber *isUbiquitous = [item valueForAttribute:NSMetadataItemIsUbiquitousKey];
        NSNumber *hasUnresolvedConflicts = [item valueForAttribute:NSMetadataUbiquitousItemHasUnresolvedConflictsKey];
        NSNumber *isDownloaded = [item valueForAttribute:NSMetadataUbiquitousItemIsDownloadedKey];
        NSNumber *isDownloading = [item valueForAttribute:NSMetadataUbiquitousItemIsDownloadingKey];
        NSNumber *isUploaded = [item valueForAttribute:NSMetadataUbiquitousItemIsUploadedKey];
        NSNumber *isUploading = [item valueForAttribute:NSMetadataUbiquitousItemIsUploadingKey];
        NSNumber *percentDownloaded = [item valueForAttribute:NSMetadataUbiquitousItemPercentDownloadedKey];
        NSNumber *percentUploaded = [item valueForAttribute:NSMetadataUbiquitousItemPercentUploadedKey];
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
    
        BOOL documentExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]];
    
        NSLog(@"isUbiquitous:%@ hasUnresolvedConflicts:%@ isDownloaded:%@ isDownloading:%@ isUploaded:%@ isUploading:%@ %%downloaded:%@ %%uploaded:%@ documentExists:%i - %@", isUbiquitous, hasUnresolvedConflicts, isDownloaded, isDownloading, isUploaded, isUploading, percentDownloaded, percentUploaded, documentExists, url);
    }
    

    you never allocate your alertQuery….

    somewhere you need to alloc,init a NSMetaDataQuery for it

    example


    NSMetadataQuery* aQuery = [[NSMetadataQuery alloc] init];
    if (aQuery) {
        // Search the Documents subdirectory only.
        [aQuery setSearchScopes:[NSArray
                    arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
    
        // Add a predicate for finding the documents.
        NSString* filePattern = [NSString stringWithFormat:@"*"];
        [aQuery setPredicate:[NSPredicate predicateWithFormat:@"%K LIKE %@",
                    NSMetadataItemFSNameKey, filePattern]];
    
       // Register for the metadata query notifications.
       [[NSNotificationCenter defaultCenter] addObserver:self
            selector:@selector(processFiles:)
            name:NSMetadataQueryDidFinishGatheringNotification
            object:nil];
       [[NSNotificationCenter defaultCenter] addObserver:self
            selector:@selector(processFiles:)
            name:NSMetadataQueryDidUpdateNotification
            object:nil];
    
       // Start the query and let it run.
       [aQuery startQuery];
    

    }

    processFiles method

    == your queryDidUpdate

    - processFiles(NSNotification*)note {
         [aQuery disableUpdates];
    
         .....
    
         [aQuery enableUpdates];
    }
    

    NOTE: disable and reenable search there!

    see:

    http://developer.apple.com/library/ios/#documentation/General/Conceptual/iCloud101/SearchingforiCloudDocuments/SearchingforiCloudDocuments.html