I'm trying to get my OS X app to update automatically from changes to an iCloud document, and I'm having a really hard time getting the notification to work. Specifically, I was given some code that should list out all the results of my NSMetadataQuery
(just so I can do some testing and confirmation), but it's returning 0 results. This, despite the fact that clearly something is there in my ubiquity container because the data is being uploaded from my OS X app successfully, and downloaded again successfully at launch. Here's what I've got right now.
CloudDocument.h:
@property (nonatomic, strong) NSMetadataQuery *alertQuery;
CloudDocument.m:
@implementation CloudDocument
@synthesize alertQuery;
-(id)init {
self = [super init];
if (self) {
alertQuery = [[NSMetadataQuery alloc] init];
if (alertQuery) {
// Search the Documents subdirectory only.
[alertQuery setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// Add a predicate for finding the documents.
NSString *filePattern = @"*";
[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!!! - %@", [notification name]);
// 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 = (int)[alertQuery resultCount];
NSLog(@"%i", resultCount);
for (int i = 0; i < resultCount; i++) {
NSMetadataItem *item = [alertQuery resultAtIndex:i];
[self logAllCloudStorageKeysForMetadataItem:item];
}
[alertQuery enableUpdates];
}
- (void)logAllCloudStorageKeysForMetadataItem:(NSMetadataItem *)item {
NSLog(@"LogAll gets called");
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);
}
-(NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
[Data setSyncProgressIndicators:NO];
return [NSKeyedArchiver archivedDataWithRootObject:[Data getAllNotes]];
}
-(BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
[Data setSyncProgressIndicators:YES];
NSDictionary *dict = (NSDictionary *)[NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)data];
[Data didReceiveCloudData:dict];
[Data setSyncProgressIndicators:NO];
return YES;
}
+(BOOL)autosavesInPlace {
return YES;
}
@end
The results that I'm seeing are that queryDidUpdate:
gets called 2-3 times at launch, all from NSMetadataQueryDidFinishGatheringNotification
. Thing is, the "resultCount" variable is always 0, so logAllCloudStorageKeysForMetadataItem
never gets called, and NSMetadataQueryDidUpdateNotification
never gets called at all, no matter how often I edit the cloud document.
Can anyone offer any advice about why the search is returning 0 results, despite the fact that there is clearly a file being synced back and forth? Or even better, can you see what's wrong with the code entirely, and what I can do to get NSMetadataQueryDidUpdateNotification
on track and telling me when there have been edits to my file? I am saving the file, btw, using the method:
+(NSURL *)notesURL {
NSURL *url = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
return [url URLByAppendingPathComponent:kAllNotes];
}
where "kAllNotes" = "notes".
Thank you in advance for your help!
EDIT: Note that when I say it's syncing successfully, it's not doing it because of the NSMetadataQuery. I've got a cloudDoc = [[CloudDocument alloc]initWithContentsOfURL:[self notesURL] ofType:NSPlainTextDocumentType error:nil];
line elsewhere that loads up the CloudDocument, so I know there's a document there and I know it's being changed by the iOS version of the app, but the NSMetadataQuery that I'm trying to master right now is only there to work with the NSMetadataQueryDidUpdateNotification
, and none of that seems to be working correctly.
Also, I am definitely a beginner, so code snippets and examples when making suggestions are very, very much appreciated!
Alright, I found the answer: I've been saving my file to the root folder, but searching within NSMetadataQueryUbiquitousDocumentsScope
. It should have been NSMetadataQueryUbiquitousDataScope
, and now it seems to be working perfectly. I hope this helps people in the future!