最近,我的日历软件突然“跑路了”——对,有一天调休居然不显示,而且App Store 已经很久不更新了。不如自己动手,丰衣足食?于是我决定用 Tauri 来开发一个专属的 macOS 日历小助手。今天,就跟大家分享一下我的开发过程:如何用前端技术搭配 Tauri,做出一个轻量级、极速响应的日历应用。想自己开发一个日历助手?跟我一起来吧!
为什么要用 Tauri?
作为一个前端开发工程师,我虽然会些 Swift,但远没有前端技术那么熟练。而最近听说 Tauri 2.0 发布了,它的跨平台能力和极轻量的特性吸引了我。用 Tauri 开发 macOS 应用可以继续用我熟悉的前端栈(如 JavaScript、HTML、CSS),再加上 Tauri 2.0 对 Rust 和 API 的优化,我认为这是一个完美的机会尝试一下。
成品效果
先展示下成品效果
准备工作
在开始之前,需要确保你有以下开发工具:
-
Node.js:用于管理前端依赖。
-
Rust:Tauri 的后端部分是用 Rust 写的,所以需要安装 Rust。
-
包管理工具:这里我选择使用 pnpm 来管理项目依赖,你们可以使用Bun或者其他。
-
Xcode:构建 macOS 应用需要的开发环境。
第一步:初始化 Tauri 项目
输入命令pnpm create tauri-app快速创建项目,前端用 Vue 或 React 都行。这里选择用 Vite + Vue,创建一个简单的前端项目,然后加上 Tauri 的后端。
pnpm create tauri-app
.../19305833e3a-fda2 | +3 +
.../19305833e3a-fda2 | Progress: resolved 12, reused 0, downloaded 3, added 3, done
✔ Project name · calendar
✔ Identifier · com.example.calendar
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, deno, bun)
✔ Choose your package manager · pnpm
✔ Choose your UI template · Vue - (https://vuejs.org/)
✔ Choose your UI flavor · TypeScript
Template created! To get started run:
cd calendar
pnpm install
pnpm tauri android init
pnpm tauri ios init
For Desktop development, run:
pnpm tauri dev
For Android development, run:
pnpm tauri android dev
For iOS development, run:
pnpm tauri ios dev
第二步:创建前端界面
接下来就是大家熟悉的 Vue 3 开发流程:配置路由、创建组件和编写页面界面。这里我们的代码还未开源,具体的界面代码暂时不详细展示。
第三步:修改应用窗体风格,实现 macOS 原生弹窗效果
为了让应用更具 macOS 原生体验,我参考了我之前使用的日历软件,发现它使用了 macOS 系统的 NSPopover 作为弹窗。这种弹窗效果不仅美观,还能提供更好的用户体验。经过一番调研,我找到了一个名为 tauri-plugin-nspopover 的插件,它能够帮助我们实现与 NSPopover 类似的原生弹窗效果。
这里使用Rust或者TS/JS都行,由于我在学习Rust,所以下面就用Rust给大家演示
安装 tauri-plugin-nspopover
首先 ,我们需要在项目中安装 tauri-plugin-nspopover 插件。进入 src-tauri 目录,在 Cargo.toml 文件中添加这个插件:
[dependencies]
tauri = { version = "2.0.0", features = [ "macos-private-api", "tray-icon", "unstable"] }
tauri-plugin-nspopover = { git = "https://github.com/freethinkel/tauri-nspopover-plugin.git", branch = "tauri-beta/v2", version = "3.3.0" }
配置插件
// lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_nspopover::init())
.setup(|app| {
let window = app.handle().get_webview_window("main").unwrap();
window.to_popover();
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
第四步:注册系统任务栏托盘
由于NSPopover需要一个托盘来触发的她的显示和隐藏,我们去注册一个托盘以及它的托盘事件
在tauri.conf.json 配置托盘图标、id 等信息
//tauri.conf.json
{
……
"app": {
"trayIcon": {
"iconPath": "icons/tray.png",
"iconAsTemplate": true,
"id": "main"
}
},
"bundle": {
……
}
}
注册托盘事件
// lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_nspopover::init())
.setup(|app| {
let window = app.handle().get_webview_window("main").unwrap();
window.to_popover();
tray.on_tray_icon_event(move |_, event| {
if (!handle.is_popover_shown()) {
handle.show_popover();
} else {
handle.hide_popover();
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
第五步:隐藏Dock栏图标
由于这应用是常驻后台的,所以我不太想让它在Dock栏常驻,所以通过以下设置让他在Dock隐藏
// lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_nspopover::init())
.setup(|app| {
// 隐藏Dock栏
app.set_activation_policy(ActivationPolicy::Accessory);
let window = app.handle().get_webview_window("main").unwrap();
window.to_popover();
tray.on_tray_icon_event(move |_, event| {
if (!handle.is_popover_shown()) {
handle.show_popover();
} else {
handle.hide_popover();
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
第六步:打包上架
因为需要上架macOS,而macOS审核又比较严格,所以我需要补充一些权限的声明:
- 在
tauri.conf.json同级创建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>ITSAppUsesNonExemptEncryption</key>
<false/>
</dict>
</plist>
- 在
tauri.conf.json同级创建Entitlements.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>com.apple.security.app-sandbox</key>
<true/>
//必须不然会白屏
<key>com.apple.security.network.client</key>
<true/>
// 你用到的权限
……
//团队id+应用的id
<key>com.apple.application-identifier</key>
<string>${团队Id}.${identifier}</string>
//团队id
<key>com.apple.developer.team-identifier</key>
<string>${团队Id}</string>
</dict>
</plist>
- 修改
tauri.conf.json配置Info.plist和Entitlements.plist
{
……
"bundle": {
"macOS": {
"entitlements": "./Entitlements.plist",
},
……
}
}
- 修改
tauri.conf.json配置证书和签名
{
……
"bundle": {
"macOS": {
"entitlements": "./Entitlements.plist",
"signingIdentity":"Apple Distribution:你的公司或者团队名",
"files": {
"embedded.provisionprofile": "你的provisionprofile地址"
}
},
……
}
}
- 执行打包命令
sudo pnpm run tauri build --bundles app --target universal-apple-darwin
8. 签名打包pkg
xcrun productbuild --sign "3rd Party Mac Developer Installer:你的公司或者团队名" --component "你的文件地址" /Applications "文件名.pkg"
踩坑
因为签名是需要联网认证你的证书,所以需要检查一下本地的证书,是否过期。
最后
附上下载地址