Fix(macos/ios): Add handler for web content process termination (fix #14371)#14523
Fix(macos/ios): Add handler for web content process termination (fix #14371)#14523JeffTsang wants to merge 28 commits intotauri-apps:devfrom
Conversation
Package Changes Through e491659There 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 VersionsThe following package releases are the planned based on the context of changes in this pull request.
Add another change file through the GitHub UI by following this link. Read about change files or the docs at github.com/jbolda/covector |
|
@FabianLars Now that wry has been updated, would it be possible to take a look at this PR? |
|
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! |
|
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: |
| /// | ||
| /// - **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>( |
There was a problem hiding this comment.
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.
|
@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. |
…ss termination on iOS
…com/JeffTsang/tauri into fix/web-content-process-termination
|
@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. |
There was a problem hiding this comment.
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?
Please don't unless required for something. I hate force pushing since it makes it really hard to follow the thought process etc.
with the newly exposed api you're right
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? |
|
I've reverted to reloading in the default handler. |
|
@FabianLars I've enabled the default handler on macOS as well. |
|
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. |
|
To recap the discussion so far: Regarding tests, there's no reason 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. |
I think this shows more that we don't have enough tests, but with the removal of retries i agree.
guess so, don't worry about it then
perhaps more something for the
Sleeping a night over it i agree. This is the kind of extra logic i'd rather see devs set a custom handler for. |
Co-authored-by: Fabian-Lars <[email protected]>
velocitysystems
left a comment
There was a problem hiding this comment.
Thanks @JeffTsang. Glad we can move forward together on a solution. Agree with your comments @FabianLars. Please see review comments.
crates/tauri-runtime-wry/src/lib.rs
Outdated
| 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 { |
There was a problem hiding this comment.
This busy-loop spins the CPU until the webview becomes available. Two concerns here:
- This loop has no backoff or timeout — if the webview is never available it will spin forever.
- The
lock().unwrap()and.first().unwrap()calls can panic inside theWKNavigationDelegatecallback 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() { |
There was a problem hiding this comment.
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.
|
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. |
…com/JeffTsang/tauri into fix/web-content-process-termination
|
I've simplified the default handler logic. It is even more crash-proof and now finds the webview with the matching id to reload. |
This is actually important to consider; since multi-window support will soon be added to Tauri.
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. |
|
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. |
|
If there are no other objections, it would be great if this could be moved forward. |
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.