Checking if request URL contains swift webview - ios

I am trying to check if the URL contains "external" something like "www.example.com/?external=1" then it should open in a external browser otherwise open locally below is the code please help me to get this fix. Thanks
class ViewController: UIViewController, WKNavigationDelegate {
let webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
webView.frame = view.bounds
webView.navigationDelegate = self
let url = URL(string: "https://www.example.com")!
let urlRequest = URLRequest(url: url)
webView.load(urlRequest)
webView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
view.addSubview(webView)
}
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("external"),
UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
print(url)
print("Redirected to browser. No need to open it locally")
decisionHandler(.cancel)
return
} else {
print("Open it locally")
decisionHandler(.allow)
return
}
} else {
print("not a user click")
decisionHandler(.allow)
return
}
}
}
I tried to change the code like:
url.hasPrefix("external")
But it's giving error "Value of type URL has no member 'hasPrefix'"

Related

Xcode 12.5 and swift 5+ how to open external Urls links from a WKwebView?

Im trying to open external links from a IOS App with local html files. Im using Xcode 12.5 and swift 5+.
The code in my ViewController is simple, just opening the index.html in a folder (www).
The problem now is that i need to open external links http or https in Safari of the iPhone or the iPad and not in the local app. How can i filter that? Thanks for ideas.
Here is my code so far - updated:
import UIKit
import WebKit
import PDFKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// let myURL = URL(string:"www/index.html")
// let myRequest = URLRequest(url: myURL!)
// webView.load(myRequest)
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "www")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
}
// WKWebViewNavigationDelegate
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
// Check for links.
if navigationAction.navigationType == .linkActivated {
// Make sure the URL is set.
guard let url = navigationAction.request.url else {
decisionHandler(.allow)
return
}
// Check for the scheme component.
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
if components?.scheme == "http" || components?.scheme == "https" {
if navigationAction.targetFrame == nil {
UIApplication.shared.open(url)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
// Open the link in the external browser.
UIApplication.shared.open(url)
// Cancel the decisionHandler because we managed the navigationAction.
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
Apparently your webView(_:decidePolicyFor:decisionHandler:) delegate function never gets called. Check it by adding a breakpoint or a print.
To fix this, just set the navigationDelegate of your WKWebView and conform to WKNavigationDelegate:
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate { // <- conform to WKNavigationDelegate
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self // <- set the navigationDelegate property
webView.uiDelegate = self
view = webView
}
}

How to get headers from WKWbView finish loads

I am using WKWebView in app & i am looking for getting all headers from wkwebview in finish loads method webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { Just like in UIWebView
func webViewDidFinishLoad(_ webView: UIWebView) {
print("Finished loads---", webView.request?.allHTTPHeaderFields)
}
How can we achieve that in WKWebView ?
You need to set your view controller as the WKWebView Navigation Delegate WKNavigationDelegate and implement its decidePolicyFor method:
optional func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void)
Try like this:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
let allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields ?? [:]
for (key, value) in allHTTPHeaderFields {
print("key:", key, "value:", value)
}
if navigationAction.navigationType == .linkActivated {
if let url = navigationAction.request.url,
let host = url.host, !host.hasPrefix("www.google.com"),
UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
print(url)
print("Redirected to browser. No need to open it locally")
decisionHandler(.cancel)
} else {
print("Open it locally")
decisionHandler(.allow)
}
} else {
print("not a user click")
decisionHandler(.allow)
}
}
Use WKWebView's webView(_:decidePolicyFor:decisionHandler:).
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
// MARK: - IBOutlet
#IBOutlet weak var webView: WKWebView!
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let urlStr = "https://www.google.com/"
if let url = URL(string: urlStr) {
let request = URLRequest(url: url)
webView.load(request)
//webView.allowsBackForwardNavigationGestures = false
//webView.navigationDelegate = self
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
let request = navigationAction.request
if let host = navigationAction.request.url?.host {
if host == "www.google.com" {
if let headers = request.allHTTPHeaderFields {
print("Google Headers: \(headers)")
} else {
print("Nope, sir")
}
}
else if host == "www.apple.com" {
if let headers = request.allHTTPHeaderFields {
print("Apple Headers: \(headers)")
} else {
print("Nope, sir")
}
}
}
decisionHandler(.allow)
}
}
And I get the following.
["User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X)
AppleWebKit/605.1.15 (KHTML, like Gecko)", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8"]
Headers: ["Accept-Language": "en-us", "Upgrade-Insecure-Requests":
"1", "Accept-Encoding": "gzip, deflate, br", "User-Agent":
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X)
AppleWebKit/605.1.15 (KHTML, like Gecko)", "Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8"]

WKWebView: mailto links in html content not opening mail app

I created a very simple iOS app (Swift 5). It's just a WKWebView that loads my PWA url.
Everything works fine except all Mail me links. When I click them, nothing happens, my mail app doesn't open.
This is the code of my ViewController.swift:
//
// ViewController.swift
// panel
//
// Created by kevin on 25/07/2019.
// Copyright © 2019 umono. All rights reserved.
//
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string:"https://someUrlToMyApp.appspot.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
if #available(iOS 11.0, *) {
webView.scrollView.contentInsetAdjustmentBehavior = .never;
}
}
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.dataDetectorTypes = [.all]
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
}
EDIT:
Thx guy's, here is my working code:
//
// ViewController.swift
// panel
//
// Created by kevin on 25/07/2019.
// Copyright © 2019 umono. All rights reserved.
//
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string:"https://someUrlToMyApp.appspot.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
if #available(iOS 11.0, *) {
webView.scrollView.contentInsetAdjustmentBehavior = .never;
}
webView.navigationDelegate = self
}
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
}
decisionHandler(.allow)
}
}
One way to do what you want would be to implement WKNavigationDelegate:
import UIKit
import WebKit
class ViewController: UIViewController {
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard
let file = Bundle.main.path(forResource: "test", ofType: "html"),
let html = try? String(contentsOfFile: file) else {
return
}
webView.navigationDelegate = self
webView.loadHTMLString(html, baseURL: nil)
}
#IBAction func didTapButton(_ sender: Any) {
let email = "email#email.com"
guard
let url = URL(string: "mailto:\(email)") else {
return
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
guard
let url = navigationAction.request.url,
let scheme = url.scheme else {
decisionHandler(.cancel)
return
}
if (scheme.lowercased() == "mailto") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
// here I decide to .cancel, do as you wish
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
}
Here you have a ViewController that has webView as an outlet, this WKWebView would load an html file like this:
Mail me
And I also added in storyboard a button just for reference, which would have the IBAction didTapButton described above.
The key here is:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void)
Which would give you the URL and let you decide what policy is suitable for it. Here I check if it contains mailto: as I already know this is what you're interested in so if it does, I simply open the URL as I would do if the user presses an UIButton visible on screen.
Hope it helps, cheers!
LE: Make sure you run on a real device (simulators don't have Mail app installed), also make sure you have the Mail app installed, cause I didn't..

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