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
Related
I am adopting my iOS widget to my Mac app, and one thing I can't figure out is when I do a deep-link from the widget to the Mac app, where would that get detected? On iOS, it calls:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
But I can't find any equivalent method in the NSApplicationDelegate. The closest method is
- (void)application: (NSApplication *)application openURLs:(nonnull NSArray<NSURL *> *)urls
But this doesn't get invoked from the widget's Link.
Is there some other way to handle this on macOS? Do I need to configure something in particular for the openURLs method to be called?
EDIT:
After more testing, I've realized that the application: openURLs method should be called, whether through the widget Link or directly called using the URL scheme (as defined in the Info.plist). I verified this with another test app as well, where I can use [[NSWorkspace sharedWorkspace] openURL: testURL] to open the test app and the application: openURLs DOES get called. But it's not working for my main app for some reason, even though I've followed the same steps and defined the URL scheme in CFBundleURLTypes in the Info.plist file. The main app was created many years ago, whereas the test app was created just today.
Any ideas why the openURLs method isn't getting called in my main app?
I am trying to implement the new ASWebAuthenticationSession in MacOS 10.15 and I'm getting a bit confused about the callbackURLScheme.
The header file for ASWebAuthenticationSession says:
The callback URL usually has a custom URL scheme. For the app to
receive the callback URL, it needs to either register the custom URL
scheme in its Info.plist, or set the scheme to callbackURLScheme
argument in the initializer.
So I initialize like this:
self.webAuthSession = [[ASWebAuthenticationSession alloc] initWithURL:self.authURL
callbackURLScheme:#"myurlscheme://"
completionHandler:^(NSURL * _Nullable callbackURL, NSError * _Nullable error) {
}];
The oauth provider I am trying to access, does not support custom URL schemes, so I have it set to redirect to a web address which in turn redirects to my URL Scheme.
So, after clicking Login in my MacOS app, Safari presents a window that allows me to login to the service. After successfully logging in the redirect happens but the web page just goes to a screen saying
Safari can't open the specified address.
I can see that the address is myurlscheme://?code=1234567890abcdefgetc... which is correct but it's obviously not calling the ASWebAuthenticationSession callback.
If I add the URL Scheme to the plist, then the safari window shows a prompt asking if you want to open it in the my app.
I know I can then fetch the code from the App Delegate's application:openURLs: method, but this is fiddly, has an unnecessary prompt for the user to click, and also leaves the ASWebAuth... safari window open. This should all be automatic through the callback.
The only way I get the callback to fire, is by closing the safari window in which case it calls back with error.
I have just returned to this to try again and it seems to be working fine on MacOS 10.15.1 and 10.15.2 beta.
I have noticed that the callbackURLScheme parameter must not include ://
I'm not able to go back and test it again on 10.15 but i'm happy to use this now assuming everyone on 10.15 would have updated to 10.15.1
Darren is right in MacOS you must remove the :// from the callbackURLScheme. iOS works fine no matter if you :// or not. However, in MacOS Catalyst there seems to be a bug. My app is getting focused once I login, but isn't getting the callback. Haven't found a way to fix it yet.
I managed to launch my application whenever a URL scheme is called via the browser, by putting this scheme in the info.plist and adding these 2 lines:
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:self andSelector:#selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
and correctly implementing my getUrl function.
So I tried putting the two lines above in my appDelegates's applicationWillFinishLaunching, applicationDidFinishLaunching and init. But in the three cases, I have the same scenario: if my app is already running, then the getUrl function is correctly called, by if the app is not launched, then getUrl is never called.
I found on other posts (How to handle with a default URL scheme and get url event on app open in objective c (Mac OSX)) but the suggestion which comes often is using the applicationWillFinishLaunching, which didn't work in my case.
Edit: here is my info.plist: http://pastebin.com/yM8zabvY
I'm not sure where things are going sour for you. I've set up a test project, in which I just pasted the CFBundleURLTypes key from your Info.plist, and I used this in my app delegate:
-(void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor*)reply
{
NSLog(#"%#", event);
}
-(void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
// Register ourselves as a URL handler for this URL
[[NSAppleEventManager sharedAppleEventManager]
setEventHandler:self
andSelector:#selector(getUrl:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
}
And it works just like I'd expect. I get the log message regardless of whether the app was already running or not when I run open "feed://test" in the terminal.
Have you verified that your app is the default handler for the feed: scheme?
You can inspect the system's URL schemes using RCDefaultApp or the Launch Services API (LSCopyDefaultHandlerForURLScheme).
I am developing a Mac application which registers a custom URL scheme. The application launches but my selector method is not being called so I am not able to parse the URL. After the application is launched, subsequent URL scheme calls result in a call to the selector method.
I had the
[NSAppleEventManager sharedAppleEventManager] setEventHandler:self
andSelector:#selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
on AppDelegate applicationDidFinishLaunching and now moved it to the init method without any result.
Any ideas on how to solve it?
Actually, moving the event handler registration to the init method did the trick. In my case the URL was launching the XCode archived application instead of the newly archived one.
I am working on a Cocoa WebView based browser application.
Basically, I would like to have it so when the user navigates to a url, for example:
http://a/b.php, by clicking on a link on the previous page of a website, my application intercepts the "on navigate" event and changes the URL to http://a/b.php?enableapi=1
Any ideas??
Thanks in advance.
You need to assign an object as the web view's WebPolicyDelegate and implement the webView:decidePolicyForNavigationAction:request:frame:decisionListener: method.
In that method, you must call one of the WebPolicyDecisionListener protocol methods on the object that is passed as the decisionlistener parameter to the method. The three WebPolicyDecisionListener protocol methods are ignore, use or download.
You can then pass ignore to the listener and handle the link some other way for those links you're interested in intercepting.