Swift中WKWebView在didFailProvisionalNavigation时崩溃

6
我们遇到了一个间歇性(在某些设备上,某些时间发生)的崩溃问题,我们很难确定问题所在,并且无法按需重现。这与Swift 3和WKWebView组件有关,具体来说,当尝试通过switch语句获取错误代码时,其回调协议会导致崩溃。请参见下面的内容:
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {

  if let err = error as? URLError {

    switch(err.code) {  //  Exception occurs on this line
    case .cancelled:
      Hint(hide: true)

    case .cannotFindHost:
      Hint(hide: false, hint:.CannotFindHost)

    case .notConnectedToInternet:
      Hint(hide: false, hint: .NoInternet)

    case .resourceUnavailable:
      Hint(hide: false)

    case .timedOut:
      Hint(hide: false)

    default:
      Hint(hide: false)
      print("error code: " + String(describing: err.code) + "  does not fall under known failures")
    }
  }
}

func Hint(hide: Bool, hint:SomeCustomEnum = SomeCustomEnum.Default) {
     //Dosomething with ui to let user know something bad happened
}

错误堆栈信息如下:
0 _BridgedStoredNSError.code.getter 1 _BridgedStoredNSError.code.getter 2 WebKitController.webView(WKWebView, didFailProvisionalNavigation : WKNavigation!, withError : Error) -> ()
3 @obj WebKitController.webView(WKWebView, didFailProvisionalNavigation : WKNavigation!, withError : Error) -> ()
...
通过审查代码,似乎应该没有问题,因为在调用switch语句时,变量err成功地被可选解包为有效的URLError对象。此时, err.code对于URLError来说不是可选项,因此switch语句应保证有该值。
目前尝试人为制造错误并排除问题,但并没有得到很多启示。例如,如果我创建自己的自定义错误而没有code属性,然后尝试将其强制转换为URLError,它会优雅地退出可选分配。
如果您有任何帮助或建议来解决问题甚至进一步进行故障排除,我们将不胜感激,在此期间将继续尝试在一致的基础上复制。

问题提得很好。您是否在访问自己拥有的服务器上的URL?如果是这样,服务器日志是否提供了任何关于导致应用程序崩溃的错误的见解? - Mike Taverne
那是一个很好的建议,但在我来得及处理之前,我的一位同事已经能够复现了。复现只需要使用默认的应用程序传输安全性(允许任意负载=否)模式下的“http”网址进行导航即可。虽然这确实使我们能够解决问题,但我将提交一个基于URLError转换的错误报告,因为它不包含.code属性。 - Glorifundel
1个回答

5
当查看Swift Bug(https://bugs.swift.org)提交网站时,我能够找到问题的描述,即将错误转换为URLError可能导致属性.code丢失:

https://bugs.swift.org/browse/SR-3879?jql=text%20~%20%22URLError%22

这里有一个链接,指向下面的参考资料,似乎是解决方案(仍在进行中)。

https://bugs.swift.org/browse/SR-3881

实际上,URLError 缺少两个 .code 定义:

NSURLErrorAppTransportSecurityRequiresSecureConnection NSURLErrorDataLengthExceedsMaximum

因此,如果在引用 URLError 的 .code 属性时遇到崩溃,则可以通过将其转换为 NSError 并检查 NSError 的 .code 属性来进行检查。

我们正在使用临时解决方法来缓解这个问题,直到 bug 得到解决(下面仅针对 NSURLErrorAppTransportSecurityRequiresSecureConnection(int -1022)类型的故障):

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

  let nserr = error as NSError
  if nserr.code == -1022 {
    Hint(hide: false, hint: .NSURLErrorAppTransportSecurityRequiresSecureConnection)

  } else if let err = error as? URLError {

    switch(err.code) {  //  Exception no longer occurs
    case .cancelled:
      Hint(hide: true)

    case .cannotFindHost:
      Hint(hide: false, hint:.CannotFindHost)

    case .notConnectedToInternet:
      Hint(hide: false, hint: .NoInternet)

    case .resourceUnavailable:
      Hint(hide: false)

    case .timedOut:
      Hint(hide: false)

    default:
      Hint(hide: false)
      print("error code: " + String(describing: err.code) + "  does not fall under known failures")
    }
  }
}

func Hint(hide: Bool, hint:SomeCustomEnum = SomeCustomEnum.Default) {
     //Dosomething with ui to let user know something bad happened
}

这应该是 if nserr.code == -1022 { 而不是 if let nserr.code == -1022 {,对吗? - Jannie Theunissen
是的,请原谅打字错误。正在解决中。 - Glorifundel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接