Skip to content

Commit

Permalink
feat: initial work for iOS plugins (#6205)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Feb 11, 2023
1 parent f379e2f commit 05dad08
Show file tree
Hide file tree
Showing 51 changed files with 1,072 additions and 69 deletions.
6 changes: 6 additions & 0 deletions .changes/android-apis-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri-runtime": minor
"tauri-runtime-wry": minor
---

Add `find_class`, `run_on_android_context` on `RuntimeHandle`.
6 changes: 6 additions & 0 deletions .changes/cli-mobile-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"cli.rs": minor
"cli.js": minor
---

Add commands to add native Android and iOS functionality to plugins.
7 changes: 7 additions & 0 deletions .changes/invoke-return-bool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tauri-macros": major
"tauri-codegen": major
"tauri": major
---

Return `bool` in the invoke handler.
5 changes: 5 additions & 0 deletions .changes/mobile-plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": minor
---

Run Android and iOS native plugins on the invoke handler if a Rust plugin command is not found.
5 changes: 5 additions & 0 deletions .changes/plugin-init-fns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": minor
---

Added `initialize_android_plugin` and `initialize_ios_plugin` APIs on `AppHandle`.
5 changes: 5 additions & 0 deletions .changes/tauri-build-mobile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri-build": minor
---

Add `mobile::PluginBuilder` for running build tasks related to Tauri plugins.
7 changes: 7 additions & 0 deletions .changes/with-webview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tauri": minor
"tauri-runtime": minor
"tauri-runtime-wry": minor
---

Implemented `with_webview` on Android and iOS.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,7 @@ test_video.mp4
# old cli directories
/tooling/cli.js
/tooling/cli.rs

# Swift
Package.resolved
.build
28 changes: 28 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// swift-tools-version:5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "TauriWorkspace",
products: [
.library(name: "Tauri", targets: ["Tauri"]),
],
dependencies: [
.package(url: "https://github.com/Brendonovich/swift-rs", revision: "eb6de914ad57501da5019154d476d45660559999"),
],
targets: [
.target(
name: "Tauri",
dependencies: [
.product(name: "SwiftRs", package: "swift-rs"),
],
path: "core/tauri/ios/Sources/Tauri"
),
.testTarget(
name: "TauriTests",
dependencies: ["Tauri"],
path: "core/tauri/ios/Tests/TauriTests"
),
]
)
3 changes: 3 additions & 0 deletions core/tauri-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ heck = "0.4"
json-patch = "0.2"
walkdir = "2"

[target."cfg(target_os = \"macos\")".dependencies]
swift-rs = { git = "https://github.com/Brendonovich/swift-rs", rev = "eb6de914ad57501da5019154d476d45660559999", features = ["build"] }

[target."cfg(windows)".dependencies]
winres = "0.1"
semver = "1"
Expand Down
87 changes: 57 additions & 30 deletions core/tauri-build/src/mobile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use anyhow::Result;
#[derive(Default)]
pub struct PluginBuilder {
android_path: Option<PathBuf>,
ios_path: Option<PathBuf>,
}

