diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index cff94db7d0ab..168c08e8cbed 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.25.1 + +* Relands update to prevent message calls when application will terminate. +* Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. + ## 3.25.0 * Adds support for retrieving cookies with `PlatformWebViewCookieManager.getCookies`. 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..2a9ae0617c94 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/WebViewFlutterPluginTests.swift @@ -0,0 +1,85 @@ +// 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) + } + + func testInstanceManagerIsDeallocatedInSceneDidDisconnect() { + 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() + } + + let scene = UIApplication.shared.connectedScenes.first! + + // Ensure method is from `FlutterSceneLifeCycleDelegate`. + (plugin as FlutterSceneLifeCycleDelegate).sceneDidDisconnect?(scene) + 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..8c9be5e494c8 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,37 @@ public class WebViewFlutterPlugin: NSObject, FlutterPlugin { let plugin = WebViewFlutterPlugin(binaryMessenger: binaryMessenger) let viewFactory = FlutterViewFactory(instanceManager: plugin.proxyApiRegistrar!.instanceManager) + + #if os(iOS) + registrar.addApplicationDelegate(plugin) + registrar.addSceneDelegate(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, FlutterSceneLifeCycleDelegate + { + public func applicationWillTerminate(_ application: UIApplication) { + tearDownProxyAPIRegistrar() + } + + public func sceneDidDisconnect(_ scene: UIScene) { + 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..176440d4d088 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 @@ -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/example/macos/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/macos/Runner.xcodeproj/project.pbxproj index b8645353ae56..115f5c2b3249 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/macos/Runner.xcodeproj/project.pbxproj @@ -61,6 +61,10 @@ 8FF1FEBD2D37201300A5E400 /* ScriptMessageProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF1FE912D37201300A5E400 /* ScriptMessageProxyAPITests.swift */; }; 8FF1FEBE2D37201300A5E400 /* FrameInfoProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF1FE862D37201300A5E400 /* FrameInfoProxyAPITests.swift */; }; 8FF1FEBF2D37201300A5E400 /* HTTPCookieProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF1FE882D37201300A5E400 /* HTTPCookieProxyAPITests.swift */; }; + 8FFBB0682FA1019E00F6659D /* PlatformViewImplTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFBB0652FA1019E00F6659D /* PlatformViewImplTests.swift */; }; + 8FFBB0692FA1019E00F6659D /* ColorProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFBB0642FA1019E00F6659D /* ColorProxyAPITests.swift */; }; + 8FFBB06A2FA1019E00F6659D /* ProxyAPIRegistrarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFBB0662FA1019E00F6659D /* ProxyAPIRegistrarTests.swift */; }; + 8FFBB06B2FA1019E00F6659D /* WebViewFlutterPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFBB0672FA1019E00F6659D /* WebViewFlutterPluginTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -148,6 +152,10 @@ 8FF1FEA02D37201300A5E400 /* WebViewConfigurationProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebViewConfigurationProxyAPITests.swift; path = ../../darwin/Tests/WebViewConfigurationProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8FF1FEA12D37201300A5E400 /* WebViewProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebViewProxyAPITests.swift; path = ../../darwin/Tests/WebViewProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8FF1FEC02D37201500A5E400 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; + 8FFBB0642FA1019E00F6659D /* ColorProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ColorProxyAPITests.swift; path = ../../darwin/Tests/ColorProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8FFBB0652FA1019E00F6659D /* PlatformViewImplTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlatformViewImplTests.swift; path = ../../darwin/Tests/PlatformViewImplTests.swift; sourceTree = SOURCE_ROOT; }; + 8FFBB0662FA1019E00F6659D /* ProxyAPIRegistrarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ProxyAPIRegistrarTests.swift; path = ../../darwin/Tests/ProxyAPIRegistrarTests.swift; sourceTree = SOURCE_ROOT; }; + 8FFBB0672FA1019E00F6659D /* WebViewFlutterPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebViewFlutterPluginTests.swift; path = ../../darwin/Tests/WebViewFlutterPluginTests.swift; sourceTree = SOURCE_ROOT; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ @@ -173,6 +181,10 @@ 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( + 8FFBB0642FA1019E00F6659D /* ColorProxyAPITests.swift */, + 8FFBB0652FA1019E00F6659D /* PlatformViewImplTests.swift */, + 8FFBB0662FA1019E00F6659D /* ProxyAPIRegistrarTests.swift */, + 8FFBB0672FA1019E00F6659D /* WebViewFlutterPluginTests.swift */, 8FEC64912DA303E200C48569 /* GetTrustResultResponseProxyAPITests.swift */, 8FEC64922DA303E200C48569 /* SecCertificateProxyAPITests.swift */, 8FEC64932DA303E200C48569 /* SecTrustProxyAPITests.swift */, @@ -373,7 +385,7 @@ ); mainGroup = 33CC10E42044A3C60003C045; packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; @@ -477,6 +489,10 @@ 8FF1FEB52D37201300A5E400 /* WebsiteDataStoreProxyAPITests.swift in Sources */, 8FF1FEB62D37201300A5E400 /* PreferencesProxyAPITests.swift in Sources */, 8FF1FEB72D37201300A5E400 /* URLCredentialProxyAPITests.swift in Sources */, + 8FFBB0682FA1019E00F6659D /* PlatformViewImplTests.swift in Sources */, + 8FFBB0692FA1019E00F6659D /* ColorProxyAPITests.swift in Sources */, + 8FFBB06A2FA1019E00F6659D /* ProxyAPIRegistrarTests.swift in Sources */, + 8FFBB06B2FA1019E00F6659D /* WebViewFlutterPluginTests.swift in Sources */, 8FF1FEB82D37201300A5E400 /* URLRequestProxyAPITests.swift in Sources */, 8FF1FEB92D37201300A5E400 /* UserScriptProxyAPITests.swift in Sources */, 8FF1FEBA2D37201300A5E400 /* ScriptMessageHandlerProxyAPITests.swift in Sources */, @@ -848,7 +864,7 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { isa = XCLocalSwiftPackageReference; relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; }; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 391af935df1a..23e262f4af67 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the webview_flutter_wkwebview plugin. publish_to: none environment: - sdk: ^3.9.0 - flutter: ">=3.35.0" + sdk: ^3.10.0 + flutter: ">=3.38.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 9f52248af709..19d2345ad094 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,11 +2,11 @@ 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.25.0 +version: 3.25.1 environment: - sdk: ^3.9.0 - flutter: ">=3.35.0" + sdk: ^3.10.0 + flutter: ">=3.38.0" flutter: plugin: