Skip to content

Commit 0aa48fb

Browse files
feat: Support custom CFBundleVersion for iOS and macOS (#13030)
* feat: Add bundleVersion for iOS and macOS * feat: Use bundleVersion for CFBundleVersion * feat: Synchronize bundleVersion with Info.plist * cleanup * fix bundle version, enhance prerelease/buildnumber support and checks * fix change file * tauri-bundler to change file * expand doc --------- Co-authored-by: Lucas Nogueira <[email protected]>
1 parent 628f4a9 commit 0aa48fb

File tree

15 files changed

+200
-40
lines changed

15 files changed

+200
-40
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"tauri-utils": minor:feat
3+
"@tauri-apps/cli": minor:feat
4+
"tauri-cli": minor:feat
5+
"tauri-bundler": minor:feat
6+
---
7+
8+
Added `bundleVersion` to iOS and macOS configuration to support specifying a `CFBundleVersion`.

crates/tauri-bundler/src/bundle.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ pub use self::{
1919
category::AppCategory,
2020
settings::{
2121
AppImageSettings, BundleBinary, BundleSettings, CustomSignCommandSettings, DebianSettings,
22-
DmgSettings, MacOsSettings, PackageSettings, PackageType, Position, RpmSettings, Settings,
23-
SettingsBuilder, Size, UpdaterSettings,
22+
DmgSettings, IosSettings, MacOsSettings, PackageSettings, PackageType, Position, RpmSettings,
23+
Settings, SettingsBuilder, Size, UpdaterSettings,
2424
},
2525
};
2626
#[cfg(target_os = "macos")]

crates/tauri-bundler/src/bundle/macos/app.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,6 @@ fn create_info_plist(
191191
bundle_icon_file: Option<PathBuf>,
192192
settings: &Settings,
193193
) -> crate::Result<()> {
194-
let format = time::format_description::parse("[year][month][day].[hour][minute][second]")
195-
.map_err(time::error::Error::from)?;
196-
let build_number = time::OffsetDateTime::now_utc()
197-
.format(&format)
198-
.map_err(time::error::Error::from)?;
199-
200194
let mut plist = plist::Dictionary::new();
201195
plist.insert("CFBundleDevelopmentRegion".into(), "English".into());
202196
plist.insert("CFBundleDisplayName".into(), settings.product_name().into());
@@ -226,7 +220,15 @@ fn create_info_plist(
226220
"CFBundleShortVersionString".into(),
227221
settings.version_string().into(),
228222
);
229-
plist.insert("CFBundleVersion".into(), build_number.into());
223+
plist.insert(
224+
"CFBundleVersion".into(),
225+
settings
226+
.macos()
227+
.bundle_version
228+
.as_deref()
229+
.unwrap_or_else(|| settings.version_string())
230+
.into(),
231+
);
230232
plist.insert("CSResourcesFileMapped".into(), true.into());
231233
if let Some(category) = settings.app_category() {
232234
plist.insert(

crates/tauri-bundler/src/bundle/macos/ios.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,11 @@ fn generate_info_plist(
178178
writeln!(
179179
file,
180180
" <key>CFBundleVersion</key>\n <string>{}</string>",
181-
settings.version_string()
181+
settings
182+
.ios()
183+
.bundle_version
184+
.as_deref()
185+
.unwrap_or_else(|| settings.version_string())
182186
)?;
183187
writeln!(
184188
file,

crates/tauri-bundler/src/bundle/settings.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,13 @@ pub struct DmgSettings {
306306
pub application_folder_position: Position,
307307
}
308308

309+
/// The iOS bundle settings.
310+
#[derive(Clone, Debug, Default)]
311+
pub struct IosSettings {
312+
/// The version of the build that identifies an iteration of the bundle.
313+
pub bundle_version: Option<String>,
314+
}
315+
309316
/// The macOS bundle settings.
310317
#[derive(Clone, Debug, Default)]
311318
pub struct MacOsSettings {
@@ -323,6 +330,8 @@ pub struct MacOsSettings {
323330
/// List of custom files to add to the application bundle.
324331
/// Maps the path in the Contents directory in the app to the path of the file to include (relative to the current working directory).
325332
pub files: HashMap<PathBuf, PathBuf>,
333+
/// The version of the build that identifies an iteration of the bundle.
334+
pub bundle_version: Option<String>,
326335
/// A version string indicating the minimum MacOS version that the bundled app supports (e.g. `"10.11"`).
327336
/// If you are using this config field, you may also want have your `build.rs` script emit `cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.11`.
328337
pub minimum_system_version: Option<String>,
@@ -643,6 +652,8 @@ pub struct BundleSettings {
643652
pub rpm: RpmSettings,
644653
/// DMG-specific settings.
645654
pub dmg: DmgSettings,
655+
/// iOS-specific settings.
656+
pub ios: IosSettings,
646657
/// MacOS-specific settings.
647658
pub macos: MacOsSettings,
648659
/// Updater configuration.
@@ -1190,6 +1201,11 @@ impl Settings {
11901201
&self.bundle_settings.dmg
11911202
}
11921203

1204+
/// Returns the iOS settings.
1205+
pub fn ios(&self) -> &IosSettings {
1206+
&self.bundle_settings.ios
1207+
}
1208+
11931209
/// Returns the MacOS settings.
11941210
pub fn macos(&self) -> &MacOsSettings {
11951211
&self.bundle_settings.macos

crates/tauri-cli/config.schema.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
]
3232
},
3333
"version": {
34-
"description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field. If removed the version number from `Cargo.toml` is used.\n\n By default version 1.0 is used on Android.",
34+
"description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field.\n\n If removed the version number from `Cargo.toml` is used.\n It's recommended to manage the app versioning in the Tauri config.\n\n ## Platform-specific\n\n - **macOS**: Translates to the bundle's CFBundleShortVersionString property and is used as the default CFBundleVersion.\n You can set an specific bundle version using [`bundle > macOS > bundleVersion`](MacConfig::bundle_version).\n - **iOS**: Translates to the bundle's CFBundleShortVersionString property and is used as the default CFBundleVersion.\n You can set an specific bundle version using [`bundle > iOS > bundleVersion`](IosConfig::bundle_version).\n The `tauri ios build` CLI command has a `--build-number <number>` option that lets you append a build number to the app version.\n - **Android**: By default version 1.0 is used. You can set a version code using [`bundle > android > versionCode`](AndroidConfig::version_code).\n\n By default version 1.0 is used on Android.",
3535
"type": [
3636
"string",
3737
"null"
@@ -3297,6 +3297,13 @@
32973297
"type": "string"
32983298
}
32993299
},
3300+
"bundleVersion": {
3301+
"description": "The version of the build that identifies an iteration of the bundle.\n\n Translates to the bundle's CFBundleVersion property.",
3302+
"type": [
3303+
"string",
3304+
"null"
3305+
]
3306+
},
33003307
"minimumSystemVersion": {
33013308
"description": "A version string indicating the minimum macOS X version that the bundled application supports. Defaults to `10.13`.\n\n Setting it to `null` completely removes the `LSMinimumSystemVersion` field on the bundle's `Info.plist`\n and the `MACOSX_DEPLOYMENT_TARGET` environment variable.\n\n An empty string is considered an invalid value so the default value is used.",
33023309
"default": "10.13",
@@ -3498,6 +3505,13 @@
34983505
"null"
34993506
]
35003507
},
3508+
"bundleVersion": {
3509+
"description": "The version of the build that identifies an iteration of the bundle.\n\n Translates to the bundle's CFBundleVersion property.",
3510+
"type": [
3511+
"string",
3512+
"null"
3513+
]
3514+
},
35013515
"minimumSystemVersion": {
35023516
"description": "A version string indicating the minimum iOS version that the bundled application supports. Defaults to `13.0`.\n\n Maps to the IPHONEOS_DEPLOYMENT_TARGET value.",
35033517
"default": "13.0",

crates/tauri-cli/src/interface/rust.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use notify_debouncer_full::new_debouncer;
2222
use serde::{Deserialize, Deserializer};
2323
use tauri_bundler::{
2424
AppCategory, AppImageSettings, BundleBinary, BundleSettings, DebianSettings, DmgSettings,
25-
MacOsSettings, PackageSettings, Position, RpmSettings, Size, UpdaterSettings, WindowsSettings,
25+
IosSettings, MacOsSettings, PackageSettings, Position, RpmSettings, Size, UpdaterSettings,
26+
WindowsSettings,
2627
};
2728
use tauri_utils::config::{parse::is_configuration_file, DeepLinkProtocol, Updater};
2829

@@ -1016,24 +1017,26 @@ impl RustAppSettings {
10161017
.workspace
10171018
.and_then(|v| v.package);
10181019

1020+
let version = config.version.clone().unwrap_or_else(|| {
1021+
cargo_package_settings
1022+
.version
1023+
.clone()
1024+
.expect("Cargo manifest must have the `package.version` field")
1025+
.resolve("version", || {
1026+
ws_package_settings
1027+
.as_ref()
1028+
.and_then(|p| p.version.clone())
1029+
.ok_or_else(|| anyhow::anyhow!("Couldn't inherit value for `version` from workspace"))
1030+
})
1031+
.expect("Cargo project does not have a version")
1032+
});
1033+
10191034
let package_settings = PackageSettings {
10201035
product_name: config
10211036
.product_name
10221037
.clone()
10231038
.unwrap_or_else(|| cargo_package_settings.name.clone()),
1024-
version: config.version.clone().unwrap_or_else(|| {
1025-
cargo_package_settings
1026-
.version
1027-
.clone()
1028-
.expect("Cargo manifest must have the `package.version` field")
1029-
.resolve("version", || {
1030-
ws_package_settings
1031-
.as_ref()
1032-
.and_then(|p| p.version.clone())
1033-
.ok_or_else(|| anyhow::anyhow!("Couldn't inherit value for `version` from workspace"))
1034-
})
1035-
.expect("Cargo project does not have a version")
1036-
}),
1039+
version: version.clone(),
10371040
description: cargo_package_settings
10381041
.description
10391042
.clone()
@@ -1418,9 +1421,13 @@ fn tauri_config_to_bundle_settings(
14181421
y: config.macos.dmg.application_folder_position.y,
14191422
},
14201423
},
1424+
ios: IosSettings {
1425+
bundle_version: config.ios.bundle_version,
1426+
},
14211427
macos: MacOsSettings {
14221428
frameworks: config.macos.frameworks,
14231429
files: config.macos.files,
1430+
bundle_version: config.macos.bundle_version,
14241431
minimum_system_version: config.macos.minimum_system_version,
14251432
exception_domain: config.macos.exception_domain,
14261433
signing_identity,

crates/tauri-cli/src/mobile/init.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub fn exec(
142142
// Generate Xcode project
143143
Target::Ios => {
144144
let (config, metadata) =
145-
super::ios::get_config(&app, tauri_config_, None, &Default::default());
145+
super::ios::get_config(&app, tauri_config_, None, &Default::default())?;
146146
map.insert("apple", &config);
147147
super::ios::project::gen(
148148
tauri_config_,

crates/tauri-cli/src/mobile/ios/build.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use std::{
3636
env::{set_current_dir, var, var_os},
3737
fs,
3838
path::PathBuf,
39+
str::FromStr,
3940
};
4041

4142
#[derive(Debug, Clone, Parser)]
@@ -166,7 +167,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
166167
tauri_config_,
167168
build_options.features.as_ref(),
168169
&Default::default(),
169-
);
170+
)?;
170171
(interface, config)
171172
};
172173

@@ -182,9 +183,36 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
182183
inject_resources(&config, tauri_config.lock().unwrap().as_ref().unwrap())?;
183184

184185
let mut plist = plist::Dictionary::new();
185-
let version = interface.app_settings().get_package_settings().version;
186-
plist.insert("CFBundleShortVersionString".into(), version.clone().into());
187-
plist.insert("CFBundleVersion".into(), version.into());
186+
{
187+
let tauri_config_guard = tauri_config.lock().unwrap();
188+
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
189+
let app_version = tauri_config_
190+
.version
191+
.clone()
192+
.unwrap_or_else(|| interface.app_settings().get_package_settings().version);
193+
194+
let mut version = semver::Version::from_str(&app_version)
195+
.with_context(|| format!("failed to parse {app_version:?} as a semver string"))?;
196+
if !version.pre.is_empty() {
197+
log::info!(
198+
"CFBundleShortVersionString cannot have prerelease identifier; stripping {}",
199+
version.pre.as_str()
200+
);
201+
version.pre = semver::Prerelease::EMPTY;
202+
}
203+
if !version.build.is_empty() {
204+
log::info!(
205+
"CFBundleShortVersionString cannot have build number; stripping {}",
206+
version.build.as_str()
207+
);
208+
version.build = semver::BuildMetadata::EMPTY;
209+
}
210+
211+
plist.insert(
212+
"CFBundleShortVersionString".into(),
213+
version.to_string().into(),
214+
);
215+
};
188216

189217
let info_plist_path = config
190218
.project_dir()

crates/tauri-cli/src/mobile/ios/dev.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ fn run_command(options: Options, noise_level: NoiseLevel) -> Result<()> {
168168
tauri_config_,
169169
dev_options.features.as_ref(),
170170
&Default::default(),
171-
);
171+
)?;
172172

173173
(interface, config)
174174
};

0 commit comments

Comments
 (0)