From 3f62c70d6b9a9eeeb7c302b010c858405a1bb761 Mon Sep 17 00:00:00 2001 From: Zephiris Evergreen Date: Thu, 16 Jul 2026 19:33:22 -0700 Subject: [PATCH] Add `WebviewBuilder::limit_navigations_to_app_bound_domains` (#13848) * Draft: Add `WebviewBuilder::limit_navigations_to_app_bound_domains` only on iOS. * removed cfg flag from new setters * change version bump to minor * fix clippy lint for limit_navigations_to_app_bound_domains * add tauri.conf.json support, polish doc comments * add back missing comma, autogenerate config files * minor documentation formatting changes * Fix limitNavigationsToAppBoundDomains config binding, edit documentation. * Have the CLI automatically inject `localhost` into the `WKAppBoundDomains` array in `Info.plist` when `limitNavigationsToAppBoundDomains` is enabled. Update the `merge_plist` helper to merge arrays in place while skipping duplicate entries. * Extract all window domains for WKAppBoundDomains array * refine docs for limit_navigations_to_app_bound_domains WindowConfig parameter * Revert automatic insertion of WKAppBoundDomains, update documentation to match. * have docs specify tauri-src/Info.ios.plist * cli doesn't have changes anymore * Combine ios blocks * Remove duplicated builder call * format --------- Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com> Co-authored-by: Tony --- .changes/app-bound-domains.md | 8 +++ crates/tauri-cli/config.schema.json | 5 ++ crates/tauri-runtime-wry/src/lib.rs | 4 ++ crates/tauri-runtime/src/webview.rs | 64 +++++++++++++++++++ .../schemas/config.schema.json | 5 ++ crates/tauri-utils/src/config.rs | 55 ++++++++++++++++ crates/tauri/src/webview/mod.rs | 56 ++++++++++++++++ crates/tauri/src/webview/webview_window.rs | 56 ++++++++++++++++ 8 files changed, 253 insertions(+) create mode 100644 .changes/app-bound-domains.md diff --git a/.changes/app-bound-domains.md b/.changes/app-bound-domains.md new file mode 100644 index 000000000000..ea5a24f56939 --- /dev/null +++ b/.changes/app-bound-domains.md @@ -0,0 +1,8 @@ +--- +"tauri": minor:feat +"tauri-runtime": minor:feat +"tauri-runtime-wry": minor:feat +"tauri-utils": minor:feat +--- + +Add `WebviewBuilder::limit_navigations_to_app_bound_domains`, `WebviewWindowBuilder::limit_navigations_to_app_bound_domains`, and limitNavigationsToAppBoundDomains to tauri.config.json. diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json index 4022d224f363..f62f28ee4ddd 100644 --- a/crates/tauri-cli/config.schema.json +++ b/crates/tauri-cli/config.schema.json @@ -614,6 +614,11 @@ } ] }, + "limitNavigationsToAppBoundDomains": { + "description": "Whether to limit navigations to App-Bound Domains. This is necessary to\n enable Service Workers on iOS according to\n [StackOverflow](https://stackoverflow.com/questions/49673399/service-workers-unavailable-in-wkwebview-in-ios-11-3/64155509#64155509).\n\n Default is false.\n\n Note: If you set this to `true` make sure to add localhost and any [`registrable\n domains`](https://developer.mozilla.org/en-US/docs/Glossary/Registrable_domain)\n used in this webview to tauri-src/Info.ios.plist:\n\n ```xml\n \n \n WKAppBoundDomains\n \n localhost\n aregistrabledomain.example\n \n \n \n ```\n\n You must add `localhost` if any webview with this set to true opens a\n local webpage, makes any localhost calls, or uses the isolation pattern\n because Tauri uses the `localhost` domain for hosting the application\n webpage, the IPC protocol, and the isolation pattern's iframe.\n\n Requests served through custom uri schemes are allowed so long as they use\n a registrable domain specified in the `WKAppBoundDomains` array for all the\n requests from the app, including requests for the `localhost` domain.\n\n In theory, you can whitelist an entire uri scheme by including the\n protocol name followed by a colon. For example, to allow all requests\n using a custom \"stream\" uri scheme (see [this tauri\n example](https://github.com/tauri-apps/tauri/blob/dev/examples/streaming/main.rs)),\n you could add `stream:` to the AppBoundDomains array. That said, I'm not\n sure whether Apple would let your app through app review if you do\n whitelist an entire protocol because this feature is not mentioned in\n [their blog post on App-Bound\n Domains](https://webkit.org/blog/10882/app-bound-domains/).\n\n See https://webkit.org/blog/10882/app-bound-domains/ and\n https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/limitsnavigationstoappbounddomains\n for the official documentation on App-Bound Domains.\n\n ## Platform-specific\n\n - **iOS**: Supported since version 14.0+.\n - **Linux / Windows / Android / MacOS:** Unsupported.", + "default": false, + "type": "boolean" + }, "activityName": { "description": "The name of the Android activity to create for this window.", "type": [ diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 8a3da8d31fdc..178ad2ce4905 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -5051,6 +5051,10 @@ You may have it installed on another user account, but it is not available for t #[cfg(target_os = "ios")] { + webview_builder = webview_builder.with_limit_navigations_to_app_bound_domains( + webview_attributes.limit_navigations_to_app_bound_domains, + ); + if let Some(input_accessory_view_builder) = webview_attributes.input_accessory_view_builder { webview_builder = webview_builder .with_input_accessory_view_builder(move |webview| input_accessory_view_builder.0(webview)); diff --git a/crates/tauri-runtime/src/webview.rs b/crates/tauri-runtime/src/webview.rs index 8700041e9353..61bc15d0366b 100644 --- a/crates/tauri-runtime/src/webview.rs +++ b/crates/tauri-runtime/src/webview.rs @@ -402,6 +402,8 @@ pub struct WebviewAttributes { /// This relies on [`objc2_ui_kit`] which does not provide a stable API yet, so it can receive breaking changes in minor releases. #[cfg(target_os = "ios")] pub input_accessory_view_builder: Option, + #[cfg(target_os = "ios")] + pub limit_navigations_to_app_bound_domains: bool, /// Set the environment for the webview. /// Useful if you need to share the same environment, for instance when using the [`PendingWebview::new_window_handler`]. @@ -460,6 +462,7 @@ impl From<&WindowConfig> for WebviewAttributes { ConfigScrollBarStyle::FluentOverlay => ScrollBarStyle::FluentOverlay, _ => ScrollBarStyle::Default, }) + .limit_navigations_to_app_bound_domains(config.limit_navigations_to_app_bound_domains) .general_autofill_enabled(config.general_autofill_enabled); #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))] @@ -538,6 +541,8 @@ impl WebviewAttributes { general_autofill_enabled: true, #[cfg(target_os = "ios")] input_accessory_view_builder: None, + #[cfg(target_os = "ios")] + limit_navigations_to_app_bound_domains: false, #[cfg(windows)] environment: None, #[cfg(any( @@ -791,6 +796,65 @@ impl WebviewAttributes { self } + /// Whether to limit navigations to App-Bound Domains. This is necessary to + /// enable Service Workers on iOS according to + /// [StackOverflow](https://stackoverflow.com/questions/49673399/service-workers-unavailable-in-wkwebview-in-ios-11-3/64155509#64155509). + /// + /// Default is false. + /// + /// Note: If you pass in `true` make sure to add localhost and any [`registrable + /// domains`](https://developer.mozilla.org/en-US/docs/Glossary/Registrable_domain) + /// used in this webview to tauri-src/Info.ios.plist: + /// + /// ```xml + /// + /// + /// WKAppBoundDomains + /// + /// localhost + /// aregistrabledomain.example + /// + /// + /// + /// ``` + /// + /// You must add `localhost` if any webview with this set to true opens a + /// local webpage, makes any localhost calls, or uses the isolation pattern + /// because Tauri uses the `localhost` domain for hosting the application + /// webpage, the IPC protocol, and the isolation pattern's iframe. + /// + /// Requests served through custom uri schemes are allowed so long as they use + /// a registrable domain specified in the `WKAppBoundDomains` array for all the + /// requests from the app, including requests for the `localhost` domain. + /// + /// In theory, you can whitelist an entire uri scheme by including the + /// protocol name followed by a colon. For example, to allow all requests + /// using a custom "stream" uri scheme (see [this tauri + /// example](https://github.com/tauri-apps/tauri/blob/dev/examples/streaming/main.rs)), + /// you could add `stream:` to the AppBoundDomains array. That said, I'm not + /// sure whether Apple would let your app through app review if you do + /// whitelist an entire protocol because this feature is not mentioned in + /// [their blog post on App-Bound + /// Domains](https://webkit.org/blog/10882/app-bound-domains/). + /// + /// See https://webkit.org/blog/10882/app-bound-domains/ and + /// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/limitsnavigationstoappbounddomains + /// for the official documentation on App-Bound Domains. + /// + /// ## Platform-specific + /// + /// - **iOS**: Supported since version 14.0+. + /// - **Linux / Windows / Android / MacOS:** Unsupported. + #[must_use] + #[allow(unused_variables, unused_mut)] + pub fn limit_navigations_to_app_bound_domains(mut self, limit_navigations: bool) -> Self { + #[cfg(target_os = "ios")] + { + self.limit_navigations_to_app_bound_domains = limit_navigations; + } + self + } + /// Change the default background throttling behavior. /// /// By default, browsers use a suspend policy that will throttle timers and even unload diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json index 4022d224f363..f62f28ee4ddd 100644 --- a/crates/tauri-schema-generator/schemas/config.schema.json +++ b/crates/tauri-schema-generator/schemas/config.schema.json @@ -614,6 +614,11 @@ } ] }, + "limitNavigationsToAppBoundDomains": { + "description": "Whether to limit navigations to App-Bound Domains. This is necessary to\n enable Service Workers on iOS according to\n [StackOverflow](https://stackoverflow.com/questions/49673399/service-workers-unavailable-in-wkwebview-in-ios-11-3/64155509#64155509).\n\n Default is false.\n\n Note: If you set this to `true` make sure to add localhost and any [`registrable\n domains`](https://developer.mozilla.org/en-US/docs/Glossary/Registrable_domain)\n used in this webview to tauri-src/Info.ios.plist:\n\n ```xml\n \n \n WKAppBoundDomains\n \n localhost\n aregistrabledomain.example\n \n \n \n ```\n\n You must add `localhost` if any webview with this set to true opens a\n local webpage, makes any localhost calls, or uses the isolation pattern\n because Tauri uses the `localhost` domain for hosting the application\n webpage, the IPC protocol, and the isolation pattern's iframe.\n\n Requests served through custom uri schemes are allowed so long as they use\n a registrable domain specified in the `WKAppBoundDomains` array for all the\n requests from the app, including requests for the `localhost` domain.\n\n In theory, you can whitelist an entire uri scheme by including the\n protocol name followed by a colon. For example, to allow all requests\n using a custom \"stream\" uri scheme (see [this tauri\n example](https://github.com/tauri-apps/tauri/blob/dev/examples/streaming/main.rs)),\n you could add `stream:` to the AppBoundDomains array. That said, I'm not\n sure whether Apple would let your app through app review if you do\n whitelist an entire protocol because this feature is not mentioned in\n [their blog post on App-Bound\n Domains](https://webkit.org/blog/10882/app-bound-domains/).\n\n See https://webkit.org/blog/10882/app-bound-domains/ and\n https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/limitsnavigationstoappbounddomains\n for the official documentation on App-Bound Domains.\n\n ## Platform-specific\n\n - **iOS**: Supported since version 14.0+.\n - **Linux / Windows / Android / MacOS:** Unsupported.", + "default": false, + "type": "boolean" + }, "activityName": { "description": "The name of the Android activity to create for this window.", "type": [ diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index e7d4afec9a04..4297760ae58b 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -2282,6 +2282,58 @@ pub struct WindowConfig { /// - **Linux / Android / iOS / macOS**: Unsupported. Only supports `Default` and performs no operation. #[serde(default, alias = "scroll-bar-style")] pub scroll_bar_style: ScrollBarStyle, + + /// Whether to limit navigations to App-Bound Domains. This is necessary to + /// enable Service Workers on iOS according to + /// [StackOverflow](https://stackoverflow.com/questions/49673399/service-workers-unavailable-in-wkwebview-in-ios-11-3/64155509#64155509). + /// + /// Default is false. + /// + /// Note: If you set this to `true` make sure to add localhost and any [`registrable + /// domains`](https://developer.mozilla.org/en-US/docs/Glossary/Registrable_domain) + /// used in this webview to tauri-src/Info.ios.plist: + /// + /// ```xml + /// + /// + /// WKAppBoundDomains + /// + /// localhost + /// aregistrabledomain.example + /// + /// + /// + /// ``` + /// + /// You must add `localhost` if any webview with this set to true opens a + /// local webpage, makes any localhost calls, or uses the isolation pattern + /// because Tauri uses the `localhost` domain for hosting the application + /// webpage, the IPC protocol, and the isolation pattern's iframe. + /// + /// Requests served through custom uri schemes are allowed so long as they use + /// a registrable domain specified in the `WKAppBoundDomains` array for all the + /// requests from the app, including requests for the `localhost` domain. + /// + /// In theory, you can whitelist an entire uri scheme by including the + /// protocol name followed by a colon. For example, to allow all requests + /// using a custom "stream" uri scheme (see [this tauri + /// example](https://github.com/tauri-apps/tauri/blob/dev/examples/streaming/main.rs)), + /// you could add `stream:` to the AppBoundDomains array. That said, I'm not + /// sure whether Apple would let your app through app review if you do + /// whitelist an entire protocol because this feature is not mentioned in + /// [their blog post on App-Bound + /// Domains](https://webkit.org/blog/10882/app-bound-domains/). + /// + /// See https://webkit.org/blog/10882/app-bound-domains/ and + /// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/limitsnavigationstoappbounddomains + /// for the official documentation on App-Bound Domains. + /// + /// ## Platform-specific + /// + /// - **iOS**: Supported since version 14.0+. + /// - **Linux / Windows / Android / MacOS:** Unsupported. + #[serde(default, alias = "limit-navigations-to-app-bound-domains")] + pub limit_navigations_to_app_bound_domains: bool, /// The name of the Android activity to create for this window. #[serde(default, alias = "activity-name")] pub activity_name: Option, @@ -2379,6 +2431,7 @@ impl Default for WindowConfig { data_directory: None, data_store_identifier: None, scroll_bar_style: ScrollBarStyle::Default, + limit_navigations_to_app_bound_domains: false, activity_name: None, created_by_activity_name: None, requested_by_scene_identifier: None, @@ -3956,6 +4009,7 @@ mod build { let data_directory = opt_lit(self.data_directory.as_ref().map(path_buf_lit).as_ref()); let data_store_identifier = opt_vec_lit(self.data_store_identifier, identity); let scroll_bar_style = &self.scroll_bar_style; + let limit_navigations_to_app_bound_domains = self.limit_navigations_to_app_bound_domains; let activity_name = opt_lit(self.activity_name.as_ref()); let created_by_activity_name = opt_lit(self.created_by_activity_name.as_ref()); let requested_by_scene_identifier = opt_lit(self.requested_by_scene_identifier.as_ref()); @@ -4022,6 +4076,7 @@ mod build { data_directory, data_store_identifier, scroll_bar_style, + limit_navigations_to_app_bound_domains, activity_name, created_by_activity_name, requested_by_scene_identifier, diff --git a/crates/tauri/src/webview/mod.rs b/crates/tauri/src/webview/mod.rs index fba41cf321dd..a56ba676371a 100644 --- a/crates/tauri/src/webview/mod.rs +++ b/crates/tauri/src/webview/mod.rs @@ -1221,6 +1221,62 @@ fn main() { .allow_link_preview(allow_link_preview); self } + /// Whether to limit navigations to App-Bound Domains. This is necessary to + /// enable Service Workers on iOS according to + /// [StackOverflow](https://stackoverflow.com/questions/49673399/service-workers-unavailable-in-wkwebview-in-ios-11-3/64155509#64155509). + /// + /// Default is false. + /// + /// Note: If you pass in `true` make sure to add localhost and any [`registrable + /// domains`](https://developer.mozilla.org/en-US/docs/Glossary/Registrable_domain) + /// used in this webview to tauri-src/Info.ios.plist: + /// + /// ```xml + /// + /// + /// WKAppBoundDomains + /// + /// localhost + /// aregistrabledomain.example + /// + /// + /// + /// ``` + /// + /// You must add `localhost` if any webview with this set to true opens a + /// local webpage, makes any localhost calls, or uses the isolation pattern + /// because Tauri uses the `localhost` domain for hosting the application + /// webpage, the IPC protocol, and the isolation pattern's iframe. + /// + /// Requests served through custom uri schemes are allowed so long as they use + /// a registrable domain specified in the `WKAppBoundDomains` array for all the + /// requests from the app, including requests for the `localhost` domain. + /// + /// In theory, you can whitelist an entire uri scheme by including the + /// protocol name followed by a colon. For example, to allow all requests + /// using a custom "stream" uri scheme (see [this tauri + /// example](https://github.com/tauri-apps/tauri/blob/dev/examples/streaming/main.rs)), + /// you could add `stream:` to the AppBoundDomains array. That said, I'm not + /// sure whether Apple would let your app through app review if you do + /// whitelist an entire protocol because this feature is not mentioned in + /// [their blog post on App-Bound + /// Domains](https://webkit.org/blog/10882/app-bound-domains/). + /// + /// See https://webkit.org/blog/10882/app-bound-domains/ and + /// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/limitsnavigationstoappbounddomains + /// for the official documentation on App-Bound Domains. + /// + /// ## Platform-specific + /// + /// - **iOS**: Supported since version 14.0+. + /// - **Linux / Windows / Android / MacOS:** Unsupported. + #[must_use] + pub fn limit_navigations_to_app_bound_domains(mut self, limit_navigations: bool) -> Self { + self.webview_attributes = self + .webview_attributes + .limit_navigations_to_app_bound_domains(limit_navigations); + self + } /// Allows overriding the keyboard accessory view on iOS. /// Returning `None` effectively removes the view. diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 5be5d441eafa..dea14ad5e49f 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -1322,6 +1322,62 @@ impl> WebviewWindowBuilder<'_, R, M> { self } + /// Whether to limit navigations to App-Bound Domains. This is necessary to + /// enable Service Workers on iOS according to + /// [StackOverflow](https://stackoverflow.com/questions/49673399/service-workers-unavailable-in-wkwebview-in-ios-11-3/64155509#64155509). + /// + /// Default is false. + /// + /// Note: If you pass in `true` make sure to add localhost and any [`registrable + /// domains`](https://developer.mozilla.org/en-US/docs/Glossary/Registrable_domain) + /// used in this webview to tauri-src/Info.ios.plist: + /// + /// ```xml + /// + /// + /// WKAppBoundDomains + /// + /// localhost + /// aregistrabledomain.example + /// + /// + /// + /// ``` + /// + /// You must add `localhost` if any webview with this set to true opens a + /// local webpage, makes any localhost calls, or uses the isolation pattern + /// because Tauri uses the `localhost` domain for hosting the application + /// webpage, the IPC protocol, and the isolation pattern's iframe. + /// + /// Requests served through custom uri schemes are allowed so long as they use + /// a registrable domain specified in the `WKAppBoundDomains` array for all the + /// requests from the app, including requests for the `localhost` domain. + /// + /// In theory, you can whitelist an entire uri scheme by including the + /// protocol name followed by a colon. For example, to allow all requests + /// using a custom "stream" uri scheme (see [this tauri + /// example](https://github.com/tauri-apps/tauri/blob/dev/examples/streaming/main.rs)), + /// you could add `stream:` to the AppBoundDomains array. That said, I'm not + /// sure whether Apple would let your app through app review if you do + /// whitelist an entire protocol because this feature is not mentioned in + /// [their blog post on App-Bound + /// Domains](https://webkit.org/blog/10882/app-bound-domains/). + /// + /// See https://webkit.org/blog/10882/app-bound-domains/ and + /// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/limitsnavigationstoappbounddomains + /// for the official documentation on App-Bound Domains. + /// + /// ## Platform-specific + /// + /// - **iOS**: Supported since version 14.0+. + /// - **Linux / Windows / Android / MacOS:** Unsupported. + pub fn limit_navigations_to_app_bound_domains(mut self, limit_navigations: bool) -> Self { + self.webview_builder = self + .webview_builder + .limit_navigations_to_app_bound_domains(limit_navigations); + self + } + /// Set the environment for the webview. /// Useful if you need to share the same environment, for instance when using the [`Self::on_new_window`]. #[cfg(all(feature = "wry", windows))]