Stop navigation WKWebview - ios

This is my situation:
I have a view controller within a WKWebView. This webview starts with a page "A". In this page there are some links (href) and I want that for some of these links must open in the external browser.
For this reason I set the WKWebView delegate:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let url = webView.url?.absoluteString
{
if(self.isExternalURL(url))
{
let urlT = URL(string: url)!
decisionHandler(.cancel)
UIApplication.shared.open(urlT, options: [:], completionHandler: nil)
}
else
{
decisionHandler(.allow)
}
}
else
{
decisionHandler(.allow)
}
}
private func isExternalURL(url:String) -> Bool
{
//......check link
}
My problem is that if I select an external link the external browser opens, but the webview does not remain on page A, but it also loads the external link while I would like it to remain on page A.
I dont know why

You should use navigationAction.request.url instead of webView.url
webView.url - Already loaded url
navigationAction.request.url - New url to load
Change
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url?.absoluteString
{
if self.isExternalURL(url)
{
decisionHandler(.cancel)
UIApplication.shared.open(navigationAction.request.url, options: [:], completionHandler: nil)
}
else
{
decisionHandler(.allow)
}
}
else
{
decisionHandler(.allow)
}
}

You can try to use webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!)
func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
if let url = navigationAction.request.url?.absoluteString
{
if self.isExternalURL(url)
{
webView.stopLoading()
UIApplication.shared.open(navigationAction.request.url, options: [:], completionHandler: nil)
}
}
}

Related

How to open WhatsApp from WKWebView?

I have an iOS app that contains a WebView that has a button that opens WhatsApp. The button is not working. What I should do to let WebView open WhatsApp?
If I open website in browser, it opens WhatsApp.
Here is my solution I found after research:
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void)
{
if let requestURL = navigationAction.request.url?.absoluteString,
!requestURL.contains("https") && !requestURL.contains("http") {
if requestURL == "whatsapp://send",
let urlString = requestURL.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
UIApplication.shared.open(whatsappURL as URL, options: [:], completionHandler: { (Bool) in
})
} else {
// Handle a problem
}
}
}
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}

WKWebview not downloading swift

I have been struggling for a while now. My Webview doesn't download files, doesn't matter what kind of file and I don't get anything in the console.
Here is my code to handle the download
var filePathDestination: URL?
weak var downloadDelegate: WebDownloadable?
func generateTempFile(with suggestedFileName: String?) -> URL {
let temporaryDirectoryFolder = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
return temporaryDirectoryFolder.appendingPathComponent(suggestedFileName ?? ProcessInfo().globallyUniqueString)
}
func downloadFileOldWay(fileURL: URL, optionSessionCookies: [HTTPCookie]?) {
// Your classic URL Session Data Task
}
func cleanUp() {
filePathDestination = nil
}
func downloadDidFinish(_ download: WKDownload) {
guard let filePathDestination = filePathDestination else {
return
}
downloadDelegate?.downloadDidFinish(fileResultPath: filePathDestination)
cleanUp()
}
func download(_ download: WKDownload,
didFailWithError error: Error,
resumeData: Data?) {
downloadDelegate?.downloadDidFail(error: error, resumeData: resumeData)
}
func download(_ download: WKDownload, decideDestinationUsing
response: URLResponse, suggestedFilename: String,
completionHandler: #escaping (URL?) -> Void) {
filePathDestination = generateTempFile(with: suggestedFilename)
if(filePathDestination != nil){
print(filePathDestination!)
}
completionHandler(filePathDestination)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: #escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
if navigationAction.shouldPerformDownload {
decisionHandler(.download, preferences)
} else {
decisionHandler(.allow, preferences)
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
if navigationResponse.canShowMIMEType {
decisionHandler(.allow)
} else {
decisionHandler(.download)
}
}
func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
download.delegate = downloadDelegate
}
func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
download.delegate = downloadDelegate
}
When I print out navigationAction.request.url I get the right URL, but no download. Any help would be appreciated

URL is not opening in WKWebView in swift

I have a requirement to open the URL in WKWebView and login to the portal. As user logged in successfully after that I have to perform download operation from WKWebView, Everything is working fine but it's opening in external safari browser but as per requirement it should open in WKWebView
Outlet for WKWebView
#IBOutlet var webView: WKWebView!
Function to call URL in WKWebVIew
func loadURLInWebView() {
let url = URL(string: "https://www-qa.yyy.com/content/dash/en/public/login.html")
let urlRequest = URLRequest(url: url!)
if let webView = webView {
webView.load(urlRequest)
}
}
after adding decidePolicyFor delegate it's opening url in safari but it should open in WKWebView. I am not able to find the issue.
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
let url = navigationAction.request.url
guard url != nil else {
print(url!)
decisionHandler(.allow)
return
}
if url!.description.lowercased().starts(with: "http://") ||
url!.description.lowercased().starts(with: "https://") {
decisionHandler(.cancel)
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
} else {
decisionHandler(.allow)
}
}
you are handling guard statement in the wrong way. In guard else, the condition will be called when the URL is nil.
If you do print(URL!) in else your app will crash. moreover when URL is nill, you should call cancel Handler.
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url else { // else will be called when url is nil
decisionHandler(.cancel)
return
}
if url.description.lowercased().starts(with: "http://") ||
url.description.lowercased().starts(with: "https://") {
decisionHandler(.allow)
} else {
decisionHandler(.cancel)
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}

WKWeb View does finish loading of page in swift iOS?

I have created a WKWebView. It loads the url of MSB ie. My school bucks. It loads almost all url for payment. But at one url didFinishLoad is not getting called. I have added all the delegates for the web view.but certainly it is not working. The whole process works fine in the web browser of iPhone.
Below is the code I have added below code for that
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
self.webView = WKWebView(frame: frame, configuration: configuration)
self.view.addSubview(webView)
self.webView.navigationDelegate = self
self.webView.uiDelegate = self
Delegates Methods for the navigation
// MARK: - WKWebView Delegation
extension PaymentWebViewController:WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
DILog.print(items: "didFinish")
self.hideLoader()
if let strUrl = webView.url?.absoluteString {
self.readRedirectUrl(stringUrl: strUrl)
}
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
DILog.print(items: "faluere")
self.hideLoader()
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
DILog.print(items: "didStatrt")
self.showLoader()
if let strUrl = webView.url?.absoluteString {
DILog.print(items: "URL IS \(strUrl)")
}
}
func webView(_ webView: WKWebView,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust)
{ DILog.print(items: "Auth Channlenge1")
let cred = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(.useCredential, cred)
}
else
{ DILog.print(items: "Auth Channlenge2")
completionHandler(.performDefaultHandling, nil)
}
}
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
DILog.print(items: "webViewWebContentProcessDidTerminate")
}
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
DILog.print(items: "didCommit")
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
DILog.print(items: "decidePolicyFor Action")
decisionHandler(WKNavigationActionPolicy.allow)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
DILog.print(items: "decidePolicyFor response ")
decisionHandler(.allow)
}
}
You need to add exception (SecTrustSetExceptions) for that particular url. As there is some conflict with its signing identity and your WKWebView.
I was also facing same issue in my app and successfully solved by using below snippet.
try this:
extension PaymentWebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let serverTrust = challenge.protectionSpace.serverTrust
let exceptions = SecTrustCopyExceptions(serverTrust!)
SecTrustSetExceptions(serverTrust!, exceptions)
completionHandler(.useCredential, URLCredential(trust: serverTrust!))
}
}
Hope you've already setup this in your info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>

Migrating UIWebView delegate to WKWebView delegate method

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) {}

Resources