How to get headers from WKWbView finish loads - ios

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"]

Related

Checking if request URL contains swift webview

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'"

How do I open phone from tel: url link from WebKit iOS app using Swift

I am creating an iPhone app and in part using WebKit to display a web page in the app. Currently, when I click a button that is displayed on the website it should open the phone app (with a tel: link) and dial the number. Unfortunately in the app, it does nothing, however, it works fine in chrome/ safari.
I have tried to scour the internet and attempted several suggestions but nothing seems to do anything so far.
import UIKit
import WebKit
class ShopViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
#IBOutlet weak var backButton: UIBarButtonItem!
#IBOutlet weak var shopWebKit: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
shopWebKit.navigationDelegate = self
shopWebKit.uiDelegate = self
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear( animated )
let urlString:String = "https://www.somewebsite.com"
let url:URL = URL(string: urlString)!
let urlRequest:URLRequest = URLRequest(url: url)
shopWebKit.load(urlRequest)
}
#IBAction func backButtonTapped(_ sender: Any) {
if shopWebKit.canGoBack{
shopWebKit.goBack()
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
backButton.isEnabled = webView.canGoBack
}
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}
}
I am hoping to be able to click any tel: link button loaded in the WebKit and have it open the phone app and dial the number. Please help.
Try below code it's working fine swift5. first add 'navigationDelegate' to your WKWebView.
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
switch navigationAction.request.url?.scheme {
case "tel":
UIApplication.shared.open(navigationAction.request.url!, options: [:], completionHandler: nil)
decisionHandler(.cancel)
break
default:
decisionHandler(.allow)
break
}
}
Note: try testing with actual device.
Here is just a code to open the phone app and make calls. Probably it can help u.
if let url = URL(string: "tel:+100000000"), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}

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

WKWebView issues with tel: links

