- Setting Up an iCloud-Compatible Project
- How iCloud Works
- iCloud Container Folders
- Recipe: Trying Out iCloud
- Working with UIDocument
- Recipe: Subclassing UIDocument
- Metadata Queries and the Cloud
- Handy Routines
- Recipe: Accessing the Ubiquitous Key-Value Store
- Recipe: UIManagedDocument and Core Data
- Summary
Metadata Queries and the Cloud
Two new metadata scopes, NSMetadataQueryUbiquitousDataScope and NSMetadataQueryUbiquitousDocumentsScope, point to the iCloud’s Data and Documents folders. You can add an ongoing query to monitor these folders, creating a callback whenever the folder contents change. This allows you to know when new items appear or disappear and update your file listings accordingly. Listing 18-1 offers a pair of methods that demonstrate how to use this technology to query and monitor the folder:
- The first method, startMonitoringUbiquitousDocumentsFolder, sets up a monitor for the Documents folder scope, listening for update notifications. When these occur, the method uses an informal delegate callback to update a client.
- The second method, stopMonitoringUbiquitousDocumentsFolder, cancels the notification observation, allowing your code to stop any ongoing monitoring.
To search the ubiquitous container outside the Documents folder, use the NSMetadataQueryUbiquitousDataScope search scope instead of NSMetadataQueryUbiquitousDocumentsScope. You can add both scopes via setSearchScopes: or pass an empty array to remove search scope limitations. In iOS 5, you cannot yet use the NSMetadataQuery class to search sandboxes or other parts of iOS outside of authorized iCloud container storage.
The startMonitoringUbiquitousDocumentsFolder method, shown here, automatically extracts filenames (via the NSMetadataItemFSNameKey key) to an array and returns those to an informal delegate. You might want to adapt the method to return the raw NSMetadataItem objects or URLs (NSMetadataItemURLKey) instead.
Use file updates to refresh data sources but never depend on them to precisely indicate an update event. You may receive some callbacks that seem untied to anything other than iCloud checking in. These callbacks are never excessive in terms of overwhelming your application, but “contents changed” doesn’t always precisely mean that contents have actually changed. You will receive a callback when new items appear or are removed, but you may also receive callbacks when iCloud is just “being iCloud.”
If you use more than one container in your application, make sure you adapt your callbacks to determine which container was updated. Listing 18-1 assumes you’ll be working with a single ubiquitous Documents folder.
Listing 18-1 Monitoring iCloud
- (void) startMonitoringUbiquitousDocumentsFolder { // Remove any existing query – stored in local instance variable if (query) [query stopQuery]; // Search for all file names query = [[NSMetadataQuery alloc] init]; query.predicate = [NSPredicate predicateWithFormat: @"NSMetadataItemFSNameKey == '*'"]; query.searchScopes = [NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]; // Subscribe to query updates [[NSNotificationCenter defaultCenter] addObserverForName: NSMetadataQueryDidUpdateNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification __strong *notification) { NSLog(@"Contents changed in ubiquitous documents folder"); // disable the query while iterating through its results [query disableUpdates]; NSMutableArray *array = [NSMutableArray array]; for (NSMetadataItem *item in query.results) [array addObject: [item valueForAttribute:NSMetadataItemFSNameKey]]; [query enableUpdates]; // use an informal delegate callback with file names if (delegate) SAFE_PERFORM_WITH_ARG(delegate, @selector(folderContentsHaveChanged:), array); }]; [query startQuery]; } - (void) stopMonitoringUbiquitousDocumentsFolder { [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:nil]; [query stopQuery]; query = nil; }