Describe the problem
A plugin author cannot get a value set on their own Rust Builder into their Android or iOS plugin. The only configuration a native plugin receives comes from the app's tauri.conf.json.
Plugin::initialize builds the PluginApi from the config Tauri read out of plugins.<name>, and register_android_plugin / register_ios_plugin forward that same raw_config to the native load():
// tauri/src/plugin.rs:799
raw_config: Arc::new(config.clone()),
// tauri/src/plugin/mobile.rs:172 and :262
let config = self.raw_config.clone();
raw_config is private, only ever assigned in initialize, and plugin::Builder exposes no method to contribute to it. The path is closed by construction.
This matters because a Rust builder is the idiomatic way to configure a Tauri plugin — tauri-plugin-log, tauri-plugin-updater, tauri-plugin-store and tauri-plugin-window-state all use one — but the pattern stops working the moment a plugin has a native mobile implementation. And tauri.conf.json can only hold a literal, so a value like concat!("my-app/", env!("CARGO_PKG_VERSION")), or one read from an environment variable or a Cargo feature, cannot be expressed there at all.
It looks like nobody has solved this so much as avoided it: across plugins-workspace, opener is the only plugin with both a plugin-owned Rust Builder and a native mobile layer, and its builder field never crosses the bridge.
Describe the solution you'd like
Additive methods on PluginApi that take the config at the registration call site:
let handle = api.register_android_plugin_with_config(
"org.example.plugin", "ExamplePlugin",
serde_json::json!({ "userAgent": user_agent }),
)?;
- No native-side change. Android's
Plugin.getConfig(Class) and Swift's parseConfig(_:) already consume this exact JSON string.
- No merge policy to invent. A
Builder::default_config() merged with tauri.conf.json raises questions with no obviously right answer — which side wins, and is the merge deep or shallow. At the registration site the author already has api.config() in scope and can merge however the plugin wants.
- Purely additive. Existing
register_*_plugin behaviour is unchanged.
A Builder::mobile_config(impl Serialize) populating raw_config before registration would work too, and reads better at the definition site. Either is fine; merge semantics are the only reason to prefer the explicit one.
Alternatives considered
Push the value with a plugin command after registration — the current workaround. Define a native configure command and invoke it from setup() right after register_*_plugin, via run_mobile_plugin.
It works, and the ordering is safe: registration blocks until the native plugin is loaded, and setup() runs before the event loop exists, so it lands before the webview can invoke anything. run_mobile_plugin also bypasses the ACL, so the command needs no build.rs COMMANDS entry or permission and stays unreachable from JS. The costs:
- The same boilerplate command in Kotlin and Swift for every plugin that needs it.
- A failed push propagates out of
setup(), aborting plugin initialisation — and with the .expect(...) every documented bootstrap uses, panicking the app at launch. That is harsh for what may be a cosmetic setting, with nowhere obvious to degrade gracefully.
- It is undiscoverable. Nothing in the plugin-development docs suggests it and no official plugin demonstrates it, so each author rediscovers the limitation from the Tauri source and reinvents the same fix.
Require tauri.conf.json — what the ecosystem does today. Rules out computed values, and splits a plugin's configuration across two places when it also has Rust-side options.
Keep the implementation in Rust and skip the native plugin — how tauri-plugin-http sidesteps the problem entirely, with no android/ or ios/ directory. Not available to plugins that exist precisely because they need platform APIs.
Describe the problem
A plugin author cannot get a value set on their own Rust
Builderinto their Android or iOS plugin. The only configuration a native plugin receives comes from the app'stauri.conf.json.Plugin::initializebuilds thePluginApifrom the config Tauri read out ofplugins.<name>, andregister_android_plugin/register_ios_pluginforward that sameraw_configto the nativeload():raw_configis private, only ever assigned ininitialize, andplugin::Builderexposes no method to contribute to it. The path is closed by construction.This matters because a Rust builder is the idiomatic way to configure a Tauri plugin —
tauri-plugin-log,tauri-plugin-updater,tauri-plugin-storeandtauri-plugin-window-stateall use one — but the pattern stops working the moment a plugin has a native mobile implementation. Andtauri.conf.jsoncan only hold a literal, so a value likeconcat!("my-app/", env!("CARGO_PKG_VERSION")), or one read from an environment variable or a Cargo feature, cannot be expressed there at all.It looks like nobody has solved this so much as avoided it: across
plugins-workspace,openeris the only plugin with both a plugin-owned RustBuilderand a native mobile layer, and its builder field never crosses the bridge.Describe the solution you'd like
Additive methods on
PluginApithat take the config at the registration call site:Plugin.getConfig(Class)and Swift'sparseConfig(_:)already consume this exact JSON string.Builder::default_config()merged withtauri.conf.jsonraises questions with no obviously right answer — which side wins, and is the merge deep or shallow. At the registration site the author already hasapi.config()in scope and can merge however the plugin wants.register_*_pluginbehaviour is unchanged.A
Builder::mobile_config(impl Serialize)populatingraw_configbefore registration would work too, and reads better at the definition site. Either is fine; merge semantics are the only reason to prefer the explicit one.Alternatives considered
Push the value with a plugin command after registration — the current workaround. Define a native
configurecommand and invoke it fromsetup()right afterregister_*_plugin, viarun_mobile_plugin.It works, and the ordering is safe: registration blocks until the native plugin is loaded, and
setup()runs before the event loop exists, so it lands before the webview can invoke anything.run_mobile_pluginalso bypasses the ACL, so the command needs nobuild.rsCOMMANDSentry or permission and stays unreachable from JS. The costs:setup(), aborting plugin initialisation — and with the.expect(...)every documented bootstrap uses, panicking the app at launch. That is harsh for what may be a cosmetic setting, with nowhere obvious to degrade gracefully.Require
tauri.conf.json— what the ecosystem does today. Rules out computed values, and splits a plugin's configuration across two places when it also has Rust-side options.Keep the implementation in Rust and skip the native plugin — how
tauri-plugin-httpsidesteps the problem entirely, with noandroid/orios/directory. Not available to plugins that exist precisely because they need platform APIs.