I want to build URI (or URL scheme) support in my app.
I do a LSSetDefaultHandlerForURLScheme()
in my + (void)initialize
and I setted the s
The problem is, how to handle the schemes when they are called.
That's where the Apple Events come in. When Launch Services wants your app to open a URL, it sends your app a kInternetEventClass
/kAEGetURL
event.
The Cocoa Scripting Guide uses this very task as an example of installing an event handler.
I'm just adding slightly different Swift 4/5 version of the code:
func applicationWillFinishLaunching(_ notification: Notification) {
NSAppleEventManager
.shared()
.setEventHandler(
self,
andSelector: #selector(handleURL(event:reply:)),
forEventClass: AEEventClass(kInternetEventClass),
andEventID: AEEventID(kAEGetURL)
)
}
@objc func handleURL(event: NSAppleEventDescriptor, reply: NSAppleEventDescriptor) {
if let path = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue?.removingPercentEncoding {
NSLog("Opened URL: \(path)")
}
}
As you are mentioning AppleScript, I suppose you are working on Mac OS X.
A simple way to register and use a custom URL scheme is to define the scheme in your .plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>URLHandlerTestApp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>urlHandlerTestApp</string>
</array>
</dict>
</array>
To register the scheme, put this in your AppDelegate's initialization:
[[NSAppleEventManager sharedAppleEventManager]
setEventHandler:self
andSelector:@selector(handleURLEvent:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
Whenever your application gets activated via URL scheme, the defined selector gets called.
A stub for the event-handling method, that shows how to get the URL string:
- (void)handleURLEvent:(NSAppleEventDescriptor*)event
withReplyEvent:(NSAppleEventDescriptor*)replyEvent
{
NSString* url = [[event paramDescriptorForKeyword:keyDirectObject]
stringValue];
NSLog(@"%@", url);
}
Apple's documentation: Installing a Get URL Handler
Update
I just noticed a problem for sandboxed apps that install the event handler in applicationDidFinishLaunching:
. With enabled sandboxing, the handler method doesn't get called when the app is launched by clicking a URL that uses the custom scheme.
By installing the handler a bit earlier, in applicationWillFinishLaunching:
, the method gets called as expected:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
[[NSAppleEventManager sharedAppleEventManager]
setEventHandler:self
andSelector:@selector(handleURLEvent:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
}
- (void)handleURLEvent:(NSAppleEventDescriptor*)event
withReplyEvent:(NSAppleEventDescriptor*)replyEvent
{
NSString* url = [[event paramDescriptorForKeyword:keyDirectObject]
stringValue];
NSLog(@"%@", url);
}
On the iPhone, the easiest way to handle URL-scheme activation is, to implement UIApplicationDelegate's application:handleOpenURL:
- Documentation
All credits should go to weichsel and kch
I'm just adding swift(2.2/3.0) code for your convenience
func applicationWillFinishLaunching(_ notification: Notification) {
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(self.handleGetURL(event:reply:)), forEventClass: UInt32(kInternetEventClass), andEventID: UInt32(kAEGetURL) )
}
func handleGetURL(event: NSAppleEventDescriptor, reply:NSAppleEventDescriptor) {
if let urlString = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue {
print("got urlString \(urlString)")
}
}
You can define the “get URL” command in a scripting terminology SDEF and implement the corresponding method. For example, Terminal’s SDEF contains the following command definition for handling URLs
<command name="get URL" code="GURLGURL" description="Open a command an ssh, telnet, or x-man-page URL." hidden="yes">
<direct-parameter type="text" description="The URL to open." />
</command>
and declares that the application responds to it:
<class name="application" code="capp" description="The application's top-level scripting object.">
<cocoa class="TTApplication"/>
<responds-to command="get URL">
<cocoa method="handleGetURLScriptCommand:" />
</responds-to>
</class>
The TTApplication class (a subclass of NSApplication) defines the method:
- (void)handleGetURLScriptCommand:(NSScriptCommand *)command { … }