Managed iOS native detachFromEngine flutter plugin event and updated dispose methods, Updated Android native HeadlessInAppWebViewManager.dispose and HeadlessInAppWebView.dispose methods

This commit is contained in:
Lorenzo Pichilli 2022-04-23 04:02:37 +02:00
parent d1e4dc55d0
commit 2d31a2f58b
25 changed files with 245 additions and 58 deletions

View File

@ -1,3 +1,8 @@
## 5.4.1
- Managed iOS native `detachFromEngine` flutter plugin event and updated `dispose` methods
- Updated Android native `HeadlessInAppWebViewManager.dispose` and `HeadlessInAppWebView.dispose` methods
## 5.4.0+3 ## 5.4.0+3
- Fixed Android error in some cases when calling `setServiceWorkerClient` java method on `ServiceWorkerManager` initialization - Fixed Android error in some cases when calling `setServiceWorkerClient` java method on `ServiceWorkerManager` initialization

View File

@ -110,12 +110,16 @@ public class HeadlessInAppWebView implements MethodChannel.MethodCallHandler {
public void dispose() { public void dispose() {
channel.setMethodCallHandler(null); channel.setMethodCallHandler(null);
HeadlessInAppWebViewManager.webViews.remove(id); HeadlessInAppWebViewManager.webViews.remove(id);
ViewGroup contentView = (ViewGroup) plugin.activity.findViewById(android.R.id.content); if (plugin != null) {
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0); ViewGroup contentView = (ViewGroup) plugin.activity.findViewById(android.R.id.content);
if (mainView != null) { ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
mainView.removeView(flutterWebView.getView()); if (mainView != null && flutterWebView != null) {
mainView.removeView(flutterWebView.getView());
}
}
if (flutterWebView != null) {
flutterWebView.dispose();
} }
flutterWebView.dispose();
flutterWebView = null; flutterWebView = null;
plugin = null; plugin = null;
} }

View File

@ -26,7 +26,9 @@ import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin; import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebView; import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebView;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodCall;
@ -77,6 +79,10 @@ public class HeadlessInAppWebViewManager implements MethodChannel.MethodCallHand
public void dispose() { public void dispose() {
channel.setMethodCallHandler(null); channel.setMethodCallHandler(null);
Collection<HeadlessInAppWebView> headlessInAppWebViews = webViews.values();
for (HeadlessInAppWebView headlessInAppWebView : headlessInAppWebViews) {
headlessInAppWebView.dispose();
}
webViews.clear(); webViews.clear();
} }
} }

View File

