Skip to content

[feat] Let a plugin's Rust builder pass configuration to its Android/iOS plugin #15925

Description

@velocitysystems

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions