Using it from Rust
The plugin creates one LicenseManager per app. Reach it from anything that implements Tauri’s Manager, an App, an AppHandle, a Window or a Webview, with license():
use tauri_plugin_licensemanager::{LicenseState, LicensemanagerExt};
let state = app.license().state(); // LicenseState::Activelicense() panics when the plugin isn’t registered: register it before calling it, like in setup.
Gating commands
What must stay locked without a license belongs in Rust, where the customer can’t change it. Check the state at the start of each command that needs a license:
use tauri_plugin_licensemanager::{LicenseState, LicensemanagerExt};
#[tauri::command]
fn export_pdf(app: tauri::AppHandle, path: String) -> Result<(), String> {
if app.license().state() != LicenseState::Active {
return Err("This feature needs an active license.".into());
}
// …
Ok(())
}state() is cheap: it reads the state in memory, without a request. Check it on every call rather than caching it, so a license refused in the background locks the feature right away.
Waiting for startup
ready() resolves with the state once the startup validation is done, or right away with ACTIVE when the stored token is valid. To decide something on launch, like which window to open, wait for it off the main thread:
use tauri_plugin_licensemanager::{LicenseState, LicensemanagerExt};
tauri::Builder::default()
.plugin(tauri_plugin_licensemanager::Builder::new(PRODUCT_ID, PUBLIC_KEY).build())
.setup(|app| {
let handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
let state = handle.license().ready().await;
let page = if state == LicenseState::Active { "index.html" } else { "activate.html" };
let _ = tauri::WebviewWindowBuilder::new(&handle, "main", tauri::WebviewUrl::App(page.into()))
.build();
});
Ok(())
})Most apps don’t need it: one window that renders the right screen with watchLicense is simpler.
Reacting to changes
subscribe() returns a tokio::sync::watch::Receiver, which holds the current state and wakes on every change:
let handle = app.handle().clone();
let mut states = handle.license().subscribe();
tauri::async_runtime::spawn(async move {
while states.changed().await.is_ok() {
let state = *states.borrow();
update_menu(&handle, state == LicenseState::Active);
}
});Or register a callback with on_state_change, called on each change, not with the current state. It returns a Subscription: the callback is removed when it’s dropped, so keep it as long as you need it. A callback that panics doesn’t break the SDK or the other callbacks.
Your windows don’t need any of this: the plugin already emits each change to them. See listening to changes.
Activating and refreshing
Both are async, and available from Rust as well as from your webview:
use tauri_plugin_licensemanager::{LicenseErrorCode, LicensemanagerExt};
match app.license().set_license_key(&key).await {
Ok(state) => println!("{state:?}"), // LicenseState::Active
Err(error) if error.code == LicenseErrorCode::NotFound => show_error("Unknown key"),
Err(error) => show_error(&error.message),
}
let state = app.license().refresh().await; // never failsSee activating a license and refresh().
The LicenseManager
| Method | Returns |
|---|---|
state() | The current LicenseState. |
ready().await | The state on startup. |
set_license_key(&str).await | Result<LicenseState, LicenseError>. |
refresh().await | The new LicenseState. |
license_key() | Option<String>: the stored key. |
offline_state() | OfflineState. See offline state. |
latest_allowed_version() | LatestAllowedVersion. |
portal_url() | The link to the customer portal. |
fingerprint().await | This computer’s fingerprint. |
is_clock_tampered() | bool. |
on_state_change(callback) | A Subscription. |
subscribe() | A watch::Receiver<LicenseState>. |
dispose() | Stops the renewals and the clock check. The plugin calls it when the app exits. |
LicenseManager is cheap to clone: clones share the same state. Clone it to move it into a task, or call license() again on a cloned AppHandle. See the API reference.
The licensing logic doesn’t depend on Tauri, but it’s only published as the plugin for now. If you’d like to use it in a Rust app without Tauri, write to us.