List files available in iCloud - xcode

Is there a manner to list all files (documents+data) I have in the iCloud (from a Mac) ?
I believe that the NSMetadataQuery object can help me with that, but is there any sample code out there ?
Thanks !

Here some sample code to do a query for txt files in your iCloud folder. If you want to look for other files, simple replace the predicate (NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K ENDSWITH '.txt'", NSMetadataItemFSNameKey];).
To list all files, you could simply do #"NOT %K.pathExtension = '.'" but I'm not sure if this is the most elegant method. Suggestions welcome.
Have a look at this post to get the context and full code sample. Here is just the method to look for files.
-(void)loadDocument {
// (2) iCloud query: Looks if there are txt files in the cloud
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;
//SCOPE
[query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
//PREDICATE
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K ENDSWITH '.txt'", NSMetadataItemFSNameKey];
[query setPredicate:pred];
//FINISHED?
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[query startQuery];
}

Related

NSMetadataQuery NSMetadataQueryUbiquitousDocumentsScope Search Root Directory Only?

I am using the NSMetaDataQuery to search for iCloud documents, as recommended, but it's also searching sub-directories. Is there any way to have it search only the root directory? My code at the moment is...
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;
[query setSearchScopes:[NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *pred = [NSPredicate predicateWithFormat:
#"%K == %#", NSMetadataItemFSNameKey, optionsFilename];
Thanks!
Ray
I hope it isn't too late for my answer...
I have the same issue but after a while I found a way to get this done: Compare resource URL without last path component against desired directory path.
NSPredicate(format: "%K.URLByDeletingLastPathComponent.path == %#", argumentArray: [NSMetadataItemURLKey, iCloudDocsURL.path])
This predicate works like a charm!
Cheers

Core data predicates with key-paths

So i'm trying to work out how to use these predicates, i've read the Apple doc and am trying to use it (https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pUsing.html) and i have the predicate set up, but it keep getting Thread 1: EXC_BAD_ACCESS (code =) etc.etc.
NSError *error;
NSLog(#"1");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Fruit" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSLog(#"2");
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"Source.sourceName contains[cd] %#", "Apple Tree"];
[fetchRequest setPredicate:predicate];
NSLog(#"3");
NSArray *fetchResult = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"4");
testLbl.text = [fetchResult objectAtIndex:0];
Thats the code i'm using, as for the Core Data we have...
Entities Fruit & Source
Attributes fruitName & sourceName
Relationship (one to one) fruitSource<--------->sourceFruit
What i want to do is pull out any fruit that comes from an Apple Tree... >.<
There are two different problems:
To get from Fruit to the related Source you have to use the relationship: #"fruitSource.sourceName contains ..." instead of #"Source.sourceName contains ...".
(This is probably causing the exception.) The %# format requires an Objective-C object as argument, not a C string: #"Apple Tree" instead of "Apple Tree".
So the predicate should look like this:
[NSPredicate predicateWithFormat:#"fruitSource.sourceName CONTAINS[cd] %#", #"Apple Tree"]

avoid duplicate results on Core Data fetch

I have a subclass of the CoreDataTableViewController (subclass of UITAbleViewController dome by the people on Stanford done to link CoreData and TableViews). On this Class, I want to perform a fecth, sorting by an attribute called "definition" and the code which executes it is the following:
- (void)setupFetchedResultsController{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:self.entity];
request.propertiesToFetch=[NSArray arrayWithObject:#"definition"];
request.returnsDistinctResults=YES;
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"%K != nil", #"definition"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"%K != ''", #"definition"];
NSPredicate *predicate3= [NSPredicate predicateWithFormat:#"%K contains[cd] %#", #"definition", self.seachBar.text];
NSArray *prepredicateArray;
if ([self.seachBar.text length]) {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2, predicate3,nil];
}else {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2,nil];
}
request.predicate=[NSCompoundPredicate andPredicateWithSubpredicates:prepredicateArray];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"definition" ascending:YES ]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
}
If I understood it correctly, setting request.returnsDistinctResults=YES; should avoid fetching duplicates. However it doesn't work and I'm seeing duplicates of this attribute's value.
Is there something I'm missing there? I'd appreciate some pointings there. Thank you in advance.
EDIT: If anyone is having the same issue here, after applying David's answer the resulting fetchedResultsController is just a NSDIctionary with object with only the requested value, which for displaying only purposes is quite fine. One thing I've done in cellForRowAtIndexPath in order to display the results on the cell label is:
Before:
HNMR *hnmr = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text=hnmr.definition;
After:
cell.textLabel.text=[[self.fetchedResultsController objectAtIndexPath:indexPath] valueForKey:#"definition"];
From the documentation of returnsDistinctResults:
This value is only used if a value has been set for propertiesToFetch.
From the documentation of propertiesToFetch:
This value is only used if resultType is set to NSDictionaryResultType.
From the documentation of resultType:
The default value is NSManagedObjectResultType.
This all tells me that the propertiesToFetch is ignored because you haven't set the resultType yourself and the default it to return managed objects instead of dictionaries. Since the propertiesToFetch is ignored the returnsDistinctResults is ignored as well and thus you are still getting duplicates.
Try setting the result type to return dictionaries instead of managed objects.
request.resultType = NSDictionaryResultType;
In addition to David Rönnqvist answer I suggest a useful link (with a sample) on selecting distinct values with Core Data:
core-data-how-to-do-a-select-distinct
Hope that helps.

Reading a plist

I'm trying to read ~/Library/Preferences/com.apple.mail.plist (on Snow Leopard) to get the email address and other information to enter into the about dialog. I'm using the following code, which is obviously wrong:
NSBundle* bundle;
bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"~/Library/Preferences/com.apple.mail.plist" ofType:#"plist"];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSString *item = [plistData valueForKeyPath:#"MailAccounts.Item 2.AccountName"];
NSLog(#"Result = %#", item);
Moreover, the value I need to read is MailAcounts -> Item 2 -> AccountName and I am not sure I am doing this correctly (due to the space in the Item 2 key).
I tried reading Apple's developer guide to plist files but no help there.
How can I read a plist and extract the values as an NSString?
Thanks.
The first level is an array, so you need to use "MailAccounts.AccountName" and treat it as NSArray*:
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSArray *item = [plistData valueForKeyPath:#"MailAccounts.AccountName"];
NSLog(#"Account: %#", [item objectAtIndex:2]);
Alternatively you can go by keys and pull the array from "MailAccounts" first using valueForKey: (which will yield NSArray*) and then objectAtIndex: to get the dictionary of that particular account (useful if you need more than the name).
Two things:
You don't want or need to use NSBundle to get the path to the file. The file lies outside of the app bundle. So you should just have
NSString *plistPath = #"~/Library/Preferences/com.apple.mail.plist";
You have to expand the tilde in the path to the user directory. NSString has a method for this. Use something like
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];

How to get folder changes notifications(folder watcher) in cocoa

I am new to Cocoa Application development. I want my application to be notified when any file under a given directory is modified(folder watcher). Modified means deleted, added, content of file is changed. I tried using FSEvents also with using NSWorkspace's notification center or delegate messages as in UKKQueue at http://www.zathras.de/angelweb/sourcecode.htm#UKKQueue. My application got notification when any file under directory is modified. But the problem is that its not giving name or path of specific file which is modified. It gives path of directory but not path of specific file.
Any idea how can I watch folder for modification in specific file??
You have to write code to keep track of the contents of the folder and then whenever you receive an FSEvent notification that the folder contents have changed, you need to compare your stored information about the folder contents with the actual, current contents.
This could be something as simple as a mutable array ivar named something like folderContents, which contains a set of file attributes dictionaries. You could use the dictionary returned from the -attributesOfItemAtPath:error: method of NSFileManager or a subset of it.
All you'd need to do when you receive a folder notification is iterate through the stored dictionaries and check to see whether any files have been added, removed or modified. The NSFileManager attributes dictionary contains all the info you need to do this.
You'd then need to update your stored information about the folder with the updated information.
NSMetadataQuery works well for watching folders:
- (void)setupWatchedFolder {
NSString *watchedFolder = #"/path/to/foo";
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
[query setSearchScopes:#[watchedFolder]];
[query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE '*.*'", NSMetadataItemFSNameKey]];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(queryFoundStuff:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[nc addObserver:self selector:#selector(queryFoundStuff:) name:NSMetadataQueryDidUpdateNotification object:query];
[query startQuery];
}
- (void)queryFoundStuff:(NSNotification *)notification {
NSMetadataQuery *query = self.metadataQuery;
[query disableUpdates];
NSMutableArray *results = [NSMutableArray arrayWithCapacity:self.metadataQuery.resultCount];
for (NSUInteger i=0; i<self.metadataQuery.resultCount; i++) {
[results addObject:[[self.metadataQuery resultAtIndex:i] valueForAttribute:NSMetadataItemPathKey]];
}
// do something with you search results
// self.results = results;
[query enableUpdates];
}

Resources