'Error Domain=WebKitErrorDomain Code=102 "Frame load interrupted" iOS WKwebview WKnavigationdelegate

I´m working on an app that will show a website. But I'm running into errors when loading the website in app. I´ts also working perfectly fine on android.

The error:

WebPageProxy::didFailProvisionalLoadForFrame: frameID = 3, domain = WebKitErrorDomain, code = 102 Error Domain=WebKitErrorDomain Code=102 "Frame load interrupted" UserInfo={_WKRecoveryAttempterErrorKey=<WKReloadFrameErrorRecoveryAttempter: 0x600001922120>, NSErrorFailingURLStringKey=https://site.site/login, NSErrorFailingURLKey=https://site.site/login, NSLocalizedDescription=Frame load interrupted} Failed Provisinal Navigation

My code:

//  ViewController.swift
import UIKit
import WebKit
import SafariServices
import PushNotifications

class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {

    let pushNotifications = PushNotifications.shared
    var webView: WKWebView!
    var manifest = WebAppManifest.shared

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage){

        if let objStr = message.body as? String {

            var arr = [" "," "]

            if objStr.contains(";") {

                arr = objStr.components(separatedBy: ";")

            }

            let tokenProvider = BeamsTokenProvider(authURL: "https://site.site/beams/token") { () -> (AuthData) in

                let headers : [String: String] = [:]

                var queryParams : [String: String] = [:]

                if objStr.contains(";") {

                    queryParams = ["api_token":arr[1]]

                }

                return AuthData(headers: headers, queryParams: queryParams)

            }

            

            if objStr.contains(";") {

                self.pushNotifications.setUserId(arr[0], tokenProvider: tokenProvider, completion: { error in

                    guard error == nil else {

                        print(error.debugDescription)

                        return

                    }

                    print("Succesfully authenticated with Pusher Beams")

                })

            } else {

                self.pushNotifications.setUserId(objStr, tokenProvider: tokenProvider, completion: { error in

                    guard error == nil else {

                        print(error.debugDescription)

                        return

                    }

                    print("Succesfully authenticated with Pusher Beams")

                })

            }

        }

    }

    override func viewDidLoad() {

        super.viewDidLoad()

        let contentcontroller = WKUserContentController()

        contentcontroller.add(self, name: "callbackHandler")

        let config = WKWebViewConfiguration()

        config.userContentController = contentcontroller

        // Set-up the UI

        self.view.backgroundColor = UIColor(fromHex: manifest.theme_color)

        // Creates the web view engine

        webView = WKWebView()

        view.addSubview(webView)

        webView.navigationDelegate = self

        // Display attribute

        var guide: AnyObject = self.view.safeAreaLayoutGuide

        if manifest.display == "fullscreen" {

            guide = self.view

            webView.scrollView.contentInsetAdjustmentBehavior = .always

        }

        // Make the Web View take the whole screen

        webView.translatesAutoresizingMaskIntoConstraints = false

        webView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true

        webView.rightAnchor.constraint(equalTo: guide.rightAnchor).isActive = true

        webView.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true

        webView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true

        // It will enable navigation gestures on the web view

        webView.allowsBackForwardNavigationGestures = true

        let url = URL(string: "https://" + manifest.origin + manifest.start_url)!

        print(pushNotifications.getDeviceInterests())

        webView.load(URLRequest(url: url))

    }

    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {

        get {

            switch manifest.orientation {

            case "landscape":

                return UIInterfaceOrientationMask.landscape

            case "portrait":

                return UIInterfaceOrientationMask.portrait

            default:

                return UIInterfaceOrientationMask.all

            }

        }

        set { self.supportedInterfaceOrientations = newValue }

    }

    override var prefersStatusBarHidden: Bool {

        return manifest.display == "fullscreen"

    }

    override var preferredStatusBarStyle: UIStatusBarStyle {

        return UIColor(fromHex: manifest.theme_color).isDark() ? .lightContent : .darkContent

    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

        view.setNeedsLayout()

    }

    // MARK: loadWebPage function

    func loadWebPage(url: URL)  {

        var components = URLComponents(url: url, resolvingAgainstBaseURL: false)

    components?.query = nil

        // If already has a query then append a param

        // otherwise create a new one

        if let query = url.query {

            // If isFromMobile param already exists jsut reassign the existing query

            if query.contains("source=TWA&platform=iOS") {

                components?.query = query

            } else {

                 components?.query = query + "&source=TWA&platform=iOS"

            }

        } else {

            components?.query = "source=TWA&platform=iOS"

        }

        print(components!.url!)

        let customRequest = URLRequest(url: components!.url!)

        webView!.load(customRequest)

    }

    // MARK: NavigationAction handler

    // Defines if it should navigate to a new URL

    // Logic to check if the destination URL is in the scope

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

        print(navigationAction.request.url?.absoluteURL)

        if let host = navigationAction.request.url?.host,

           let path = navigationAction.request.url?.path{

            if host.contains(manifest.origin) &&

                path.starts(with: manifest.scope){

                let query = navigationAction.request.url?.query

                if  query != nil{

                    print("two")

                    // MARK: Pusher clear state

                    if path.contains("logout") || path.contains("login") {

                        pushNotifications.clearAllState {

                            print("Cleared all state!")

                        }

                    }

                    decisionHandler(.allow)

                } else {

                    print("one")

                    loadWebPage(url: navigationAction.request.url!)

                    decisionHandler(.cancel)

                }

                return

            } else {

                print("wrong")

                // Destination URL is out of the scope

                if navigationAction.request.url?.scheme == "http" ||

                    navigationAction.request.url?.scheme == "https" {

                    // Opens an In-App browser

                    decisionHandler(.cancel)

                    let safariVC = SFSafariViewController(url: navigationAction.request.url!)

                    safariVC.preferredBarTintColor = UIColor(fromHex: manifest.theme_color)

                    safariVC.preferredControlTintColor =

                        UIColor(fromHex: manifest.theme_color).isDark() ? UIColor.white : UIColor.black

                    present(safariVC, animated: true)

                } else {

                    // It looks like a different protocol

                    // We ask the OS to open it

                    UIApplication.shared.open(navigationAction.request.url!)

                }

            }

        } else {

            decisionHandler(.cancel)

        }

    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse,

                 decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {

        print("error")

        if let response = navigationResponse.response as? HTTPURLResponse {

            if response.statusCode >= 400 {

                print("400 error")

                handleError()

            }

        }

        decisionHandler(.allow)

    }

    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {

        print(error, "Failed Navigation")

        handleError()

    }

    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {

        print(error, "Failed Provisinal Navigation")

        handleError()

    }

    // Error handling in case the content of the PWA can't be loaded

    func handleError() {

        webView.loadHTMLString("<h1>:(</h1>", baseURL: nil)

        let alert = UIAlertController(title: "Error", message: "There was a problem loading the App from the Internet. Please check your connection and try again", preferredStyle: .alert)

        alert.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: { (action) in

            exit(100)

        }))

        present(alert, animated: true)

    }

}

I have already made multiple apps of this type of but never had this error pop up. Any suggestion on how to fix this?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source