I have been playing around with NSWorkspace.
[[NSWorkspace sharedWorkspace] launchApplication:#"Photos"] works
[[NSWorkspace sharedWorkspace] launchApplication:#"Contacts"] does not work
[[NSWorkspace sharedWorkspace] launchApplication:#"/Applications/Contacts.app"] works
(Mac OS Sierra)
So, I was wondering if there is any safe way to find the "real" application path. Maybe the Contacts app has different names in different localization of the System ? Why is just "Photos" working fine ?
I would imagine that there should be a way, from an Apple documented application identifier to get the application path, but I found nothing.
Remember that launchApplication: returns a BOOL if it succeeded or not.
To be really certain of the app you are launching, you can also try looking it up via NSWorkspace's URLForApplicationWithBundleIdentifier: API, into which you can plug Contact's application ID of com.apple.AddressBook.
With the result, you can call the explicit location of the app via:
- (NSRunningApplication *)launchApplicationAtURL:(NSURL *)url
options:(NSWorkspaceLaunchOptions)options
configuration:(NSDictionary<NSString *,id> *)configuration
error:(NSError * _Nullable *)error;
The best way to open an app is from file url path. I have did the same for my application like below:
NSString *appPathIs = Url.path;
I take url path in one string and then open it with as you have indicated
[[NSWorkspace sharedWorkspace] launchApplication:appPathIs];
I have tried every way to achieve the same but sometime they work and sometime not. If you want to try another one then please consider this:
NSString *appPathIs = [[NSWorkspace sharedWorkspace] fullPathForApplication:appName];
NSString *identifier = [[NSBundle bundleWithPath:appPathIs] bundleIdentifier];
NSString *path = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:identifier];
[[NSWorkspace sharedWorkspace] launchApplication:appPathIs];
Related
I created an app which is launched from a custom url in any OSX browser. This worked just fine by adding a standard CFBundleURLName entry to the app's plist.
My application works by reading by parsing some of the parameters on the custom url and then reacting to them.
So for example with a custom url of:
foobar://param1/param2/param3
When clicking on the above url in a browser, OSX would launch my app and pass the actual custom url itself as the first argument to the app. Therefore in the app I could read the first arg and get the url the opened the app, and parse it for params I need.
This works fine in OSX 10.5-10.8, but in 10.9 Mavericks it appears to work slightly differently. Namely that if the application is not already running, it still launches the app but does not pass the custom url as first argument - so the app thinks it's just been launched manually by the user (such as selecting it from launchpad) rather than directly from a browser.
Weirdly, if the application is already open, then clicking the custom url DOES send the url string over to the app as first argument and functionality within the app occurs as expected.
I've tested this across 10.6->10.9 with new and old versions of my app and all exhibit the same behaviour. All work fine on first launch with versions before 10.9 Mavericks, but in 10.9 they don't get the url passed as first arg but then work on 2nd click once already running.
If anyone could shed some light on this I would be very grateful.
Where do you set up your URL handler? It needs to happen early. If you currently have it in applicationDidFinishLaunching, try to move it to applicationWillFinishLaunching.
The following works for me and logs the URL at launch even when the app is not running before I open the URL in Safari, for example. When I change WillFinishLaunching to DidFinishLaunching, I see exactly the behavior you describe.
#implementation AppDelegate
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:self andSelector:#selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
NSAppleEventDescriptor *obj = [event descriptorForKeyword:keyDirectObject];
DescType type = [obj descriptorType];
if (type == typeChar) {
NSData *data = [obj data];
if (data) {
NSString *urlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlString];
NSLog(#"url: %#", url);
}
}
}
#end
reading Adium code today, found an interesting usage of NSURL:
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:#"adium://%#/adium", [messageStyle.bundle bundleIdentifier]]];
[[webView mainFrame] loadHTMLString:[messageStyle baseTemplateForChat:chat] baseURL:baseURL];
I tried to log the url and got this adium://im.adium.Smooth Operator.style/adium, Then I created a blank project to see how to create such an NSURL but failed. When I sending loadHTMLString message to a webview's frame in my project, if the baseURL is nil, everything is fine, if not, I got a blank page in the view.
here is my code, the project name is webkit
NSURL *baseURL = [NSURL URLWithString:#"webkit://resource"];
//if baseURL is nil or [[NSBundle mainBundle] bundleURL], everything is fine
[[webView mainFrame] loadHTMLString:#"<html><head></head><body><div>helloworld</div></body></html>"
baseURL: baseURL];
[frameView setDocumentView:webView];
[[frameView documentView] setFrame:[frameView visibleRect]];
the question is how to make a self defined protocol instead of http://?
adium://%#/adium , first section is called protocol you can also register your protocol webkit: Take a look at How to map a custom protocol to an application on the Mac? and Launch Scripts from Webpage Links
[NSURLProtocol registerClass:[AIAdiumURLProtocol class]];
[ESWebView registerURLSchemeAsLocal:#"adium"];
I tried to find where did adium define the adium schema in the info.plist, unfortunately, there's nothing there, only some irc/xmpp protocols.
so I finally launched the debugger, and found the code above in the AIWebKitDelegate init method, anyway this is another way to register a self defined protocol~
I have implemented a login item as I was recommended in this post. In the helper app I launch the main app using NSWorkspace method launchApplication:showIcon:autolaunch:, sending YES to autolaunch parameter.
The problem is in following: I need to check if the applications was autolaunched not to show start tooltip. The only variant I found is [[NSUserDefaults standardUserDefaults] boolForKey:#"autolaunch"], and it returns NO always.
The problem could be solved using launch arguments - but sandbox, unfortunately, cuts them too.
Is there something I missed?
I use the following code to hide the launched application:
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL fileURLWithPath:appPath] options:NSWorkspaceLaunchAndHide configuration:nil error:nil];
If you want to set additional parameters, you can give the method a custom configuration dictionary.
If you have the launcher inside the application bundle:
NSString *appPath=[[[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] stringByAppendingPathComponent:#"MacOS/myApp"];
I am developing a desktop application which deals with images. I want to provide a feature that e-mails selected image (single image) to the default e-mail client. So, I am using mailto: as follows:
NSString *eMail = [NSString stringWithFormat: #"mailto:?subject=Test&body=Image];
[[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: eMail]];
How do I send the image as an attachment using mailto:?
I tried the following: But, it does not work.
NSString *eMail = [NSString stringWithFormat: #"mailto:?subject=Test&body=Image&attachment=%#", #"Test.png"];
[[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: eMail]];
What can I try next?
How do I get the reference to a folder for storing per-user-per-application settings when writing an Objective-C Cocoa app in Xcode?
In .NET I would use the Environment.SpecialFolder enumeration:
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
What's the Cocoa equivalent?
In Mac OSX application preferences are stored automatically through NSUserDefaults, which saves them to a .plist file ~/Library/Preferences/. You shouldn't need to do anything with this file, NSUserDefaults will handle everything for you.
If you have a data file in a non-document based application (such as AddressBook.app), you should store it in ~/Library/Application Support/Your App Name/. There's no built-in method to find or create this folder, you'll need to do it yourself. Here's an example from one of my own applications, if you look at some of the Xcode project templates, you'll see a similar method.
+ (NSString *)applicationSupportFolder;
{
// Find this application's Application Support Folder, creating it if
// needed.
NSString *appName, *supportPath = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
if ( [paths count] > 0)
{
appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleExecutable"];
supportPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:appName];
if ( ![[NSFileManager defaultManager] fileExistsAtPath:supportPath] )
if ( ![[NSFileManager defaultManager] createDirectoryAtPath:supportPath attributes:nil] )
supportPath = nil;
}
return supportPath;
}
Keep in mind that if your app is popular you'll probably get requests to be able to have multiple library files for different users sharing the same account. If you want to support this, the convention is to prompt for a path to use when the application is started holding down the alt/option key.
For most stuff, you should just use the NSUserDefaults API which takes care of persisting settings on disk for you.