@ -31,7 +31,11 @@ class CredentialDatabase: NSObject, FlutterPlugin {
switch call.method { switch call.method {
case "getAllAuthCredentials": case "getAllAuthCredentials":
var allCredentials: [[String: Any?]] = [] var allCredentials: [[String: Any?]] = []
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials { guard let credentialStore = CredentialDatabase.credentialStore else {
result(allCredentials)
return
}
for (protectionSpace, credentials) in credentialStore.allCredentials {
var crendentials: [[String: Any?]] = [] var crendentials: [[String: Any?]] = []
for c in credentials { for c in credentials {
let credential: [String: Any?] = c.value.toMap() let credential: [String: Any?] = c.value.toMap()
@ -43,10 +47,17 @@ class CredentialDatabase: NSObject, FlutterPlugin {
"credentials": crendentials "credentials": crendentials
] ]
allCredentials.append(dict) allCredentials.append(dict)
} } }
}
result(allCredentials) result(allCredentials)
break break
case "getHttpAuthCredentials": case "getHttpAuthCredentials":
var crendentials: [[String: Any?]] = []
guard let credentialStore = CredentialDatabase.credentialStore else {
result(crendentials)
return
}
let host = arguments!["host"] as! String let host = arguments!["host"] as! String
let urlProtocol = arguments!["protocol"] as? String let urlProtocol = arguments!["protocol"] as? String
let urlPort = arguments!["port"] as? Int ?? 0 let urlPort = arguments!["port"] as? Int ?? 0
@ -54,9 +65,8 @@ class CredentialDatabase: NSObject, FlutterPlugin {
if let r = realm, r.isEmpty { if let r = realm, r.isEmpty {
realm = nil realm = nil
} }
var crendentials: [[String: Any?]] = []
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials { for (protectionSpace, credentials) in credentialStore.allCredentials {
if protectionSpace.host == host && protectionSpace.realm == realm && if protectionSpace.host == host && protectionSpace.realm == realm &&
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort { protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
for c in credentials { for c in credentials {
@ -68,6 +78,11 @@ class CredentialDatabase: NSObject, FlutterPlugin {
result(crendentials) result(crendentials)
break break
case "setHttpAuthCredential": case "setHttpAuthCredential":
guard let credentialStore = CredentialDatabase.credentialStore else {
result(false)
return
}
let host = arguments!["host"] as! String let host = arguments!["host"] as! String
let urlProtocol = arguments!["protocol"] as? String let urlProtocol = arguments!["protocol"] as? String
let urlPort = arguments!["port"] as? Int ?? 0 let urlPort = arguments!["port"] as? Int ?? 0
@ -78,11 +93,17 @@ class CredentialDatabase: NSObject, FlutterPlugin {
let username = arguments!["username"] as! String let username = arguments!["username"] as! String
let password = arguments!["password"] as! String let password = arguments!["password"] as! String
let credential = URLCredential(user: username, password: password, persistence: .permanent) let credential = URLCredential(user: username, password: password, persistence: .permanent)
CredentialDatabase.credentialStore!.set(credential, credentialStore.set(credential,
for: URLProtectionSpace(host: host, port: urlPort, protocol: urlProtocol, realm: realm, authenticationMethod: NSURLAuthenticationMethodHTTPBasic)) for: URLProtectionSpace(host: host, port: urlPort, protocol: urlProtocol,
realm: realm, authenticationMethod: NSURLAuthenticationMethodHTTPBasic))
result(true) result(true)
break break
case "removeHttpAuthCredential": case "removeHttpAuthCredential":
guard let credentialStore = CredentialDatabase.credentialStore else {
result(false)
return
}
let host = arguments!["host"] as! String let host = arguments!["host"] as! String
let urlProtocol = arguments!["protocol"] as? String let urlProtocol = arguments!["protocol"] as? String
let urlPort = arguments!["port"] as? Int ?? 0 let urlPort = arguments!["port"] as? Int ?? 0
@ -96,7 +117,7 @@ class CredentialDatabase: NSObject, FlutterPlugin {
var credential: URLCredential? = nil; var credential: URLCredential? = nil;
var protectionSpaceCredential: URLProtectionSpace? = nil var protectionSpaceCredential: URLProtectionSpace? = nil
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials { for (protectionSpace, credentials) in credentialStore.allCredentials {
if protectionSpace.host == host && protectionSpace.realm == realm && if protectionSpace.host == host && protectionSpace.realm == realm &&
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort { protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
for c in credentials { for c in credentials {
@ -113,12 +134,17 @@ class CredentialDatabase: NSObject, FlutterPlugin {
} }
if let c = credential, let protectionSpace = protectionSpaceCredential { if let c = credential, let protectionSpace = protectionSpaceCredential {
CredentialDatabase.credentialStore!.remove(c, for: protectionSpace) credentialStore.remove(c, for: protectionSpace)
} }
result(true) result(true)
break break
case "removeHttpAuthCredentials": case "removeHttpAuthCredentials":
guard let credentialStore = CredentialDatabase.credentialStore else {
result(false)
return
}
let host = arguments!["host"] as! String let host = arguments!["host"] as! String
let urlProtocol = arguments!["protocol"] as? String let urlProtocol = arguments!["protocol"] as? String
let urlPort = arguments!["port"] as? Int ?? 0 let urlPort = arguments!["port"] as? Int ?? 0
@ -130,7 +156,7 @@ class CredentialDatabase: NSObject, FlutterPlugin {
var credentialsToRemove: [URLCredential] = []; var credentialsToRemove: [URLCredential] = [];
var protectionSpaceCredential: URLProtectionSpace? = nil var protectionSpaceCredential: URLProtectionSpace? = nil
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials { for (protectionSpace, credentials) in credentialStore.allCredentials {
if protectionSpace.host == host && protectionSpace.realm == realm && if protectionSpace.host == host && protectionSpace.realm == realm &&
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort { protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
protectionSpaceCredential = protectionSpace protectionSpaceCredential = protectionSpace
@ -145,16 +171,21 @@ class CredentialDatabase: NSObject, FlutterPlugin {
if let protectionSpace = protectionSpaceCredential { if let protectionSpace = protectionSpaceCredential {
for credential in credentialsToRemove { for credential in credentialsToRemove {
CredentialDatabase.credentialStore!.remove(credential, for: protectionSpace) credentialStore.remove(credential, for: protectionSpace)
} }
} }
result(true) result(true)
break break
case "clearAllAuthCredentials": case "clearAllAuthCredentials":
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials { guard let credentialStore = CredentialDatabase.credentialStore else {
result(false)
return
}
for (protectionSpace, credentials) in credentialStore.allCredentials {
for credential in credentials { for credential in credentials {
CredentialDatabase.credentialStore!.remove(credential.value, for: protectionSpace) credentialStore.remove(credential.value, for: protectionSpace)
} }
} }
result(true) result(true)
@ -164,4 +195,11 @@ class CredentialDatabase: NSObject, FlutterPlugin {
break break
} }
} }
public func dispose() {
CredentialDatabase.channel?.setMethodCallHandler(nil)
CredentialDatabase.channel = nil
CredentialDatabase.registrar = nil
CredentialDatabase.credentialStore = nil
}
} }

View File

@ -57,4 +57,15 @@ public class HeadlessInAppWebViewManager: NSObject, FlutterPlugin {
headlessInAppWebView.onWebViewCreated() headlessInAppWebView.onWebViewCreated()
flutterWebView.makeInitialLoad(params: params as NSDictionary) flutterWebView.makeInitialLoad(params: params as NSDictionary)
} }
public func dispose() {
HeadlessInAppWebViewManager.channel?.setMethodCallHandler(nil)
HeadlessInAppWebViewManager.channel = nil
HeadlessInAppWebViewManager.registrar = nil
let headlessWebViews = HeadlessInAppWebViewManager.webViews.values
headlessWebViews.forEach { (headlessWebView: HeadlessInAppWebView) in
headlessWebView.dispose()
}
HeadlessInAppWebViewManager.webViews.removeAll()
}
} }

View File

@ -140,4 +140,10 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
} }
result(true) result(true)
} }
public func dispose() {
InAppBrowserManager.channel?.setMethodCallHandler(nil)
InAppBrowserManager.channel = nil
InAppBrowserManager.registrar = nil
}
} }

View File

@ -182,6 +182,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
deinit { deinit {
print("InAppBrowserWebViewController - dealloc") print("InAppBrowserWebViewController - dealloc")
dispose()
} }
public override func viewDidDisappear(_ animated: Bool) { public override func viewDidDisappear(_ animated: Bool) {
@ -550,7 +551,10 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
} }
public func dispose() { public func dispose() {
webView.dispose() onExit()
channel?.setMethodCallHandler(nil)
channel = nil
webView?.dispose()
webView = nil webView = nil
view = nil view = nil
if previousStatusBarStyle != -1 { if previousStatusBarStyle != -1 {
@ -563,18 +567,15 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
backButton.target = nil backButton.target = nil
reloadButton.target = nil reloadButton.target = nil
shareButton.target = nil shareButton.target = nil
onExit()
channel?.setMethodCallHandler(nil)
channel = nil
methodCallDelegate?.webView = nil methodCallDelegate?.webView = nil
methodCallDelegate = nil methodCallDelegate = nil
} }
public func onBrowserCreated() { public func onBrowserCreated() {
channel!.invokeMethod("onBrowserCreated", arguments: []) channel?.invokeMethod("onBrowserCreated", arguments: [])
} }
public func onExit() { public func onExit() {
channel!.invokeMethod("onExit", arguments: []) channel?.invokeMethod("onExit", arguments: [])
} }
} }

View File

@ -164,7 +164,8 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
func dispose() { func dispose() {
channel?.setMethodCallHandler(nil) channel?.setMethodCallHandler(nil)
methodCallDelegate?.webView = nil channel = nil
methodCallDelegate?.dispose()
methodCallDelegate = nil methodCallDelegate = nil
webView?.dispose() webView?.dispose()
webView = nil webView = nil

View File

@ -1673,8 +1673,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
completionHandler(.useCredential, credential) completionHandler(.useCredential, credential)
break break
case 2: case 2:
if InAppWebView.credentialsProposed.count == 0 { if InAppWebView.credentialsProposed.count == 0, let credentialStore = CredentialDatabase.credentialStore {
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials { for (protectionSpace, credentials) in credentialStore.allCredentials {
if protectionSpace.host == host && protectionSpace.realm == realm && if protectionSpace.host == host && protectionSpace.realm == realm &&
protectionSpace.protocol == prot && protectionSpace.port == port { protectionSpace.protocol == prot && protectionSpace.port == port {
for credential in credentials { for credential in credentials {
@ -2900,6 +2900,12 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
} }
public func dispose() { public func dispose() {
channel = nil
removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
removeObserver(self, forKeyPath: #keyPath(WKWebView.title))
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.zoomScale))
resumeTimers() resumeTimers()
stopLoading() stopLoading()
disposeWebMessageChannels() disposeWebMessageChannels()
@ -2920,15 +2926,10 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
InAppWebView.windowWebViews.removeValue(forKey: wId) InAppWebView.windowWebViews.removeValue(forKey: wId)
} }
configuration.userContentController.dispose(windowId: windowId) configuration.userContentController.dispose(windowId: windowId)
removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
removeObserver(self, forKeyPath: #keyPath(WKWebView.title))
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
for imp in customIMPs { for imp in customIMPs {
imp_removeBlock(imp) imp_removeBlock(imp)
} }
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.zoomScale))
longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected)) longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected))
longPressRecognizer.delegate = nil longPressRecognizer.delegate = nil
scrollView.removeGestureRecognizer(longPressRecognizer) scrollView.removeGestureRecognizer(longPressRecognizer)
@ -2945,7 +2946,6 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
navigationDelegate = nil navigationDelegate = nil
scrollView.delegate = nil scrollView.delegate = nil
isPausedTimersCompletionHandler = nil isPausedTimersCompletionHandler = nil
channel = nil
SharedLastTouchPointTimestamp.removeValue(forKey: self) SharedLastTouchPointTimestamp.removeValue(forKey: self)
callAsyncJavaScriptBelowIOS14Results.removeAll() callAsyncJavaScriptBelowIOS14Results.removeAll()
super.removeFromSuperview() super.removeFromSuperview()

View File

@ -553,8 +553,12 @@ public class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
} }
} }
deinit { public func dispose() {
print("InAppWebViewMethodHandler - dealloc")
webView = nil webView = nil
} }
deinit {
print("InAppWebViewMethodHandler - dealloc")
dispose()
}
} }

View File

@ -72,4 +72,12 @@ class InAppWebViewStatic: NSObject, FlutterPlugin {
completionHandler(defaultUserAgent) completionHandler(defaultUserAgent)
} }
} }
public func dispose() {
InAppWebViewStatic.channel?.setMethodCallHandler(nil)
InAppWebViewStatic.channel = nil
InAppWebViewStatic.registrar = nil
InAppWebViewStatic.webViewForUserAgent = nil
InAppWebViewStatic.defaultUserAgent = nil
}
} }

