Prechádzať zdrojové kódy

feat(mobile): add plugin config to the Plugin class (#6763)

Lucas Fernandes Nogueira 2 rokov pred
rodič
commit
dfa407ffcb

+ 5 - 0
.changes/mobile-plugin-config.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Expose plugin configuration on the Android and iOS plugin classes.

+ 5 - 0
.changes/simplify-ios-plugin-init-fn.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Change iOS plugin init function signature to `func init_plugin() -> Plugin`.

+ 5 - 1
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<String, PermissionState?> = getPermissionStates()
     if (permissionsResult.isEmpty()) {
       // if no permissions are defined on the plugin, resolve undefined

+ 1 - 1
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<String, CommandData> = HashMap()
   private val permissionCallbackMethods: HashMap<String, Method> = HashMap()
   private val startActivityCallbackMethods: HashMap<String, Method> = HashMap()

+ 2 - 2
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)

+ 5 - 0
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) {}
 

+ 8 - 5
core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift

@@ -46,7 +46,8 @@ public class PluginManager {
 		}
 	}
 
-	func load<P: Plugin>(webview: WKWebView?, name: String, plugin: P) {
+	func load<P: Plugin>(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<P: Plugin>(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
 	)
 }
 

+ 11 - 5
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];
     }

+ 4 - 4
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<JValue<'a>, JniError> {
-  if let JsonValue::Object(_) = &json {
+  if let JsonValue::Object(_) = json {
     json_to_java::<R>(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

+ 1 - 1
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")]

+ 3 - 1
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<R: Runtime> PluginHandle<R> {
 pub struct PluginApi<R: Runtime, C: DeserializeOwned> {
   handle: AppHandle<R>,
   name: &'static str,
+  raw_config: Arc<JsonValue>,
   config: C,
 }
 
@@ -467,6 +468,7 @@ impl<R: Runtime, C: DeserializeOwned> Plugin<R> for TauriPlugin<R, C> {
         PluginApi {
           name: self.name,
           handle: app.clone(),
+          raw_config: Arc::new(config.clone()),
           config: serde_json::from_value(config)?,
         },
       )?;

+ 26 - 7
core/tauri/src/plugin/mobile.rs

@@ -118,20 +118,35 @@ impl<R: Runtime, C: DeserializeOwned> PluginApi<R, C> {
   #[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<PluginHandle<R>, 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<R: Runtime, C: DeserializeOwned> PluginApi<R, C> {
       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<R: Runtime, C: DeserializeOwned> PluginApi<R, C> {
       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::<R>(env, activity, runtime_handle, plugin_config)?
         ],
       )?;
 
@@ -190,6 +207,7 @@ impl<R: Runtime, C: DeserializeOwned> PluginApi<R, C> {
 
     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<R: Runtime, C: DeserializeOwned> PluginApi<R, C> {
           &runtime_handle,
           plugin_name,
           plugin_class,
+          &plugin_config,
         );
         tx.send(result).unwrap();
       });
@@ -288,7 +307,7 @@ impl<R: Runtime> PluginHandle<R> {
         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<R: Runtime> PluginHandle<R> {
       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<R: Runtime> PluginHandle<R> {
       );
 
     handle.run_on_android_context(move |env, activity, _webview| {
-      if let Err(e) = run::<R>(id, plugin_name, method, payload, &handle_, env, activity) {
+      if let Err(e) = run::<R>(id, plugin_name, method, &payload, &handle_, env, activity) {
         tx_.send(Err(e)).unwrap();
       }
     });

+ 2 - 2
core/tauri/src/window.rs

@@ -1509,7 +1509,7 @@ impl<R: Runtime> Window<R> {
                     &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<R: Runtime> Window<R> {
                     activity: JObject<'_>,
                     webview: JObject<'_>,
                   ) -> Result<(), JniError> {
-                    let data = crate::jni_helpers::to_jsobject::<R>(env, activity, runtime_handle, message.payload)?;
+                    let data = crate::jni_helpers::to_jsobject::<R>(env, activity, runtime_handle, &message.payload)?;
                     let plugin_manager = env
                       .call_method(
                         activity,

+ 2 - 2
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()
 }

+ 2 - 4
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<R: Runtime>() -> TauriPlugin<R> {{
   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()

+ 2 - 2
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()
 }