I've looked though a lot of the answers for this question but they seem to be outdated now and none of the solutions are working for me and just give lots of errors.
I'm just gettign into xcode and apps and am loading a local html file "index.html" into a WKWebView. This is fine, loads fine, displays as it should, BUT I have some tel: links on the page which don't work, I tap on them and nothing. I am using fraework7 for my html files and have added the "external" class, which works in safari etc.
UPDATE: Using the following code I can click on a link and it will try and call, but then gives a fatal error
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate{
#IBOutlet weak var webview: WKWebView!
override func viewDidLoad() {
webview.navigationDelegate = self
super.viewDidLoad()
let htmlpath = Bundle.main.path(forResource: "index", ofType: "html")
let url = URL(fileURLWithPath: htmlpath!)
let request = URLRequest(url: url)
webview.load(request)
webview.scrollView.bounces = false
webview.configuration.dataDetectorTypes = .phoneNumber
// Do any additional setup after loading the view, typically from a nib.
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Swift.Void) {
if navigationAction.request.url?.scheme == "tel" {
UIApplication.shared.openURL(navigationAction.request.url!)
decisionHandler(.cancel)
}
decisionHandler(.allow)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
and html looks like:
<tr>
<td>Police / Fire / Ambulance</td>
<td>Emergency Services</td>
<td>999</td>
</tr>
obviously part of a larger table but this is the example.
Any pointers would be greatly appreciated as I've been going round in circles for ages.
Managed to find a solution finally:
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate{
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
webView.navigationDelegate = self
webView.uiDelegate = self
super.viewDidLoad()
let htmlpath = Bundle.main.path(forResource: "index", ofType: "html")
let url2 = URL(fileURLWithPath: htmlpath!)
let request = URLRequest(url: url2)
webView.load(request)
webView.scrollView.bounces = false
//webview.configuration.dataDetectorTypes = .phoneNumber
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.request.url?.scheme == "tel" {
UIApplication.shared.openURL(navigationAction.request.url!)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
Using this seems to pick up the tel: links fine, an answer I found on here previously didn't have the else statement so fired decisionHandler more than once which created the error, the else statement fixed that and now seems fine.
It's not just tel: links that aren't handled out of the box by WKWebView. Other links like mailto: and facetime: aren't handled either. For a complete list of these kinds of special links, see Apple's documentation.
You should decide which of Apple's special links you want handled (see the link above), and override the navigation handler in a manner similar to the following:
webView.navigationDelegate = self
...
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url else {
decisionHandler(.allow)
return
}
if ["tel", "sms", "facetime"].contains(url.scheme) && UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
The only reason I can think of why Apple didn't include this in the first place is because when you try launching one of these links in Mobile Safari, it shows you a dialog such as the following:
This website has been blocked from automatically starting a call.
[Ignore] [Allow Call]
Thus, they may expect you want to implement something similar in your app instead of just launching the call.
Note however, that each type of link behaves differently:
tel: dialog that shows the phone number with call/cancel buttons.
sms: launches the Messages app.
mailto: launches the Mail app.
So if you do want to have some kind of modal that allows the user to cancel if they didn't mean to perform the action, depending on which actions you support it might already have similar behavior (e.g. tel:).
Just use WKNavigationDelegate and dataDetectorTypes for WKWebView configuration like below:
webView.navigationDelegate = self
webView.configuration.dataDetectorTypes = [.link, .phoneNumber]
extension PDFWebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let requestUrl = navigationAction.request.url, requestUrl.scheme == "tel" {
UIApplication.shared.open(requestUrl, options: [:], completionHandler: nil)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
While suggested answers work fine - I would recommend to use white-list approach instead of black-list. We know that WKWebView can only handle http/https links so we can gracefully handle the error of opening any other kind of link.
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.handleNavigationError(error)
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
self.handleNavigationError(error)
}
private func handleNavigationError(_ error: Error) {
if let failedUrl = (error as NSError).userInfo[NSURLErrorFailingURLErrorKey] as? URL, failedUrlScheme = failedUrl.scheme?.lowercased(), !["http", "https"].contains(failedUrlScheme) {
UIApplication.shared.open(failedUrl, completionHandler: nil)
} else {
// handle other errors if needed
}
}
In this case you will support all the types of external app links like tel:, sms:, faceTime:, itms-services: etc.

Why WKWebView not returning all cookies?

Hi I replaced UIWebview with WKWebview. Because on multiple frame loading UIWebview keyborad is dismissing. So i used WKWebView. my issue was gone now.
But on WKWebView im not getting cookies. it is returning only JSESSIONID cookie, Where as on UIWebView im getting all the cookies.
Please help me on this.
Here is my code snippet for start WKWebView.
func startWebView() {
URLCache.shared.removeAllCachedResponses()
if webView == nil {
let source =
"var meta = document.createElement('meta'); " +
"meta.name = 'viewport'; " +
"meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'; " +
"var head = document.getElementsByTagName('head')[0]; " +
"head.appendChild(meta);"
let script = WKUserScript(source:source,
injectionTime: .atDocumentEnd,
forMainFrameOnly: true)
let userContentController = WKUserContentController()
userContentController.addUserScript(script)
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
let prefs = WKPreferences()
prefs.javaScriptEnabled = true
configuration.preferences = prefs
webView = WKWebView(frame: CGRect.zero, configuration: configuration)
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = true
webContainerView.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
let vdict = ["WV": webView!]
webContainerView.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "H:|[WV]|",
options: [], metrics: nil,
views: vdict)
)
webContainerView.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "V:|[WV]|",
options: [], metrics: nil,
views: vdict)
)
}
var urlstr = LoginServer.url.absoluteString
//#if DEBUG
let url : URL = URL(string: urlstr)!
var urlRequest = URLRequest(url: url,
cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData,
timeoutInterval: 60.0)
if Reachability.shared.isNetworkAvailable(){
urlRequest.cachePolicy = .reloadRevalidatingCacheData
_ = webView.load(urlRequest)
}
else{
loadingIndicator.stopAnimating()
loadingIndicator.isHidden = true
}
}
Here is the code for fetch cookies
func webView(_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: #escaping (WKNavigationResponsePolicy) -> Swift.Void)
{
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)
}
}
}
decisionHandler(.allow)
}
You should be able to access all the cookies with the following code
let cookies = HTTPCookieStorage.shared.cookies
for cookie in cookies! {
print(cookie.description)
print("found cookie " + cookie.name + " " + cookie.value)
}
Below is a summary of my web view controller
class MyWebViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
override func viewDidLoad() {
super.viewDidLoad()
// create WKWebViewConfiguration
let webViewConfig = WKWebViewConfiguration()
let userContentController = WKUserContentController();
userContentController.add(self, name: "myApp")
webViewConfig.userContentController = userContentController
// init and load request in webview.
webView = WKWebView(frame: self.view.frame, configuration:webViewConfig)
webView.navigationDelegate = self
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(webView)
let request = URLRequest(url: webAddress)
webView.load(request)
}
//
// WKNavigationDelegate
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler:#escaping ((WKNavigationActionPolicy) -> Void)) {
print("decidePolicyForNavigationAction with url:\(String(describing: navigationAction.request.url!))")
if let url = navigationAction.request.url {
// url request is outside of my website, then use safari to view address
if webAddress.host != url.host {
UIApplication.shared.open(url)
decisionHandler(.cancel)
}
}
decisionHandler(.allow)
}
func webView(_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: #escaping (WKNavigationResponsePolicy) -> Swift.Void)
{
let cookies = HTTPCookieStorage.shared.cookies
for cookie in cookies! {
print(cookie.description)
print("found cookie " + cookie.name + " " + cookie.value)
}
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print("Navigation error :\(error.localizedDescription)")
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print("Loading error :\(error.localizedDescription)")
}
//
// WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
}
var webView: WKWebView!
var config = ResolveConfig()
let webAddress = URL(string:"https://mywebaddr.com")!
}

Resources