/* |
File: CloudDocumentsController.m |
Abstract: Utility class used to keep track of known documents in the cloud. |
Version: 1.0 |
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
|
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
|
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
|
Copyright (C) 2013 Apple Inc. All Rights Reserved. |
|
*/ |
|
#import "CloudDocumentsController.h" |
|
@interface CloudDocumentsController () |
|
- (void)removeQueryObservers; |
- (void)addQueryObservers; |
|
@property (nonatomic, strong) NSMetadataQuery *ubiquitousQuery; |
@property (nonatomic, strong) NSMutableArray *foundDocuments; |
|
@end |
|
|
#pragma mark - |
|
@implementation CloudDocumentsController |
|
static CloudDocumentsController *cloudDocumentsController; |
|
// ------------------------------------------------------------------------------- |
// singleton class |
// ------------------------------------------------------------------------------- |
+ (CloudDocumentsController *)sharedInstance |
{ |
if (cloudDocumentsController == nil) |
{ |
// note: an empty file type means find all types documents |
cloudDocumentsController = [[CloudDocumentsController alloc] initWithType:@""]; |
} |
return cloudDocumentsController; |
} |
|
// ------------------------------------------------------------------------------- |
// setupQuery |
// ------------------------------------------------------------------------------- |
- (void)setupQuery |
{ |
_ubiquitousQuery = [[NSMetadataQuery alloc] init]; |
_ubiquitousQuery.notificationBatchingInterval = 15; |
_ubiquitousQuery.searchScopes = [NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]; |
|
NSString *filePattern = nil; |
if ([self.fileType isEqualToString:@""]) |
{ |
filePattern = [NSString stringWithFormat:@"*.*"]; |
} |
else |
{ |
filePattern = [NSString stringWithFormat:@"*.%@", self.fileType]; |
} |
|
_ubiquitousQuery.predicate = [NSPredicate predicateWithFormat:@"%K LIKE %@", NSMetadataItemFSNameKey, filePattern]; |
// or |
// _ubiquitousQuery.predicate = [NSPredicate predicateWithFormat:@"%K ENDSWITH %@", NSMetadataItemFSNameKey, self.fileType]; |
} |
|
// ------------------------------------------------------------------------------- |
// initWithType:fileType |
// ------------------------------------------------------------------------------- |
- (id)initWithType:(NSString *)fileType |
{ |
self = [super init]; |
|
_fileType = fileType; |
|
self.foundDocuments = [NSMutableArray array]; |
|
[self setupQuery]; |
|
return self; |
} |
|
// ------------------------------------------------------------------------------- |
// initWithType:fileType |
// |
// Our client is explicty setting the file type, so we need to re-setup the query. |
// ------------------------------------------------------------------------------- |
- (void)setFileType:(NSString *)fileType |
{ |
_fileType = fileType; |
[self setupQuery]; |
} |
|
|
#pragma mark - Exported APIs |
|
// ------------------------------------------------------------------------------- |
// numberOfDocuments |
// ------------------------------------------------------------------------------- |
- (NSUInteger)numberOfDocuments |
{ |
return _foundDocuments.count; |
} |
|
// ------------------------------------------------------------------------------- |
// startScanning |
// ------------------------------------------------------------------------------- |
- (BOOL)startScanning |
{ |
[self addQueryObservers]; |
|
BOOL started = [_ubiquitousQuery startQuery]; |
if (!started) |
{ |
self.ubiquitousQuery = nil; |
} |
|
return started; |
} |
|
// ------------------------------------------------------------------------------- |
// stopScanning |
// ------------------------------------------------------------------------------- |
- (void)stopScanning |
{ |
[self.ubiquitousQuery stopQuery]; |
[self removeQueryObservers]; |
|
self.ubiquitousQuery = nil; |
} |
|
// ------------------------------------------------------------------------------- |
// restartScan |
// ------------------------------------------------------------------------------- |
- (void)restartScan |
{ |
[self.ubiquitousQuery stopQuery]; |
|
self.ubiquitousQuery = nil; |
|
[self setupQuery]; |
[self startScanning]; |
} |
|
// ------------------------------------------------------------------------------- |
// urlForDocumentAtIndex:index |
// ------------------------------------------------------------------------------- |
- (NSURL *)urlForDocumentAtIndex:(NSInteger)index |
{ |
return [_foundDocuments objectAtIndex:index]; |
} |
|
// ------------------------------------------------------------------------------- |
// titleForDocumentAtIndex:index |
// ------------------------------------------------------------------------------- |
- (NSString *)titleForDocumentAtIndex:(NSInteger)index |
{ |
NSURL *url = [_foundDocuments objectAtIndex:index]; |
NSURL *urlWithoutExtension = [url URLByDeletingPathExtension]; |
return [urlWithoutExtension lastPathComponent]; |
} |
|
// ------------------------------------------------------------------------------- |
// iconForDocumentAtIndex:index |
// ------------------------------------------------------------------------------- |
- (NSImage *)iconForDocumentAtIndex:(NSInteger)index |
{ |
NSImage *icon = nil; |
|
NSURL *url = [_foundDocuments objectAtIndex:index]; |
[url getResourceValue:&icon forKey:NSURLEffectiveIconKey error:nil]; |
|
return icon; |
} |
|
// ------------------------------------------------------------------------------- |
// modDateForDocumentAtIndex:index |
// ------------------------------------------------------------------------------- |
- (NSDate *)modDateForDocumentAtIndex:(NSInteger)index |
{ |
NSDate *modDate = nil; |
|
NSURL *url = [_foundDocuments objectAtIndex:index]; |
[url getResourceValue:&modDate forKey:NSURLContentModificationDateKey error:nil]; |
|
return modDate; |
} |
|
// ------------------------------------------------------------------------------- |
// documentIsUploadedAtIndex:index |
// ------------------------------------------------------------------------------- |
- (BOOL)documentIsUploadedAtIndex:(NSInteger)index |
{ |
NSURL *url = [_foundDocuments objectAtIndex:index]; |
|
// get uploaded state: true if there is data present in the cloud for this item |
NSNumber *isUploaded = nil; |
[url getResourceValue:&isUploaded forKey:NSURLUbiquitousItemIsUploadedKey error:nil]; |
return [isUploaded boolValue]; |
} |
|
// ------------------------------------------------------------------------------- |
// documentIsDownloadedAtIndex:index |
// ------------------------------------------------------------------------------- |
- (BOOL)documentIsDownloadedAtIndex:(NSInteger)index |
{ |
NSURL *url = [_foundDocuments objectAtIndex:index]; |
|
// get uploaded state: true if there is data present in the cloud for this item |
NSNumber *isDownloaded = nil; |
[url getResourceValue:&isDownloaded forKey:NSURLUbiquitousItemIsUploadedKey error:nil]; |
return [isDownloaded boolValue]; |
} |
|
// ------------------------------------------------------------------------------- |
// sortDocuments |
// ------------------------------------------------------------------------------- |
- (void)sortDocuments |
{ |
NSArray *sortedResults = [_foundDocuments sortedArrayUsingComparator:^(id obj1, id obj2) { |
return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; |
}]; |
self.foundDocuments = [NSMutableArray arrayWithArray:sortedResults]; |
} |
|
// ------------------------------------------------------------------------------- |
// addCloudDocumentWithURL:cloudDocumentURL |
// ------------------------------------------------------------------------------- |
- (void)addCloudDocumentWithURL:(NSURL *)cloudDocumentURL |
{ |
[_foundDocuments addObject:cloudDocumentURL]; |
|
// sort the list of documents by name |
[self sortDocuments]; |
} |
|
|
#pragma mark - |
|
// ------------------------------------------------------------------------------- |
// addQueryObservers |
// ------------------------------------------------------------------------------- |
- (void)addQueryObservers |
{ |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStart:) |
name:NSMetadataQueryDidStartGatheringNotification |
object:_ubiquitousQuery]; |
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(gathering:) |
name:NSMetadataQueryGatheringProgressNotification |
object:_ubiquitousQuery]; |
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishGathering:) |
name:NSMetadataQueryDidFinishGatheringNotification |
object:_ubiquitousQuery]; |
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdate:) |
name:NSMetadataQueryDidUpdateNotification |
object:_ubiquitousQuery]; |
} |
|
// ------------------------------------------------------------------------------- |
// removeQueryObservers |
// ------------------------------------------------------------------------------- |
- (void)removeQueryObservers |
{ |
[[NSNotificationCenter defaultCenter] removeObserver:self |
name:NSMetadataQueryDidStartGatheringNotification |
object:nil]; |
[[NSNotificationCenter defaultCenter] removeObserver:self |
name:NSMetadataQueryGatheringProgressNotification |
object:nil]; |
[[NSNotificationCenter defaultCenter] removeObserver:self |
name:NSMetadataQueryDidFinishGatheringNotification |
object:nil]; |
[[NSNotificationCenter defaultCenter] removeObserver:self |
name:NSMetadataQueryDidUpdateNotification |
object:nil]; |
} |
|
// ------------------------------------------------------------------------------- |
// dealloc |
// ------------------------------------------------------------------------------- |
- (void)dealloc |
{ |
[self removeQueryObservers]; |
} |
|
// ------------------------------------------------------------------------------- |
// didStart:note |
// |
// NSMetadataQuery has started. |
// ------------------------------------------------------------------------------- |
- (void)didStart:(NSNotification *)note |
{ |
NSLog(@"didStart..."); |
|
// call our delegate that we started scanning out ubiquitous container |
if ([self.delegate respondsToSelector:@selector(didStartRetrievingCloudDocuments)]) |
{ |
[self.delegate didStartRetrievingCloudDocuments]; |
} |
} |
|
// ------------------------------------------------------------------------------- |
// gathering:note |
// |
// NSMetadataQuery is gathering the results. |
// ------------------------------------------------------------------------------- |
- (void)gathering:(NSNotification *)note |
{ |
NSLog(@"gathering..."); |
//.. do what ever you need to do while gathering results |
} |
|
// ------------------------------------------------------------------------------- |
// handleQueryUpdates:ubiquitousQuery |
// |
// Used for examining what new results came from our NSMetadataQuery. |
// This method is shared between "finishGathering" and "didUpdate" methods. |
// ------------------------------------------------------------------------------- |
- (void)handleQueryUpdates:(NSMetadataQuery *)ubiquitousQuery |
{ |
// we should invoke this method before iterating over query results that could |
// change due to live updates |
[self.ubiquitousQuery disableUpdates]; |
|
// load the new array of NSMetadataItems from 'ubiquitousQuery', |
// store the NSURL for each, sort the list |
// |
[self.foundDocuments removeAllObjects]; |
NSMetadataItem *item = nil; |
for (item in [self.ubiquitousQuery results]) |
{ |
NSURL *itemURL = [item valueForAttribute:NSMetadataItemURLKey]; |
|
NSNumber *isInvisible = nil; |
[itemURL getResourceValue:&isInvisible forKey:NSURLIsHiddenKey error:nil]; |
if (isInvisible && ![isInvisible boolValue]) |
{ |
[self.foundDocuments addObject:itemURL]; |
} |
} |
[self sortDocuments]; |
|
// notify our delegate we received an update |
if ([self.delegate respondsToSelector:@selector(didRetrieveCloudDocuments)]) |
{ |
[self.delegate didRetrieveCloudDocuments]; |
} |
|
// enable updates again |
[self.ubiquitousQuery enableUpdates]; |
} |
|
// ------------------------------------------------------------------------------- |
// finishGathering:note |
// ------------------------------------------------------------------------------- |
- (void)finishGathering:(NSNotification *)note |
{ |
NSLog(@"finishGathering..."); |
[self handleQueryUpdates:self.ubiquitousQuery]; |
} |
|
// ------------------------------------------------------------------------------- |
// didUpdate:note |
// ------------------------------------------------------------------------------- |
- (void)didUpdate:(NSNotification *)note |
{ |
NSLog(@"didUpdate..."); |
[self handleQueryUpdates:self.ubiquitousQuery]; |
} |
|
@end |