Querying with spotlight - macos

I'm working on a tiny project for LEOPARD (10.5) and I'm kinda rookie with Objective-C programming. I've been searching for some tutorials on internet but I'm still confused! I need to use Leopard's spotlight feature to search for every .app file installed at the user's computer. I also need its name, path and icon. All queried data must be saved in a text file. How can I do that???
Thank you!

Define the query, and observe the query termination.
- (void)searchApplications {
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
query.predicate = [NSPredicate predicateWithFormat:#"kMDItemContentTypeTree == 'com.apple.application'"];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryDidFinish:)
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
[query startQuery];
}
In the query termination function, loop through the results and extract the data you want.
- (void)queryDidFinish:(NSNotification *)notification {
NSMetadataQuery *query = (NSMetadataQuery *)[notification object];
[query stopQuery];
NSMutableArray *paths = [NSMutableArray array];
for(NSMetadataItem *mdItem in query.results) {
NSString *name = [mdItem valueForAttribute:(NSString *)kMDItemDisplayName];
NSString *path = [mdItem valueForAttribute:(NSString *)kMDItemPath];
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:path];
[paths addObject:path];
}
[query release];
[paths writeToFile:#"/tmp/applications.txt" atomically:YES];
}

Related

How do I add a NSError check with initWithContentsOfFile when looking for an image?

In my app, the user can save an image to their documents directory. At launch, I grab the image, add a border, and put it into a UIImageview like this....
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
NSString *docDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/ImageOne.jpg", docDirectory];
UIImage *unborderedImage = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
//image found....add border
UIImage *imageWithBorder = [self addBorderToImage:unborderedImage];
imageOneView.image = imageWithBorder;
Ideally, I like to check that the image is there first before adding a border. If not, load an "image not available" placeholder. Something like this:
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
NSString *docDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/ImageOne.jpg", docDirectory];
UIImage *unborderedImage = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
NSError * error;
if (error != nil) {
//image found....add border
UIImage *imageWithBorder = [self addBorderToImage:unborderedImage];
imageOneView.image = imageWithBorder;
} else
//no image saved
[imageOneView setImage:[UIImage imageNamed:#"photoNotAvailable.png"]];
}
Of course, this doesn't work. I just can't seem to figure out how handle if "ImageOne.jpg" isn't found.
As it turns out, I need to do other things with the image elsewhere within the app. This would also depend on whether or not the user had saved an image or not. So in my method where the user can save the image, I send out a NSNotification that the image has changed. Then on my MainView, I look for the notification and key off that.
When saved:
[collectionOneUserDefinedDefaults setObject:#"image added" forKey:#"collectionOneImageAdded"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"collectionOneImageChanged" object:self];
Then in my MainView I look for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateCollectionOneImage) name:#"collectionOneImageChanged" object:nil];
- (void)updateCollectionOneImage {
//check if an image was ever saved, if so, replace the noPhotoAvailalble placeholder
NSUserDefaults *collectionOneUserDefinedDefaults = [NSUserDefaults standardUserDefaults];
NSString *collectionOneImageTextString = [collectionOneUserDefinedDefaults stringForKey:#"collectionOneImageAdded"];
if (collectionOneImageTextString == nil || [collectionOneImageTextString isEqualToString:#""]) {
[collectionOneImage setImage:[UIImage imageNamed:#"photoNotAvailable.png"]];
}
else {
//pull in collection one image from the Documents folder
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
NSString *docDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/CollectionOneImage.jpg", docDirectory];
UIImage *unborderedImage = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
//image found....add border
UIImage *imageWithBorder = [self addBorderToImage:unborderedImage];
collectionOneImage.image = imageWithBorder;
}
}
It works perfectly. It in turn builds in the error handling. If the user saved and image, it gets loaded. If not, a placeholder image is loaded.
Your error handling here is doing nothing and is misguided. All you need do is simply check if unborderedImage is nil or not.

Accessing the desktop in a Sandboxed app

I'm building an application that has a feature to open newly taken screenshots. I would like to distribute it using the Mac App Store. Unfortunately, it needs to be sandboxed.
To find the new screenshots I run a NSMetaDataQuery. It returns a few entries but unfortunately I can't get their URL since they are on the Desktop (out of my app's sandbox).
How can I fix this ?
Here is some of the code
query = [[NSMetadataQuery alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(queryUpdated:) name:NSMetadataQueryDidStartGatheringNotification object:query];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(queryUpdated:) name:NSMetadataQueryDidUpdateNotification object:query];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(queryUpdated:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[query setDelegate:self];
[query setPredicate:[NSPredicate predicateWithFormat:#"kMDItemIsScreenCapture = 1"]];
[query startQuery];
numberOfScreenshots = [query resultCount];
[self uploadToAmazonS3:[[[query results]objectAtIndex:([query resultCount]-1)]valueForAttribute:NSMetadataItemURLKey]];
Thanks
Without asking user permission, you can access only Music, Movies, Pictures and Download folders.
You have to ask user, to grant you access to Desktop Folder. Then use mechanism called Security-Scoped Bookmarks, read more about it in AppSandboxDesignGuide.
Use NSOpenPanel to choose directory.
Save bookmark for future use, for example in NSUserDefaults.
Get access
1 and 2
NSOpenPanel *openPanel = [[NSOpenPanel alloc] init];
[openPanel setCanChooseFiles:NO];
[openPanel setCanChooseDirectories:YES];
[openPanel setCanCreateDirectories:YES];
[openPanel beginWithCompletionHandler:^(NSInteger result){
if (result == NSFileHandlingPanelOKButton) {
for (NSURL *fileURL in [openPanel URLs]) {
NSString *filename = [fileURL path];
[[NSUserDefaults standardUserDefaults] setObject:filename forKey:#"PathToFolder"];
NSError *error = nil;
NSData *bookmark = [fileURL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&error];
if (error) {
NSLog(#"Error creating bookmark for URL (%#): %#", fileURL, error);
[NSApp presentError:error];
} else {
[[NSUserDefaults standardUserDefaults] setObject:bookmark forKey:#"PathToFolder"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
break;
}
}
}];
3.
NSError *error = nil;
NSData *bookmark = [[NSUserDefaults standardUserDefaults] objectForKey:#"PathToFolder"];
bookmarkedURL = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:nil error:&error];
BOOL ok = [bookmarkedURL startAccessingSecurityScopedResource];
NSLog(#"Accessed ok: %d %#", ok, [bookmarkedURL relativePath]);
So, that sould be it.
After you have allowed access you also need to get the path to the real home folder - all the other file APIs only provide the path to the sandboxed folder even when access to the real one as been enabled. To get around this annoying problem, Apple recommend doing this to get the real home folder:
#include <sys/types.h>
#include <pwd.h>
const char *home = getpwuid(getuid())->pw_dir;
NSString *path = [[NSFileManager defaultManager]
stringWithFileSystemRepresentation:home
length:strlen(home)];
NSString *realHomeDirectory = [[NSURL fileURLWithPath:path isDirectory:YES] path];

copy and paste text + image from NSPasteboard

we are working on a C++ Qt applciation that copies selected text and/or images from external applications, modifies it and then paste it back. Since we are on Mac, we are doing this part with Objective-C.
We are having problems trying to get an image from the Pasteboard. It works fine for text, but we are not sure about how to handle images or combination of text+image.
Since we dont know what the user might select, we should be able to perform a generic retrieval of content of the pasteboard to modify it and putting it back in the pasteboard.
We've try this:
//we thought about retrieving some generic item from pasteboard, using NSPasteboardItem
NSArray *classes = [[NSArray alloc] initWithObjects:[NSPasteboardItem class], nil];
NSDictionary *options = [NSDictionary dictionary];
NSArray *auxArray = [[NSPasteboard generalPasteboard] readObjectsForClasses:classes options:options];
NSData *archived_data = [auxArray objectAtIndex:0];
Our solution for handling text was:
NSString *text = [[NSPasteoard generalPasteboard] stringForType:NSStringPboardType];
string text_str = string([text UTF8String]);
It didnt work, so, How can we get the user selection from the pasteboard?
We need to get the raw bytes or rtf content in order to modify it as we need, and then putting it back in the pasteboard and paste it back replacing the original user selection.
Thanks!
I think this function will help you
- (IBAction)paste:sender
{
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSArray *classArray = [NSArray arrayWithObject:[NSImage class]];
NSDictionary *options = [NSDictionary dictionary];
BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
if (ok)
{
NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
NSImage *image = [objectsToPaste objectAtIndex:0];
[imageView setImage:image];
}
}

How can I add and store data in a UITableView?

I'm creating an app that inserts values to a tableview. I also want to add the timestamp on when the value is added to the table. The scenario would be this:
I write some random text in a text field.
I push a button
The text from the text field is saved in the table together with the time I tapped the button.
Done.
(NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
(void)applicationWillTerminate:(NSNotification *)notification {
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:field1.text];
[array addObject:field2.text];
[array addObject:field3.text];
[array addObject:field4.text];
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
}
Depending on how large your data is, you can store it in a plist, or sqlite3.
Here's a link that might be useful:
http://www.bogotobogo.com/XcodeSDK-Chapter10.html

Can't use two "audioPlayerDidFinishPlaying"

Can you please tell me how to fix this? I want to release two different AVAudioPlayer when they finish playing, but separately.
Here's my code:
.h File
#interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
NSString *path;
}
- (IBAction)Short:(id)sender;
- (IBAction)BeatLong:(id)sender;
.m File
AVAudioPlayer *media;
AVAudioPlayer *media2;
- (IBAction)Short:(id)sender
{
path = [[NSBundle mainBundle] pathForResource:#"Short" ofType:#"wav"];
media = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[media setDelegate:self];
[media play];
}
- (IBAction)Beat:(id)sender
{
path = [[NSBundle mainBundle] pathForResource:#"Beat" ofType:#"mp3"];
media2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[media2 setDelegate:self];
[media2 play];
}
(Open the image in a new tab to see it better ^^,)
Nay, that's illegal on the grounds of the language.
You have to distinguish the different players according to the AVAudioPlayer * pointer
submitted with the message.
If you just want to release it, just write
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)aPlayer successfully:(BOOL)flag
{
[aPlayer release];
}
and you're done and illegal too, since you don't own aPlayer.
But a better solution would be to detect which audio player you own and release it.
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)aPlayer successfully:(BOOL)flag
{
if ( aPlayer == self.media )
[self.media release];
else if ( aPlayer == self.media2 )
[self.media2 release];
// other players cannot be released, since we don't know anything about their owner.
}

Resources