View File

@ -100,6 +100,11 @@ class MyCookieManager: NSObject, FlutterPlugin {
isHttpOnly: Bool?, isHttpOnly: Bool?,
sameSite: String?, sameSite: String?,
result: @escaping FlutterResult) { result: @escaping FlutterResult) {
guard let httpCookieStore = MyCookieManager.httpCookieStore else {
result(false)
return
}
var properties: [HTTPCookiePropertyKey: Any] = [:] var properties: [HTTPCookiePropertyKey: Any] = [:]
properties[.originURL] = url properties[.originURL] = url
properties[.name] = name properties[.name] = name
@ -138,7 +143,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
let cookie = HTTPCookie(properties: properties)! let cookie = HTTPCookie(properties: properties)!
MyCookieManager.httpCookieStore!.setCookie(cookie, completionHandler: {() in httpCookieStore.setCookie(cookie, completionHandler: {() in
result(true) result(true)
}) })
} }
@ -146,8 +151,13 @@ class MyCookieManager: NSObject, FlutterPlugin {
public static func getCookies(url: String, result: @escaping FlutterResult) { public static func getCookies(url: String, result: @escaping FlutterResult) {
var cookieList: [[String: Any?]] = [] var cookieList: [[String: Any?]] = []
guard let httpCookieStore = MyCookieManager.httpCookieStore else {
result(cookieList)
return
}
if let urlHost = URL(string: url)?.host { if let urlHost = URL(string: url)?.host {
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in httpCookieStore.getAllCookies { (cookies) in
for cookie in cookies { for cookie in cookies {
if urlHost.hasSuffix(cookie.domain) || ".\(urlHost)".hasSuffix(cookie.domain) { if urlHost.hasSuffix(cookie.domain) || ".\(urlHost)".hasSuffix(cookie.domain) {
var sameSite: String? = nil var sameSite: String? = nil
@ -189,7 +199,12 @@ class MyCookieManager: NSObject, FlutterPlugin {
public static func getAllCookies(result: @escaping FlutterResult) { public static func getAllCookies(result: @escaping FlutterResult) {
var cookieList: [[String: Any?]] = [] var cookieList: [[String: Any?]] = []
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in guard let httpCookieStore = MyCookieManager.httpCookieStore else {
result(cookieList)
return
}
httpCookieStore.getAllCookies { (cookies) in
for cookie in cookies { for cookie in cookies {
var sameSite: String? = nil var sameSite: String? = nil
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
@ -221,7 +236,12 @@ class MyCookieManager: NSObject, FlutterPlugin {
} }
public static func deleteCookie(url: String, name: String, domain: String, path: String, result: @escaping FlutterResult) { public static func deleteCookie(url: String, name: String, domain: String, path: String, result: @escaping FlutterResult) {
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in guard let httpCookieStore = MyCookieManager.httpCookieStore else {
result(false)
return
}
httpCookieStore.getAllCookies { (cookies) in
for cookie in cookies { for cookie in cookies {
var originURL = "" var originURL = ""
if cookie.properties![.originURL] is String { if cookie.properties![.originURL] is String {
@ -234,7 +254,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
continue continue
} }
if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.name == name && cookie.path == path { if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.name == name && cookie.path == path {
MyCookieManager.httpCookieStore!.delete(cookie, completionHandler: { httpCookieStore.delete(cookie, completionHandler: {
result(true) result(true)
}) })
return return
@ -245,7 +265,12 @@ class MyCookieManager: NSObject, FlutterPlugin {
} }
public static func deleteCookies(url: String, domain: String, path: String, result: @escaping FlutterResult) { public static func deleteCookies(url: String, domain: String, path: String, result: @escaping FlutterResult) {
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in guard let httpCookieStore = MyCookieManager.httpCookieStore else {
result(false)
return
}
httpCookieStore.getAllCookies { (cookies) in
for cookie in cookies { for cookie in cookies {
var originURL = "" var originURL = ""
if cookie.properties![.originURL] is String { if cookie.properties![.originURL] is String {
@ -258,7 +283,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
continue continue
} }
if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.path == path { if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.path == path {
MyCookieManager.httpCookieStore!.delete(cookie, completionHandler: nil) httpCookieStore.delete(cookie, completionHandler: nil)
} }
} }
result(true) result(true)
@ -272,4 +297,11 @@ class MyCookieManager: NSObject, FlutterPlugin {
result(true) result(true)
}) })
} }
public func dispose() {
MyCookieManager.channel?.setMethodCallHandler(nil)
MyCookieManager.channel = nil
MyCookieManager.registrar = nil
MyCookieManager.httpCookieStore = nil
}
} }

View File

@ -53,7 +53,13 @@ class MyWebStorageManager: NSObject, FlutterPlugin {
public static func fetchDataRecords(dataTypes: Set<String>, result: @escaping FlutterResult) { public static func fetchDataRecords(dataTypes: Set<String>, result: @escaping FlutterResult) {
var recordList: [[String: Any?]] = [] var recordList: [[String: Any?]] = []
MyWebStorageManager.websiteDataStore!.fetchDataRecords(ofTypes: dataTypes) { (data) in
guard let websiteDataStore = MyWebStorageManager.websiteDataStore else {
result(recordList)
return
}
websiteDataStore.fetchDataRecords(ofTypes: dataTypes) { (data) in
for record in data { for record in data {
recordList.append([ recordList.append([
"displayName": record.displayName, "displayName": record.displayName,
@ -68,7 +74,13 @@ class MyWebStorageManager: NSObject, FlutterPlugin {
public static func removeDataFor(dataTypes: Set<String>, recordList: [[String: Any?]], result: @escaping FlutterResult) { public static func removeDataFor(dataTypes: Set<String>, recordList: [[String: Any?]], result: @escaping FlutterResult) {
var records: [WKWebsiteDataRecord] = [] var records: [WKWebsiteDataRecord] = []
MyWebStorageManager.websiteDataStore!.fetchDataRecords(ofTypes: dataTypes) { (data) in
guard let websiteDataStore = MyWebStorageManager.websiteDataStore else {
result(false)
return
}
websiteDataStore.fetchDataRecords(ofTypes: dataTypes) { (data) in
for record in data { for record in data {
for r in recordList { for r in recordList {
let displayName = r["displayName"] as! String let displayName = r["displayName"] as! String
@ -78,16 +90,28 @@ class MyWebStorageManager: NSObject, FlutterPlugin {
} }
} }
} }
MyWebStorageManager.websiteDataStore!.removeData(ofTypes: dataTypes, for: records) { websiteDataStore.removeData(ofTypes: dataTypes, for: records) {
result(true) result(true)
} }
} }
} }
public static func removeDataModifiedSince(dataTypes: Set<String>, timestamp: Int64, result: @escaping FlutterResult) { public static func removeDataModifiedSince(dataTypes: Set<String>, timestamp: Int64, result: @escaping FlutterResult) {
guard let websiteDataStore = MyWebStorageManager.websiteDataStore else {
result(false)
return
}
let date = NSDate(timeIntervalSince1970: TimeInterval(timestamp)) let date = NSDate(timeIntervalSince1970: TimeInterval(timestamp))
MyWebStorageManager.websiteDataStore!.removeData(ofTypes: dataTypes, modifiedSince: date as Date) { websiteDataStore.removeData(ofTypes: dataTypes, modifiedSince: date as Date) {
result(true) result(true)
} }
} }
public func dispose() {
MyWebStorageManager.channel?.setMethodCallHandler(nil)
MyWebStorageManager.channel = nil
MyWebStorageManager.registrar = nil
MyWebStorageManager.websiteDataStore = nil
}
} }

View File

@ -60,4 +60,10 @@ class PlatformUtil: NSObject, FlutterPlugin {
formatter.timeZone = timezone formatter.timeZone = timezone
return formatter.string(from: PlatformUtil.getDateFromMilliseconds(date: date)) return formatter.string(from: PlatformUtil.getDateFromMilliseconds(date: date))
} }
public func dispose() {
PlatformUtil.channel?.setMethodCallHandler(nil)
PlatformUtil.channel = nil
PlatformUtil.registrar = nil
}
} }

View File

@ -109,5 +109,6 @@ public class PullToRefreshControl : UIRefreshControl, FlutterPlugin {
deinit { deinit {
print("PullToRefreshControl - dealloc") print("PullToRefreshControl - dealloc")
dispose()
} }
} }

View File

@ -91,4 +91,10 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
result(FlutterError.init(code: "ChromeSafariBrowserManager", message: "SafariViewController is not available!", details: nil)) result(FlutterError.init(code: "ChromeSafariBrowserManager", message: "SafariViewController is not available!", details: nil))
} }
public func dispose() {
ChromeSafariBrowserManager.channel?.setMethodCallHandler(nil)
ChromeSafariBrowserManager.channel = nil
ChromeSafariBrowserManager.registrar = nil
}
} }

View File

@ -22,6 +22,7 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
deinit { deinit {
print("SafariViewController - dealloc") print("SafariViewController - dealloc")
dispose()
} }
public func prepareMethodChannel() { public func prepareMethodChannel() {
@ -123,20 +124,21 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
// } // }
public func onChromeSafariBrowserOpened() { public func onChromeSafariBrowserOpened() {
channel!.invokeMethod("onChromeSafariBrowserOpened", arguments: []) channel?.invokeMethod("onChromeSafariBrowserOpened", arguments: [])
} }
public func onChromeSafariBrowserCompletedInitialLoad() { public func onChromeSafariBrowserCompletedInitialLoad() {
channel!.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", arguments: []) channel?.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", arguments: [])
} }
public func onChromeSafariBrowserClosed() { public func onChromeSafariBrowserClosed() {
channel!.invokeMethod("onChromeSafariBrowserClosed", arguments: []) channel?.invokeMethod("onChromeSafariBrowserClosed", arguments: [])
} }
public func dispose() { public func dispose() {
channel?.setMethodCallHandler(nil)
channel = nil
delegate = nil delegate = nil
channel!.setMethodCallHandler(nil)
} }
} }
@ -180,7 +182,12 @@ class CustomUIActivity : UIActivity {
} }
override func perform() { override func perform() {
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_chromesafaribrowser_" + viewId, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger()) guard let registrar = SwiftFlutterPlugin.instance?.registrar else {
return
}
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_chromesafaribrowser_" + viewId,
binaryMessenger: registrar.messenger())
let arguments: [String: Any?] = [ let arguments: [String: Any?] = [
"url": url.absoluteString, "url": url.absoluteString,

View File

@ -49,16 +49,39 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
headlessInAppWebViewManager = HeadlessInAppWebViewManager(registrar: registrar) headlessInAppWebViewManager = HeadlessInAppWebViewManager(registrar: registrar)
chromeSafariBrowserManager = ChromeSafariBrowserManager(registrar: registrar) chromeSafariBrowserManager = ChromeSafariBrowserManager(registrar: registrar)
inAppWebViewStatic = InAppWebViewStatic(registrar: registrar) inAppWebViewStatic = InAppWebViewStatic(registrar: registrar)
credentialDatabase = CredentialDatabase(registrar: registrar)
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
myCookieManager = MyCookieManager(registrar: registrar) myCookieManager = MyCookieManager(registrar: registrar)
} }
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
myWebStorageManager = MyWebStorageManager(registrar: registrar) myWebStorageManager = MyWebStorageManager(registrar: registrar)
} }
credentialDatabase = CredentialDatabase(registrar: registrar)
} }
public static func register(with registrar: FlutterPluginRegistrar) { public static func register(with registrar: FlutterPluginRegistrar) {
SwiftFlutterPlugin.instance = SwiftFlutterPlugin(with: registrar) SwiftFlutterPlugin.instance = SwiftFlutterPlugin(with: registrar)
} }
public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
platformUtil?.dispose()
platformUtil = nil
inAppBrowserManager?.dispose()
inAppBrowserManager = nil
headlessInAppWebViewManager?.dispose()
headlessInAppWebViewManager = nil
chromeSafariBrowserManager?.dispose()
chromeSafariBrowserManager = nil
inAppWebViewStatic?.dispose()
inAppWebViewStatic = nil
credentialDatabase?.dispose()
credentialDatabase = nil
if #available(iOS 11.0, *) {
(myCookieManager as! MyCookieManager?)?.dispose()
myCookieManager = nil
}
if #available(iOS 9.0, *) {
(myWebStorageManager as! MyWebStorageManager?)?.dispose()
myWebStorageManager = nil
}
}
} }

View File

@ -21,7 +21,7 @@ extension WKUserContentController {
var contentWorlds: Set<WKContentWorld> { var contentWorlds: Set<WKContentWorld> {
get { get {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self)) let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
return WKUserContentController._contentWorlds[tmpAddress]! return WKUserContentController._contentWorlds[tmpAddress] ?? []
} }
set(newValue) { set(newValue) {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self)) let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
@ -33,7 +33,7 @@ extension WKUserContentController {
var userOnlyScripts: [WKUserScriptInjectionTime:OrderedSet<UserScript>] { var userOnlyScripts: [WKUserScriptInjectionTime:OrderedSet<UserScript>] {
get { get {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self)) let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
return WKUserContentController._userOnlyScripts[tmpAddress]! return WKUserContentController._userOnlyScripts[tmpAddress] ?? [:]
} }
set(newValue) { set(newValue) {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self)) let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
@ -45,7 +45,7 @@ extension WKUserContentController {
var pluginScripts: [WKUserScriptInjectionTime:OrderedSet<PluginScript>] { var pluginScripts: [WKUserScriptInjectionTime:OrderedSet<PluginScript>] {
get { get {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self)) let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
return WKUserContentController._pluginScripts[tmpAddress]! return WKUserContentController._pluginScripts[tmpAddress] ?? [:]
} }
set(newValue) { set(newValue) {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self)) let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))

View File

@ -23,5 +23,6 @@ public class WebMessage : NSObject {
deinit { deinit {
print("WebMessage - dealloc") print("WebMessage - dealloc")
dispose()
} }
} }

View File

@ -126,6 +126,7 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
public func dispose() { public func dispose() {
channel?.setMethodCallHandler(nil) channel?.setMethodCallHandler(nil)
channel = nil
for port in ports { for port in ports {
port.dispose() port.dispose()
} }
@ -140,11 +141,11 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
} }
})(); })();
""") """)
channel = nil
webView = nil webView = nil
} }
deinit { deinit {
print("WebMessageChannel - dealloc") print("WebMessageChannel - dealloc")
dispose()
} }
} }

