Why is WKWebView not opening links with target="_blank"? - ios

WKWebView does not open any links which have target="_blank" a.k.a. 'Open in new Window' attribute in their HTML <a href>-Tag.

My solution is to cancel the navigation and load the request with loadRequest: again. This will be come the similar behavior like UIWebView which always open new window in the current frame.
Implement the WKUIDelegate delegate and set it to _webview.uiDelegate. Then implement:
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}

The answer from #Cloud Xu is the correct answer. Just for reference, here it is in Swift:
// this handles target=_blank links by opening them in the same view
func webView(webView: WKWebView!, createWebViewWithConfiguration configuration: WKWebViewConfiguration!, forNavigationAction navigationAction: WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
if navigationAction.targetFrame == nil {
webView.loadRequest(navigationAction.request)
}
return nil
}

To use latest version of Swift 4.2+
import WebKit
Extend your class with WKUIDelegate
Set delegate for webview
self.webView.uiDelegate = self
Implement protocol method
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}

Add yourself as the WKNavigationDelegate
_webView.navigationDelegate = self;
and implement following code in the delegate callback decidePolicyForNavigationAction:decisionHandler:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
//this is a 'new window action' (aka target="_blank") > open this URL externally. If we´re doing nothing here, WKWebView will also just do nothing. Maybe this will change in a later stage of the iOS 8 Beta
if (!navigationAction.targetFrame) {
NSURL *url = navigationAction.request.URL;
UIApplication *app = [UIApplication sharedApplication];
if ([app canOpenURL:url]) {
[app openURL:url];
}
}
decisionHandler(WKNavigationActionPolicyAllow);
}
P.S.: This code is from my little project STKWebKitViewController, which wraps a usable UI around WKWebView.

If you've already set the WKWebView.navigationDelegate
WKWebView.navigationDelegate = self;
you just need to implement:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
BOOL shouldLoad = [self shouldStartLoadWithRequest:navigationAction.request]; // check the url if necessary
if (shouldLoad && navigationAction.targetFrame == nil) {
// WKWebView ignores links that open in new window
[webView loadRequest:navigationAction.request];
}
// always pass a policy to the decisionHandler
decisionHandler(shouldLoad ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}
this way you don't need to implement the WKUIDelegate method.

Cloud xu's answer solves my issue.
In case someone need the equivalent Swift(4.x/5.0) version, here is it:
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if let frame = navigationAction.targetFrame,
frame.isMainFrame {
return nil
}
// for _blank target or non-mainFrame target
webView.load(navigationAction.request)
return nil
}
Of course you have to set webView.uiDelegate firstly.

None of those solutions worked for me, I did solve the issue by :
1) Implementing WKUIDelegate
#interface ViewController () <WKNavigationDelegate, WKUIDelegate>
2) Setting the UIDelegate delegate of the wkWebview
self.wkWebview.UIDelegate = self;
3) Implementing the createWebViewWithConfiguration method
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[[UIApplication sharedApplication] openURL:[navigationAction.request URL]];
}
return nil; }

I confirm that Bill Weinman's Swift code is correct. But need to mention that you also need to delegate UIDelegate for it to work, in case you are new to iOS developing like me.
Something like this:
self.webView?.UIDelegate = self
So there's three places that you need to make changes.

You can also push another view controller, or open a new tab, etc:
func webView(webView: WKWebView, createWebViewWithConfiguration configuration: WKWebViewConfiguration, forNavigationAction navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
var wv: WKWebView?
if navigationAction.targetFrame == nil {
if let vc = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController") as? ViewController {
vc.url = navigationAction.request.URL
vc.webConfig = configuration
wv = vc.view as? WKWebView
self.navigationController?.pushViewController(vc, animated: true)
}
}
return wv
}

