Tauri(六)—— 通过浏览器唤起桌面应用

2,057 阅读3分钟

前言

网上大部分的文章说的都是 Tauri V1 版本的。本文主要说一下 Tauri V2 版本的实现。

在 Tauri V2 应用中,支持通过特定的 URL 来在浏览器中打开 App 功能,可以通过设置协议和配置相应的内容来实现。接下来,就让我们一步步实现此功能。

安装插件

安装 @tauri-apps/plugin-deep-link

是为了将 Tauri 应用程序设置为 URL 的默认处理程序。

配置方式 1:修改 Cargo.toml

src-tauri/Cargo.toml 文件中增加:

[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" }

配置方式 2:通过 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

安装 tauri-plugin-single-instance

是为了确保 tauri 应用程序的单个实例正在运行。

配置方式:修改 Cargo.toml

src-tauri/Cargo.toml 文件中增加:

[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" }

设置自定义协议

Tauri 支持使用 tauri:// 自定义协议来在浏览器中打开应用的内容。

src-tauri/tauri.conf.json 文件中增加:

  "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"]
      }
    }
  }

实现自定义协议逻辑

src-tauri/src/lib.rs 文件中增加:

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| {
            // println!("{}, {argv:?}, {cwd}", app.package_info().name);
            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");
}

权限配置

src-tauri/capabilities/default.json 文件中增加:

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

MacOS 配置

在 Tauri 项目中,src-tauri/Info.plist 是一个 macOS 特有的文件,它是 macOS 应用程序的 信息属性列表。这个文件是 macOS 应用的重要组成部分,用于定义应用程序的基本属性和行为。例如,应用的名称、图标、权限、支持的 URL 协议等都可以在这里配置。

src-tauri/Info.plist 文件:

<?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>

前端调用

Tauri APP 代码:

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");
          // 要执行的逻辑
          break;

        default:
          console.log("Unhandled deep link path:", urlObject.pathname);
      }

      setLastUrl(url);
    } catch (err) {
      console.error("Failed to parse URL:", err);
      setError("Invalid URL format");
    }
  };
  
// Fetch the initial deep link intent
  useEffect(() => {
    // coco://oauth_calback?code=&provider=
    // handleOAuthCallback("cu0bpu53q95r66at2010", "coco-cloud");
    // 
    getCurrentDeepLinkUrls()
      .then((urls) => {
        console.log("URLs:", urls);
        if (urls && urls.length > 0) {
          handleUrl(urls[0]);
        }
      })
      .catch((err) => {
        console.error("Failed to get initial URLs:", err);
        setError("Failed to get initial URLs");
      });

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

    return () => {
      unlisten.then((fn) => fn());
    };
  }, []);

浏览器 web 端代码

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

适用场景

  1. 通过 Web 页面触发桌面应用逻辑
  2. 配合 OAuth 登录、支付回调等需要浏览器完成的操作
  3. 跨平台数据交互
  4. 在线文档与帮助中心
  5. 动态加载和更新

小结

通过以上步骤,我们实现了 Tauri 应用在浏览器中打开的功能,支持灵活配置自定义协议和多平台适配。

参考资料

  1. www.npmjs.com/package/@ta…
  2. crates.io/crates/taur…
  3. github.com/infinilabs/…

开源

最近,我正在基于 Tauri 开发一款项目,名为 Coco。目前已开源,项目仍在不断完善中,欢迎大家前往支持并为项目点亮免费的 star 🌟!

作为个人的第一个 Tauri 项目,开发过程中也是边探索边学习。希望能与志同道合的朋友一起交流经验、共同成长!

代码中如有问题或不足之处,期待小伙伴们的宝贵建议和指导!

非常感谢您的支持与关注!