View File

@ -222,5 +222,6 @@ public class WebMessageListener : FlutterMethodCallDelegate {
deinit { deinit {
print("WebMessageListener - dealloc") print("WebMessageListener - dealloc")
dispose()
} }
} }

View File

@ -118,5 +118,6 @@ public class WebMessagePort : NSObject {
deinit { deinit {
print("WebMessagePort - dealloc") print("WebMessagePort - dealloc")
dispose()
} }
} }

View File

@ -12,7 +12,7 @@ var SharedLastTouchPointTimestamp: [InAppWebView: Int64] = [:]
public class Util { public class Util {
public static func getUrlAsset(assetFilePath: String) throws -> URL { public static func getUrlAsset(assetFilePath: String) throws -> URL {
let key = SwiftFlutterPlugin.instance!.registrar!.lookupKey(forAsset: assetFilePath) let key = SwiftFlutterPlugin.instance?.registrar?.lookupKey(forAsset: assetFilePath)
guard let assetURL = Bundle.main.url(forResource: key, withExtension: nil) else { guard let assetURL = Bundle.main.url(forResource: key, withExtension: nil) else {
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0) throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
} }
@ -20,7 +20,7 @@ public class Util {
} }
public static func getAbsPathAsset(assetFilePath: String) throws -> String { public static func getAbsPathAsset(assetFilePath: String) throws -> String {
let key = SwiftFlutterPlugin.instance!.registrar!.lookupKey(forAsset: assetFilePath) let key = SwiftFlutterPlugin.instance?.registrar?.lookupKey(forAsset: assetFilePath)
guard let assetAbsPath = Bundle.main.path(forResource: key, ofType: nil) else { guard let assetAbsPath = Bundle.main.path(forResource: key, ofType: nil) else {
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0) throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
} }

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 5.4.0+3 version: 5.4.1
homepage: https://github.com/pichillilorenzo/flutter_inappwebview homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: environment: