completed ChannelDelegate iOS implementation

This commit is contained in:
Lorenzo Pichilli 2022-05-08 12:56:28 +02:00
parent 66add9f8ac
commit 373e970e80
43 changed files with 2272 additions and 1602 deletions

View File

@ -104,6 +104,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
pullToRefreshLayout.prepare();
webView = findViewById(R.id.webView);
webView.id = id;
webView.windowId = windowId;
webView.inAppBrowserDelegate = this;
webView.plugin = manager.plugin;

View File

@ -1112,7 +1112,10 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
public void onLoadResourceCustomScheme(String url, @NonNull LoadResourceCustomSchemeCallback callback) {
MethodChannel channel = getChannel();
if (channel == null) return;
if (channel == null) {
callback.defaultBehaviour(null);
return;
}
Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
channel.invokeMethod("onLoadResourceCustomScheme", obj, callback);
@ -1146,7 +1149,10 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
public void shouldInterceptRequest(WebResourceRequestExt request, @NonNull ShouldInterceptRequestCallback callback) {
MethodChannel channel = getChannel();
if (channel == null) return;
if (channel == null) {
callback.defaultBehaviour(null);
return;
}
channel.invokeMethod("shouldInterceptRequest", request.toMap(), callback);
}

View File

@ -7,7 +7,7 @@
import Foundation
class CredentialDatabase: ChannelDelegate {
public class CredentialDatabase: ChannelDelegate {
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_credential_database"
static var registrar: FlutterPluginRegistrar?
static var credentialStore: URLCredentialStorage?
@ -193,4 +193,8 @@ class CredentialDatabase: ChannelDelegate {
CredentialDatabase.registrar = nil
CredentialDatabase.credentialStore = nil
}
deinit {
dispose()
}
}

View File

@ -61,4 +61,8 @@ public class HeadlessInAppWebViewManager: ChannelDelegate {
}
HeadlessInAppWebViewManager.webViews.removeAll()
}
deinit {
dispose()
}
}

View File

@ -8,7 +8,7 @@
import Foundation
public class HeadlessWebViewChannelDelegate : ChannelDelegate {
private var headlessWebView: HeadlessInAppWebView?
private weak var headlessWebView: HeadlessInAppWebView?
public init(headlessWebView: HeadlessInAppWebView, channel: FlutterMethodChannel) {
super.init(channel: channel)
@ -56,4 +56,8 @@ public class HeadlessWebViewChannelDelegate : ChannelDelegate {
super.dispose()
headlessWebView = nil
}
deinit {
dispose()
}
}

View File

@ -21,4 +21,8 @@ public class InAppBrowserChannelDelegate : ChannelDelegate {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onExit", arguments: arguments)
}
deinit {
dispose()
}
}

View File

@ -138,4 +138,8 @@ public class InAppBrowserManager: ChannelDelegate {
super.dispose()
InAppBrowserManager.registrar = nil
}
deinit {
dispose()
}
}

View File

