WKWeb View does finish loading of page in swift iOS? - 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>

Related

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

SwiftUI WKWebView disable link interactions

I am looking for a way to disable a user interacting with links and images in SwiftUI using WKWebView. I have tried various ways but none have worked, and I have seen and tried the following post but nothing worked:
WKWebview allowsLinkPreview to false breaks text selection
WKWebview: Disable interaction and clicks on links
I am displaying a website that I have permission to display but due to its abnormal HTML structure it makes it hard to display the context, so I decided to try disabling user interactions with links. I do still want user interactions enabled so the user can scroll through the calendar presented to them, but not be able to click on links. If you have any suggestion I am open to them.
Here is my code:
struct CalanderWeb : UIViewRepresentable {
#State var request: URLRequest
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.scrollView.isScrollEnabled = true
uiView.isOpaque = false
uiView.allowsBackForwardNavigationGestures = false
uiView.load(request)
}
func makeCoordinator() -> CalanderWeb.Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, WKNavigationDelegate {
let parent: CalanderWeb
init(_ parent: CalanderWeb) {
self.parent = parent
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
decisionHandler(.cancel)
}
}
}
Thank You.
Here is correct delegate callback
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == .linkActivated {
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
depending of used HTML, probably also might be disabled .formSubmitted, if needed.
You'll have to override the method decidePolicyForNavigationAction in WKNavigationDelegate and provide .cancel in the completion block parameter decisionHandler. Here is the sample code:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
decisionHandler(.cancel)
}

Stop navigation WKWebview

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

How can I tap a phone number with WKWebView

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

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