Skip to content

Commit 8a1ae2d

Browse files
authored
feat(plugin): add option to use a Xcode project for iOS (#9843)
* wip * add option to use xcode project * configuration * clear env * add change file * remove xcuserdatad * delete xcuserstate
1 parent 51b5d58 commit 8a1ae2d

19 files changed

Lines changed: 512 additions & 41 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"tauri-cli": patch:feat
3+
"@tauri-apps/cli": patch:feat
4+
"tauri-plugin": patch:feat
5+
"tauri-utils": patch:feat
6+
"tauri": patch:feat
7+
---
8+
9+
Added an option to use a Xcode project for the iOS plugin instead of a plain SwiftPM project.

core/tauri-plugin/src/build/mobile.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub(crate) fn setup(
9090
&[".build", "Package.resolved", "Tests"],
9191
)
9292
.context("failed to copy tauri-api to the plugin project")?;
93-
tauri_utils::build::link_swift_library(
93+
tauri_utils::build::link_apple_library(
9494
&std::env::var("CARGO_PKG_NAME").unwrap(),
9595
manifest_dir.join(path),
9696
);

core/tauri-utils/src/build.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@
66
77
/// Link a Swift library.
88
#[cfg(target_os = "macos")]
9-
pub fn link_swift_library(name: &str, source: impl AsRef<std::path::Path>) {
9+
pub fn link_apple_library(name: &str, source: impl AsRef<std::path::Path>) {
10+
if source.as_ref().join("Package.swift").exists() {
11+
link_swift_library(name, source);
12+
} else {
13+
link_xcode_library(name, source);
14+
}
15+
}
16+
17+
/// Link a Swift library.
18+
#[cfg(target_os = "macos")]
19+
fn link_swift_library(name: &str, source: impl AsRef<std::path::Path>) {
1020
let source = source.as_ref();
1121

1222
let sdk_root = std::env::var_os("SDKROOT");
@@ -23,3 +33,59 @@ pub fn link_swift_library(name: &str, source: impl AsRef<std::path::Path>) {
2333
std::env::set_var("SDKROOT", root);
2434
}
2535
}
36+
37+
/// Link a Xcode library.
38+
#[cfg(target_os = "macos")]
39+
fn link_xcode_library(name: &str, source: impl AsRef<std::path::Path>) {
40+
use std::{path::PathBuf, process::Command};
41+
42+
let source = source.as_ref();
43+
let configuration = if std::env::var("DEBUG")
44+
.map(|v| v == "true")
45+
.unwrap_or_default()
46+
{
47+
"Debug"
48+
} else {
49+
"Release"
50+
};
51+
52+
let (sdk, arch) = match std::env::var("TARGET").unwrap().as_str() {
53+
"aarch64-apple-ios" => ("iphoneos", "arm64"),
54+
"aarch64-apple-ios-sim" => ("iphonesimulator", "arm64"),
55+
"x86_64-apple-ios" => ("iphonesimulator", "x86_64"),
56+
_ => return,
57+
};
58+
59+
let out_dir = std::env::var_os("OUT_DIR").map(PathBuf::from).unwrap();
60+
let derived_data_path = out_dir.join(format!("derivedData-{name}"));
61+
62+
let status = Command::new("xcodebuild")
63+
.arg("build")
64+
.arg("-scheme")
65+
.arg(name)
66+
.arg("-configuration")
67+
.arg(configuration)
68+
.arg("-sdk")
69+
.arg(sdk)
70+
.arg("-arch")
71+
.arg(arch)
72+
.arg("-derivedDataPath")
73+
.arg(&derived_data_path)
74+
.arg("BUILD_LIBRARY_FOR_DISTRIBUTION=YES")
75+
.arg("OTHER_SWIFT_FLAGS=-no-verify-emitted-module-interface")
76+
.current_dir(source)
77+
.env_clear()
78+
.status()
79+
.unwrap();
80+
81+
assert!(status.success());
82+
83+
let lib_out_dir = derived_data_path
84+
.join("Build")
85+
.join("Products")
86+
.join(format!("{configuration}-{sdk}"));
87+
88+
println!("cargo:rerun-if-changed={}", source.display());
89+
println!("cargo:rustc-link-search=native={}", lib_out_dir.display());
90+
println!("cargo:rustc-link-lib=static={name}");
91+
}

core/tauri/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ fn main() {
312312
if target_os == "ios" {
313313
let lib_path =
314314
PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("mobile/ios-api");
315-
tauri_utils::build::link_swift_library("Tauri", &lib_path);
315+
tauri_utils::build::link_apple_library("Tauri", &lib_path);
316316
println!("cargo:ios_library_path={}", lib_path.display());
317317
}
318318
}

core/tauri/mobile/ios-api/Package.swift

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,35 @@
66
import PackageDescription
77

88
let package = Package(
9-
name: "Tauri",
10-
platforms: [
11-
.macOS(.v10_13),
12-
.iOS(.v11),
13-
],
14-
products: [
15-
// Products define the executables and libraries a package produces, and make them visible to other packages.
16-
.library(
17-
name: "Tauri",
18-
type: .static,
19-
targets: ["Tauri"]),
20-
],
21-
dependencies: [
22-
// Dependencies declare other packages that this package depends on.
23-
.package(name: "SwiftRs", url: "https://github.com/Brendonovich/swift-rs", from: "1.0.0"),
24-
],
25-
targets: [
26-
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
27-
// Targets can depend on other targets in this package, and on products in packages this package depends on.
28-
.target(
29-
name: "Tauri",
30-
dependencies: [
31-
.byName(name: "SwiftRs"),
32-
],
33-
path: "Sources"
34-
),
35-
.testTarget(
36-
name: "TauriTests",
37-
dependencies: ["Tauri"]
38-
),
39-
]
9+
name: "Tauri",
10+
platforms: [
11+
.macOS(.v10_13),
12+
.iOS(.v11),
13+
],
14+
products: [
15+
// Products define the executables and libraries a package produces, and make them visible to other packages.
16+
.library(
17+
name: "Tauri",
18+
type: .static,
19+
targets: ["Tauri"])
20+
],
21+
dependencies: [
22+
// Dependencies declare other packages that this package depends on.
23+
.package(name: "SwiftRs", url: "https://github.com/Brendonovich/swift-rs", from: "1.0.0")
24+
],
25+
targets: [
26+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
27+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
28+
.target(
29+
name: "Tauri",
30+
dependencies: [
31+
.byName(name: "SwiftRs")
32+
],
33+
path: "Sources"
34+
),
35+
.testTarget(
36+
name: "TauriTests",
37+
dependencies: ["Tauri"]
38+
),
39+
]
4040
)

examples/api/src-tauri/Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/api/src-tauri/Cargo.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ name = "api_lib"
1111
crate-type = ["staticlib", "cdylib", "rlib"]
1212

1313
[build-dependencies]
14-
tauri-build = { path = "../../../core/tauri-build", features = ["codegen", "isolation"] }
14+
tauri-build = { path = "../../../core/tauri-build", features = [
15+
"codegen",
16+
"isolation",
17+
] }
1518

1619
[dependencies]
1720
serde_json = "1.0"
18-
serde = { version = "1.0", features = [ "derive" ] }
21+
serde = { version = "1.0", features = ["derive"] }
1922
tiny_http = "0.11"
2023
log = "0.4"
2124
tauri-plugin-sample = { path = "./tauri-plugin-sample/" }
@@ -28,15 +31,15 @@ features = [
2831
"image-png",
2932
"isolation",
3033
"macos-private-api",
31-
"tray-icon"
34+
"tray-icon",
3235
]
3336

3437
[dev-dependencies.tauri]
3538
path = "../../../core/tauri"
3639
features = ["test"]
3740

3841
[features]
39-
prod = [ "tauri/custom-protocol" ]
42+
prod = ["tauri/custom-protocol"]
4043

4144
# default to small, optimized release binaries
4245
[profile.release]

tooling/cli/src/mobile/ios/dev.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ fn run_dev(
234234
}
235235
Err(e) => {
236236
crate::dev::kill_before_dev_process();
237-
Err(e.into())
237+
Err(e)
238238
}
239239
}
240240
} else {

tooling/cli/src/plugin/init.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
VersionMetadata,
1010
};
1111
use anyhow::Context;
12-
use clap::Parser;
12+
use clap::{Parser, ValueEnum};
1313
use handlebars::{to_json, Handlebars};
1414
use heck::{ToKebabCase, ToPascalCase, ToSnakeCase};
1515
use include_dir::{include_dir, Dir};
@@ -53,6 +53,17 @@ pub struct Options {
5353
/// Whether to initialize Android and iOS projects for the plugin.
5454
#[clap(long)]
5555
pub(crate) mobile: bool,
56+
/// Type of framework to use for the iOS project.
57+
#[clap(long)]
58+
pub(crate) ios_framework: Option<IosFrameworkKind>,
59+
}
60+
61+
#[derive(Debug, Clone, ValueEnum)]
62+
pub enum IosFrameworkKind {
63+
/// Swift Package Manager project
64+
Spm,
65+
/// Xcode project
66+
Xcode,
5667
}
5768

5869
impl Options {
@@ -155,6 +166,8 @@ pub fn command(mut options: Options) -> Result<()> {
155166
None
156167
};
157168

169+
let ios_framework = options.ios_framework.unwrap_or(IosFrameworkKind::Spm);
170+
158171
let mut created_dirs = Vec::new();
159172
template::render_with_generator(
160173
&handlebars,
@@ -193,7 +206,18 @@ pub fn command(mut options: Options) -> Result<()> {
193206
return Ok(None);
194207
}
195208
}
196-
"ios" if !(options.ios || options.mobile) => return Ok(None),
209+
"ios-spm" | "ios-xcode" if !(options.ios || options.mobile) => return Ok(None),
210+
"ios-spm" if !matches!(ios_framework, IosFrameworkKind::Spm) => return Ok(None),
211+
"ios-xcode" if !matches!(ios_framework, IosFrameworkKind::Xcode) => return Ok(None),
212+
"ios-spm" | "ios-xcode" => {
213+
let folder_name = components.next().unwrap().as_os_str().to_string_lossy();
214+
215+
path = Path::new("ios")
216+
.join(Component::Normal(&std::ffi::OsString::from(
217+
&folder_name.replace("{{ plugin_name }}", &plugin_name),
218+
)))
219+
.join(components.collect::<PathBuf>());
220+
}
197221
"guest-js" | "rollup.config.js" | "tsconfig.json" | "package.json"
198222
if options.no_api =>
199223
{

tooling/cli/src/plugin/new.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ pub struct Options {
3535
/// Whether to initialize Android and iOS projects for the plugin.
3636
#[clap(long)]
3737
mobile: bool,
38+
/// Type of framework to use for the iOS project.
39+
#[clap(long)]
40+
pub(crate) ios_framework: Option<super::init::IosFrameworkKind>,
3841
}
3942

4043
impl From<Options> for super::init::Options {
@@ -49,6 +52,7 @@ impl From<Options> for super::init::Options {
4952
android: o.android,
5053
ios: o.ios,
5154
mobile: o.mobile,
55+
ios_framework: o.ios_framework,
5256
}
5357
}
5458
}

0 commit comments

Comments
 (0)