Skip to content

Fix(macos/ios): Add handler for web content process termination (fix #14371)#14523

Open
JeffTsang wants to merge 28 commits intotauri-apps:devfrom
JeffTsang:fix/web-content-process-termination
Open

Fix(macos/ios): Add handler for web content process termination (fix #14371)#14523
JeffTsang wants to merge 28 commits intotauri-apps:devfrom
JeffTsang:fix/web-content-process-termination

Conversation

@JeffTsang
Copy link
Copy Markdown

Fixes #14371 and depends on tauri-apps/wry#1624

On iOS, the webview can become blank and unresponsive when resuming from the background. This can be fixed by handling the termination of the web content process. This PR adds a handler for web content process termination.

@JeffTsang JeffTsang requested a review from a team as a code owner November 23, 2025 09:11
@github-project-automation github-project-automation bot moved this to 📬Proposal in Roadmap Nov 23, 2025
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Nov 23, 2025

Package Changes Through e491659

There are 9 changes which include tauri-macos-sign with patch, tauri-build with patch, tauri with minor, tauri-runtime with minor, tauri-runtime-wry with minor, tauri-bundler with minor, tauri-cli with minor, @tauri-apps/cli with minor, tauri-utils with minor

Planned Package Versions

The following package releases are the planned based on the context of changes in this pull request.

package current next
tauri-utils 2.8.3 2.9.0
tauri-macos-sign 2.3.3 2.3.4
tauri-bundler 2.8.1 2.9.0
tauri-runtime 2.10.1 2.11.0
tauri-runtime-wry 2.10.1 2.11.0
tauri-codegen 2.5.5 2.5.6
tauri-macros 2.5.5 2.5.6
tauri-plugin 2.5.4 2.5.5
tauri-build 2.5.6 2.5.7
tauri 2.10.3 2.11.0
@tauri-apps/cli 2.10.1 2.11.0
tauri-cli 2.10.1 2.11.0

Add another change file through the GitHub UI by following this link.


Read about change files or the docs at github.com/jbolda/covector

@JeffTsang
Copy link
Copy Markdown
Author

@FabianLars Now that wry has been updated, would it be possible to take a look at this PR?

@zakxxi
Copy link
Copy Markdown

zakxxi commented Feb 20, 2026

Hey 👋 Just wanted to check in on the status of this PR. I'm building a mobile app with SvelteKit + Tauri and experiencing this exact issue on iOS › the webview goes sometime blank after resuming from background. This fix would be really helpful. Is there anything blocking the review? Thanks for the work on this!

@JeffTsang
Copy link
Copy Markdown
Author

Unfortunately, it looks like this PR won't be reviewed any time soon unless someone can bring this to the Tauri team's attention.

In the meantime, you can patch wry and add your own code to the web_content_process_did_terminate function in src/wkwebview/navigation.rs

You may also need to run the following code on your webview when your app enters the foreground:

document.body.style.display='none';
document.body.offsetHeight;
document.body.style.display='';

///
/// - **Linux / Windows / Android:** Unsupported.
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn on_web_content_process_terminate<F: Fn(Webview<R>) + Send + 'static>(
Copy link
Copy Markdown
Contributor

@velocitysystems velocitysystems Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: webViewWebContentProcessDidTerminate is the name of the underlying WebKit API. I see that on_web_content_process_terminate is the naming used in WRY, but ideally this should follow the same naming as WebKit.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was naming the function for wry, I opted for the shorter name because it meant the same thing. I would have kept the original name if there was a webViewWebContentProcessWillTerminate event.

@JeffTsang
Copy link
Copy Markdown
Author

@velocitysystems I've added a default handler for iOS. It reloads the webview since tauri-runtime-wry doesn't have access to get_app_url.

@JeffTsang
Copy link
Copy Markdown
Author

@velocitysystems I've moved the default handler so that I can use navigate with a verified url. This should be a more reliable fix than reloading.

Copy link
Copy Markdown
Contributor

@velocitysystems velocitysystems left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @JeffTsang. I've left further comments and feedback on your PR.

  • There are no unit tests for the handler wiring logic.
  • There is no logging during the lifecycle

Also - I'm still concerned about the reload() vs navigate() with URL reconstruction.

The default handler reconstructs the current URL, validates it against the app's base URL, and navigates to it (falling back to /index.html). This feels unnecessary — when webViewWebContentProcessDidTerminate
fires, WKWebView is in a well-defined state: the content process is gone, but the navigation state (URL history, current URL) is preserved in the UI process. reload() handles this correctly in one call.

The navigate() approach also has downsides:

  • If the user was on /settings/profile, the /index.html fallback loses their place
  • The string prefix check (starts_with(app_url.as_str())) is a fragile way to validate URL origin
  • Two unwrap() calls in a crash-recovery callback risk panicking the app — worse than the blank screen

Thoughts @FabianLars?

@github-project-automation github-project-automation bot moved this from 📬Proposal to 🏗 In progress in Roadmap Mar 17, 2026
@FabianLars
Copy link
Copy Markdown
Member

Can all the commits be collapsed into a single fix: ... commit? Rather than merging the dev branch multiple times, it may be better to rebase in the future which avoids merge commits.

Please don't unless required for something. I hate force pushing since it makes it really hard to follow the thought process etc.

This is likely a a minor:feat not a patch:bug since this is an additive change

with the newly exposed api you're right

I'm still concerned about the reload() vs navigate() with URL reconstruction.

How about a very conservative approach? For the initial PR only reload the page. If the app dev wants to they can now add a handler that works for their app as they know best where to navigate to. Then we can investigate some more whether we can do something with navigation or even app restarts. This way we would at least unblock this PR.

Reading through the capacitor discussion, I do not reallyyy share your concerns about losing app state etc on navigation as it seems like reloading/navigation may not even be enough to restore functionality?

@JeffTsang
Copy link
Copy Markdown
Author

I've reverted to reloading in the default handler.

@JeffTsang
Copy link
Copy Markdown
Author

@FabianLars I've enabled the default handler on macOS as well.

@JeffTsang JeffTsang changed the title Fix(ios): Add handler for web content process termination (fix #14371) Fix(macos/ios): Add handler for web content process termination (fix #14371) Mar 23, 2026
@JeffTsang
Copy link
Copy Markdown
Author

I've moved the function to tauri::Builder as requested in #15162.

If there's anything else that needs to be done, let me know.

@JeffTsang
Copy link
Copy Markdown
Author

To recap the discussion so far:

Regarding tests, there's no reason on_web_content_process_terminate needs testing any more than a function like on_page_load. If you still insist on testing, it would probably require a refactor of prepare_pending_webview that is clearly outside of the scope of this PR.

Regarding logging, I don't think it would be particularly useful to see that the web content process was terminated in the logs. It just adds more noise that you have to filter out. But this is easily added if you want it.

Regarding rate limited reloads, if reloading your app causes the web content process to be terminated, there's something seriously wrong with your app. It's doubtful that reloading 3 times every 10 seconds is going to fix this.

@FabianLars
Copy link
Copy Markdown
Member

Regarding tests, there's no reason on_web_content_process_terminate needs testing any more than a function like on_page_load.

I think this shows more that we don't have enough tests, but with the removal of retries i agree.

it would probably require a refactor of prepare_pending_webview that is clearly outside of the scope of this PR.

guess so, don't worry about it then

Regarding logging, I don't think it would be particularly useful to see that the web content process was terminated in the logs. It just adds more noise that you have to filter out. But this is easily added if you want it.

perhaps more something for the tracing feature flag but we're missing that in many places so we can skip it here for now

Regarding rate limited reloads, if reloading your app causes the web content process to be terminated, there's something seriously wrong with your app. It's doubtful that reloading 3 times every 10 seconds is going to fix this.

Sleeping a night over it i agree. This is the kind of extra logic i'd rather see devs set a custom handler for.

Copy link
Copy Markdown
Contributor

@velocitysystems velocitysystems left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @JeffTsang. Glad we can move forward together on a solution. Agree with your comments @FabianLars. Please see review comments.

let window_id_ = window_id.clone();
webview_builder = webview_builder.with_on_web_content_process_terminate_handler(move || {
let windows = &context_.main_thread.windows.0;
let webview = loop {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This busy-loop spins the CPU until the webview becomes available. Two concerns here:

  1. This loop has no backoff or timeout — if the webview is never available it will spin forever.
  2. The lock().unwrap() and .first().unwrap() calls can panic inside the WKNavigationDelegate callback resulting in an app crash.

Can we consider moving the default handler out of tauri-runtime-wry and into the Tauri layer (e.g. into_pending_webview in crates/tauri/src/webview/mod.rs), where you have access to manager.get_webview(&label). This gives you a safe one-liner:

if let Some(webview) = manager.get_webview(&label) {                                                                                                   
    let _ = webview.reload();
}

No loop, no unwraps, graceful no-op if the webview isn't found. #15162 uses this approach to avoid this issue.

}));

#[cfg(any(target_os = "macos", target_os = "ios"))]
if pending.on_web_content_process_terminate_handler.is_none() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fallback resolves the global Builder-level handler here, but falls through to the default in tauri-runtime-wry when nothing is configured. This splits handler resolution across two crates.

Could this be consolidated so the full priority chain is resolved in one place? Something like:

// in into_pending_webview:                                                                                                                            
let handler = per_webview_handler.or_else(|| global_handler.clone());                                                                                                               
                                                                                                                                                           
pending.handler = match handler {
    Some(h) => wrap_custom_handler(manager, label, h),                                                                                                 
    None => build_default_reload_handler(manager, label),                                                                                           
};

This way tauri-runtime-wry just passes the handler through to wry without any default logic and the Tauri layer owns all the decisions.

@JeffTsang
Copy link
Copy Markdown
Author

Keeping the default handler in tauri-runtime-wry ensures that the default handler will always apply if there is no handler set on the pending webview. Setting the default handler any time before this opens up the possibility of overwriting the default handler.

There's nothing particularly complicated about the code that gets the webview and it's very unlikely to crash. In this particular situation, the webview has already stopped working, so crashing while attempting recovery isn't that egregious. If the user ends up having to force quit the app, you're actually saving them a step. But again, this is unlikely to happen in the first place.

Per-webview customization adds more complexity for a questionable use case. You would need to have multiple webviews and want to handle web content process termination differently for each webview. In any case, you can technically already achieve this by looking at the webview label in the builder hook.

@JeffTsang
Copy link
Copy Markdown
Author

JeffTsang commented Apr 2, 2026

I've simplified the default handler logic. It is even more crash-proof and now finds the webview with the matching id to reload.

@JeffTsang
Copy link
Copy Markdown
Author

On a separate note, how does the AI tool policy from #15002 apply in this context? I'm spending so much time fielding questions and suggestions generated by AI and the copied PR (#15162) is clearly generated by AI. It's kind of annoying and dragging this PR out.

@velocitysystems
Copy link
Copy Markdown
Contributor

Per-webview customization adds more complexity for a questionable use case. You would need to have multiple webviews and want to handle web content process termination differently for each webview. In any case, you can technically already achieve this by looking at the webview label in the builder hook.

This is actually important to consider; since multi-window support will soon be added to Tauri.

On a separate note, how does the AI tool policy from #15002 apply in this context? I'm spending so much time fielding questions and suggestions generated by AI and the copied PR (#15162) is clearly generated by AI. It's kind of annoying and dragging this PR out.

Peer review and collaboration are what make open-source software successful. I understand your frustration — you’ve been working on this PR for over five months, and most of the activity has only picked up in the last three weeks. We’d really like to keep this discussion constructive and work together to get it across the finish line.

@JeffTsang
Copy link
Copy Markdown
Author

Regarding per-webview customization, it's advantageous to have all your event handling logic in one place, especially since it's likely to have similar or shared code. You just have to label the webviews appropriately and adjust how you handle the event based on the webview label. Of course, this assumes you even need to handle the event differently for each webview.

Regarding collaboration, human to human collaboration can be useful and is to be expected in open source. The problem is human to robot collaboration. It's not uncommon for AI to make incorrect assumptions that compound and lead to (often overengineered) nonsense that ends up wasting everyone's time. Other times, AI makes generic suggestions that don't take the overall picture into account.

AI-written responses (such as #15162 (comment)) do not sound like normal humans, so you spend a bit of extra time thinking about what it's actually trying to say.

If someone must use AI, AI should only be used in the preliminary stages, perhaps for brainstorming. Everything that the AI suggests should then be thoroughly scrutinized by a human who can then arrive at a final conclusion through original critical thinking. Obviously, the human should type the final response without the use of AI.

Regarding the timeline of this PR, that's another issue entirely. The first PR (#14325) actually dates back to October 2025, but it was accidentally closed in November 2025. I don't know why it took so long for such a serious issue to be picked up, but I was ready to patch wry for as long as I used Tauri.

@JeffTsang
Copy link
Copy Markdown
Author

If there are no other objections, it would be great if this could be moved forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🏗 In progress

Development

Successfully merging this pull request may close these issues.

[bug] iOS webview becomes blank and unresponsive when resuming from the background [bug] TAURI UI NOT WORKING PROPERLY AFTER SYSTEM AWAKE FROM SLEEP.

4 participants