diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index c36fa74c5831..9b52c9a4b1cc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.24.4 + +* Updates plugin to prevent message calls when application will terminate. + ## 3.24.3 * Adds support to get failing url from DNS errors on iOS 26+. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/WebViewFlutterPluginTests.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/WebViewFlutterPluginTests.swift new file mode 100644 index 000000000000..ff97e15cd4b0 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/WebViewFlutterPluginTests.swift @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import XCTest + +@testable import webview_flutter_wkwebview + +#if os(iOS) + import Flutter + import UIKit +#endif + +class WebViewFlutterPluginTests: XCTestCase { + #if os(iOS) + func testInstanceManagerIsDeallocatedInApplicationWillTerminate() { + let plugin = WebViewFlutterPlugin(binaryMessenger: TestBinaryMessenger()) + plugin.proxyApiRegistrar!.setUp() + + let view = UIView() + _ = plugin.proxyApiRegistrar!.instanceManager.addHostCreatedInstance(view) + + // Attaches an associated object to the InstanceManager to listen for when it is deallocated. + var finalizer: TestFinalizer? = TestFinalizer() + + let key = malloc(1)! + defer { + free(key) + } + objc_setAssociatedObject( + plugin.proxyApiRegistrar!.instanceManager, key, finalizer, .OBJC_ASSOCIATION_RETAIN) + let expectation = self.expectation(description: "Wait for InstanceManager to be deallocated.") + TestFinalizer.onDeinit = { + expectation.fulfill() + } + + // Ensure method is from `FlutterApplicationLifeCycleDelegate`. + (plugin as FlutterApplicationLifeCycleDelegate).applicationWillTerminate!( + UIApplication.shared) + XCTAssertNil(plugin.proxyApiRegistrar) + + finalizer = nil + waitForExpectations(timeout: 5.0) + } + #endif +} + +class TestFinalizer { + static var onDeinit: (() -> Void)? + + deinit { + Self.onDeinit?() + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewFlutterPlugin.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewFlutterPlugin.swift index dc8c15df63ff..f4d69b53d171 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewFlutterPlugin.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewFlutterPlugin.swift @@ -28,13 +28,31 @@ public class WebViewFlutterPlugin: NSObject, FlutterPlugin { let plugin = WebViewFlutterPlugin(binaryMessenger: binaryMessenger) let viewFactory = FlutterViewFactory(instanceManager: plugin.proxyApiRegistrar!.instanceManager) + + #if os(iOS) + registrar.addApplicationDelegate(plugin) + #endif + registrar.register(viewFactory, withId: "plugins.flutter.io/webview") registrar.publish(plugin) } public func detachFromEngine(for registrar: FlutterPluginRegistrar) { - proxyApiRegistrar!.ignoreCallsToDart = true - proxyApiRegistrar!.tearDown() + tearDownProxyAPIRegistrar() + } + + private func tearDownProxyAPIRegistrar() { + proxyApiRegistrar?.ignoreCallsToDart = true + proxyApiRegistrar?.tearDown() + try? proxyApiRegistrar?.instanceManager.removeAllObjects() proxyApiRegistrar = nil } } + +#if os(iOS) + extension WebViewFlutterPlugin: FlutterApplicationLifeCycleDelegate { + public func applicationWillTerminate(_ application: UIApplication) { + tearDownProxyAPIRegistrar() + } + } +#endif diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index e894f75ea1f8..4d400fa2a0d0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -42,6 +42,8 @@ 8F1488FE2D2DE27000191744 /* HTTPCookieProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F1488C82D2DE27000191744 /* HTTPCookieProxyAPITests.swift */; }; 8F1488FF2D2DE27000191744 /* NavigationActionProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F1488CB2D2DE27000191744 /* NavigationActionProxyAPITests.swift */; }; 8F1489012D2DE91C00191744 /* AuthenticationChallengeResponseProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F1489002D2DE91C00191744 /* AuthenticationChallengeResponseProxyAPITests.swift */; }; + 8F63D06B2F8812E400EC5076 /* WebViewFlutterPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F63D06A2F8812E400EC5076 /* WebViewFlutterPluginTests.swift */; }; + 8F63D06C2F8812E400EC5076 /* PlatformViewImplTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F63D0692F8812E400EC5076 /* PlatformViewImplTests.swift */; }; 8FEC64852DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64812DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift */; }; 8FEC64862DA2C6DC00C48569 /* WebpagePreferencesProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64842DA2C6DC00C48569 /* WebpagePreferencesProxyAPITests.swift */; }; 8FEC64872DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64822DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift */; }; @@ -128,6 +130,8 @@ 8F1488E02D2DE27000191744 /* WebViewConfigurationProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebViewConfigurationProxyAPITests.swift; path = ../../darwin/Tests/WebViewConfigurationProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8F1488E12D2DE27000191744 /* WebViewProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebViewProxyAPITests.swift; path = ../../darwin/Tests/WebViewProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8F1489002D2DE91C00191744 /* AuthenticationChallengeResponseProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AuthenticationChallengeResponseProxyAPITests.swift; path = ../../darwin/Tests/AuthenticationChallengeResponseProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8F63D0692F8812E400EC5076 /* PlatformViewImplTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlatformViewImplTests.swift; path = ../../darwin/Tests/PlatformViewImplTests.swift; sourceTree = SOURCE_ROOT; }; + 8F63D06A2F8812E400EC5076 /* WebViewFlutterPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebViewFlutterPluginTests.swift; path = ../../darwin/Tests/WebViewFlutterPluginTests.swift; sourceTree = SOURCE_ROOT; }; 8FEC64812DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GetTrustResultResponseProxyAPITests.swift; path = ../../darwin/Tests/GetTrustResultResponseProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8FEC64822DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SecCertificateProxyAPITests.swift; path = ../../darwin/Tests/SecCertificateProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8FEC64832DA2C6DC00C48569 /* SecTrustProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SecTrustProxyAPITests.swift; path = ../../darwin/Tests/SecTrustProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; @@ -174,6 +178,8 @@ 68BDCAEA23C3F7CB00D9C032 /* RunnerTests */ = { isa = PBXGroup; children = ( + 8F63D0692F8812E400EC5076 /* PlatformViewImplTests.swift */, + 8F63D06A2F8812E400EC5076 /* WebViewFlutterPluginTests.swift */, 8F0E23512EEB5D6B002AB342 /* ColorProxyAPITests.swift */, 8F0EDFD22E1F4967001938E6 /* ProxyAPIRegistrarTests.swift */, 8FEC64812DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift */, @@ -498,6 +504,8 @@ 8F1488F42D2DE27000191744 /* URLCredentialProxyAPITests.swift in Sources */, 8F1488F52D2DE27000191744 /* URLAuthenticationChallengeProxyAPITests.swift in Sources */, 8F1488F62D2DE27000191744 /* NavigationDelegateProxyAPITests.swift in Sources */, + 8F63D06B2F8812E400EC5076 /* WebViewFlutterPluginTests.swift in Sources */, + 8F63D06C2F8812E400EC5076 /* PlatformViewImplTests.swift in Sources */, 8F1488F72D2DE27000191744 /* UIDelegateProxyAPITests.swift in Sources */, 8F1488F82D2DE27000191744 /* ScrollViewDelegateProxyAPITests.swift in Sources */, 8F1488FA2D2DE27000191744 /* FWFWebViewFlutterWKWebViewExternalAPITests.swift in Sources */, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 1b6268e8f829..7b11a8b616d8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.24.3 +version: 3.24.4 environment: sdk: ^3.9.0