DEV Community

Rain9
Rain9

Posted on

1 1 1 1 1

Tauri (6) — Invoke desktop application functionality through the browser

Introduction

Most of the online articles focus on Tauri V1. This article primarily discusses the implementation in Tauri V2.

In a Tauri V2 application, it's possible to open the app via a specific URL in the browser by configuring the protocol and relevant settings. Let’s walk through the steps to achieve this functionality.

Install Plugins

Install @tauri-apps/plugin-deep-link

This plugin is used to set the Tauri application as the default handler for a specific URL.

Method 1: Modify Cargo.toml

Add the following to the src-tauri/Cargo.toml file:

[dependencies]
tauri-plugin-deep-link = "2.0.0"
# alternatively with Git:
tauri-plugin-deep-link = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
Enter fullscreen mode Exit fullscreen mode

Method 2: Install via JS

pnpm add @tauri-apps/plugin-deep-link
# or
npm add @tauri-apps/plugin-deep-link
# or
yarn add @tauri-apps/plugin-deep-link

# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
Enter fullscreen mode Exit fullscreen mode

Install tauri-plugin-single-instance

This ensures that only a single instance of the Tauri application is running.

Configuration: Modify Cargo.toml

Add the following to the src-tauri/Cargo.toml file:

[dependencies]
tauri-plugin-single-instance = "2.0.0"
# alternatively with Git:
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
Enter fullscreen mode Exit fullscreen mode

Configure Custom Protocol

Tauri supports using custom protocols like tauri:// to open content in the application via the browser.

Add the following to the src-tauri/tauri.conf.json file:

  "plugins": {
    "features": {
      "protocol": ["all"]
    },
    "window": {},
    "websocket": {},
    "shell": {},
    "globalShortcut": {},
    "deep-link": {
      "schema": "coco",
      "mobile": [
        { "host": "app.infini.cloud", "pathPrefix": ["/open"] },
        { "host": "localhost:9000" }
      ],
      "desktop": {
        "schemes": ["coco"]
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

Implement Custom Protocol Logic

Add the following to the src-tauri/src/lib.rs file:

use tauri_plugin_deep_link::DeepLinkExt;

#[derive(Clone, serde::Serialize)]
struct Payload {
    args: Vec<String>,
    cwd: String,
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let mut ctx = tauri::generate_context!();

    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_deep_link::init())
        .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
            app.emit("single-instance", Payload { args: argv, cwd })
                .unwrap();
        }))
        .invoke_handler(tauri::generate_handler![
        ])
        .setup(|app| {
            init(app.app_handle());

            #[cfg(target_os = "macos")]
            app.set_activation_policy(ActivationPolicy::Accessory);

            #[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
            app.deep_link().register_all()?;

            app.deep_link().on_open_url(|event| {
                dbg!(event.urls());
            });

            Ok(())
        })
        .run(ctx)
        .expect("error while running tauri application");
}
Enter fullscreen mode Exit fullscreen mode

Permission Configuration

Add the following to the src-tauri/capabilities/default.json file:

  "permissions": [
    "oauth:allow-start",
    "deep-link:allow-get-current", 
    "deep-link:default",
    "deep-link:allow-register"
  ]
Enter fullscreen mode Exit fullscreen mode

MacOS Configuration

In a Tauri project, src-tauri/Info.plist is a macOS-specific file that defines key properties and behaviors for the application, such as the name, permissions, URL schemes, etc.

Modify the src-tauri/Info.plist file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>NSCameraUsageDescription</key>
  <string>Request camera access for WebRTC</string>
  <key>NSMicrophoneUsageDescription</key>
  <string>Request microphone access for WebRTC</string>

  <key>CFBundleIdentifier</key>
  <string>rs.coco.app</string>

  <key>NSPrefPaneIconLabel</key>
  <string>coco-ai</string>
  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleURLName</key>
      <string>rs.coco.app</string>
      <key>CFBundleURLSchemes</key>
      <array>
        <string>coco</string>
      </array>
    </dict>
  </array>
</dict>
</plist>
Enter fullscreen mode Exit fullscreen mode

Frontend Implementation

Tauri APP Code:

import {
  onOpenUrl,
  getCurrent as getCurrentDeepLinkUrls,
} from "@tauri-apps/plugin-deep-link";

const handleUrl = (url: string) => {
  try {
    const urlObject = new URL(url);
    console.error("urlObject:", urlObject);

    switch (urlObject.pathname) {
      case "oauth_callback":
        const code = urlObject.searchParams.get("code");
        const provider = urlObject.searchParams.get("provider");
        // Implement logic here
        break;

      default:
        console.log("Unhandled deep link path:", urlObject.pathname);
    }
  } catch (err) {
    console.error("Failed to parse URL:", err);
  }
};

// Fetch the initial deep link intent
useEffect(() => {
  getCurrentDeepLinkUrls()
    .then((urls) => {
      if (urls && urls.length > 0) {
        handleUrl(urls[0]);
      }
    })
    .catch((err) => console.error("Failed to get initial URLs:", err));

  const unlisten = onOpenUrl((urls) => handleUrl(urls[0]));

  return () => {
    unlisten.then((fn) => fn());
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

Browser Web Code:

<a href="coco://oauth_callback?code=ctvracbq50ke7o4qksj0&provider=coco-cloud">
  In order to continue, please click here to launch Coco AI
</a>
Enter fullscreen mode Exit fullscreen mode

Use Cases

  1. Triggering desktop app logic via web pages.
  2. Integrating OAuth login, payment callbacks, etc., that require browser interaction.
  3. Cross-platform data exchange.
  4. Online documentation and help center.
  5. Dynamic loading and updates.

Summary

With the steps above, we’ve implemented the functionality to open a Tauri app from the browser using a custom protocol, supporting flexible configuration and multi-platform compatibility.

References

  1. @tauri-apps/plugin-deep-link
  2. tauri-plugin-single-instance
  3. Coco App

Open Source

Recently, I’ve been working on a project based on Tauri called Coco. It’s open source and under continuous improvement. I’d love your support—please give the project a free star 🌟!

This is my first Tauri project, and I’ve been learning while exploring. I look forward to connecting with like-minded individuals to share experiences and grow together!

Thank you for your support and attention!

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay