如何在Tauri Rust开发的Mac应用中添加右键菜单?集成Finder Sync扩展存疑
在Tauri Rust应用中集成Finder Sync扩展实现右键菜单选项
要给Mac上的Tauri Rust应用添加Finder右键菜单选项(比如“更换壁纸”),核心思路是通过Finder Sync Extension(苹果原生系统扩展)实现菜单渲染,再通过进程间通信(IPC)把选中的文件路径传递给Tauri主应用执行逻辑。下面是具体步骤:
1. 给Tauri项目添加Finder Sync扩展目标
Tauri生成的Xcode项目是基础,你需要在Xcode中给它添加一个新的扩展Target:
- 打开Tauri项目的
src-tauri/tauri.xcodeproj - 点击菜单栏
File > New > Target,选择Finder Sync Extension(属于Application Extension分类) - 设置扩展的Bundle ID,必须和主应用Bundle ID关联(比如主应用是
com.yourdomain.app,扩展设为com.yourdomain.app.FinderSync) - 确保扩展和主应用使用同一个开发者证书签名(测试阶段可以用本地开发证书)
2. 编写扩展的右键菜单逻辑
在扩展的FinderSync.swift文件中,重写菜单生成方法,添加自定义菜单项并处理点击事件:
import FinderSync class FinderSync: FIFinderSync { override init() { super.init() // 设置要监听的目录(这里设为所有目录,也可以指定特定路径) FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: "/")] } // 生成右键菜单 override func menu(for menuKind: FIMenuKind) -> NSMenu? { let menu = NSMenu(title: "") let setWallpaperItem = NSMenuItem(title: "更换壁纸", action: #selector(handleSetWallpaper(_:)), keyEquivalent: "") setWallpaperItem.target = self menu.addItem(setWallpaperItem) return menu } // 处理菜单点击 @objc func handleSetWallpaper(_ sender: NSMenuItem) { // 获取用户选中的文件URL guard let selectedURLs = FIFinderSyncController.default().selectedItemURLs else { return } let filePaths = selectedURLs.compactMap { $0.path } // 把路径传递给Tauri主应用(这里用分布式通知+共享UserDefaults示例) let sharedDefaults = UserDefaults(suiteName: "com.yourdomain.app.shared")! sharedDefaults.set(filePaths, forKey: "SelectedWallpaperPaths") sharedDefaults.synchronize() // 发送通知触发主应用处理 DistributedNotificationCenter.default().post( name: NSNotification.Name("com.yourdomain.app.SetWallpaper"), object: nil ) } }
3. Tauri主应用接收并处理请求
在Rust代码中,监听扩展发送的分布式通知,读取共享的文件路径,然后执行更换壁纸的逻辑:
3.1 监听系统通知
用objc crate绑定Mac的分布式通知中心(需要在Cargo.toml中添加objc = "0.2"):
use objc::runtime::{Object, Sel}; use objc::{msg_send, sel, sel_impl}; use tauri::Manager; fn listen_for_wallpaper_notification(app: tauri::AppHandle) { // 创建通知观察者 let center = unsafe { msg_send![class!(NSDistributedNotificationCenter), defaultCenter] }; let observer: *mut Object = unsafe { msg_send![center, addObserverForName: ns_string!("com.yourdomain.app.SetWallpaper") object: nil queue: nil usingBlock: block!({ // 读取共享UserDefaults中的路径 let shared_defaults = unsafe { msg_send![class!(NSUserDefaults), alloc] }; let shared_defaults = unsafe { msg_send![shared_defaults, initWithSuiteName: ns_string!("com.yourdomain.app.shared")] }; let paths: Vec<String> = unsafe { msg_send![shared_defaults, objectForKey: ns_string!("SelectedWallpaperPaths")] }; // 执行更换壁纸逻辑 if let Some(path) = paths.first() { set_wallpaper(&path); } })] }; // 把观察者存到AppHandle的全局数据中,防止被释放 app.state().insert(observer); } // 辅助函数:把Rust字符串转成NSString fn ns_string(s: &str) -> *mut Object { unsafe { let str = msg_send![class!(NSString), stringWithUTF8String: s.as_ptr()]; str } }
3.2 实现更换壁纸逻辑
可以通过调用Mac的osascript命令或者直接调用系统API,这里用简单的命令行方式:
use std::process::Command; fn set_wallpaper(path: &str) { let script = format!("tell application \"Finder\" to set desktop picture to POSIX file \"{}\"", path); if let Err(e) = Command::new("osascript") .arg("-e") .arg(&script) .output() { eprintln!("Failed to set wallpaper: {}", e); } }
3.3 在Tauri启动时注册监听
在main.rs的setup方法中启动通知监听:
fn main() { tauri::Builder::default() .setup(|app| { listen_for_wallpaper_notification(app.handle()); Ok(()) }) .run(tauri::generate_context!()) .expect("Failed to run Tauri app"); }
4. 配置权限与打包
- 在扩展的
Info.plist中添加文件访问权限描述,比如NSDesktopFolderUsageDescription(说明为什么需要访问桌面文件) - 打包时,Xcode的Archive流程会自动把扩展包含在App Bundle中,确保签名一致
- 测试时,需要在系统偏好设置 > 扩展 > Finder扩展中启用你的扩展
内容的提问来源于stack exchange,提问作者Nane
相关产品推荐
相关产品推荐

