Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocoa protocol handler using NSAppleEventManager and kInternetEventClass/kAEGetURL

This is the Cocoa version of this question:

AEInstallEventHandler handler not being called on startup

Here's my Info.plist protocol registration:

    ...
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>My Protocol</string>
            <key>CFBundleURLIconFile</key>
            <string>myicon</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>myapp</string>
            </array>
        </dict>
    </array>

Here's where I set the method to listen for the kInternetEventClass/kAEGetURL event when a browser link is clicked with the link "myapp://unused/?a=123&b=456":

- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
    [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(getURL:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
    ...
}

Here's the handler method:

- (void)getURL:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply
{
    [[[event paramDescriptorForKeyword:keyDirectObject] stringValue] writeToFile:@"/testbed/complete_url.txt" atomically:YES];
}

Here's the test web link:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
</head>
<body>
    <a href="myapp://open/?a=123&b=456">Open My App</a>
</body>
</html>

This all works great if the application is already running.

The handler method is called and the complete url is captured.

However, if the app is not yet running the same link will launch the app, but the handler will not be invoked — which makes sense since the handler had not yet been bound to the event.

Those arguments in the URL are important for our application to coordinate with the webapp. Although the vast majority of the time our application will already be running when this click occurs, it is reasonable to expect that in some cases it will not.

I've tried inspecting the environment and process invocation arguments and I do not see the URL in either of them.

Anyone know how we can capture this URL reliably, even when our application isn't already running when the browser click happens?

like image 499
AJ.DE Avatar asked Apr 25 '13 04:04

AJ.DE


2 Answers

Apple’s SimpleScriptingPlugin sample registers the handler in applicationWillFinishLaunching:, which is probably cleaner than using init. (And like mikker said, the handler gets called before applicationDidFinishLaunching:.)

like image 198
Václav Slavík Avatar answered Nov 15 '22 04:11

Václav Slavík


I just faced this exact problem. I don't know if it's the proper solution but you can register the event handler in the app delegate's init-method instead.

// in AppDelegate.m

- (id)init
{
  self = [super init];

  if (self) {
    [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
  }

  return self;
}

One thing to note though is that handleURLEvent:withReplyEvent get's called before applicationDidFinishLaunching: if the app is started by an URL Scheme.

like image 39
mikker Avatar answered Nov 15 '22 05:11

mikker