NSInternalInconsistencyExceptionException完成处理程序传递给webView:decidePolicyForNavigationAction:decisionHandle未被调用

时间:2018-07-24 22:05:39

标签: swift wkwebview

我的一个iOS应用测试遇到了问题。任何帮助解决的问题都将不胜感激!

测试代码:

    func testOpenExternalBrowser_for_valid_urls() {
    // Given
    let vc = MockDashboardViewController()
    vc.openInternalBrowserCalled = false
    vc.openExternalBrowserCalled = false
    vc.webview = WKWebView()
    vc.webview.navigationDelegate = vc

    // When
    let url = URL.init(string: "https://example.com/restOfUrl")
    XCTAssertTrue(url!.absoluteString.contains("https://example.com"), "initial url string is wrong")
    vc.webview.load(URLRequest(url: url!));
    waitSeconds(duration: 2)

    // Then
    XCTAssertFalse(vc.openExternalBrowserCalled, "openExternalBrowserCalled value is wrong")
    XCTAssertTrue(vc.openInternalBrowserCalled, "openInternalBrowserCalled value is wrong")
}

这给了我错误:

  

错误:-[Tests.ViewControllerTests testOpenExternalBrowser_for_valid_urls]:失败:已捕获   “ NSInternalInconsistencyException”,“完成处理程序传递给   -[ViewController webView:decidePolicyForNavigationAction:decisionHandler:]不是   称为“

这是相关的WKWebKit ViewController代码:

    // MARK: WKNavigationDelegate methods
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    guard let url = navigationAction.request.url, let urlScheme = url.scheme, let urlHost = url.host else {
        //if we can't convert the URL, deny the action
        decisionHandler(WKNavigationActionPolicy.cancel);
        return;
    }

    var isPortValid = false;
    if let urlPort = url.port {
        isPortValid = urlPort == HostDefinitions.PORT;
    } else {
        isPortValid = urlScheme == DashboardConstants.HTTPS;
    }

    if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
        openInternalBrowser(url: url, decisionHandler: decisionHandler)
        return;
    }

    decisionHandler(WKNavigationActionPolicy.cancel);
    openExternalBrowser(url: url)
}

func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
    if(url.path == PathDefinitions.Login) {
        //the user has logged out or visited the login page. We will now de-auth them and direct them back to the login page
        OperationQueue.main.addOperation {
            AuthManager.logout()
            _ = self.navigationController?.popToRootViewController(animated: true);
        }
        decisionHandler(WKNavigationActionPolicy.cancel);
    } else {
        decisionHandler(WKNavigationActionPolicy.allow);
    }
}

func openExternalBrowser(url: URL) {
    AppManager.openExternalBrowser(url: url)
}

有什么想法吗?我试过添加一个return语句,并将决策处理程序添加到函数openExternalBrowser中,但均无效。该应用程序正常运行(在Safari中打开外部URL),但测试失败。

谢谢!

编辑: 这是MockDashboardViewController代码:

class MockDashboardViewController: DashboardViewController {

    var openInternalBrowserCalled = false
    var openExternalBrowserCalled = false

    var presentNotificationCalled = false

    var webviewURLString = ""

    override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
        openInternalBrowserCalled = true
    }

    override func openExternalBrowser(url: URL) {
        openExternalBrowserCalled = true
    }

    override func presentNotification(notification: DashboardNotification, showAlertView: Bool) {
        presentNotificationCalled = true
    }
}

1 个答案:

答案 0 :(得分:1)

错误消息是正确的。您在说:

let vc = MockDashboardViewController()
vc.webview.navigationDelegate = vc

因此,Web视图的导航不是您的“相关WKWebKit ViewController”,我认为它是DashboardViewController,而是DashboardViewController的子类,即MockDashboardViewController。它说:

override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
    openInternalBrowserCalled = true
}

好的,那么当调用decidePolicyFor时会发生什么?它未在MockDashboardViewController中实现,因此DashboardViewController的实现称为:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
        openInternalBrowser(url: url, decisionHandler: decisionHandler)
        return;
    }
}

这就是执行过程中发生的所有事情。现在,decisionHandler到底在哪里被调用?无处。运行时完全正确。

decisionHandler的DashboardViewController实现中确实会调用openInternalBrowser(url:decisionHandler:)。这就是为什么在您运行应用程序(未测试)时该应用程序运行良好的原因。但是在您的测试子类中,您对此进行了覆盖-并且您没有呼叫super,因此您将这些呼叫丢弃到decisionHandler