@ -24,7 +24,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
var tmpWindow: UIWindow?
var id: String = ""
var windowId: Int64?
var webView: InAppWebView!
var webView: InAppWebView?
var channelDelegate: InAppBrowserChannelDelegate?
var initialUrlRequest: URLRequest?
var initialFile: String?
@ -38,7 +38,6 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
var previousStatusBarStyle = -1
var initialUserScripts: [[String: Any]] = []
var pullToRefreshInitialSettings: [String: Any?] = [:]
var methodCallDelegate: InAppWebViewMethodHandler?
public override func loadView() {
let channel = FlutterMethodChannel(name: InAppBrowserWebViewController.METHOD_CHANNEL_NAME_PREFIX + id, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
@ -52,20 +51,24 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(settings: webViewSettings)
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView = webViewTransport.webView
webView.contextMenu = contextMenu
webView.channel = channel
webView.initialUserScripts = userScripts
webView!.contextMenu = contextMenu
webView!.initialUserScripts = userScripts
} else {
webView = InAppWebView(frame: .zero,
configuration: preWebviewConfiguration,
contextMenu: contextMenu,
channel: channel,
userScripts: userScripts)
webView = InAppWebView(id: nil,
registrar: nil,
frame: .zero,
configuration: preWebviewConfiguration,
contextMenu: contextMenu,
userScripts: userScripts)
}
webView.inAppBrowserDelegate = self
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
channel.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
guard let webView = webView else {
return
}
webView.inAppBrowserDelegate = self
webView.id = id
webView.channelDelegate = WebViewChannelDelegate(webView: webView, channel: channel)
let pullToRefreshSettings = PullToRefreshSettings()
let _ = pullToRefreshSettings.parse(settings: pullToRefreshInitialSettings)
@ -87,38 +90,41 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
public override func viewDidLoad() {
super.viewDidLoad()
webView.translatesAutoresizingMaskIntoConstraints = false
webView?.translatesAutoresizingMaskIntoConstraints = false
progressBar.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 9.0, *) {
webView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0).isActive = true
webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0).isActive = true
webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0.0).isActive = true
webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0.0).isActive = true
webView?.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0).isActive = true
webView?.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0).isActive = true
webView?.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0.0).isActive = true
webView?.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0.0).isActive = true
progressBar.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0).isActive = true
progressBar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0.0).isActive = true
progressBar.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0.0).isActive = true
} else {
view.addConstraints([
NSLayoutConstraint(item: webView!, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: webView!, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: webView!, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: webView!, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1, constant: 0)
])
view.addConstraints([
NSLayoutConstraint(item: progressBar!, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: progressBar!, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: progressBar!, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1, constant: 0)
])
if let webView = webView {
view.addConstraints([
NSLayoutConstraint(item: webView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: webView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: webView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: webView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1, constant: 0)
])
}
if let progressBar = progressBar {
view.addConstraints([
NSLayoutConstraint(item: progressBar, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: progressBar, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: progressBar, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1, constant: 0)
])
}
}
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView.load(webViewTransport.request)
webView?.load(webViewTransport.request)
} else {
if #available(iOS 11.0, *) {
if let contentBlockers = webView.settings?.contentBlockers, contentBlockers.count > 0 {
if let contentBlockers = webView?.settings?.contentBlockers, contentBlockers.count > 0 {
do {
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
let blockRules = String(data: jsonData, encoding: String.Encoding.utf8)
@ -165,27 +171,22 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
allowingReadAccessToURL = nil
}
}
webView.loadData(data: initialData, mimeType: initialMimeType!, encoding: initialEncoding!, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL)
webView?.loadData(data: initialData, mimeType: initialMimeType!, encoding: initialEncoding!, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL)
}
else if let initialUrlRequest = initialUrlRequest {
var allowingReadAccessToURL: URL? = nil
if let allowingReadAccessTo = webView.settings?.allowingReadAccessTo, let url = initialUrlRequest.url, url.scheme == "file" {
if let allowingReadAccessTo = webView?.settings?.allowingReadAccessTo, let url = initialUrlRequest.url, url.scheme == "file" {
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
if allowingReadAccessToURL?.scheme != "file" {
allowingReadAccessToURL = nil
}
}
webView.loadUrl(urlRequest: initialUrlRequest, allowingReadAccessTo: allowingReadAccessToURL)
webView?.loadUrl(urlRequest: initialUrlRequest, allowingReadAccessTo: allowingReadAccessToURL)
}
channelDelegate?.onBrowserCreated()
}
deinit {
debugPrint("InAppBrowserWebViewController - dealloc")
dispose()
}
public override func viewDidDisappear(_ animated: Bool) {
dispose()
super.viewDidDisappear(animated)
@ -203,8 +204,8 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
public func prepareWebView() {
webView.settings = webViewSettings
webView.prepare()
webView?.settings = webViewSettings
webView?.prepare()
searchBar = UISearchBar()
searchBar.keyboardType = .URL
@ -305,8 +306,8 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
public func didStartNavigation(url: URL?) {
forwardButton.isEnabled = webView.canGoForward
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView?.canGoForward ?? false
backButton.isEnabled = webView?.canGoBack ?? false
progressBar.setProgress(0.0, animated: false)
guard let url = url else {
return
@ -315,8 +316,8 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
public func didUpdateVisitedHistory(url: URL?) {
forwardButton.isEnabled = webView.canGoForward
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView?.canGoForward ?? false
backButton.isEnabled = webView?.canGoBack ?? false
guard let url = url else {
return
}
@ -324,8 +325,8 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
public func didFinishNavigation(url: URL?) {
forwardButton.isEnabled = webView.canGoForward
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView?.canGoForward ?? false
backButton.isEnabled = webView?.canGoBack ?? false
progressBar.setProgress(0.0, animated: false)
guard let url = url else {
return
@ -334,8 +335,8 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
public func didFailNavigation(url: URL?, error: Error) {
forwardButton.isEnabled = webView.canGoForward
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView?.canGoForward ?? false
backButton.isEnabled = webView?.canGoBack ?? false
progressBar.setProgress(0.0, animated: false)
}
@ -350,7 +351,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
return
}
let request = URLRequest(url: url)
webView.load(request)
webView?.load(request)
}
public func show(completion: (() -> Void)? = nil) {
@ -381,12 +382,12 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
@objc public func reload() {
webView.reload()
didUpdateVisitedHistory(url: webView.url)
webView?.reload()
didUpdateVisitedHistory(url: webView?.url)
}
@objc public func share() {
let vc = UIActivityViewController(activityItems: [webView.url?.absoluteString ?? ""], applicationActivities: [])
let vc = UIActivityViewController(activityItems: [webView?.url?.absoluteString ?? ""], applicationActivities: [])
present(vc, animated: true, completion: nil)
}
@ -413,26 +414,25 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
@objc public func goBack() {
if webView.canGoBack {
if let webView = webView, webView.canGoBack {
webView.goBack()
}
}
@objc public func goForward() {
if webView.canGoForward {
if let webView = webView, webView.canGoForward {
webView.goForward()
}
}
@objc public func goBackOrForward(steps: Int) {
webView.goBackOrForward(steps: steps)
webView?.goBackOrForward(steps: steps)
}
public func setSettings(newSettings: InAppBrowserSettings, newSettingsMap: [String: Any]) {
let newInAppWebViewOptions = InAppWebViewSettings()
let _ = newInAppWebViewOptions.parse(settings: newSettingsMap)
self.webView.setSettings(newSettings: newInAppWebViewOptions, newSettingsMap: newSettingsMap)
let newInAppWebViewSettings = InAppWebViewSettings()
let _ = newInAppWebViewSettings.parse(settings: newSettingsMap)
webView?.setSettings(newSettings: newInAppWebViewSettings, newSettingsMap: newSettingsMap)
if newSettingsMap["hidden"] != nil, browserSettings?.hidden != newSettings.hidden {
if newSettings.hidden {
@ -537,12 +537,12 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
progressBar.isHidden = newSettings.hideProgressBar
}
self.browserSettings = newSettings
self.webViewSettings = newInAppWebViewOptions
browserSettings = newSettings
webViewSettings = newInAppWebViewSettings
}
public func getSettings() -> [String: Any?]? {
let webViewSettingsMap = self.webView.getSettings()
let webViewSettingsMap = webView?.getSettings()
if (self.browserSettings == nil || webViewSettingsMap == nil) {
return nil
}
@ -568,7 +568,10 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
backButton.target = nil
reloadButton.target = nil
shareButton.target = nil
methodCallDelegate?.webView = nil
methodCallDelegate = nil
}
deinit {
debugPrint("InAppBrowserWebViewController - dealloc")
dispose()
}
}

View File

@ -7,7 +7,7 @@
import Foundation
class ContextMenuSettings: ISettings<NSObject> {
public class ContextMenuSettings: ISettings<NSObject> {
var hideDefaultSystemContextMenuItems = false;

View File

@ -10,37 +10,37 @@ import Foundation
import WebKit
@available(iOS 11.0, *)
class CustomSchemeHandler : NSObject, WKURLSchemeHandler {
public class CustomSchemeHandler : NSObject, WKURLSchemeHandler {
var schemeHandlers: [Int:WKURLSchemeTask] = [:]
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
public func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
schemeHandlers[urlSchemeTask.hash] = urlSchemeTask
let inAppWebView = webView as! InAppWebView
if let url = urlSchemeTask.request.url {
inAppWebView.onLoadResourceCustomScheme(url: url.absoluteString, result: {(result) -> Void in
if result is FlutterError {
print((result as! FlutterError).message ?? "")
let callback = WebViewChannelDelegate.LoadResourceCustomSchemeCallback()
callback.nonNullSuccess = { (response: CustomSchemeResponse) in
if (self.schemeHandlers[urlSchemeTask.hash] != nil) {
let urlResponse = URLResponse(url: url, mimeType: response.contentType, expectedContentLength: -1, textEncodingName: response.contentEncoding)
urlSchemeTask.didReceive(urlResponse)
urlSchemeTask.didReceive(response.data)
urlSchemeTask.didFinish()
self.schemeHandlers.removeValue(forKey: urlSchemeTask.hash)
}
else if (result as? NSObject) == FlutterMethodNotImplemented {}
else {
let json: [String: Any]
if let r = result {
json = r as! [String: Any]
let urlResponse = URLResponse(url: url, mimeType: (json["contentType"] as! String), expectedContentLength: -1, textEncodingName: (json["contentEncoding"] as! String))
let data = json["data"] as! FlutterStandardTypedData
if (self.schemeHandlers[urlSchemeTask.hash] != nil) {
urlSchemeTask.didReceive(urlResponse)
urlSchemeTask.didReceive(data.data)
urlSchemeTask.didFinish()
self.schemeHandlers.removeValue(forKey: urlSchemeTask.hash)
}
}
}
})
return false
}
callback.error = { (code: String, message: String?, details: Any?) in
print(code + ", " + (message ?? ""))
}
if let channelDelegate = inAppWebView.channelDelegate {
channelDelegate.onLoadResourceCustomScheme(url: url, callback: callback)
} else {
callback.defaultBehaviour(nil)
}
}
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
public func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
schemeHandlers.removeValue(forKey: urlSchemeTask.hash)
}
}

View File

@ -8,24 +8,13 @@
import Foundation
import WebKit
public class FlutterWebViewController: NSObject, FlutterPlatformView {
public class FlutterWebViewController: NSObject, FlutterPlatformView, Disposable {
private weak var registrar: FlutterPluginRegistrar?
var webView: InAppWebView?
var viewId: Any = 0
var channel: FlutterMethodChannel?
var myView: UIView?
var methodCallDelegate: InAppWebViewMethodHandler?
init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Any, params: NSDictionary) {
super.init()
self.registrar = registrar
self.viewId = viewId
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_" + String(describing: viewId),
binaryMessenger: registrar.messenger())
myView = UIView(frame: frame)
myView!.clipsToBounds = true
@ -46,23 +35,26 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
let _ = settings.parse(settings: initialSettings)
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(settings: settings)
var webView: InAppWebView?
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView = webViewTransport.webView
webView!.id = viewId
let channel = FlutterMethodChannel(name: InAppWebView.METHOD_CHANNEL_NAME_PREFIX + String(describing: viewId),
binaryMessenger: registrar.messenger())
webView!.channelDelegate = WebViewChannelDelegate(webView: webView!, channel: channel)
webView!.frame = myView!.bounds
webView!.contextMenu = contextMenu
webView!.channel = channel!
webView!.initialUserScripts = userScripts
} else {
webView = InAppWebView(frame: myView!.bounds,
webView = InAppWebView(id: viewId,
registrar: registrar,
frame: myView!.bounds,
configuration: preWebviewConfiguration,
contextMenu: contextMenu,
channel: channel!,
userScripts: userScripts)
}
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
let pullToRefreshSettings = PullToRefreshSettings()
let _ = pullToRefreshSettings.parse(settings: pullToRefreshInitialSettings)
let pullToRefreshControl = PullToRefreshControl(registrar: registrar, id: viewId, settings: pullToRefreshSettings)
@ -80,11 +72,26 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
webView!.windowCreated = true
}
public func webView() -> InAppWebView? {
for subview in myView?.subviews ?? []
{
if let item = subview as? InAppWebView
{
return item
}
}
return nil
}
public func view() -> UIView {
return myView!
}
public func makeInitialLoad(params: NSDictionary) {
guard let webView = webView() else {
return
}
let windowId = params["windowId"] as? Int64
let initialUrlRequest = params["initialUrlRequest"] as? [String: Any?]
let initialFile = params["initialFile"] as? String
@ -92,8 +99,8 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
if windowId == nil {
if #available(iOS 11.0, *) {
self.webView!.configuration.userContentController.removeAllContentRuleLists()
if let contentBlockers = webView!.settings?.contentBlockers, contentBlockers.count > 0 {
webView.configuration.userContentController.removeAllContentRuleLists()
if let contentBlockers = webView.settings?.contentBlockers, contentBlockers.count > 0 {
do {
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
let blockRules = String(data: jsonData, encoding: String.Encoding.utf8)
@ -106,7 +113,7 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
return
}
let configuration = self.webView!.configuration
let configuration = webView.configuration
configuration.userContentController.add(contentRuleList!)
self.load(initialUrlRequest: initialUrlRequest, initialFile: initialFile, initialData: initialData)
@ -120,14 +127,18 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
load(initialUrlRequest: initialUrlRequest, initialFile: initialFile, initialData: initialData)
}
else if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView!.load(webViewTransport.request)
webView.load(webViewTransport.request)
}
}
func load(initialUrlRequest: [String:Any?]?, initialFile: String?, initialData: [String: String]?) {
guard let webView = webView() else {
return
}
if let initialFile = initialFile {
do {
try webView?.loadFile(assetFilePath: initialFile)
try webView.loadFile(assetFilePath: initialFile)
}
catch let error as NSError {
dump(error)
@ -139,34 +150,31 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
let encoding = initialData["encoding"]!
let baseUrl = URL(string: initialData["baseUrl"]!)!
var allowingReadAccessToURL: URL? = nil
if let allowingReadAccessTo = webView?.settings?.allowingReadAccessTo, baseUrl.scheme == "file" {
if let allowingReadAccessTo = webView.settings?.allowingReadAccessTo, baseUrl.scheme == "file" {
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
if allowingReadAccessToURL?.scheme != "file" {
allowingReadAccessToURL = nil
}
}
webView?.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL)
webView.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL)
}
else if let initialUrlRequest = initialUrlRequest {
let urlRequest = URLRequest.init(fromPluginMap: initialUrlRequest)
var allowingReadAccessToURL: URL? = nil
if let allowingReadAccessTo = webView?.settings?.allowingReadAccessTo, let url = urlRequest.url, url.scheme == "file" {
if let allowingReadAccessTo = webView.settings?.allowingReadAccessTo, let url = urlRequest.url, url.scheme == "file" {
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
if allowingReadAccessToURL?.scheme != "file" {
allowingReadAccessToURL = nil
}
}
webView?.loadUrl(urlRequest: urlRequest, allowingReadAccessTo: allowingReadAccessToURL)
webView.loadUrl(urlRequest: urlRequest, allowingReadAccessTo: allowingReadAccessToURL)
}
}
func dispose() {
channel?.setMethodCallHandler(nil)
channel = nil
methodCallDelegate?.dispose()
methodCallDelegate = nil
webView?.dispose()
webView = nil
public func dispose() {
if let webView = webView() {
webView.dispose()
}
myView = nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
//
// WebMessageChannel.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 10/03/21.
//
import Foundation
public class WebMessageChannel : FlutterMethodCallDelegate {
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_web_message_channel_"
var id: String
var channelDelegate: WebMessageChannelChannelDelegate?
weak var webView: InAppWebView?
var ports: [WebMessagePort] = []
public init(id: String) {
self.id = id
super.init()
let channel = FlutterMethodChannel(name: WebMessageChannel.METHOD_CHANNEL_NAME_PREFIX + id,
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
self.channelDelegate = WebMessageChannelChannelDelegate(webMessageChannel: self, channel: channel)
self.ports = [
WebMessagePort(name: "port1", webMessageChannel: self),
WebMessagePort(name: "port2", webMessageChannel: self)
]
}
public func initJsInstance(webView: InAppWebView, completionHandler: ((WebMessageChannel) -> Void)? = nil) {
self.webView = webView
if let webView = self.webView {
webView.evaluateJavascript(source: """
(function() {
\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"] = new MessageChannel();
})();
""") { (_) in
completionHandler?(self)
}
} else {
completionHandler?(self)
}
}
public func toMap() -> [String:Any?] {
return [
"id": id
]
}
public func dispose() {
channelDelegate?.dispose()
channelDelegate = nil
for port in ports {
port.dispose()
}
ports.removeAll()
webView?.evaluateJavascript(source: """
(function() {
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
if (webMessageChannel != null) {
webMessageChannel.port1.close();
webMessageChannel.port2.close();
delete \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
}
})();
""")
webView = nil
}
deinit {
debugPrint("WebMessageChannel - dealloc")
dispose()
}
}

View File

@ -1,43 +1,18 @@
//
// WebMessageChannel.swift
// WebMessageChannelChannelDelegate.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 10/03/21.
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class WebMessageChannel : FlutterMethodCallDelegate {
var id: String
var channel: FlutterMethodChannel?
var webView: InAppWebView?
var ports: [WebMessagePort] = []
public class WebMessageChannelChannelDelegate : ChannelDelegate {
private weak var webMessageChannel: WebMessageChannel?
public init(id: String) {
self.id = id
super.init()
self.channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_web_message_channel_" + id,
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
self.channel?.setMethodCallHandler(self.handle)
self.ports = [
WebMessagePort(name: "port1", webMessageChannel: self),
WebMessagePort(name: "port2", webMessageChannel: self)
]
}
public func initJsInstance(webView: InAppWebView, completionHandler: ((WebMessageChannel) -> Void)? = nil) {
self.webView = webView
if let webView = self.webView {
webView.evaluateJavascript(source: """
(function() {
\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"] = new MessageChannel();
})();
""") { (_) in
completionHandler?(self)
}
} else {
completionHandler?(self)
}
public init(webMessageChannel: WebMessageChannel, channel: FlutterMethodChannel) {
super.init(channel: channel)
self.webMessageChannel = webMessageChannel
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
@ -45,7 +20,7 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
switch call.method {
case "setWebMessageCallback":
if let _ = webView, ports.count > 0 {
if let _ = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
let index = arguments!["index"] as! Int
let port = ports[index]
do {
@ -57,11 +32,11 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
}
} else {
result(true)
result(true)
}
break
case "postMessage":
if let webView = webView, ports.count > 0 {
if let webView = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
let index = arguments!["index"] as! Int
let port = ports[index]
let message = arguments!["message"] as! [String: Any?]
@ -90,7 +65,7 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
}
break
case "close":
if let _ = webView, ports.count > 0 {
if let _ = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
let index = arguments!["index"] as! Int
let port = ports[index]
do {
@ -118,34 +93,12 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
channel?.invokeMethod("onMessage", arguments: arguments)
}
public func toMap () -> [String:Any?] {
return [
"id": id
]
}
public func dispose() {
channel?.setMethodCallHandler(nil)
channel = nil
for port in ports {
port.dispose()
}
ports.removeAll()
webView?.evaluateJavascript(source: """
(function() {
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
if (webMessageChannel != null) {
webMessageChannel.port1.close();
webMessageChannel.port2.close();
delete \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
}
})();
""")
webView = nil
public override func dispose() {
super.dispose()
webMessageChannel = nil
}
deinit {
debugPrint("WebMessageChannel - dealloc")
dispose()
}
}

View File

@ -9,19 +9,19 @@ import Foundation
import WebKit
public class WebMessageListener : FlutterMethodCallDelegate {
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_web_message_listener_"
var jsObjectName: String
var allowedOriginRules: Set<String>
var channel: FlutterMethodChannel?
var webView: InAppWebView?
var channelDelegate: WebMessageListenerChannelDelegate?
weak var webView: InAppWebView?
public init(jsObjectName: String, allowedOriginRules: Set<String>) {
self.jsObjectName = jsObjectName
self.allowedOriginRules = allowedOriginRules
super.init()
self.channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_web_message_listener_" + self.jsObjectName,
let channel = FlutterMethodChannel(name: WebMessageListener.METHOD_CHANNEL_NAME_PREFIX + self.jsObjectName,
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
self.channel?.setMethodCallHandler(self.handle)
self.channelDelegate = WebMessageListenerChannelDelegate(webMessageListener: self, channel: channel)
}
public func assertOriginRulesValid() throws {
@ -122,41 +122,6 @@ public class WebMessageListener : FlutterMethodCallDelegate {
)
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "postMessage":
if let webView = webView {
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
let messageEscaped = (arguments!["message"] as! String).replacingOccurrences(of: "\'", with: "\\'")
let source = """
(function() {
var webMessageListener = window['\(jsObjectNameEscaped)'];
if (webMessageListener != null) {
var event = {data: '\(messageEscaped)'};
if (webMessageListener.onmessage != null) {
webMessageListener.onmessage(event);
}
for (var listener of webMessageListener.listeners) {
listener(event);
}
}
})();
"""
webView.evaluateJavascript(source: source) { (_) in
result(true)
}
} else {
result(true)
}
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public func isOriginAllowed(scheme: String?, host: String?, port: Int?) -> Bool {
for allowedOriginRule in allowedOriginRules {
if allowedOriginRule == "*" {
@ -204,19 +169,10 @@ public class WebMessageListener : FlutterMethodCallDelegate {
}
return false
}
public func onPostMessage(message: String?, sourceOrigin: URL?, isMainFrame: Bool) {
let arguments: [String:Any?] = [
"message": message,
"sourceOrigin": sourceOrigin?.absoluteString,
"isMainFrame": isMainFrame
]
channel?.invokeMethod("onPostMessage", arguments: arguments)
}
public func dispose() {
channel?.setMethodCallHandler(nil)
channel = nil
channelDelegate?.dispose()
channelDelegate = nil
webView = nil
}

View File

@ -0,0 +1,71 @@
//
// WebMessageListenerChannelDelegate.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class WebMessageListenerChannelDelegate : ChannelDelegate {
private weak var webMessageListener: WebMessageListener?
public init(webMessageListener: WebMessageListener, channel: FlutterMethodChannel) {
super.init(channel: channel)
self.webMessageListener = webMessageListener
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "postMessage":
if let webView = webMessageListener?.webView, let jsObjectName = webMessageListener?.jsObjectName {
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
let messageEscaped = (arguments!["message"] as! String).replacingOccurrences(of: "\'", with: "\\'")
let source = """
(function() {
var webMessageListener = window['\(jsObjectNameEscaped)'];
if (webMessageListener != null) {
var event = {data: '\(messageEscaped)'};
if (webMessageListener.onmessage != null) {
webMessageListener.onmessage(event);
}
for (var listener of webMessageListener.listeners) {
listener(event);
}
}
})();
"""
webView.evaluateJavascript(source: source) { (_) in
result(true)
}
} else {
result(true)
}
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public func onPostMessage(message: String?, sourceOrigin: URL?, isMainFrame: Bool) {
let arguments: [String:Any?] = [
"message": message,
"sourceOrigin": sourceOrigin?.absoluteString,
"isMainFrame": isMainFrame
]
channel?.invokeMethod("onPostMessage", arguments: arguments)
}
public override func dispose() {
super.dispose()
webMessageListener = nil
}
deinit {
dispose()
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,654 +0,0 @@
//
// WebViewMethodHandler.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 01/02/21.
//
import Foundation
import WebKit
public class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
var webView: InAppWebView?
init(webView: InAppWebView) {
super.init()
self.webView = webView
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "getUrl":
result(webView?.url?.absoluteString)
break
case "getTitle":
result(webView?.title)
break
case "getProgress":
result( (webView != nil) ? Int(webView!.estimatedProgress * 100) : nil )
break
case "loadUrl":
let urlRequest = arguments!["urlRequest"] as! [String:Any?]
let allowingReadAccessTo = arguments!["allowingReadAccessTo"] as? String
var allowingReadAccessToURL: URL? = nil
if let allowingReadAccessTo = allowingReadAccessTo {
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
}
webView?.loadUrl(urlRequest: URLRequest.init(fromPluginMap: urlRequest), allowingReadAccessTo: allowingReadAccessToURL)
result(true)
break
case "postUrl":
if let webView = webView {
let url = arguments!["url"] as! String
let postData = arguments!["postData"] as! FlutterStandardTypedData
webView.postUrl(url: URL(string: url)!, postData: postData.data)
}
result(true)
break
case "loadData":
let data = arguments!["data"] as! String
let mimeType = arguments!["mimeType"] as! String
let encoding = arguments!["encoding"] as! String
let baseUrl = URL(string: arguments!["baseUrl"] as! String)!
let allowingReadAccessTo = arguments!["allowingReadAccessTo"] as? String
var allowingReadAccessToURL: URL? = nil
if let allowingReadAccessTo = allowingReadAccessTo {
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
}
webView?.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL)
result(true)
break
case "loadFile":
let assetFilePath = arguments!["assetFilePath"] as! String
do {
try webView?.loadFile(assetFilePath: assetFilePath)
}
catch let error as NSError {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error.domain, details: nil))
return
}
result(true)
break
case "evaluateJavascript":
if let webView = webView {
let source = arguments!["source"] as! String
let contentWorldMap = arguments!["contentWorld"] as? [String:Any?]
if #available(iOS 14.0, *), let contentWorldMap = contentWorldMap {
let contentWorld = WKContentWorld.fromMap(map: contentWorldMap, windowId: webView.windowId)!
webView.evaluateJavascript(source: source, contentWorld: contentWorld) { (value) in
result(value)
}
} else {
webView.evaluateJavascript(source: source) { (value) in
result(value)
}
}
}
else {
result(nil)
}
break
case "injectJavascriptFileFromUrl":
let urlFile = arguments!["urlFile"] as! String
let scriptHtmlTagAttributes = arguments!["scriptHtmlTagAttributes"] as? [String:Any?]
webView?.injectJavascriptFileFromUrl(urlFile: urlFile, scriptHtmlTagAttributes: scriptHtmlTagAttributes)
result(true)
break
case "injectCSSCode":
let source = arguments!["source"] as! String
webView?.injectCSSCode(source: source)
result(true)
break
case "injectCSSFileFromUrl":
let urlFile = arguments!["urlFile"] as! String
let cssLinkHtmlTagAttributes = arguments!["cssLinkHtmlTagAttributes"] as? [String:Any?]
webView?.injectCSSFileFromUrl(urlFile: urlFile, cssLinkHtmlTagAttributes: cssLinkHtmlTagAttributes)
result(true)
break
case "reload":
webView?.reload()
result(true)
break
case "goBack":
webView?.goBack()
result(true)
break
case "canGoBack":
result(webView?.canGoBack ?? false)
break
case "goForward":
webView?.goForward()
result(true)
break
case "canGoForward":
result(webView?.canGoForward ?? false)
break
case "goBackOrForward":
let steps = arguments!["steps"] as! Int
webView?.goBackOrForward(steps: steps)
result(true)
break
case "canGoBackOrForward":
let steps = arguments!["steps"] as! Int
result(webView?.canGoBackOrForward(steps: steps) ?? false)
break
case "stopLoading":
webView?.stopLoading()
result(true)
break
case "isLoading":
result(webView?.isLoading ?? false)
break
case "takeScreenshot":
if let webView = webView, #available(iOS 11.0, *) {
let screenshotConfiguration = arguments!["screenshotConfiguration"] as? [String: Any?]
webView.takeScreenshot(with: screenshotConfiguration, completionHandler: { (screenshot) -> Void in
result(screenshot)
})
}
else {
result(nil)
}
break
case "setSettings":
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
let inAppBrowserSettings = InAppBrowserSettings()
let inAppBrowserSettingsMap = arguments!["settings"] as! [String: Any]
let _ = inAppBrowserSettings.parse(settings: inAppBrowserSettingsMap)
iabController.setSettings(newSettings: inAppBrowserSettings, newSettingsMap: inAppBrowserSettingsMap)
} else {
let inAppWebViewSettings = InAppWebViewSettings()
let inAppWebViewSettingsMap = arguments!["settings"] as! [String: Any]
let _ = inAppWebViewSettings.parse(settings: inAppWebViewSettingsMap)
webView?.setSettings(newSettings: inAppWebViewSettings, newSettingsMap: inAppWebViewSettingsMap)
}
result(true)
break
case "getSettings":
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
result(iabController.getSettings())
} else {
result(webView?.getSettings())
}
break
case "close":
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
iabController.close {
result(true)
}
} else {
result(FlutterMethodNotImplemented)
}
break
case "show":
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
iabController.show {
result(true)
}
} else {
result(FlutterMethodNotImplemented)
}
break
case "hide":
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
iabController.hide {
result(true)
}
} else {
result(FlutterMethodNotImplemented)
}
break
case "getCopyBackForwardList":
result(webView?.getCopyBackForwardList())
break
case "findAllAsync":
if let webView = webView {
let find = arguments!["find"] as! String
webView.findAllAsync(find: find, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "findNext":
if let webView = webView {
let forward = arguments!["forward"] as! Bool
webView.findNext(forward: forward, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "clearMatches":
if let webView = webView {
webView.clearMatches(completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "clearCache":
webView?.clearCache()
result(true)
break
case "scrollTo":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView?.scrollTo(x: x, y: y, animated: animated)
result(true)
break
case "scrollBy":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView?.scrollBy(x: x, y: y, animated: animated)
result(true)
break
case "pauseTimers":
webView?.pauseTimers()
result(true)
break
case "resumeTimers":
webView?.resumeTimers()
result(true)
break
case "printCurrentPage":
if let webView = webView {
webView.printCurrentPage(printCompletionHandler: {(completed, error) in
if !completed, let err = error {
print(err.localizedDescription)
result(false)
return
}
result(true)
})
} else {
result(false)
}
break
case "getContentHeight":
result(webView?.getContentHeight())
break
case "zoomBy":
let zoomFactor = (arguments!["zoomFactor"] as! NSNumber).floatValue
let animated = arguments!["animated"] as! Bool
webView?.zoomBy(zoomFactor: zoomFactor, animated: animated)
result(true)
break
case "reloadFromOrigin":
webView?.reloadFromOrigin()
result(true)
break
case "getOriginalUrl":
result(webView?.getOriginalUrl()?.absoluteString)
break
case "getZoomScale":
result(webView?.getZoomScale())
break
case "hasOnlySecureContent":
result(webView?.hasOnlySecureContent ?? false)
break
case "getSelectedText":
if let webView = webView {
webView.getSelectedText { (value, error) in
if let err = error {
print(err.localizedDescription)
result("")
return
}
result(value)
}
}
else {
result(nil)
}
break
case "getHitTestResult":
if let webView = webView {
webView.getHitTestResult { (hitTestResult) in
result(hitTestResult.toMap())
}
}
else {
result(nil)
}
break
case "clearFocus":
webView?.clearFocus()
result(true)
break
case "setContextMenu":
if let webView = webView {
let contextMenu = arguments!["contextMenu"] as? [String: Any]
webView.contextMenu = contextMenu
result(true)
} else {
result(false)
}
break
case "requestFocusNodeHref":
if let webView = webView {
webView.requestFocusNodeHref { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(nil)
}
break
case "requestImageRef":
if let webView = webView {
webView.requestImageRef { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(nil)
}
break
case "getScrollX":
if let webView = webView {
result(Int(webView.scrollView.contentOffset.x))
} else {
result(nil)
}
break
case "getScrollY":
if let webView = webView {
result(Int(webView.scrollView.contentOffset.y))
} else {
result(nil)
}
break
case "getCertificate":
result(webView?.getCertificate()?.toMap())
break
case "addUserScript":
if let webView = webView {
let userScriptMap = arguments!["userScript"] as! [String: Any?]
let userScript = UserScript.fromMap(map: userScriptMap, windowId: webView.windowId)!
webView.configuration.userContentController.addUserOnlyScript(userScript)
webView.configuration.userContentController.sync(scriptMessageHandler: webView)
}
result(true)
break
case "removeUserScript":
let index = arguments!["index"] as! Int
let userScriptMap = arguments!["userScript"] as! [String: Any?]
let userScript = UserScript.fromMap(map: userScriptMap, windowId: webView?.windowId)!
webView?.configuration.userContentController.removeUserOnlyScript(at: index, injectionTime: userScript.injectionTime)
result(true)
break
case "removeUserScriptsByGroupName":
let groupName = arguments!["groupName"] as! String
webView?.configuration.userContentController.removeUserOnlyScripts(with: groupName)
result(true)
break
case "removeAllUserScripts":
webView?.configuration.userContentController.removeAllUserOnlyScripts()
result(true)
break
case "callAsyncJavaScript":
if let webView = webView, #available(iOS 10.3, *) {
if #available(iOS 14.0, *) {
let functionBody = arguments!["functionBody"] as! String
let functionArguments = arguments!["arguments"] as! [String:Any]
var contentWorld = WKContentWorld.page
if let contentWorldMap = arguments!["contentWorld"] as? [String:Any?] {
contentWorld = WKContentWorld.fromMap(map: contentWorldMap, windowId: webView.windowId)!
}
webView.callAsyncJavaScript(functionBody: functionBody, arguments: functionArguments, contentWorld: contentWorld) { (value) in
result(value)
}
} else {
let functionBody = arguments!["functionBody"] as! String
let functionArguments = arguments!["arguments"] as! [String:Any]
webView.callAsyncJavaScript(functionBody: functionBody, arguments: functionArguments) { (value) in
result(value)
}
}
}
else {
result(nil)
}
break
case "createPdf":
if let webView = webView, #available(iOS 14.0, *) {
let configuration = arguments!["pdfConfiguration"] as? [String: Any?]
webView.createPdf(configuration: configuration, completionHandler: { (pdf) -> Void in
result(pdf)
})
}
else {
result(nil)
}
break
case "createWebArchiveData":
if let webView = webView, #available(iOS 14.0, *) {
webView.createWebArchiveData(dataCompletionHandler: { (webArchiveData) -> Void in
result(webArchiveData)
})
}
else {
result(nil)
}
break
case "saveWebArchive":
if let webView = webView, #available(iOS 14.0, *) {
let filePath = arguments!["filePath"] as! String
let autoname = arguments!["autoname"] as! Bool
webView.saveWebArchive(filePath: filePath, autoname: autoname, completionHandler: { (path) -> Void in
result(path)
})
}
else {
result(nil)
}
break
case "isSecureContext":
if let webView = webView {
webView.isSecureContext(completionHandler: { (isSecureContext) in
result(isSecureContext)
})
}
else {
result(false)
}
break
case "createWebMessageChannel":
if let webView = webView {
let _ = webView.createWebMessageChannel { (webMessageChannel) in
result(webMessageChannel.toMap())
}
} else {
result(nil)
}
break
case "postWebMessage":
if let webView = webView {
let message = arguments!["message"] as! [String: Any?]
let targetOrigin = arguments!["targetOrigin"] as! String
var ports: [WebMessagePort] = []
let portsMap = message["ports"] as? [[String: Any?]]
if let portsMap = portsMap {
for portMap in portsMap {
let webMessageChannelId = portMap["webMessageChannelId"] as! String
let index = portMap["index"] as! Int
if let webMessageChannel = webView.webMessageChannels[webMessageChannelId] {
ports.append(webMessageChannel.ports[index])
}
}
}
let webMessage = WebMessage(data: message["data"] as? String, ports: ports)
do {
try webView.postWebMessage(message: webMessage, targetOrigin: targetOrigin) { (_) in
result(true)
}
} catch let error as NSError {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error.domain, details: nil))
}
} else {
result(false)
}
break
case "addWebMessageListener":
if let webView = webView {
let webMessageListenerMap = arguments!["webMessageListener"] as! [String: Any?]
let webMessageListener = WebMessageListener.fromMap(map: webMessageListenerMap)!
do {
try webView.addWebMessageListener(webMessageListener: webMessageListener)
result(false)
} catch let error as NSError {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error.domain, details: nil))
}
} else {
result(false)
}
break
case "canScrollVertically":
if let webView = webView {
result(webView.canScrollVertically())
} else {
result(false)
}
break
case "canScrollHorizontally":
if let webView = webView {
result(webView.canScrollHorizontally())
} else {
result(false)
}
break
case "pauseAllMediaPlayback":
if let webView = webView, #available(iOS 15.0, *) {
webView.pauseAllMediaPlayback(completionHandler: { () -> Void in
result(true)
})
} else {
result(false)
}
break
case "setAllMediaPlaybackSuspended":
if let webView = webView, #available(iOS 15.0, *) {
let suspended = arguments!["suspended"] as! Bool
webView.setAllMediaPlaybackSuspended(suspended, completionHandler: { () -> Void in
result(true)
})
} else {
result(false)
}
break
case "closeAllMediaPresentations":
if let webView = self.webView, #available(iOS 14.5, *) {
// closeAllMediaPresentations with completionHandler v15.0 makes the app crash
// with error EXC_BAD_ACCESS, so use closeAllMediaPresentations v14.5
webView.closeAllMediaPresentations()
result(true)
} else {
result(false)
}
break
case "requestMediaPlaybackState":
if let webView = webView, #available(iOS 15.0, *) {
webView.requestMediaPlaybackState(completionHandler: { (state) -> Void in
result(state.rawValue)
})
} else {
result(nil)
}
break
case "getMetaThemeColor":
if let webView = webView, #available(iOS 15.0, *) {
result(webView.themeColor?.hexString)
} else {
result(nil)
}
break
case "isInFullscreen":
// if let webView = webView, #available(iOS 15.0, *) {
// result(webView.fullscreenState == .inFullscreen)
// }
if let webView = webView {
result(webView.inFullscreen)
}
else {
result(false)
}
break
case "getCameraCaptureState":
if let webView = webView, #available(iOS 15.0, *) {
result(webView.cameraCaptureState.rawValue)
} else {
result(nil)
}
break
case "setCameraCaptureState":
if let webView = webView, #available(iOS 15.0, *) {
let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none
webView.setCameraCaptureState(state) {
result(true)
}
} else {
result(false)
}
break
case "getMicrophoneCaptureState":
if let webView = webView, #available(iOS 15.0, *) {
result(webView.microphoneCaptureState.rawValue)
} else {
result(nil)
}
break
case "setMicrophoneCaptureState":
if let webView = webView, #available(iOS 15.0, *) {
let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none
webView.setMicrophoneCaptureState(state) {
result(true)
}
} else {
result(false)
}
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public func dispose() {
webView = nil
}
deinit {
debugPrint("InAppWebViewMethodHandler - dealloc")
dispose()
}
}

View File

@ -8,7 +8,7 @@
import Foundation
import WebKit
class InAppWebViewStatic: ChannelDelegate {
public class InAppWebViewStatic: ChannelDelegate {
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_static"
static var registrar: FlutterPluginRegistrar?
static var webViewForUserAgent: WKWebView?
@ -73,4 +73,8 @@ class InAppWebViewStatic: ChannelDelegate {
InAppWebViewStatic.webViewForUserAgent = nil
InAppWebViewStatic.defaultUserAgent = nil
}
deinit {
dispose()
}
}

View File

@ -9,7 +9,7 @@ import Foundation
import WebKit
@available(iOS 11.0, *)
class MyCookieManager: ChannelDelegate {
public class MyCookieManager: ChannelDelegate {
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_cookiemanager"
static var registrar: FlutterPluginRegistrar?
static var httpCookieStore: WKHTTPCookieStore?
@ -295,4 +295,8 @@ class MyCookieManager: ChannelDelegate {
MyCookieManager.registrar = nil
MyCookieManager.httpCookieStore = nil
}
deinit {
dispose()
}
}

View File

@ -9,7 +9,7 @@ import Foundation
import WebKit
@available(iOS 9.0, *)
class MyWebStorageManager: ChannelDelegate {
public class MyWebStorageManager: ChannelDelegate {
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_webstoragemanager"
static var registrar: FlutterPluginRegistrar?
static var websiteDataStore: WKWebsiteDataStore?
@ -105,4 +105,8 @@ class MyWebStorageManager: ChannelDelegate {
MyWebStorageManager.registrar = nil
MyWebStorageManager.websiteDataStore = nil
}
deinit {
dispose()
}
}

View File

@ -7,7 +7,7 @@
import Foundation
class PlatformUtil: ChannelDelegate {
public class PlatformUtil: ChannelDelegate {
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_platformutil"
static var registrar: FlutterPluginRegistrar?
@ -59,4 +59,8 @@ class PlatformUtil: ChannelDelegate {
super.dispose()
PlatformUtil.registrar = nil
}
deinit {
dispose()
}
}

View File

@ -8,9 +8,9 @@
import Foundation
public class PullToRefreshChannelDelegate : ChannelDelegate {
private var pullToRefreshControl: PullToRefreshControl?
private weak var pullToRefreshControl: PullToRefreshControl?
public init(pullToRefreshControl: PullToRefreshControl , channel: FlutterMethodChannel) {
public init(pullToRefreshControl: PullToRefreshControl, channel: FlutterMethodChannel) {
super.init(channel: channel)
self.pullToRefreshControl = pullToRefreshControl
}
@ -87,4 +87,8 @@ public class PullToRefreshChannelDelegate : ChannelDelegate {
super.dispose()
pullToRefreshControl = nil
}
deinit {
dispose()
}
}

View File

@ -94,4 +94,8 @@ public class ChromeSafariBrowserManager: ChannelDelegate {
}
ChromeSafariBrowserManager.browsers.removeAll()
}
deinit {
dispose()
}
}

View File

@ -8,8 +8,7 @@
import Foundation
public class SafariViewControllerChannelDelegate : ChannelDelegate {
private var safariViewController: SafariViewController?
private weak var safariViewController: SafariViewController?
public init(safariViewController: SafariViewController, channel: FlutterMethodChannel) {
super.init(channel: channel)
@ -60,4 +59,8 @@ public class SafariViewControllerChannelDelegate : ChannelDelegate {
super.dispose()
safariViewController = nil
}
deinit {
dispose()
}
}

View File

@ -0,0 +1,32 @@
//
// BaseCallbackResult.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 06/05/22.
//
import Foundation
public class BaseCallbackResult<T> : CallbackResult<T> {
override init() {
super.init()
self.success = { [weak self] (obj: Any?) in
let result: T? = self?.decodeResult(obj)
var shouldRunDefaultBehaviour = false
if let result = result {
shouldRunDefaultBehaviour = self?.nonNullSuccess(result) ?? shouldRunDefaultBehaviour
} else {
shouldRunDefaultBehaviour = self?.nullSuccess() ?? shouldRunDefaultBehaviour
}
if shouldRunDefaultBehaviour {
self?.defaultBehaviour(result)
}
}
self.notImplemented = { [weak self] in
self?.defaultBehaviour(nil)
}
}
}

View File

@ -0,0 +1,18 @@
//
// CallbackResult.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 06/05/22.
//
import Foundation
public class CallbackResult<T> : MethodChannelResult {
public var notImplemented: () -> Void = {}
public var success: (Any?) -> Void = {_ in }
public var error: (String, String?, Any?) -> Void = {_,_,_ in }
public var nonNullSuccess: (T) -> Bool = {_ in true}
public var nullSuccess: () -> Bool = {true}
public var defaultBehaviour: (T?) -> Void = {_ in }
public var decodeResult: (Any?) -> T? = {_ in nil}
}

View File

@ -7,7 +7,7 @@
import Foundation
class ClientCertChallenge: NSObject {
public class ClientCertChallenge: NSObject {
var protectionSpace: URLProtectionSpace!
public init(fromChallenge: URLAuthenticationChallenge) {

View File

@ -0,0 +1,33 @@
//
// ClientCertResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class ClientCertResponse : NSObject {
var certificatePath: String
var certificatePassword: String?
var keyStoreType: String?
var action: Int?
public init(certificatePath: String, certificatePassword: String? = nil, keyStoreType: String? = nil, action: Int? = nil) {
self.certificatePath = certificatePath
self.certificatePassword = certificatePassword
self.keyStoreType = keyStoreType
self.action = action
}
public static func fromMap(map: [String:Any?]?) -> ClientCertResponse? {
guard let map = map else {
return nil
}
let certificatePath = map["certificatePath"] as! String
let certificatePassword = map["certificatePassword"] as? String
let keyStoreType = map["keyStoreType"] as? String
let action = map["action"] as? Int
return ClientCertResponse(certificatePath: certificatePath, certificatePassword: certificatePassword, keyStoreType: keyStoreType, action: action)
}
}

View File

@ -0,0 +1,31 @@
//
// CreateWindowAction.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
import WebKit
public class CreateWindowAction : NSObject {
var navigationAction: WKNavigationAction
var windowId: Int64
var windowFeatures: WKWindowFeatures
var isDialog: Bool?
public init(navigationAction: WKNavigationAction, windowId: Int64, windowFeatures: WKWindowFeatures, isDialog: Bool? = nil) {
self.navigationAction = navigationAction
self.windowId = windowId
self.windowFeatures = windowFeatures
self.isDialog = isDialog
}
public func toMap () -> [String:Any?] {
var map = navigationAction.toMap()
map["windowId"] = windowId
map["windowFeatures"] = windowFeatures.toMap()
map["isDialog"] = isDialog
return map
}
}

View File

@ -0,0 +1,30 @@
//
// CustomSchemeResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class CustomSchemeResponse : NSObject {
var data: Data
var contentType: String
var contentEncoding: String
public init(data: Data, contentType: String, contentEncoding: String) {
self.data = data
self.contentType = contentType
self.contentEncoding = contentEncoding
}
public static func fromMap(map: [String:Any?]?) -> CustomSchemeResponse? {
guard let map = map else {
return nil
}
let data = map["data"] as! FlutterStandardTypedData
let contentType = map["contentType"] as! String
let contentEncoding = map["contentEncoding"] as! String
return CustomSchemeResponse(data: data.data, contentType: contentType, contentEncoding: contentEncoding)
}
}

View File

@ -0,0 +1,26 @@
//
// FlutterMethodChannel.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 06/05/22.
//
import Foundation
import Flutter
extension FlutterMethodChannel {
public func invokeMethod(_ method: String, arguments: Any, callback: MethodChannelResult) {
invokeMethod(method, arguments: arguments) {(result) -> Void in
if result is FlutterError {
let error = result as! FlutterError
callback.error(error.code, error.message, error.details)
}
else if (result as? NSObject) == FlutterMethodNotImplemented {
callback.notImplemented()
}
else {
callback.success(result)
}
}
}
}

View File

@ -0,0 +1,33 @@
//
// HttpAuthResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class HttpAuthResponse : NSObject {
var username: String
var password: String
var permanentPersistence: Bool
var action: Int?
public init(username: String, password: String, permanentPersistence: Bool, action: Int? = nil) {
self.username = username
self.password = password
self.permanentPersistence = permanentPersistence
self.action = action
}
public static func fromMap(map: [String:Any?]?) -> HttpAuthResponse? {
guard let map = map else {
return nil
}
let username = map["username"] as! String
let password = map["password"] as! String
let permanentPersistence = map["permanentPersistence"] as! Bool
let action = map["action"] as? Int
return HttpAuthResponse(username: username, password: password, permanentPersistence: permanentPersistence, action: action)
}
}

View File

@ -7,7 +7,7 @@
import Foundation
class HttpAuthenticationChallenge: NSObject {
public class HttpAuthenticationChallenge: NSObject {
var protectionSpace: URLProtectionSpace!
var previousFailureCount: Int = 0
var failureResponse: URLResponse?

View File

@ -0,0 +1,33 @@
//
// JsAlertResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 06/05/22.
//
import Foundation
public class JsAlertResponse: NSObject {
var message: String
var confirmButtonTitle: String
var handledByClient: Bool
var action: Int?
public init(message: String, confirmButtonTitle: String, handledByClient: Bool, action: Int? = nil) {
self.message = message
self.confirmButtonTitle = confirmButtonTitle
self.handledByClient = handledByClient
self.action = action
}
public static func fromMap(map: [String:Any?]?) -> JsAlertResponse? {
guard let map = map else {
return nil
}
let message = map["message"] as! String
let confirmButtonTitle = map["confirmButtonTitle"] as! String
let handledByClient = map["handledByClient"] as! Bool
let action = map["action"] as? Int
return JsAlertResponse(message: message, confirmButtonTitle: confirmButtonTitle, handledByClient: handledByClient, action: action)
}
}

View File

@ -0,0 +1,36 @@
//
// JsConfirmResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class JsConfirmResponse: NSObject {
var message: String
var confirmButtonTitle: String
var cancelButtonTitle: String
var handledByClient: Bool
var action: Int?
public init(message: String, confirmButtonTitle: String, cancelButtonTitle: String, handledByClient: Bool, action: Int? = nil) {
self.message = message
self.confirmButtonTitle = confirmButtonTitle
self.cancelButtonTitle = cancelButtonTitle
self.handledByClient = handledByClient
self.action = action
}
public static func fromMap(map: [String:Any?]?) -> JsConfirmResponse? {
guard let map = map else {
return nil
}
let message = map["message"] as! String
let confirmButtonTitle = map["confirmButtonTitle"] as! String
let cancelButtonTitle = map["cancelButtonTitle"] as! String
let handledByClient = map["handledByClient"] as! Bool
let action = map["action"] as? Int
return JsConfirmResponse(message: message, confirmButtonTitle: confirmButtonTitle, cancelButtonTitle: cancelButtonTitle, handledByClient: handledByClient, action: action)
}
}

View File

@ -0,0 +1,43 @@
//
// JsPromptResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class JsPromptResponse: NSObject {
var message: String
var defaultValue: String
var confirmButtonTitle: String
var cancelButtonTitle: String
var handledByClient: Bool
var value: String?
var action: Int?
public init(message: String, defaultValue: String, confirmButtonTitle: String, cancelButtonTitle: String, handledByClient: Bool, value: String? = nil, action: Int? = nil) {
self.message = message
self.defaultValue = defaultValue
self.confirmButtonTitle = confirmButtonTitle
self.cancelButtonTitle = cancelButtonTitle
self.handledByClient = handledByClient
self.value = value
self.action = action
}
public static func fromMap(map: [String:Any?]?) -> JsPromptResponse? {
guard let map = map else {
return nil
}
let message = map["message"] as! String
let defaultValue = map["defaultValue"] as! String
let confirmButtonTitle = map["confirmButtonTitle"] as! String
let cancelButtonTitle = map["cancelButtonTitle"] as! String
let handledByClient = map["handledByClient"] as! Bool
let value = map["value"] as? String
let action = map["action"] as? Int
return JsPromptResponse(message: message, defaultValue: defaultValue, confirmButtonTitle: confirmButtonTitle, cancelButtonTitle: cancelButtonTitle,
handledByClient: handledByClient, value: value, action: action)
}
}

View File

@ -0,0 +1,14 @@
//
// MethodChannelResult.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 06/05/22.
//
import Foundation
public protocol MethodChannelResult {
var success: (_ obj: Any?) -> Void { get set }
var error: (_ code: String, _ message: String?, _ details: Any?) -> Void { get set }
var notImplemented: () -> Void { get set }
}

View File

@ -0,0 +1,27 @@
//
// PermissionResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class PermissionResponse : NSObject {
var resources: [String]
var action: Int?
public init(resources: [String], action: Int? = nil) {
self.resources = resources
self.action = action
}
public static func fromMap(map: [String:Any?]?) -> PermissionResponse? {
guard let map = map else {
return nil
}
let resources = map["resources"] as! [String]
let action = map["action"] as? Int
return PermissionResponse(resources: resources, action: action)
}
}

View File

@ -0,0 +1,24 @@
//
// ServerTrustAuthResponse.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 07/05/22.
//
import Foundation
public class ServerTrustAuthResponse : NSObject {
var action: Int?
public init(action: Int? = nil) {
self.action = action
}
public static func fromMap(map: [String:Any?]?) -> ServerTrustAuthResponse? {
guard let map = map else {
return nil
}
let action = map["action"] as? Int
return ServerTrustAuthResponse(action: action)
}
}

View File

@ -7,7 +7,7 @@
import Foundation
class ServerTrustChallenge: NSObject {
public class ServerTrustChallenge: NSObject {
var protectionSpace: URLProtectionSpace!
public init(fromChallenge: URLAuthenticationChallenge) {

View File

@ -213,7 +213,7 @@ public class Util {
} else {
// normalize grouped zeros ::
var zeros: [String] = []
for j in ipv6.count...8 {
for _ in ipv6.count...8 {
zeros.append("0000")
}
fullIPv6[i] = zeros.joined(separator: ":")

View File

@ -8,6 +8,6 @@
import Foundation
import WebKit
class WKProcessPoolManager {
public class WKProcessPoolManager {
static let sharedProcessPool = WKProcessPool()
}