Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Custom URI scheme support (deep linking) #323

Closed
Tracked by #288
rihardsgravis opened this issue Jan 15, 2020 · 62 comments · Fixed by #8680
Closed
Tracked by #288

Feature: Custom URI scheme support (deep linking) #323

rihardsgravis opened this issue Jan 15, 2020 · 62 comments · Fixed by #8680

Comments

@rihardsgravis
Copy link

rihardsgravis commented Jan 15, 2020

Custom URL schemes provide a way to reference resources inside an app. Users tapping a custom URL e.g. yourapp://foo?bar in an email, for example, launch the app in a specified context. Other apps can also trigger the app to launch with specific context data; for example, a photo library app might display a specified image.

Tauri could have a simplified API to register/unregister the custom URI scheme protocol a listener.

A Rust package https://github.com/maidsafe/system_uri exists that does System App URI registration for macOS, Linux and Windows. Unfortunately, this package does not support passing also the url params to the application - it only triggers the application launch. An example implementation together with Tauri -https://github.com/iotaledger/spark-wallet/blob/master/src-tauri/src/main.rs

@wiredmatt
Copy link

The same goes for the "redirect uri" when authenticating with some external provider that uses Oauth (Auth0 for example).

@RangerMauve
Copy link

It'd be nice if there could be something similar to Electron's protocol API which lets you:

  • Register your application to be launched on custom URI schemes, I wonder if there's existing rust packages that support this already.
  • Add "protocol handlers" to the webview so that custom protocol schemes could invoke a function which returns a stream for the HTTP response. I think most webviews have some concept of this
  • Set privileges for certain custom protocols (this is probably hardest to do with public OS WebView APIs)

Regarding the protocol handlers, this is something that would need to be done in the webview dependency.

@nklayman
Copy link
Member

nklayman commented Mar 4, 2021