Based on allen huang answer
Details
Xcode Version 10.3 (10G8), Swift 5
Targets
detect links with target=“_blank”
push view controller with webView if current controller has navigationController
present view controller with webView in all other cases
Solution
webView.uiDelegate = self
// .....
extension ViewController: WKUIDelegate {
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
guard navigationAction.targetFrame == nil,
let url = navigationAction.request.url else { return nil }
let vc = ViewController(url: url, configuration: configuration)
if let navigationController = navigationController {
navigationController.pushViewController(vc, animated: false)
return vc.webView
}
present(vc, animated: true, completion: nil)
return nil
}
}
Full sample
Info.plist
add in your Info.plist transport security setting
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
ViewController
import UIKit
import WebKit
class ViewController: UIViewController {
private lazy var url = URL(string: "https://www.w3schools.com/html/tryit.asp?filename=tryhtml_links_target")!
private weak var webView: WKWebView!
init (url: URL, configuration: WKWebViewConfiguration) {
super.init(nibName: nil, bundle: nil)
self.url = url
navigationItem.title = ""
}
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
override func viewDidLoad() {
super.viewDidLoad()
initWebView()
webView.loadPage(address: url)
}
private func initWebView() {
let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
view.addSubview(webView)
self.webView = webView
webView.navigationDelegate = self
webView.uiDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
guard let host = webView.url?.host else { return }
navigationItem.title = host
}
}
extension ViewController: WKUIDelegate {
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
guard navigationAction.targetFrame == nil,
let url = navigationAction.request.url else { return nil }
let vc = ViewController(url: url, configuration: configuration)
if let navigationController = navigationController {
navigationController.pushViewController(vc, animated: false)
return vc.webView
}
present(vc, animated: true, completion: nil)
return nil
}
}
extension WKWebView {
func loadPage(address url: URL) { load(URLRequest(url: url)) }
func loadPage(address urlString: String) {
guard let url = URL(string: urlString) else { return }
loadPage(address: url)
}
}
Storyboards
Version 1
Version 2

To open _blank pages in Mobile Safari and if you use Swift:
webView.navigationDelegate = self
And implement this in the WKNavigationDelegate:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.targetFrame == nil, let url = navigationAction.request.url {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
decisionHandler(WKNavigationActionPolicy.allow)
}

This worked for me:
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
WKWebView *newWebview = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
newWebview.UIDelegate = self;
newWebview.navigationDelegate = self;
[newWebview loadRequest:navigationAction.request];
self.view = newWebview;
return newWebview;
}
return nil;
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webViewDidClose:(WKWebView *)webView {
self.view = self.webView;
}
As you can see, what we do here is just opening a new webViewwith the new url and controlling the posibility of being closed, just if you need a response from that second webviewto be displayed on the first.

I encountered some issues that can not be resolved by just using webView.load(navigationAction.request). So I use create a new webView to do and it just works fine.
//MARK:- WKUIDelegate
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
NSLog(#function)
if navigationAction.targetFrame == nil {
NSLog("=> Create a new webView")
let webView = WKWebView(frame: self.view.bounds, configuration: configuration)
webView.uiDelegate = self
webView.navigationDelegate = self
self.webView = webView
return webView
}
return nil
}

**Use following function to create web view**
func initWebView(configuration: WKWebViewConfiguration)
{
let webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
webView.uiDelegate = self
webView.navigationDelegate = self
view.addSubview(webView)
self.webView = webView
}
**In View Did Load:**
if webView == nil { initWebView(configuration: WKWebViewConfiguration()) }
webView?.load(url: url1)
**WKUIDelegate Method need to be implemented**
extension WebViewController: WKUIDelegate {
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
// push new screen to the navigation controller when need to open url in another "tab"
print("url:\(String(describing: navigationAction.request.url?.absoluteString))")
if let url = navigationAction.request.url, navigationAction.targetFrame == nil {
let viewController = WebViewController()
viewController.initWebView(configuration: configuration)
viewController.url1 = url
DispatchQueue.main.async { [weak self] in
self?.navigationController?.pushViewController(viewController, animated: true)
}
return viewController.webView
}
return nil
}
}
extension WKWebView
{
func load(url: URL) { load(URLRequest(url: url)) }
}

Use this method to download pdf in web view
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?

Related

Getting an ios storyboard webView app to render in safe area

