native package registers an app's declared url_schemes with the operating system on both macOS and Linux, and neither host has any path that delivers the resulting URL to the app. The registration half of the round trip is complete; the delivery half does not exist.
All references are v0.10.1. I also checked main (5 commits ahead, none touching the hosts or the packager).
The registration happens
macOS, src/tooling/package.zig:1892 and :1909, writes into Info.plist:
<key>CFBundleURLTypes</key>
...
<key>CFBundleURLSchemes</key>
Linux, src/tooling/package.zig:1972, writes into the .desktop file:
try appendFmt(allocator, &out, "x-scheme-handler/{s};", .{url_scheme.scheme});
So after packaging, both operating systems believe the app handles the scheme, and will launch or activate it with the URL.
The delivery does not
macOS. src/platform/macos/appkit_host.m has no application:openURLs:, no handleGetURLEvent:, and no NSAppleEventManager registration. The only NSURL uses in the file are WebView navigation (loadRequest, around :9145 and :9220). AppKit hands the URL to the delegate and nothing is listening.
Linux. src/platform/linux/gtk_host.c:3321 creates the application with G_APPLICATION_DEFAULT_FLAGS, which is 0, so not G_APPLICATION_HANDLES_OPEN. Only activate is connected (:3394). And :3395 runs it as:
g_application_run(G_APPLICATION(host->app), 0, NULL);
argc 0 and argv NULL, so the process's own arguments are discarded before GApplication ever sees them. Cold start drops the URI, and because the app is a GtkApplication with single-instance behaviour, a second launch carrying a URI just activates the existing window with the URI thrown away.
Why it matters
Declaring url_schemes in the manifest is the app telling the SDK it wants those URLs. The SDK tells the OS the same thing. The user clicks a link, the OS does exactly what it was asked, and the app receives nothing. There is no error and no diagnostic, so from the app author's side the feature simply does not work and there is nothing to debug.
The workaround is not symmetric. On macOS I could add my own Objective-C: my app compiles a small .m that installs an Apple Event handler for kInternetEventClass/kAEGetURL and links AppKit, which works but means shipping platform code the SDK already links for me. On Linux there is no equivalent seam. g_application_run is called inside the host with argv already discarded, so an app cannot recover the URI even for cold start without patching the SDK.
Suggested fix
A host callback. One entry in the services table, for example on_open_url_fn, delivered like any other host event. Because a URL can arrive before the app's model is ready (this is the common case: the OS launches the app because of the URL), the runtime should hold the most recent URL and hand it over once the app is running, rather than dropping it. A single slot is enough; the last URL wins.
macOS. Implement application:openURLs: on the delegate (10.13+, and it covers both cold launch and an already-running app) and forward each URL to the callback. If you would rather keep 10.12 support, the Apple Event route via NSAppleEventManager is the older equivalent.
Linux. Three small changes, and GApplication then does the hard part for you:
- Create the application with
G_APPLICATION_HANDLES_OPEN instead of G_APPLICATION_DEFAULT_FLAGS (gtk_host.c:3321).
- Connect the
open signal alongside activate (:3394).
- Pass the real
argc/argv to g_application_run (:3395).
With HANDLES_OPEN, GApplication's single-instance machinery forwards the URI from a second launch to the primary instance for you, so warm start works without any extra code. Today step 3 alone is what makes cold start impossible.
I ship a Zig canvas app that uses a custom scheme on macOS and I am preparing a Linux build, so I can test a fix on both.
native packageregisters an app's declaredurl_schemeswith the operating system on both macOS and Linux, and neither host has any path that delivers the resulting URL to the app. The registration half of the round trip is complete; the delivery half does not exist.All references are v0.10.1. I also checked
main(5 commits ahead, none touching the hosts or the packager).The registration happens
macOS,
src/tooling/package.zig:1892and:1909, writes intoInfo.plist:Linux,
src/tooling/package.zig:1972, writes into the.desktopfile:So after packaging, both operating systems believe the app handles the scheme, and will launch or activate it with the URL.
The delivery does not
macOS.
src/platform/macos/appkit_host.mhas noapplication:openURLs:, nohandleGetURLEvent:, and noNSAppleEventManagerregistration. The onlyNSURLuses in the file are WebView navigation (loadRequest, around :9145 and :9220). AppKit hands the URL to the delegate and nothing is listening.Linux.
src/platform/linux/gtk_host.c:3321creates the application withG_APPLICATION_DEFAULT_FLAGS, which is0, so notG_APPLICATION_HANDLES_OPEN. Onlyactivateis connected (:3394). And:3395runs it as:argc
0and argvNULL, so the process's own arguments are discarded before GApplication ever sees them. Cold start drops the URI, and because the app is aGtkApplicationwith single-instance behaviour, a second launch carrying a URI just activates the existing window with the URI thrown away.Why it matters
Declaring
url_schemesin the manifest is the app telling the SDK it wants those URLs. The SDK tells the OS the same thing. The user clicks a link, the OS does exactly what it was asked, and the app receives nothing. There is no error and no diagnostic, so from the app author's side the feature simply does not work and there is nothing to debug.The workaround is not symmetric. On macOS I could add my own Objective-C: my app compiles a small
.mthat installs an Apple Event handler forkInternetEventClass/kAEGetURLand links AppKit, which works but means shipping platform code the SDK already links for me. On Linux there is no equivalent seam.g_application_runis called inside the host with argv already discarded, so an app cannot recover the URI even for cold start without patching the SDK.Suggested fix
A host callback. One entry in the services table, for example
on_open_url_fn, delivered like any other host event. Because a URL can arrive before the app's model is ready (this is the common case: the OS launches the app because of the URL), the runtime should hold the most recent URL and hand it over once the app is running, rather than dropping it. A single slot is enough; the last URL wins.macOS. Implement
application:openURLs:on the delegate (10.13+, and it covers both cold launch and an already-running app) and forward each URL to the callback. If you would rather keep 10.12 support, the Apple Event route viaNSAppleEventManageris the older equivalent.Linux. Three small changes, and GApplication then does the hard part for you:
G_APPLICATION_HANDLES_OPENinstead ofG_APPLICATION_DEFAULT_FLAGS(gtk_host.c:3321).opensignal alongsideactivate(:3394).argc/argvtog_application_run(:3395).With
HANDLES_OPEN, GApplication's single-instance machinery forwards the URI from a second launch to the primary instance for you, so warm start works without any extra code. Today step 3 alone is what makes cold start impossible.I ship a Zig canvas app that uses a custom scheme on macOS and I am preparing a Linux build, so I can test a fix on both.