Is there a way to get all installed applications for the current user in cocoa?
NSArray *runningApps = [[NSWorkspace sharedWorkspace] launchedApplications];
The above gives me currently running applications but for my app I need to list all installed applications. I need the application key (e.g. com.apple.appname) so system_profiler will does not work.
For OSX, the key library for gathering information about launchable applications is Launch Services (see Apple's Launch Services Programming Guide), which will give you the information about an application such as bundle id, file types that it accepts, etc.
For actually locating all executables on the machine, you're going to want to use Spotlight in one form or the other (either the API or by calling out to mdfind).
Example of using the command line version:
mdfind "kMDItemContentType == 'com.apple.application-bundle'"
will return a list of all application paths.
Using a similar term in the spotlight API will result in an appropriate list, from which you can then either open the main bundle using NSBundle or use Launch Services to retrieve information about the app.
I don't have time to do a thorough test of this, but the basic code would be:
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
[query setSearchScopes: #[#"/Applications"]]; // if you want to isolate to Applications
NSPredicate *pred = [NSPredicate predicateWithFormat:#"kMDItemContentType == 'com.apple.application-bundle'"];
// Register for NSMetadataQueryDidFinishGatheringNotification here because you need that to
// know when the query has completed
[query setPredicate:pred];
[query startQuery];
(Revised to use #John's localization-independent query instead of my original)
Related
I have a sandboxed, Mavericks only, Core Data, non-document, Mac application.
For one attribute of one entity I selected „Index in Spotlight“,
and in a second attempt I selected „Index in Spotlight“ and „Store in External Record File“.
Following Apples Core Data Spotlight Integration Programming Guide i am at the first point, Your program:
There are three processes involved in creating and maintaining the
Spotlight index:
Your program. Within your program, the Core Data framework maintains
the persistent store and creates the external record files to trigger
the spotlight indexing
The Spotlight importer. [...]
Core Data External Record daemon. [...]
I assume, now, there must be some place where metadata (that a Spotlight Importer could index) or external record files will be generated if I run the application and add data in it.
I can nowhere find such metadata or external record files. I searched everywhere in- and outside the sandbox container of my application. (Note, i am not trying to build the Spotlight Importer yet — i am merely looking for the metadata to be indexable.)
Where would this spotlight indexable metadata normally be found?
What could be the reasons no spotlight indexable metadata is generated?
The directory for the external records has to be in ~/Library/CoreData (or the equivalent in a sandboxed application). You must create it.
Also don't forget to set the store options for your PersistentStoreCoordinator, in the Application Delegate, like this in - (NSPersistentStoreCoordinator *) persistentStoreCoordinator :
//creating the External Records Directory
error = nil;
NSString *externalRecordsSupportFolder = [#"~/Library/CoreData/YOUR_EXTERNAL_RECORD_DIRECTORY" stringByExpandingTildeInPath];
[fileManager createDirectoryAtPath:externalRecordsSupportFolder
withIntermediateDirectories:YES
attributes:nil
error:&error];
if(error){
[NSApp presentError:error];
}
//options for your main Persistent Store Coordinator
NSDictionary *storeOptions = #{
NSExternalRecordExtensionOption:#"YOUR_EXTERNAL_RECORD_EXTENSION",
NSExternalRecordsDirectoryOption:externalRecordsSupportFolder,
NSExternalRecordsFileFormatOption:NSXMLExternalRecordType
};
Then you pass the storeOptions
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![coordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:storeOptions error:&error]) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
External record data is stored in the same directory as your primary Core Data files (i.e. your SQLite file), but in a hidden subdirectory. The naming convention for the subdirectory is .ApplicationName_SUPPORT/_EXTERNAL_DATA .
For non-document-based applications, Core Data creates a directory structure within ~/Library/Caches/Metadata for the application. The directory structure may vary with the OS version, Core Data version, etc. At some level there should be an application-specific directory within that structure, and inside that should be the external records file(s) created by Core Data.
If you cannot find these files, use kqueue events, lsof, or libdispatch to monitor the filesystem for changes while you run your application. You should be able to see what locations on the file system are being accessed easily. If external records files are not being created, or are being created in some new location, that too should be obvious.
Spotlight metadata is not stored in discrete files but in Spotlight's own data. You can inspect the metadata of a file by using the mdls command from a terminal.
Example:
mdls /Applications/Maps.app
You can also use the mdimport command to tell Spotlight to index something on demand.
Example:
mdimport ~/Documents/MyAwesomeStuff
mdimport also has a command line option to use a specific importer rather than a system importer. This can be very useful for development. Both mdls and mdimport will print out help messages detailing these arguments if asked.
In the core data model editor, you can check the spotlight indexing for each individual attribute.
Select the attribute and open the Data Model inspector
Check "Index in Spotlight"
I've been trying to use NSWorkspace and fullPathForApplication but this isn't meeting my needs as it doesn't return the correct application.
I could do:
[NSBundle bundleWithPath: [[NSWorkspace sharedWorkspace] fullPathForApplication: #"My Application"]];
And this will return whichever (I believe) was installed most recently out of:
/Applications/My Application
/Applications/My Application 2
I want "My Application" to be returned, but the second is being returnd. I don't want to assume all applications are installed in /Applications/ so I'm not just going to loop through a folder.
Does anyone know how I can manually just loop through all applications and verify which is correct? Or a similar method to fullPathForApplication that would return all results, vs. just choosing one at random?
I believe the NSWorkspace method you're using is a facade for LSFindApplicationForInfo(), which is documented to return just a single application:
If more than one application is found matching the specified characteristics, Launch Services chooses one in the same manner as when locating the preferred application for opening an item.
If your app opens a specific file type or URL, use LSCopyApplicationURLsForURL(), which returns an array of matching apps.
I currently have an app in the store which is SQLite-backed and does not use Core Data. In the past when I have wanted to release an update that had SQLite changes, the update would include some code that would detect the version of the app, and programmatically update the tables if necessary. Now I am working on an update that uses Core Data. I don't care about any of the old data that is currently live, and I know how I can delete all the old SQLite tables programmatically. Are all the Core Data model files included in the update binary, or do I have to programmatically generate some or all of the Core Data model? Will the .xcdatamodeld be included with the binary? Any other pitfalls I should be wary of?
Thanks for your help
With core data it can be quite tricky when updating to a new version. I have experienced many times that even the slightest changes in the core data model cause weird app behaviour (understandable to some extend). The easiest way to avoid any unwanted sideeffects is to just change the name. Here the code
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
// D_IN;
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
// Allow inferred migration from the original version of the application.
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"myData073.sqlite"]];
NSError *error = nil;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl
options:options error:&error]){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// D_OUT;
return persistentStoreCoordinator;
}
So all you really have to do is to change the name myData073.sqlite to for example myData074.sqlite
The data model description file is not really part of the binary, but the model behind it with all the classes and access methods surely is. You do not need to worry about that. ps even during development I change the name frequently, otherwise one might waste lots of time looking for coding errors that are not really there...
An App Store update replaces the entire app bundle, so everything in your app bundle, including any Core Data model, will be included. You can test this by installing an Ad Hoc or developer build over an App Store build on your own device (without deleting first). In the compiled App, the model files are compiled or processed, and have different extensions (.momd, .mom). So you'll see those instead of .xcdatamodeld or .xcdatamodel.
Other pitfalls: Now, the Core Data data file is not part of your bundle. If you want the updated app to start with an empty database, you don't have to do anything special. But if you want to install/update with a prepopulated database, you have to figure out a way to get that database into a read-write location on the device. Basically, you generate a Core Data file, included it in the app bundle as part of your Xcode project, then at first launch, programmatically copy it somewhere in your Documents or Library folders (using the correct Cocoa API for finding these).
Pitfall #2: modifying an empty core data file with SQLLite on the Mac is suggested on some web sites, but specifically dis-recommended by Apple. Instead, you might write an iOS app that puts data into core data and run it in the simulator. How do I initialize a store with default data?
As user387184 indicated, your next update that changes the model on an existing Core Data database can get tricky. Try to get the model as right as you can the first time.
My objective-c app needs to be aware if the firewall in OSX is running, so it can tell the user to turn it off or create a new rule.
Also, is it possible to create rules directly from my app so users never need to handle networking issues?
John
I am writing a function that will provide you the status of OSX firewall :)
-(BOOL)getFirewallStatus{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSSystemDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
path = [NSString stringWithFormat:#"%#/%#",path,#"Preferences/com.apple.alf.plist"];
path = [path stringByReplacingOccurrencesOfString:#"/System"
withString:#""];
NSDictionary* _dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// firewall status
int status = [[_dictionary valueForKey:#"globalstate"] integerValue];
if (status == 0)
{
return NO;
}
return YES;
}
If your application is being run by the user (i.e., double-clicked in the Finder), any attempt by your application to create a socket listener will prompt the user to allow/deny that listener - and subsequently adjust the firewall settings accordingly - without any programmatic intervention on the part of your application.
If the firewall in question is your router (a problem I recently had to deal with), you have a few options. The best supported option is Bonjour/mDNSResponder (as long as you don't want to support a double-nat'ed situation). Apple provides an Objective-C wrapper application around the rather obtuse dns_sd.h:
http://developer.apple.com/library/mac/#samplecode/PortMapper/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007879-Intro-DontLinkElementID_2
Going the 3rd party route, take a look at TCM Port Mapper. It uses some deprecated features and it'll take a bit of effort to get it running with ARC support (if that's important to you).
http://code.google.com/p/tcmportmapper/
Both support UPnP and NAT-PMP.
Finally, if your application is running as a daemon (without a user interface), you're going to have to become acquainted with ipfw. Brace yourself. Google for "ipfw os x". StackOverflow is preventing me from posting more than two links. Brilliant.
Hope this helps....
I am writing an application in Objective-C whose functionality is to call and execute another application.Pls can i know the procedure???Let me also know where i need to keep the calling application which has to load and execute???
The only way to "launch" another application is if the other application "cooperates" with yours, if you will.
The cooperation comes in the form of a URL protocol scheme that an application exposes. Examples are mailto: (exposed by the system mail application) and sms: (exposed by the Messages app).
http://wiki.akosma.com/IPhone_URL_Schemes seems to have a comprehensive list of third party apps exposing a custom protocol scheme that you can make use of.
Launching another app is a matter of calling [[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"twitterrific://..."]; (for example).
As of OS 3.0 you can also check if a given URL protocol scheme is available via UIApplication's -canOpenURL: — checking if some app has registered the protocol scheme, or in other words checking if an application is installed.
If you are writing both apps yourself then launching a custom protocol scheme is pretty simple, however you can't launch an arbitrary iPhone app, and you wouldn't be able to enumerate available apps anyway because of the sandbox.
I'm not sure I fully understand your question, but if all you're trying to do is launch another app, there are a number of ways to do this. The most straightforward is probably by using the NSTask class. Here is a simple example of launching iCal from within an Objective-C application using an NSTask.
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/Applications/iCal.app/Contents/MacOS/iCal"];
[task launch];
[task release];
See the -launchApplication: method of the NSWorkspace class.
1 Register a custom URL Scheme - In your applications Info.plist specify the subproperties of the CFBundleURLTypes
CFBundleURLName
A string that is the abstract name for the URL type. To ensure
uniqueness, it is recommended that
you specify a reverse-DNS style of
identifier, for example,
com.acme.myscheme. The URL-type name
provided here is used as a key to a
localized string in the
InfoPlist.strings file in a
language-localized bundle
subdirectory. The localized string is
the human-readable name of the URL
type in a given language.
CFBundleURLSchemes
An array of URL schemes for URLs belonging to this URL type. Each
scheme is a string. URLs belonging to
a given URL type are characterized by
their scheme components.
2 implement the handleOpenURL method to receive the notifications
(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if ([[url scheme] isEqualToString:#"myscheme"]) {
...
3 in the calling application construct a URL to the custom scheme you have defined and open it. The iPhone OS uses this for http, mailto, tel, and sms URL schemes
NSURL *myURL = [NSURL URLWithString:#"myscheme://?params..."];
[[UIApplication sharedApplication] openURL:myURL];