Probably a simple fix but I'm trying to get my simple ios storyboard app to render in the safe area on my iPhone 11.
It's currently taking up the entire view space and not using the safe area. I'm not confident whether I need to make any changes to the code or is it simply a setting in the storyboard view which is not on.
It's my first app. Thanks for your help.
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string:"https://google.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
if #available(iOS 11.0, *) {
webView.scrollView.contentInsetAdjustmentBehavior = .never;
}
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = true
}
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.dataDetectorTypes = [.all]
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
guard
let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
}
let string = url.absoluteString
if (string.contains("mailto:")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
if (string.contains("sms:")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
}
class FullScreenWKWebView: WKWebView {
override var safeAreaInsets: UIEdgeInsets {
return UIEdgeInsets(top: 100, left: 0, bottom: 0, right: 0)
}
}
That's happening because you're setting the webView as the controller's view. I think the easiest approach is by setting the webView as a subView of the viewController main view. Start by moving the initialisation code from the loadView method to the viewDidLoad, add the webView as a subView and finally constrainer the webView anchors to the safeArea anchors.
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let webConfiguration = WKWebViewConfiguration()
webConfiguration.dataDetectorTypes = [.all]
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(webView)
NSLayoutConstraint.activate([
webView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
let myURL = URL(string:"https://google.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
if #available(iOS 11.0, *) {
webView.scrollView.contentInsetAdjustmentBehavior = .never;
}
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = true
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
guard
let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
}
let string = url.absoluteString
if (string.contains("mailto:")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
if (string.contains("sms:")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
}
If the previous option is not valid, you can use your code and change the webView bounds using the viewSafeAreaInsetsDidChange method, making it aware of the safe area insets. Just add the following code:
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
view.bounds = view.safeAreaLayoutGuide.layoutFrame
}

WKWebView how to find the request in the WKNavigationDelegate methods

I'm trying to migrate from a UIWebView to WKWebView. shouldStartLoadWithRequest delegate method I could find the request as below:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"request: %#", request);
if ([request.URL.host isEqualToString:#"jsbridge"])
{
NSString* requestType = [request.URL.pathComponents objectAtIndex:2];
NSString* key = [request.URL.pathComponents objectAtIndex:3];
NSLog(#"requestType: %# - key: %#", requestType, key);
if([requestType isEqualToString:#"audio"])
{
[self playAudio:key];
}
return YES;
}
I need help finding the same request in the WKNavigationDelegate methods. Do I use the didCommit or didFinishNavigation methods and if so how do I find the request that it came from? Could someone give me an example? Wish I could do this is Swift but I have to stick to Objective-C. Thanks in advance.
As I know the equivalent of webView:shouldStartLoadWithRequest:navigationType: in UIWebView is webView:decidePolicyForNavigationAction:decisionHandler: in WKWebView.
Inside webView:decidePolicyForNavigationAction:decisionHandler:, you can get the request from navigationAction.
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURLRequest *request = navigationAction.request;
// Do whatever you want with the request
decisionHandler(WKNavigationActionPolicyAllow);
}
Equivalent for UIWebview to WKWebview is as follows:
didFailLoadWithError => didFailNavigation
webViewDidFinishLoad => didFinishNavigation
webViewDidStartLoad => didStartProvisionalNavigation
shouldStartLoadWithRequest => decidePolicyForNavigationAction
The shouldStartWithRequest delegate method in WKWebview (Swift version) is as follows:
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: ((WKNavigationActionPolicy) -> Void)) {
if (webView.url?.host ?? "") == "jsbridge" {
let requestType = webView.url?.pathComponents[2]
let key = webView.url?.pathComponents[3]
print("requestType: \(requestType ?? "") - key: \(key ?? "")")
if (requestType == "audio") {
playAudio(key)
}
}
}
I created the custom class
class PKWebKitView: WKWebView,WKUIDelegate,WKNavigationDelegate {
var webView :WKWebView!
func loadRequestUrl(myRequest : URLRequest) {
self.navigationDelegate = self
self.uiDelegate = self
self.load(myRequest)
} }
extension PKWebKitView: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let host = navigationAction.request.url?.absoluteString.removingPercentEncoding {
if host.contains(<endpoint check>) {
if let delegateWrapper = wkWebviewDelegate {
decisionHandler(.cancel)
}
}
if host.contains(<endpoint check>) {
let responseString = host
let query = "<Your query>"
wkWebviewDelegate?.launchScreen(query)
}
}
To Use:
let webConfiguration = WKWebViewConfiguration()
webView = PKWebKitView(frame: webViewContainer.frame, configuration: webConfiguration)
webView.loadRequestUrl(myRequest

WKWebView: Use same delegate code (WKNavigationDelegate) in multiple view controllers

I have an application with 5 navigation tabs, each one with a different View Controller that has a WKWebView inside it. I would like to somehow use the same delegate code to prevent copy and pasting in each one.
How do I reuse the "decidePolicyFor" code in different View Controllers?
class DashboardViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WebViewUtils.getWKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
webView.load(Constants.DASHBOARD)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.request.url?.scheme == "tel" {
UIApplication.shared.open(navigationAction.request.url!, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
decisionHandler(.cancel)
}
else {
decisionHandler(.allow)
}
}
}
You can implement this by dependency injection like this:
class NavigationBehavior: NSObject, WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url, url.scheme == "tel" {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
class DashboardViewController: UIViewController, WKUIDelegate {
lazy var webView: WKWebView = {
let webConfiguration = WKWebViewConfiguration()
return WKWebView(frame: .zero, configuration: webConfiguration)
}()
let navigationDelegate: WKNavigationDelegate
init(navigationDelegate: WKNavigationDelegate) {
self.navigationDelegate = navigationDelegate
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
webView.uiDelegate = self
webView.navigationDelegate = navigationDelegate
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
webView.load(Constants.DASHBOARD)
}
}
let delegate = NavigationBehavior()
let vc1 = DashboardViewController(navigationDelegate: delegate)
let vc2 = ProfileViewController(navigationDelegate: delegate)
You can extract the navigation delegate code in an object like this:
class MyWebViewNavigationDelegate: NSObject, WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.request.url?.scheme == "tel" {
UIApplication.shared.open(navigationAction.request.url!, options: [:], completionHandler: nil)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
and then use instances of this object throughout the app:
class DashboardViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView!
let webViewNavigationDelegate = MyWebViewNavigationDelegate()
override func loadView() {
let webConfiguration = WebViewUtils.getWKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = webViewNavigationDelegate
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
webView.load(Constants.DASHBOARD)
}
}

How to fetch cookies values from WKWebView in Swift 4?

I'm not able to get cookies from this website - "https://bødekontrol.dk"
I'm using Xcode 9.4.1 and iOS 11.
I have followed below code,
import UIKit
import WebKit
class ViewController: UIViewController {
var urlString = "https://bødekontrol.dk"
var webView: WKWebView!
fileprivate var webViewIsInited = false
override func viewWillLayoutSubviews() {
if !webViewIsInited {
webViewIsInited = true
if webView == nil {
webView = WKWebView(frame: UIScreen.main.bounds, configuration: WKWebViewConfiguration())
}
view.addSubview(webView)
webView.navigationDelegate = self
webView.uiDelegate = self
webView.loadUrl(string: urlString)
}
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
decisionHandler(.allow)
if let httpResponse = navigationResponse.response as? HTTPURLResponse {
if let headers = httpResponse.allHeaderFields as? [String: String], let url = httpResponse.url {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: url)
for cookie in cookies {
print(cookie.description)
print("found cookie " + cookie.name + " " + cookie.value)
}
}
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("didFinish navigation")
}
}
extension ViewController: WKUIDelegate {
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
let vc = ViewController()
vc.urlString = navigationAction.request.url?.absoluteString ?? "http://google.com"
vc.view.frame = UIScreen.main.bounds
vc.webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
navigationController?.pushViewController(vc, animated: false)
return vc.webView
}
return nil
}
}
extension WKWebView {
func loadUrl(string: String) {
if let encoded = string.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let url = URL(string: encoded)
{
if self.url?.host == url.host {
self.reload()
} else {
load(URLRequest(url: url))
}
}
}
}
Implement this protocol WKHTTPCookieStoreObserver and check function of this protocol.
cookiesDidChange

Allow document upload in input file on WKWebView

I have an html input type="file" upload form on a page loaded through wkwebview, and I'm trying to allow the ability to upload common document files (pdf, docx, etc). Currently I have the ability to take pictures and browse for images, but documents are greyed out and unavailable. I'm a web developer, and swift is greek to me, so any help would be greatly appreciated!
Here is my view controller
import UIKit
import WebKit
import IJProgressView
import FirebaseMessaging
class ViewController: UIViewController, WKNavigationDelegate {
let webView = WKWebView()
var initialstart = true
override func viewDidLoad() {
webView.backgroundColor = UIColor(red:0.17, green:0.24, blue:0.31, alpha:1.0)
webView.isOpaque = false
webView.allowsBackForwardNavigationGestures = true
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.receivedUrlFromPushNotification(notification:)), name: NSNotification.Name(rawValue: "ReceivedPushNotification"), object: nil)
// Do any additional setup after loading the view, typically from a nib.
guard let url = URL(string: "https://www.website.com/users/account") else { return};
webView.frame = view.bounds;
webView.navigationDelegate = self;
webView.load(URLRequest(url: url));
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight];
view.addSubview(webView)
}
func receivedUrlFromPushNotification (notification: Notification) {
let notification: [AnyHashable : Any] = notification.userInfo!
let url = URL(string: notification["url"] as! String);
webView.load(URLRequest(url: url!))
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == .linkActivated {
if let url = navigationAction.request.url,
let host = url.host, !host.hasPrefix("www.website.com"),
UIApplication.shared.canOpenURL(url) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url)
} else {
// Fallback on earlier versions
UIApplication.shared.openURL(url)
}
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
} else {
decisionHandler(.allow)
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let url = webView.url;
let path = url?.path;
UIApplication.shared.isNetworkActivityIndicatorVisible = false
IJProgressView.shared.hideProgressView()
view.isOpaque = true
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
if (initialstart == true) {
initialstart = false
} else {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
IJProgressView.shared.showProgressView(view)
}
}
}
For OSX, You can try to implement the delegate method
runOpenPanelWithParameters. It is a WKUIDelegate method.
Check the accepted answer for a similar question: Input type=file not working in WebView of OS X application
In my case, after file gets uploaded, the page was getting redirected to the base url.The problem was that I was calling
webview.load(request)
from the viewDidAppear() function.
Moving the call to the viewDidLoad() fixed my problem.
Click here to see more details

Resources