From dfa407ffcbc8a853d61139b68b55747ae49fb231 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 23 Apr 2023 05:15:13 -0700 Subject: [PATCH] feat(mobile): add plugin config to the Plugin class (#6763) --- .changes/mobile-plugin-config.md | 5 +++ .changes/simplify-ios-plugin-init-fn.md | 5 +++ .../src/main/java/app/tauri/plugin/Plugin.kt | 6 +++- .../java/app/tauri/plugin/PluginHandle.kt | 2 +- .../java/app/tauri/plugin/PluginManager.kt | 4 +-- .../ios-api/Sources/Tauri/Plugin/Plugin.swift | 5 +++ .../mobile/ios-api/Sources/Tauri/Tauri.swift | 13 +++++--- core/tauri/src/ios.rs | 16 ++++++--- core/tauri/src/jni_helpers.rs | 8 ++--- core/tauri/src/lib.rs | 2 +- core/tauri/src/plugin.rs | 4 ++- core/tauri/src/plugin/mobile.rs | 33 +++++++++++++++---- core/tauri/src/window.rs | 4 +-- .../ios/Sources/ExamplePlugin.swift | 4 +-- tooling/cli/src/plugin/ios.rs | 6 ++-- .../plugin/ios/Sources/ExamplePlugin.swift | 4 +-- 16 files changed, 84 insertions(+), 37 deletions(-) create mode 100644 .changes/mobile-plugin-config.md create mode 100644 .changes/simplify-ios-plugin-init-fn.md diff --git a/.changes/mobile-plugin-config.md b/.changes/mobile-plugin-config.md new file mode 100644 index 00000000000..593262cb312 --- /dev/null +++ b/.changes/mobile-plugin-config.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Expose plugin configuration on the Android and iOS plugin classes. diff --git a/.changes/simplify-ios-plugin-init-fn.md b/.changes/simplify-ios-plugin-init-fn.md new file mode 100644 index 00000000000..f6afed50b9a --- /dev/null +++ b/.changes/simplify-ios-plugin-init-fn.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Change iOS plugin init function signature to `func init_plugin() -> Plugin`. diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt index e2f1fa75ef5..560e39f7cb5 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt @@ -26,6 +26,10 @@ abstract class Plugin(private val activity: Activity) { open fun load(webView: WebView) {} + fun getConfig(): JSObject { + return handle!!.config + } + /** * Start activity for result with the provided Intent and resolve calling the provided callback method name. * @@ -74,7 +78,7 @@ abstract class Plugin(private val activity: Activity) { */ @Command @PermissionCallback - fun checkPermissions(invoke: Invoke) { + open fun checkPermissions(invoke: Invoke) { val permissionsResult: Map = getPermissionStates() if (permissionsResult.isEmpty()) { // if no permissions are defined on the plugin, resolve undefined diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt index 97b498f18cd..c8d1c2a7c1b 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt @@ -17,7 +17,7 @@ import app.tauri.annotation.Command import app.tauri.annotation.TauriPlugin import java.lang.reflect.Method -class PluginHandle(private val manager: PluginManager, val name: String, private val instance: Plugin) { +class PluginHandle(private val manager: PluginManager, val name: String, private val instance: Plugin, val config: JSObject) { private val commands: HashMap = HashMap() private val permissionCallbackMethods: HashMap = HashMap() private val startActivityCallbackMethods: HashMap = HashMap() diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt index d79a8cb2f54..77fd8599295 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt @@ -69,8 +69,8 @@ class PluginManager(val activity: AppCompatActivity) { } @JniMethod - fun load(webView: WebView?, name: String, plugin: Plugin) { - val handle = PluginHandle(this, name, plugin) + fun load(webView: WebView?, name: String, plugin: Plugin, config: JSObject) { + val handle = PluginHandle(this, name, plugin, config) plugins[name] = handle if (webView != null) { plugin.load(webView) diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift index e7d49173244..693edb5f708 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift @@ -7,6 +7,11 @@ import os.log open class Plugin: NSObject { public let manager: PluginManager = PluginManager.shared + public var config: JSObject = [:] + + internal func setConfig(_ config: JSObject) { + self.config = config + } @objc open func load(webview: WKWebView) {} diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift index 205b6eff54e..9f14f640fbf 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift @@ -46,7 +46,8 @@ public class PluginManager { } } - func load(webview: WKWebView?, name: String, plugin: P) { + func load(name: String, plugin: P, config: JSObject, webview: WKWebView?) { + plugin.setConfig(config) let handle = PluginHandle(plugin: plugin) if let webview = webview { handle.instance.load(webview: webview) @@ -91,11 +92,13 @@ extension PluginManager: NSCopying { } } -public func registerPlugin(webview: WKWebView?, name: String, plugin: P) { +@_cdecl("register_plugin") +func registerPlugin(name: SRString, plugin: NSObject, config: NSDictionary, webview: WKWebView?) { PluginManager.shared.load( - webview: webview, - name: name, - plugin: plugin + name: name.toString(), + plugin: plugin as! Plugin, + config: JSTypes.coerceDictionaryToJSObject(config, formattingDatesAsStrings: true)!, + webview: webview ) } diff --git a/core/tauri/src/ios.rs b/core/tauri/src/ios.rs index 1796d73889b..5b9af9ec3d8 100644 --- a/core/tauri/src/ios.rs +++ b/core/tauri/src/ios.rs @@ -38,9 +38,15 @@ swift!(pub fn run_plugin_method( data: *const c_void, callback: PluginMessageCallback )); +swift!(pub fn register_plugin( + name: &SRString, + plugin: *const c_void, + config: *const c_void, + webview: *const c_void +)); swift!(pub fn on_webview_created(webview: *const c_void, controller: *const c_void)); -pub fn json_to_dictionary(json: JsonValue) -> id { +pub fn json_to_dictionary(json: &JsonValue) -> id { if let serde_json::Value::Object(map) = json { unsafe { let dictionary: id = msg_send![class!(NSMutableDictionary), alloc]; @@ -78,14 +84,14 @@ impl NSString { } } -unsafe fn add_json_value_to_array(array: id, value: JsonValue) { +unsafe fn add_json_value_to_array(array: id, value: &JsonValue) { match value { JsonValue::Null => { let null: id = msg_send![class!(NSNull), null]; let () = msg_send![array, addObject: null]; } JsonValue::Bool(val) => { - let value = if val { YES } else { NO }; + let value = if *val { YES } else { NO }; let v: id = msg_send![class!(NSNumber), numberWithBool: value]; let () = msg_send![array, addObject: v]; } @@ -123,7 +129,7 @@ unsafe fn add_json_value_to_array(array: id, value: JsonValue) { } } -unsafe fn add_json_entry_to_dictionary(data: id, key: String, value: JsonValue) { +unsafe fn add_json_entry_to_dictionary(data: id, key: &str, value: &JsonValue) { let key = NSString::new(&key); match value { JsonValue::Null => { @@ -131,7 +137,7 @@ unsafe fn add_json_entry_to_dictionary(data: id, key: String, value: JsonValue) let () = msg_send![data, setObject:null forKey: key]; } JsonValue::Bool(val) => { - let flag = if val { YES } else { NO }; + let flag = if *val { YES } else { NO }; let value: id = msg_send![class!(NSNumber), numberWithBool: flag]; let () = msg_send![data, setObject:value forKey: key]; } diff --git a/core/tauri/src/jni_helpers.rs b/core/tauri/src/jni_helpers.rs index 85c37cc8d6e..e6edb281077 100644 --- a/core/tauri/src/jni_helpers.rs +++ b/core/tauri/src/jni_helpers.rs @@ -15,11 +15,11 @@ fn json_to_java<'a, R: Runtime>( env: JNIEnv<'a>, activity: JObject<'a>, runtime_handle: &R::Handle, - json: JsonValue, + json: &JsonValue, ) -> Result<(&'static str, JValue<'a>), JniError> { let (class, v) = match json { JsonValue::Null => ("Ljava/lang/Object;", JObject::null().into()), - JsonValue::Bool(val) => ("Z", val.into()), + JsonValue::Bool(val) => ("Z", (*val).into()), JsonValue::Number(val) => { if let Some(v) = val.as_i64() { ("J", v.into()) @@ -74,9 +74,9 @@ pub fn to_jsobject<'a, R: Runtime>( env: JNIEnv<'a>, activity: JObject<'a>, runtime_handle: &R::Handle, - json: JsonValue, + json: &JsonValue, ) -> Result, JniError> { - if let JsonValue::Object(_) = &json { + if let JsonValue::Object(_) = json { json_to_java::(env, activity, runtime_handle, json).map(|(_class, data)| data) } else { // currently the Kotlin lib cannot handle nulls or raw values, it must be an object diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index ede4a5992c1..f631f96335b 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -157,7 +157,7 @@ #[macro_export] macro_rules! ios_plugin_binding { ($fn_name: ident) => { - tauri::swift_rs::swift!(fn $fn_name(name: &::tauri::swift_rs::SRString, webview: *const ::std::ffi::c_void)); + tauri::swift_rs::swift!(fn $fn_name() -> *const ::std::ffi::c_void); } } #[cfg(target_os = "ios")] diff --git a/core/tauri/src/plugin.rs b/core/tauri/src/plugin.rs index d0d10d600a4..c7a67913cec 100644 --- a/core/tauri/src/plugin.rs +++ b/core/tauri/src/plugin.rs @@ -12,7 +12,7 @@ use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; use tauri_macros::default_runtime; -use std::{collections::HashMap, fmt, result::Result as StdResult}; +use std::{collections::HashMap, fmt, result::Result as StdResult, sync::Arc}; /// Mobile APIs. #[cfg(mobile)] @@ -99,6 +99,7 @@ impl PluginHandle { pub struct PluginApi { handle: AppHandle, name: &'static str, + raw_config: Arc, config: C, } @@ -467,6 +468,7 @@ impl Plugin for TauriPlugin { PluginApi { name: self.name, handle: app.clone(), + raw_config: Arc::new(config.clone()), config: serde_json::from_value(config)?, }, )?; diff --git a/core/tauri/src/plugin/mobile.rs b/core/tauri/src/plugin/mobile.rs index fefa4dc1b25..614aaf9aac7 100644 --- a/core/tauri/src/plugin/mobile.rs +++ b/core/tauri/src/plugin/mobile.rs @@ -118,20 +118,35 @@ impl PluginApi { #[cfg(target_os = "ios")] pub fn register_ios_plugin( &self, - init_fn: unsafe fn(&swift_rs::SRString, *const std::ffi::c_void), + init_fn: unsafe fn() -> *const std::ffi::c_void, ) -> Result, PluginInvokeError> { if let Some(window) = self.handle.manager.windows().values().next() { let (tx, rx) = channel(); let name = self.name; + let config = self.raw_config.clone(); window .with_webview(move |w| { - unsafe { init_fn(&name.into(), w.inner() as _) }; + unsafe { + crate::ios::register_plugin( + &name.into(), + init_fn(), + crate::ios::json_to_dictionary(&config) as _, + w.inner() as _, + ) + }; tx.send(()).unwrap(); }) .map_err(|_| PluginInvokeError::UnreachableWebview)?; rx.recv().unwrap(); } else { - unsafe { init_fn(&self.name.into(), std::ptr::null()) }; + unsafe { + crate::ios::register_plugin( + &self.name.into(), + init_fn(), + crate::ios::json_to_dictionary(&self.raw_config) as _, + std::ptr::null(), + ) + }; } Ok(PluginHandle { name: self.name, @@ -155,6 +170,7 @@ impl PluginApi { runtime_handle: &R::Handle, plugin_name: &'static str, plugin_class: String, + plugin_config: &serde_json::Value, ) -> Result<(), JniError> { let plugin_manager = env .call_method( @@ -177,11 +193,12 @@ impl PluginApi { env.call_method( plugin_manager, "load", - "(Landroid/webkit/WebView;Ljava/lang/String;Lapp/tauri/plugin/Plugin;)V", + "(Landroid/webkit/WebView;Ljava/lang/String;Lapp/tauri/plugin/Plugin;Lapp/tauri/plugin/JSObject;)V", &[ webview.into(), env.new_string(plugin_name)?.into(), plugin.into(), + crate::jni_helpers::to_jsobject::(env, activity, runtime_handle, plugin_config)? ], )?; @@ -190,6 +207,7 @@ impl PluginApi { let plugin_class = format!("{}/{}", plugin_identifier.replace('.', "/"), class_name); let plugin_name = self.name; + let plugin_config = self.raw_config.clone(); let runtime_handle = self.handle.runtime_handle.clone(); let (tx, rx) = channel(); self @@ -203,6 +221,7 @@ impl PluginApi { &runtime_handle, plugin_name, plugin_class, + &plugin_config, ); tx.send(result).unwrap(); }); @@ -288,7 +307,7 @@ impl PluginHandle { id, &self.name.into(), &method.as_ref().into(), - crate::ios::json_to_dictionary(serde_json::to_value(payload).unwrap()) as _, + crate::ios::json_to_dictionary(&serde_json::to_value(payload).unwrap()) as _, crate::ios::PluginMessageCallback(plugin_method_response_handler), ); } @@ -317,7 +336,7 @@ impl PluginHandle { id: i32, plugin: &'static str, method: String, - payload: serde_json::Value, + payload: &serde_json::Value, runtime_handle: &R::Handle, env: JNIEnv<'_>, activity: JObject<'_>, @@ -373,7 +392,7 @@ impl PluginHandle { ); handle.run_on_android_context(move |env, activity, _webview| { - if let Err(e) = run::(id, plugin_name, method, payload, &handle_, env, activity) { + if let Err(e) = run::(id, plugin_name, method, &payload, &handle_, env, activity) { tx_.send(Err(e)).unwrap(); } }); diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 656d25ea186..ae74d499529 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -1509,7 +1509,7 @@ impl Window { &heck::ToLowerCamelCase::to_lower_camel_case(message.command.as_str()) .as_str() .into(), - crate::ios::json_to_dictionary(message.payload) as _, + crate::ios::json_to_dictionary(&message.payload) as _, callback.0, error.0, ) @@ -1543,7 +1543,7 @@ impl Window { activity: JObject<'_>, webview: JObject<'_>, ) -> Result<(), JniError> { - let data = crate::jni_helpers::to_jsobject::(env, activity, runtime_handle, message.payload)?; + let data = crate::jni_helpers::to_jsobject::(env, activity, runtime_handle, &message.payload)?; let plugin_manager = env .call_method( activity, diff --git a/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift b/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift index 3240e5c9b36..8837a5a7be8 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift +++ b/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift @@ -15,6 +15,6 @@ class ExamplePlugin: Plugin { } @_cdecl("init_plugin_sample") -func initPlugin(name: SRString, webview: WKWebView?) { - Tauri.registerPlugin(webview: webview, name: name.toString(), plugin: ExamplePlugin()) +func initPlugin() -> Plugin { + return ExamplePlugin() } diff --git a/tooling/cli/src/plugin/ios.rs b/tooling/cli/src/plugin/ios.rs index 0018a9938ae..b40c890d04b 100644 --- a/tooling/cli/src/plugin/ios.rs +++ b/tooling/cli/src/plugin/ios.rs @@ -99,15 +99,13 @@ tauri-build = "{}" let init_fn = format!( r#" #[cfg(target_os = "ios")] -extern "C" {{ - fn init_plugin_{name}(webview: tauri::cocoa::base::id); -}} +tauri::ios_plugin_binding!(init_plugin_{name}); pub fn init() -> TauriPlugin {{ Builder::new("{name}") .setup(|app| {{ #[cfg(target_os = "ios")] - app.initialize_ios_plugin(init_plugin_{name})?; + app.register_ios_plugin(init_plugin_{name})?; Ok(()) }}) .build() diff --git a/tooling/cli/templates/plugin/ios/Sources/ExamplePlugin.swift b/tooling/cli/templates/plugin/ios/Sources/ExamplePlugin.swift index 86140b74e3c..3923f512913 100644 --- a/tooling/cli/templates/plugin/ios/Sources/ExamplePlugin.swift +++ b/tooling/cli/templates/plugin/ios/Sources/ExamplePlugin.swift @@ -11,6 +11,6 @@ class ExamplePlugin: Plugin { } @_cdecl("init_plugin_{{ plugin_name_snake_case }}") -func initPlugin(name: SRString, webview: WKWebView?) { - Tauri.registerPlugin(webview: webview, name: name.toString(), plugin: ExamplePlugin()) +func initPlugin() -> Plugin { + return ExamplePlugin() }