impl PluginBuilder {
Expand All @@ -23,53 +24,79 @@ impl PluginBuilder {
self
}

/// Sets the iOS project path.
pub fn ios_path<P: Into<PathBuf>>(mut self, ios_path: P) -> Self {
self.ios_path.replace(ios_path.into());
self
}

/// Injects the mobile templates in the given path relative to the manifest root.
pub fn run(self) -> Result<()> {
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os == "android" {
if let Some(path) = self.android_path {
let manifest_dir = var("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap();
if let Ok(project_dir) = var("TAURI_ANDROID_PROJECT_PATH") {
let source = manifest_dir.join(path);
let pkg_name = var("CARGO_PKG_NAME").unwrap();
match target_os.as_str() {
"android" => {
if let Some(path) = self.android_path {
let manifest_dir = var("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap();
if let Ok(project_dir) = var("TAURI_ANDROID_PROJECT_PATH") {
let source = manifest_dir.join(path);
let pkg_name = var("CARGO_PKG_NAME").unwrap();

println!("cargo:rerun-if-env-changed=TAURI_ANDROID_PROJECT_PATH");
println!("cargo:rerun-if-env-changed=TAURI_ANDROID_PROJECT_PATH");

let project_dir = PathBuf::from(project_dir);
let project_dir = PathBuf::from(project_dir);

inject_android_project(source, project_dir.join("tauri-plugins").join(&pkg_name))?;
inject_android_project(source, project_dir.join("tauri-plugins").join(&pkg_name))?;

let gradle_settings_path = project_dir.join("tauri.settings.gradle");
let gradle_settings = fs::read_to_string(&gradle_settings_path)?;
let include = format!(
"include ':{pkg_name}'
let gradle_settings_path = project_dir.join("tauri.settings.gradle");
let gradle_settings = fs::read_to_string(&gradle_settings_path)?;
let include = format!(
"include ':{pkg_name}'
project(':{pkg_name}').projectDir = new File('./tauri-plugins/{pkg_name}')"
);
if !gradle_settings.contains(&include) {
fs::write(
&gradle_settings_path,
format!("{gradle_settings}\n{include}"),
)?;
}

let app_build_gradle_path = project_dir.join("app").join("tauri.build.gradle.kts");
let app_build_gradle = fs::read_to_string(&app_build_gradle_path)?;
let implementation = format!(r#"implementation(project(":{pkg_name}"))"#);
let target = "dependencies {";
if !app_build_gradle.contains(&implementation) {
fs::write(
&app_build_gradle_path,
app_build_gradle.replace(target, &format!("{target}\n {implementation}")),
)?
);
if !gradle_settings.contains(&include) {
fs::write(
&gradle_settings_path,
format!("{gradle_settings}\n{include}"),
)?;
}

let app_build_gradle_path = project_dir.join("app").join("tauri.build.gradle.kts");
let app_build_gradle = fs::read_to_string(&app_build_gradle_path)?;
let implementation = format!(r#"implementation(project(":{pkg_name}"))"#);
let target = "dependencies {";
if !app_build_gradle.contains(&implementation) {
fs::write(
&app_build_gradle_path,
app_build_gradle.replace(target, &format!("{target}\n {implementation}")),
)?
}
}
}
}
#[cfg(target_os = "macos")]
"ios" => {
if let Some(path) = self.ios_path {
link_swift_library(&std::env::var("CARGO_PKG_NAME").unwrap(), path);
}
}
_ => (),
}

Ok(())
}
}

#[cfg(target_os = "macos")]
#[doc(hidden)]
pub fn link_swift_library(name: &str, source: impl AsRef<Path>) {
let source = source.as_ref();
println!("cargo:rerun-if-changed={}", source.display());
swift_rs::build::SwiftLinker::new("10.13")
.with_ios("11")
.with_package(name, source)
.link();
}

#[doc(hidden)]
pub fn inject_android_project(source: impl AsRef<Path>, target: impl AsRef<Path>) -> Result<()> {
let source = source.as_ref();
Expand Down
13 changes: 8 additions & 5 deletions core/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,7 @@ type FileDropHandler = dyn Fn(&Window, WryFileDropEvent) -> bool + 'static;
#[cfg(all(desktop, feature = "system-tray"))]
pub use tauri_runtime::TrayId;

#[cfg(any(desktop, target_os = "android"))]
mod webview;
#[cfg(any(desktop, target_os = "android"))]
pub use webview::Webview;

#[cfg(all(desktop, feature = "system-tray"))]
Expand Down Expand Up @@ -1028,7 +1026,6 @@ pub enum ApplicationMessage {
}

pub enum WindowMessage {
#[cfg(any(desktop, target_os = "android"))]
WithWebview(Box<dyn FnOnce(Webview) + Send>),
AddEventListener(Uuid, Box<dyn Fn(&WindowEvent) + Send>),
AddMenuEventListener(Uuid, Box<dyn Fn(&MenuEvent) + Send>),
Expand Down Expand Up @@ -1205,7 +1202,6 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
id
}

#[cfg(any(desktop, target_os = "android"))]
fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> {
send_user_message(
&self.context,
Expand Down Expand Up @@ -2305,7 +2301,6 @@ fn handle_user_message<T: UserEvent>(
});
if let Some((Some(window), window_event_listeners, menu_event_listeners)) = w {
match window_message {
#[cfg(any(target_os = "android", desktop))]
WindowMessage::WithWebview(f) => {
if let WindowHandle::Webview { inner: w, .. } = &window {
#[cfg(any(
Expand All @@ -2328,6 +2323,14 @@ fn handle_user_message<T: UserEvent>(
ns_window: w.ns_window(),
});
}
#[cfg(target_os = "ios")]
{
use wry::webview::WebviewExtIOS;
f(Webview {
webview: w.webview(),
manager: w.manager(),
});
}
#[cfg(windows)]
{
f(Webview {
Expand Down
10 changes: 10 additions & 0 deletions core/tauri-runtime-wry/src/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ mod imp {
}
}

#[cfg(target_os = "ios")]
mod imp {
use cocoa::base::id;

pub struct Webview {
pub webview: id,
pub manager: id,
}
}

#[cfg(windows)]
mod imp {
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
Expand Down
2 changes: 1 addition & 1 deletion core/tauri-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ pub trait Dispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'static
/// Registers a window event handler.
fn on_menu_event<F: Fn(&window::MenuEvent) + Send + 'static>(&self, f: F) -> Uuid;

#[cfg(any(desktop, target_os = "android"))]
/// Runs a closure with the platform webview object as argument.
fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()>;

/// Open the web inspector which is usually called devtools.
Expand Down
3 changes: 3 additions & 0 deletions core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ jni = "0.20"
[target."cfg(target_os = \"ios\")".dependencies]
log = "0.4"
libc = "0.2"
objc = "0.2"
cocoa = "0.24"
swift-rs = { git = "https://github.com/Brendonovich/swift-rs", rev = "eb6de914ad57501da5019154d476d45660559999" }

[build-dependencies]
heck = "0.4"
Expand Down
7 changes: 7 additions & 0 deletions core/tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ fn main() {
.expect("failed to copy tauri-api Android project");
}
}

#[cfg(target_os = "macos")]
{
if target_os == "ios" {
tauri_build::mobile::link_swift_library("Tauri", "./mobile/ios-api");
}
}
}

// create aliases for the given module with its apis.
Expand Down
9 changes: 9 additions & 0 deletions core/tauri/mobile/ios-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
34 changes: 34 additions & 0 deletions core/tauri/mobile/ios-api/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "Tauri",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "Tauri",
type: .static,
targets: ["Tauri"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/Brendonovich/swift-rs", revision: "eb6de914ad57501da5019154d476d45660559999"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "Tauri",
dependencies: [
.product(name: "SwiftRs", package: "swift-rs"),
],
path: "Sources"
),
.testTarget(
name: "TauriTests",
dependencies: ["Tauri"]
),
]
)
3 changes: 3 additions & 0 deletions core/tauri/mobile/ios-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Tauri

Tauri iOS API.
Loading

0 comments on commit 05dad08

Please sign in to comment.