You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ma...@apache.org on 2026/08/07 09:45:49 UTC

(cordova-ios) branch master updated: fix(wkwebview): retain UIDelegate to restore alert/confirm/prompt (#1686)

This is an automated email from the ASF dual-hosted git repository.

GitToTheHub pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-ios.git


The following commit(s) were added to refs/heads/master by this push:
     new ba8b9ecb fix(wkwebview): retain UIDelegate to restore alert/confirm/prompt (#1686)
ba8b9ecb is described below

commit ba8b9ecb00b62349c5d7be3a83c349292fbcc717
Author: Manuel Beck <ma...@outlook.de>
AuthorDate: Fri Aug 7 11:45:43 2026 +0200

    fix(wkwebview): retain UIDelegate to restore alert/confirm/prompt (#1686)
    
    - Fixes a regression introduced by #1664 where the property `uiDelegate` was removed from `CDVWebViewEngine`.
    - `WKWebView.UIDelegate` is weak, so the default `CDVWebViewUIDelegate` could be deallocated after assignment, which breaks JavaScript alert/confirm/prompt handling. Retain the delegate in `CDVWebViewEngine` by a strong property `uiDelegate` and clear it during dispose.
    - Add a regression test that verifies the web view engine keeps a non-nil uiDelegate.
    - Fixes #1684
    
    * fix: document `uiDelegate`, assign it in `updateWithInfo:`
    
    - The property is not only for retaining the UIDelegate, it's also part of the exposed CDVWebViewEngine API as readonly, so callers can inspect what delegate is currently installed. It is also set through `updateWithInfo:`, which a plugin can call and which is documented in `guides/Setting Delegates, Preferences and Script Message Handlers in the WebView.md`
    
    * doc: document `uiDelegate` only in header file
    
    Generated-By: GPT-5.3-Codex
---
 .../Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.h   |  4 ++++
 .../Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m   | 15 ++++++++++++---
 tests/CordovaLibTests/CDVWebViewEngineTests.swift         | 11 +++++++++++
 3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.h b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.h
index 95343b82..84b8ce93 100644
--- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.h
+++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.h
@@ -22,6 +22,10 @@
 
 @interface CDVWebViewEngine : CDVPlugin <CDVWebViewEngineProtocol, WKScriptMessageHandler, WKNavigationDelegate>
 
+// WKUIDelegate reference for this engine, which can also be set via `updateWithInfo:`
+// by a plugin. Additionally it is a helper property to retain the UIDelegate, since
+// WKWebView.UIDelegate is weak and would otherwise be deallocated after assignment
+// which would cause JavaScript alert/confirm/prompt handling to stop working.
 @property (nonatomic, strong, readonly) id <WKUIDelegate> uiDelegate;
 
 - (void)allowsBackForwardNavigationGestures:(CDVInvokedUrlCommand*)command;
diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
index 250f2a9b..97f22d2e 100644
--- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
+++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
@@ -43,6 +43,7 @@
 @property (nonatomic, readwrite) NSString *CDV_ASSETS_URL;
 @property (nonatomic, readwrite) Boolean cdvIsFileScheme;
 @property (nullable, nonatomic, strong, readwrite) WKWebViewConfiguration *configuration;
+@property (nonatomic, strong, readwrite) id <WKUIDelegate> uiDelegate;
 
 @end
 
@@ -270,7 +271,8 @@
     // CDVWebViewUIDelegate instance. This gives library consumers a way to
     // override the UI delegate and customize behaviour if needed.
     if ([self.viewController conformsToProtocol:@protocol(WKUIDelegate)]) {
-        wkWebView.UIDelegate = (id<WKUIDelegate>)self.viewController;
+        self.uiDelegate = (id<WKUIDelegate>)self.viewController;
+        wkWebView.UIDelegate = self.uiDelegate;
     } else {
         CDVWebViewUIDelegate *uiDelegate = [[CDVWebViewUIDelegate alloc] initWithViewController:self.viewController];
 
@@ -278,7 +280,11 @@
         uiDelegate.mediaPermissionGrantType = [self parsePermissionGrantType:[settings cordovaSettingForKey:@"MediaPermissionGrantType"]];
         uiDelegate.allowNewWindows = [settings cordovaBoolSettingForKey:@"AllowNewWindows" defaultValue:NO];
 
-        wkWebView.UIDelegate = uiDelegate;
+        // Assigning to self.uiDelegate keeps a strong reference to the delegate,
+        // since WKWebView.UIDelegate is weak and would cause the delegate to be
+        // deallocated
+        self.uiDelegate = uiDelegate;
+        wkWebView.UIDelegate = self.uiDelegate;
     }
 
     if ([self.viewController conformsToProtocol:@protocol(WKNavigationDelegate)]) {
@@ -301,6 +307,7 @@
 {
     WKWebView* wkWebView = (WKWebView*)_engineWebView;
     [wkWebView.configuration.userContentController removeScriptMessageHandlerForName:CDV_BRIDGE_NAME];
+    self.uiDelegate = nil;
     _engineWebView = nil;
 
     [super dispose];
@@ -427,7 +434,9 @@
     }
 
     if (uiDelegate && [uiDelegate conformsToProtocol:@protocol(WKUIDelegate)]) {
-        wkWebView.UIDelegate = uiDelegate;
+        // Keep a strong reference to the delegate, since WKWebView.UIDelegate is weak
+        self.uiDelegate = uiDelegate;
+        wkWebView.UIDelegate = self.uiDelegate;
     }
 
     if (settings && [settings isKindOfClass:[CDVSettingsDictionary class]]) {
diff --git a/tests/CordovaLibTests/CDVWebViewEngineTests.swift b/tests/CordovaLibTests/CDVWebViewEngineTests.swift
index 929acdaf..4a75c28d 100644
--- a/tests/CordovaLibTests/CDVWebViewEngineTests.swift
+++ b/tests/CordovaLibTests/CDVWebViewEngineTests.swift
@@ -38,6 +38,17 @@ class ScriptHandlingViewController: ViewController, WKScriptMessageHandler {
 }
 
 class CDVWebViewEngineTests: XCTestCase {
+    @MainActor func testWebViewUIDelegateIsRetained() {
+        let vc = ViewController()
+        vc.webContentFolderName = "www"
+        vc.startPage = "index.html"
+
+        vc.loadViewIfNeeded()
+
+        let uiDelegate = (vc.webViewEngine as AnyObject).value(forKey: "uiDelegate")
+        XCTAssertNotNil(uiDelegate)
+    }
+
     @MainActor func testViewControllerScriptMessageHandler() {
         let vc = ScriptHandlingViewController()
         vc.webContentFolderName = "www"


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org