In the dev branch of Tauri, we already use custom protocols to load web assets (See tauri-apps/wry#65). However, you can't create your own, is this a feature that would be useful?

@RangerMauve
Copy link

@nklayman It'd be super useful for me! I'm working on a p2p web browser called Agregore which uses Electron's protocol handlers to support loading content from p2p protocols like IPFS and Hyprcore-protcol as well as indie protocols like Gemini.

I like electron, but it'd be nice to be able to go closer to the metal and ditch all the JavaScript that's on the backend and use Rust. 😁

@RangerMauve
Copy link

The other thing which I think is missing in a lot of webview libraries is the registerScemesAsPriviledged API that Electron provides.

This is what lets custom protocol handlers to access APIs which are normally only available via HTTPs as well as make the URL parser treat them as "proper" URLs so that they can get a correct hostname set rather than everything going into the pathname. Not sure if this is possible with Tauri, though.

@nothingismagick
Copy link
Sponsor Member

As a matter of fact, this is what wry is actually doing with its custom scheme @RangerMauve see:

https://github.com/tauri-apps/wry/blob/c49846cfc41bb548a685edeac5f8036501f7dcec/src/webview/mimetype.rs#L115

@RangerMauve
Copy link

Oh wow! That's really cool to see @nothingismagick

It'd be super handy if wry provided APIs to register different protocol schemes like that.
I'd love to ditch all the extra bloat from Electron + Node!

@nothingismagick
Copy link
Sponsor Member

@lucasfernog
Copy link
Member

#1553 is related to some of what has been discussed here (although that's not related to the original issue, which is just about deeplink support).

@juzi5201314
Copy link

Hello, I want to know how this function is progressing.
I am using the dev branch of tauri and wry, but the registered protocol is still unavailable.
Failed to launch'tes://az' because the scheme does not have a registered handler.
For some applications that use oauth, this is a very convenient feature.

@tststs
Copy link

tststs commented Aug 7, 2021

+1 or could someone show an example on how to use it?

@lucasfernog
Copy link
Member

The work on this feature is frozen because we're focusing on bug fixes ahead of a stable release and our audit.

@RangerMauve
Copy link

@lucasfernog Thanks for the update! Is this something you'll be able to continue on after the release/audit?

@lucasfernog
Copy link
Member

@lucasfernog Thanks for the update! Is this something you'll be able to continue on after the release/audit?

Yeah hopefully I can get back to it after the release.

@RangerMauve
Copy link

Sweet, I'm excited to see how it progresses. 😁 This functionality can enable a lot of interesting use cases (especially for people making experimental browsers like myself).

@cloudsolace
Copy link

@lucasfernog Sorry to push you on this but its a feature we need it ASAP for us to use with oath. Also please consider how to communicate the change in deeplink to sidecar

@nothingismagick
Copy link
Sponsor Member

@hellomocks - we are currently in a feature freeze while the audit is ongoing and have some important revision work to do, so as Lucas said, this is not something that the core team will be getting to before the end of September.

@lemarier
Copy link
Member

lemarier commented Sep 6, 2021

@hellomocks - we are currently in a feature freeze while the audit is ongoing and have some important revision work to do, so as Lucas said, this is not something that the core team will be getting to before the end of September.

I would add, the custom URI support is handled by the registry keys on Windows and on macOS it's handled by a custom PLIST;

something like this may work;

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>CustomID</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>tauriapp</string>
    </array>
  </dict>
</array>

Tauri supports custom plist on macOS, for Windows registry key they can probably be done through a custom WIX template or you can do it in rust when the app launch for the first time. Linux with some research it can be done easily, but it depends how you package your app.

@ioneyed
Copy link
Contributor

ioneyed commented Sep 15, 2021

or you can do it in rust when the app launch for the first time. Linux with some research it can be done easily, but it depends how you package your app.

The initial poster mentioned implementing this via the system_uri package and @Shot on Discord implemented a forked version of that crate. I tested it out as well using the mentioned library but with just the registry keys the default command will just keep opening new instances of the application instead of reusing the existing instance.

Are you aware of a way to intercept the triggers or to redirect the invocation to an existing window instead of opening a second instance?

@ioneyed
Copy link
Contributor

ioneyed commented Sep 16, 2021

This can be achieved with WIX but currently don't understand how to get the context replacement working with fragments. However, the wix settings allow you to override the default template which can be modified to achieve this effort.

<Component Id="ApplicationURI" Guid="*">
                <RegistryKey Root="HKCR" Key="acme">
                    <RegistryValue Type="string" Value="acme" />
                    <RegistryValue Name="URL Protocol" Type="string" Value=""/>
                    <RegistryKey Key="shell">
                        <RegistryKey Key="open">
                            <RegistryKey Key="command">
                                <RegistryValue Type="string" Value='{{{app_exe_source}}} "%1"'/>
                            </RegistryKey>
                        </RegistryKey>
                    </RegistryKey>
                </RegistryKey>
            </Component>

I placed this component in the

 <DirectoryRef Id="INSTALLDIR">

section of the default WIX template (https://github.com/tauri-apps/tauri/blob/b0a8c38a736544cdd70fd10155e5ad3a25c81535/tooling/bundler/src/bundle/windows/templates/main.wxs)

Then I updated my tauri.conf.json bundle section to look the following:

"windows": {
        "certificateThumbprint": null,
        "digestAlgorithm": "sha256",
        "timestampUrl": "",
        "wix":{
          "template":"wixFragments/template.wxs" // <-- wixFragments is a folder in my src-tauri parent directory
        }
      }

This will create the registry keys under Computer\HKEY_CLASSES_ROOT\acme\shell\open\command which is where the file associates/commands live to be found for invocation. This will allow acme:// to open the application. I will caveat that this approach will launch a new instance every time it encounters the scheme. It will not re-use an existing instance and pass that information into the instance. I am still digging into that bit of work as well as how to make this part of a fragment instead of a custom template.

My list of items to continue investigating:

  • How to make it re-use an instance
  • How to invoke a command when a scheme is invoked with parameters
  • File associations (which is registry driven like custom uris)

@amrbashir amrbashir changed the title Feature: Custom URI scheme support Feature: Custom URI scheme support (deep linking) Oct 30, 2021
@RiChrisMurphy
Copy link

has there been any updates on this and if there are, is there an example?

@FabianLars
Copy link
Member

@RiChrisMurphy No, otherwise it would be listed or linked here...

@vikigenius

This comment has been minimized.

@cloudsolace

This comment has been minimized.

@alectrocute

This comment has been minimized.

@mainrs

This comment has been minimized.

@gegles
Copy link

gegles commented Jan 3, 2023

Thanks @FabianLars! We're not in a super rush as we're just at the beginning of rewriting our app with Tauri, so hopefully, by the time we get there, it'll be available.

In the meantime, do you know of any plugins/impls that get you this feature somewhat? Like on macOS and Windows primarily. Thanks!

@FabianLars
Copy link
Member

For windows i added some notes here already: #323 (comment) - the plugin i linked should work on Linux too, but there might be an issue with how it sets the binary path, i really need to check that when i have the time.

macOS is the only problematic thing here again, it's the only platform where this can't be added on-top and needs changes in tao/winit because of Apple's interesting design choices.

@gegles
Copy link

gegles commented Jan 8, 2023

macOS is the only problematic thing here again, it's the only platform where this can't be added on-top and needs changes in tao/winit because of Apple's interesting design choices.

@FabianLars, I am still very new to Rust/Tauri, so this may be completely dumb, but could the fruitbasket crate and specifically the example here, be leveraged, at least as a workaround in your tauri-plugin-deep-link to get the macOS functionality?

Happy to help building this if you think it is viable.

@FabianLars
Copy link
Member

FabianLars commented Feb 27, 2023

@gegles while i was never able to make fruitbasket work in my app (on my stupid vm) it still ended up being a valuable resource in creating something that does seem to work for me. In other words, the plugin should support macos now too.

@gegles
Copy link

gegles commented Feb 27, 2023

@gegles while i was never able to make fruitbasket work in my app (on my stupid vm) it still ended up being a valuable resource in creating something that does seem to work for me. In other words, the plugin should support macos now too.

@FabianLars, thank you so much! I've been following your PR closely, and I've now put someone from my team on this to see if everything works for us. We'll let you know, but so appreciate all your work already!

@lars-berger
Copy link

@FabianLars Been using the plugin on Windows 10/11 for a few months now, and it's been working flawlessly. Attempting to port to Mac at the moment - could you clarify what changes need to be made to the Tauri config? I'm just running the generated default config, but it's not registering the scheme in development + release mode. AFAIK there isn't a way to register deep links in develop mode, right?

@FabianLars
Copy link
Member

FabianLars commented Mar 13, 2023

AFAIK there isn't a way to register deep links in develop mode, right?

Correct, there used to be deprecated apis for that but they stopped working in the latest macos versions so i didn't even bother adding them.

Basically you need to completely build the app (either .app or .dmg) and install it, running just the binary is not enough. Some resources said that you sometimes have to restart/logout for it to take effect but it worked for me without that.
Oh and, don't forget to add and configure the Info.plist file next to your tauri.conf.json file, that's what actually registers the schemes.

p.s. once you have an app installed you can use tauri dev again (close the installed app first) and it should redirect requests to the dev instance but that's a bit wonky so i may remove it in the future.

Soooo, the tldr is:

  • Copy the example Info.plist file next to your tauri config and change it according to your needs (this is really important)
  • add prepare at the top, and call register in the setup hook like in the example main.rs file. The scheme in the register function will be ignored on macos.
  • Run tauri build and install the app. Maybe restart the system.

Edit: One thing i forgot, the emit_all call in the setup hook is a bit stupid on macos, if your app gets launched by the uri scheme, because the js listeners won't be registered at that point.

@RandomEngy
Copy link

FYI there's some caveats about what programs are allowed to take focus on Windows. Normally when implementing single instance deep linking you call AllowSetForegroundWindow to give another app permission to take foreground focus. Otherwise, it may just blink helplessly on the taskbar. But when that protocol activation comes from a Windows notification, that call fails with ERROR_ACCESS_DENIED because one of a list of conditions must be met for the call to succeed.

Chromium works around this by sending a dummy key press event, which lets AllowSetForegroundWindow succeed.

@FabianLars 's tauri-plugin-deep-link works pretty well when launching via Start -> Run, but was suffering from the same focus issue when launched from a Windows Notification. It may be that implementing a Chrome-style workaround is what's required for that scenario. The workaround functioned perfectly for me in Electron.

@FabianLars
Copy link
Member

@RandomEngy This sounds like a general issue not just related to custom schemes, right? If so please open a separate issue for this! (preferably with a minimal reproduction app)

p.s. does tauri's window.set_focus() api work as a user-side workaround?

@RandomEngy
Copy link

This issue happens when a second instance is launched from Windows Notifications. A protocol launch is one way for this to happen. I brought it up here because any implementation that tries to activate the primary app instance should be aware of this. I'll get together a small repro app using tauri-plugin-deep-link that demonstrates this. But I wouldn't say it's a bug on Tauri itself right now; there's no built-in feature that tries to activate a primary instance from a secondary one.

The call to window.set_focus() is part of the repro steps:

  1. Set up tauri_plugin_deep_link::register with a handler that tries to call window.set_focus()
  2. Open the app
  3. Fire a notification that opens the app with a protocol
  4. Put the app in the background by focusing a different app
  5. Click the notification

Expected result:
App comes to the foreground

Actual result:
App flashes in the taskbar and does not come to the foreground

@RandomEngy
Copy link

Filed FabianLars/tauri-plugin-deep-link#26 with a minimal repro repository.

@joshua7v
Copy link

joshua7v commented Apr 6, 2023

Edit: One thing i forgot, the emit_all call in the setup hook is a bit stupid on macos, if your app gets launched by the uri scheme, because the js listeners won't be registered at that point.

@FabianLars Hi, I'm using tauri-plugin-deep-link on Windows only
It seems the js listeners also not run on Windows if the app gets launched by the uri scheme
I don't know much of rust, just literaly copy the code in example, does it expected to work?

also, the url scheme could launch the app, but seems cannot focus the app if already launched (the listeners gets called, just the window does not get focused), is it an unsolved issue?

@FabianLars
Copy link
Member

@joshua7v

It seems the js listeners also not run on Windows if the app gets launched by the uri scheme
I don't know much of rust, just literaly copy the code in example, does it expected to work?

This is expected behavior. There is a comment and a small example in the repo example showing how to read the url if the primary instance was started via the scheme.

also, the url scheme could launch the app, but seems cannot focus the app if already launched (the listeners gets called, just the window does not get focused), is it an unsolved issue?

That kinda depends on your code i think, you may have to call window.set_focus() (should also work in js), if that doesn't work, please open an issue at my repo and we'll take a look.

@joshua7v
Copy link

joshua7v commented Apr 6, 2023

@FabianLars

Thanks for the helpful guidance, I did miss the comment under example code, however the listeners still don't get invoked after putting the code in. The set_focus() works well

I have fired an new issue at the deep link repo, hope you could easily reproduce it.

@nanderstabel
Copy link

@FabianLars So it seems that deep linking on different OS comes with different set of problems.. Do you know whether deep linking for Tauri Mobile will come with an extra set of complications?

@FabianLars
Copy link
Member

The link handling on iOS seems to be covered by the macos implementation already. Registering the links are similar but doesn't re-use the same plist file (since it's not an .app bundle) afaik.

The android implementation looks like it can't reuse anything. I don't know how hard that will be since I didn't really look into it yet but I'd imagine it also has some annoying quirks like all the other platforms.

(P.S. either way I'm happy that I don't have to handle that in the plugin :D )

@gustavoseixas
Copy link

gustavoseixas commented Jul 21, 2023

The same goes for the "redirect uri" when authenticating with some external provider that uses Oauth (Auth0 for example).

How you bypass this,please?

@ken0x0a
Copy link

ken0x0a commented Jul 22, 2023

on macOS, you can use ASWebAuthenticationSession.
That's not for deeplinking, just for Authentication though

@rr13k

This comment was marked as off-topic.

@kris-ava
Copy link

kris-ava commented Oct 12, 2023

After spending some time trying to figure out why tauri-plugin-deep-link stopped working in alpha.14 on Mac, I decided to share my findings:

The way the plugin gathers the URLs is through registering a handler on NSAppleEventManager . This is the OLD way. The new way is to use application:getURLS. And interestingly enough, this is what TAO already does (starting with this commit), but from what I understand not all TAO events are available in Tauri. What I discovered from a random person on the internet is that once you register the event handler (the old way) the method on the delegate (the new way) will not be called anymore. Which led me to an idea, that maybe the NSAppleEventManager was being called too early. I added an arbitrary 5 second delay before registering the event handler and it worked (and unsurprisingly, judgning from the lack of the relevant trace level log messages, the getURLs method stopped being called).

All of this leads me to a conclusion that for Tauri v2, the best way to implement custom scheme support on Mac OS is to propagate the event triggered by getURLs all the way to Tauri and use it instead of the NSAppleEventHandler.

Hope my discovery session aids someone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.