Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from Apr 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/mobile-plugin-config.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Expose plugin configuration on the Android and iOS plugin classes.
5 changes: 5 additions & 0 deletions .changes/simplify-ios-plugin-init-fn.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Change iOS plugin init function signature to `func init_plugin() -> Plugin`.
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -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()
Expand Down
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift
Expand Up @@ -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) {}

Expand Down
13 changes: 8 additions & 5 deletions core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift
Expand Up @@ -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)
Expand Down Expand Up @@ -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
)
}

Expand Down
16 changes: 11 additions & 5 deletions core/tauri/src/ios.rs
Expand Up @@ -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];
Expand Down Expand Up @@ -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];
}
Expand Down Expand Up @@ -123,15 +129,15 @@ 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 => {
let null: id = msg_send![class!(NSNull), null];
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];
}
Expand Down
8 changes: 4 additions & 4 deletions core/tauri/src/jni_helpers.rs
Expand Up @@ -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())
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/src/lib.rs
Expand Up @@ -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")]
Expand Down
4 changes: 3 additions & 1 deletion core/tauri/src/plugin.rs
Expand Up @@ -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)]
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -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)?,
},
)?;
Expand Down
33 changes: 26 additions & 7 deletions core/tauri/src/plugin/mobile.rs
Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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)?
],
)?;

Expand All @@ -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
Expand All @@ -203,6 +221,7 @@ impl<R: Runtime, C: DeserializeOwned> PluginApi<R, C> {
&runtime_handle,
plugin_name,
plugin_class,
&plugin_config,
);
tx.send(result).unwrap();
});
Expand Down Expand Up @@ -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),
);
}
Expand Down Expand Up @@ -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<'_>,
Expand Down Expand Up @@ -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();
}
});
Expand Down
4 changes: 2 additions & 2 deletions core/tauri/src/window.rs
Expand Up @@ -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,
)
Expand Down Expand Up @@ -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,
Expand Down
Expand Up @@ -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()
}
6 changes: 2 additions & 4 deletions tooling/cli/src/plugin/ios.rs
Expand Up @@ -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()
Expand Down
Expand Up @@ -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()
}