NSWorkspace throws error when launch the mail application - cocoa

I tried the below code, it is working fine for me. Also am able to launch the mail application as well.
//Note the below path is coming from bundle identifier of Mail APP
NSString *path=#"/Applications/Mail.app"
NSURL *mailURL = [NSURL URLWithString:path];
NSError *err=nil;
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:mailURL
options:NSWorkspaceLaunchDefault
configuration: someData
error:&err];
But it throws me the below error message on console, What it means actually. How to resolve the below issue.
CFURLCopyResourcePropertyForKey failed because it was passed this URL which has no scheme: /Applications/Mail.app

The error is thrown because you are not creating a valid URL. The URL needs a scheme, in your case it is file: so the correct URL is file:///Applications/Mail. You need to create a file URL which works as follows:
NSString *path=#"/Applications/Mail.app";
NSURL *mailURL = [NSURL fileURLWithPath:path];
Note that your code breaks if the user ha moved Mail.app to another location. Also note that if the user doesn't use Apple's Mail app, it won't work well for the user either.
One possibility of doing it a more correct way is given here: How to launch New Message window in Mail.app from my application
Another option is to get the URL for Mail.app in a more fleixble way covering for users that have moved Mail.app. The idea is to use the bundle identifier and then ask NSWorkspace to launch this application by using
- (BOOL)launchAppWithBundleIdentifier:(NSString *)bundleIdentifier
options:(NSWorkspaceLaunchOptions)options
additionalEventParamDescriptor:(NSAppleEventDescriptor *)descriptor
launchIdentifier:(NSNumber **)identifier
(see also in detail here http://theocacao.com/document.page/183).

Related

Has there been a change to the way Mac OSX Mavericks handles CFBundleURLName "Custom url" launches for applications?

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

how to make NSURL point to local dir?

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~

checking app download completion from my own app

so what I'm trying to do here is the following: let's say the user has already installed my app, app "A", from the store. On certain conditions, app "A" will open an URL pointing to a specific app page,app "B", on the App Store passing a redemption code so the user of app A is able to download app "B" without paying any additional money. So here's what I'm doing:
NSString *urlBase = #"https://phobos.apple.com/WebObjects/MZFinance.woa/wa/freeProductCodeWizard?code=";
NSURL *urlRedemptionCode = [NSURL URLWithString:[urlBase stringByAppendingString:code]];
[[UIApplication sharedApplication] openURL:urlRedemptionCode];
This is working fine, so the question is: how do I know that the download of app "B" was completed correctly (or with errors) so I can take appropriate action in app "A"?
Thanks so much.
You could use NSURLConnection in combination with NSURLRequest and NSURLResponse instead of directly calling [[UIApplication sharedApplication] openURL:yourURL].
NSURLConnection will allow you to set a delegate that will be notified when:
Data is (partially) received
Connection failed
Download finished
etc.
Check Using NSURLConnection at the official documentation.

How to get filename when using Launch Services

I register my app with:
NSString *self_id=[[NSBundle mainBundle] bundleIdentifier];
CFStringRef cfString;
cfString = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassFilenameExtension,
CFSTR("fdp"),
kUTTypeData);
OSStatus a = LSSetDefaultRoleHandlerForContentType((CFStringRef)cfString,kLSRolesViewer,(CFStringRef)self_id);
But on the other side, how can i get the filename the finder send to my app?
what the mechanism should i set for getting the filename?
Is there any document about this problem?
If your application is document-based, the document controller will create a document object for each opened file automatically. You don't need to do anything at run time to handle the files; just implement your document class and declare the right things in your Info.plist, as described in Document-Based Applications Overview.
If your application is not document-based, you need to create an object to be the delegate of the application object. In that object, respond to the application:openFiles: message.

Opening a url on launch

What method must I implement in my cocoa application’s delegate so that on launch, it’ll open a url? (http/https, in this case) I’ve already implemented the url schemes, I just need to know how I can get my application to open on a url notification.
Update: I’m sorry, I wasn’t very clear. My application IS a browser that support https/http urls, but can only open them when it’s already running. What can I do to implement support for open urls in my app on launch?
When an application finishes launching on OS X, NSApp (the global NSApplication instance for the program) sends its delegate the applicationDidFinishLaunching: message (via the notification system). You can implement that method in your delegate to handle the notification and open a browser window in response, using NSWorkspace. Something like the following would work:
// Your NSApp delegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:#"http://www.example.com/"]];
}
It's not a delegate method. You need to implement an Apple Event handler for the getURL event.
As luck would have it, this is exactly the case Apple uses to demonstrate implementing an Apple Event handler.
I already had implemented the getURL event, so that alone isn’t enough to get the application to open a url on launch. The trick is that the AppleEvent must be installed in applicationWillFinishLaunching: not applicationDidFinishLaunching:. Otherwise, the event isn’t sent at all because the app hasn’t registered it in time.
To implement a protocol handler that you can select (in Safari preferences, for example) as the "default browser" and which will launch in response to HTTP / HTTPS, you need to do a few things.
Add .scriptSuite and .scriptTerminology files to your project resources. These will tell Mac OS X that you'll be handling the GetURL command.
Add a CFBundleURLTypes key to your Info.plist file listing the "URL Schemes" that your app will handle.
Also in Info.plist, add the NSAppleScriptEnabled key with the value YES.
Add a new class to your application as a subclass of NSScriptCommand and implement the -(id)performDefaultImplementation selector. From within this function you will find the clicked URL in [self directParameter]. Pass this on to your app's URL handler!
For the full details check out the article:
http://www.xmldatabases.org/WK/blog/1154_Handling_URL_schemes_in_Cocoa.item

Resources