I'm trying to handle clicks on "tel:xxxxx" links in WKWebView. The code below works on iPhone, but on an iPad the callback webView(_ webView:,decidePolicyFor:,decisionHandler:) is not called. Is there any workaround?
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
webView.loadHTMLString("<html><body>Click here to call</body></html>", baseURL: nil)
}
// MARK: - WKNavigationDelegate
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
print("navigation to url: \(url)")
}
decisionHandler(.allow)
}
}
Related
I'm trying to build an external webpage in a fullscreen mobile app in iOS. Everything is working fine.
But how is it possible to open external urls (outside google.com) in Safari?
import UIKit
import WebKit
class ViewController: UIViewController {
#IBOutlet var mWebKit: WKWebView!
let urlMy = URL(string: "https://www.google.com/")
override func viewDidLoad() {
super.viewDidLoad()
let request = URLRequest(url: urlMy!)
mWebKit.load(request)
// Do any additional setup after loading the view.
}
}
Thank you very much!
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.targetFrame == nil{
if #available(iOS 10.0, *) {
UIApplication.shared.open(navigationAction.request.url!, options: [:])
} else {
UIApplication.shared.openURL(navigationAction.request.url!)
// Fallback on earlier versions
}
}
}
I'm looking for a way to enable the "pinch to zoom" magnification gesture when open the local PDF file in WKWebview on iOS 10. As I know the pinch to zoom is enabled on the iOS 12
class ViewController: UIViewController {
var wkWebView: WKWebView?
#IBOutlet var webView: UIView!
fileprivate var delegate = AuthenticatedWebViewNavigationDelegate()
override func viewDidLoad() {
super.viewDidLoad()
if wkWebView == nil {
createWebView()
}
let filePath = Bundle.main.path(forResource: "local", ofType:
"pdf")
let baseUrl = URL(fileURLWithPath: filePath!)
wkWebView?.loadFileURL(baseUrl, allowingReadAccessTo: baseUrl)
}
}
class AuthenticatedWebViewNavigationDelegate: NSObject, WKNavigationDelegate {
weak var viewController: ViewController?
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
decisionHandler(.allow)
}
public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
decisionHandler(.allow)
}
public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print("error")
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("finish")
}
public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print("error")
}
}
I found the scrollview.pinchGestureRecognizer is disabled on the wkwebview of iOS 10
So the solution is to enable the pinchGestureRecognizer, I put the function in the following function:
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if #available(iOS 11.0, *) {
} else {
if webView.url?.scheme == "file" {
webView.scrollView.pinchGestureRecognizer?.isEnabled = true
}
}
print("finish")
}
Swift 4
iOS 11.4
I'm trying to learn how to use the WKWebView but I'm having an issue where any phone-number links won't actually display the popup to call that number.
I followed this answer and the error that prints is listed below. The website I'm using is a wix site that uses the quickactionbar (I know this uses tel:xxxxx so it should be working since it works on safari) Any idea why it's doing this?
Edit:
Also, I'm not entirely sure what to search for in order to get the answer I need. If you know exactly what to search to find the answer to this, I'll be more than happy with that as well.
It recognizes it's a phone number if I long press but won't do anything if I just tap it
import Foundation
import WebKit
class WebKitViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView!
#IBOutlet weak var backButton: UIBarButtonItem!
#IBAction func backButtonAction(_ sender: UIBarButtonItem) {
if self.webView.canGoBack {
self.webView.goBack()
}
}
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: "Link")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
// func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// backButton.isEnabled = webView.canGoBack
// }
//Code from answer on StackOverflow
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
print("didStartProvisionalNavigation: \(navigation)")
}
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation) {
print("didReceiveServerRedirectForProvisionalNavigation: \(navigation)")
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation, withError error: Error) {
print("didFailProvisionalNavigation: \(navigation), error: \(error)")
}
func webView(_ webView: WKWebView, didFinishLoading navigation: WKNavigation) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
print("didFinishLoadingNavigation: \(navigation)")
}
func _webViewWebProcessDidCrash(_ webView: WKWebView) {
print("WebContent process crashed; reloading")
}
func webView(_ webView: WKWebView,createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?{
//if <a> tag does not contain attribute or has _blank then navigationAction.targetFrame will return nil
if let trgFrm = navigationAction.targetFrame {
if(!trgFrm.isMainFrame){
UIApplication.shared.isNetworkActivityIndicatorVisible = true
self.webView.load(navigationAction.request)
}
}
return nil
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!){
UIApplication.shared.isNetworkActivityIndicatorVisible = false
print("didFinish: \(String(describing: self.webView.url)); stillLoading:\(self.webView.isLoading ? "NO" : "YES")")
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (_: WKNavigationResponsePolicy) -> Void) {
print("decidePolicyForNavigationResponse")
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (_: WKNavigationActionPolicy) -> Void) {
decisionHandler(.allow)
}
}
The error:
didFailProvisionalNavigation: <WKNavigation: 0x101865bd0>, error: Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={_WKRecoveryAttempterErrorKey=<WKReloadFrameErrorRecoveryAttempter: 0x101856b80>, NSErrorFailingURLStringKey=tel:+12199874536, NSErrorFailingURLKey=tel:+12199874536, NSLocalizedDescription=unsupported URL, NSUnderlyingError=0x1018641a0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}}
you can also set dataDetectorTypes to .phoneNumber on your WKWebViewConfiguration. All detected phone numbers will transformed to contain links around the phone number.
configuration.dataDetectorTypes = .phoneNumber
Use this delagate func-
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)
} else {
decisionHandler(.allow)
}
}
Having some trouble redirecting a user to a link from a WKWebView.
User is essentially meant to be authenticating on a website using WKWebView, the website checks the authentication which in turn redirects the user back to our app.
My initial thought is that there is some sort of restriction to allow the user to exit the app during the redirecting process. Which is why I implemented WKNavigationDelegate and saw that when we try authenticating the code below didReceive challenge runs through the second if case - which I only assume that its a good thing. Though the app does not get redirected.
Below is the code I have.
import UIKit
import WebKit
class WebViewViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.isHidden = false
tabBarController?.tabBar.isHidden = true
webView.navigationDelegate = self
if let url = URL(string: "SomeUrl") {
let request = URLRequest(url: url)
webView.load(request)
}
}
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = false
}
override func viewWillDisappear(_ animated: Bool) {
self.tabBarController?.tabBar.isHidden = false
}
func webViewDidFinishLoad(_ webView: UIWebView) {
if let URL = webView.request?.url {
OAuthHelper.oAuthManager.processOAuthStep1Response(url: URL)
}
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.previousFailureCount > 0 {
completionHandler(Foundation.URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
} else if let serverTrust = challenge.protectionSpace.serverTrust {
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
} else {
print("unknown state. error: \(challenge.error)")
// do something w/ completionHandler here
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if (navigationAction.navigationType == .linkActivated){
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
I did not fully understand the problem. If the question is that if you want the user to redirect from Webview to Native App, then you do have different options. I will be providing the options. Let me know if this is you are looking at.
Use WKURLSchemeHandler to handle the custom URLs. Read my article about this. https://medium.com/#kumarreddy_b/custom-scheme-handling-in-uiwebview-wkwebview-bbeb2f3f6cc1. https://github.com/BKRApps/KRWebView
you will be getting the urls in func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void), here you can intercept the URL and then use your URL scheme to get into the native app. Use OpenUrl to do this. But this is deprecated.
I am working on migrating UIWebView to WKWebView. I have changed all the Delegate methods. I need WKWebView delegate methods equal to below UIWebView delegate method. The app is working fine. but login session is not retaining
UIWebView:
extension WebViewController: UIWebViewDelegate {
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
guard let url = request.url else {
return true
}
guard !url.absoluteString.contains("data:application/pdf") else {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.action,
target: self,
action: #selector(share(sender:)))
return true
}
guard url.pathExtension != "pdf" else {
let safariVC = SFSafariViewController(url: url)
safariVC.modalPresentationStyle = .popover
present(safariVC, animated: true, completion: nil)
return false
}
guard url.isLogin() == false else {
AppDelegate.navigationController.signOut(.sessionOnly)
return false
}
guard let mobileSite = url.asMobileSite() else {
return true
}
let mobileRedirect = URLRequest(url: mobileSite)
webView.loadRequest(mobileRedirect)
return false
}
func webViewDidStartLoad(_ webView: UIWebView) {
numberOfDidStartLoads += 1
}
func webViewDidFinishLoad(_ webView: UIWebView) {
numberOfDidStartLoads -= 1
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
numberOfDidStartLoads -= 1
}
}
And i tried below code and getting session expire.
extension WebViewController: UIWebViewDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (_: WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url else {
decisionHandler(.allow)
return
}
guard !url.absoluteString.contains("data:application/pdf") else {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.action,
target: self,
action: #selector(share(sender:)))
decisionHandler(.allow)
return
}
guard url.pathExtension != "pdf" else {
let safariVC = SFSafariViewController(url: url)
safariVC.modalPresentationStyle = .popover
present(safariVC, animated: true, completion: nil)
decisionHandler(.cancel)
return
}
guard url.isLogin() == false else {
AppDelegate.navigationController.signOut(.sessionOnly)
decisionHandler(.cancel)
return
}
guard let mobileSite = url.asMobileSite() else {
decisionHandler(.allow)
return
}
let mobileRedirect = URLRequest(url: mobileSite)
webView.load(mobileRedirect)
decisionHandler(.cancel)
return
decisionHandler(.allow)
}
func webViewDidStartLoad(_ webView: UIWebView) {
numberOfDidStartLoads += 1
}
func webViewDidFinishLoad(_ webView: UIWebView) {
numberOfDidStartLoads -= 1
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
numberOfDidStartLoads -= 1
}
}
Please help me to resolve this issue. I have made some mistake in changing code from UIWebView to WKWebView.
You might required to implement the following in your code, which means instead of using UIWebViewDelegate protocol try to use WKNavigationDelegate protocol. I guess you are missing one of the most important function when you are handling with sessions.
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print(#function)
completionHandler(.performDefaultHandling,nil)
}
There are different types of AuthChallengeDisposition , like
public enum AuthChallengeDisposition : Int {
case useCredential
case performDefaultHandling
case cancelAuthenticationChallenge
case rejectProtectionSpace
}
Complete WKNavigationDelegate protocols are
extension ViewController: WKNavigationDelegate{
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
print(#function)
}
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
print(#function)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print(#function)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print(#function)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print(#function)
}
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
print(#function)
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print(#function)
}
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print(#function)
completionHandler(.performDefaultHandling,nil)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
print(#function)
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
print(#function)
decisionHandler(.allow)
}
}
I guess you can use webView(_:decidePolicyFor:decisionHandler:) and you block/cancel or allow requests. That should work in the same way.
Disclaimer: I haven't tested that yet, I'll do so as soon as I find some time.
analyzing your code, i have found a statement that is never reached cause of "return" called before.
The statement is:
decisionHandler(.allow)
You can find it as last line of code for the function:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (_: WKNavigationActionPolicy) -> Void)
that you have up this method:
func webViewDidStartLoad(_ webView: UIWebView) {
numberOfDidStartLoads += 1
}
UIwebview depreciated its new app release to app store you can use the UIWebview app can rejected so please once check and update to WKwebview
----------------- UIWebViewDelegate Delegates -------------
func webViewDidStartLoad(_ webView: UIWebView) {
}
func webViewDidFinishLoad(_ webView: UIWebView) {
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
}
--------------- Replace the WKNavigationDelegate Delegates--------------
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {}