How do I keep a WKWebView object from crashing?

拜拜、爱过 提交于 2021-02-20 14:08:43

问题


Scenario

I'm building an iOS application in Swift. One feature is to have a live video feed as the application background. The video feed is originating from a Raspberry Pi on a local network using sudo motion. Motion is successfully hosting the feed on default port 8081.

The Swift app has a WKWebView object with the source pointing to my Raspberry Pi's motion port.

Suspected Issue

The webpage at port 8081 is constantly refreshing to load the most recent frame from the camera.

Problem

When running the app, the feed connects successfully and loads the first frame, and occasionally a second but then cuts off.

On a few occasions I received the following error in the terminal: [ProcessSuspension] 0x282022a80 - ProcessAssertion() Unable to acquire assertion for process with PID 0 leading me to believe that it is a memory management issue related to the constantly refreshing nature of the webpage.

Current Configuration

Currently, my call to .load() the WKWebView object is in ViewController.swift > override func viewDidLoad().

Proposed Resolution

Do I need to build some form of loop structure where I load a frame, pause execution and then call the WKWebView to reload a new frame a few seconds later.

I'm very new to Swift so patience with my question format is highly appreciated.


回答1:


The WkWebView and motion loading worked in Xcode 9 with iOS 11 versions but doesn't seem to work anymore with iOS 12. You are correct that the webkit is crashing on the second image.

Due to you being new to Swift I would advise reading this link on delegates because this solution I am providing will make more sense to you. Swift Delegates In summary, "Delegates are a design pattern that allows one object to send messages to another object when a specific event happens."

With this solution/hack we are going to use several of the WKNavigationDelegates to inform us when the WkWebView is doing specific tasks and inject our solution to the problem. You can find out all of the delegates the WKWebKit has here WKNavigationDelegates.

The following code can be used in a brand new iOS project and replace the code in the ViewController.swift. It requires no interface builder or IBOutlet connections. It will create a single web view on the view and point to the address 192.168.2.75:6789. I have included inline comments to attempt to explain what the code is doing.

  1. We are loading the HTTP Response twice from motion in decidePolicyFor navigationResponse delegate and keeping track with a counter. I have left some print statements so you can see what the response is. The first is a header and the second is the image information.
  2. When our counter gets to 3 items (ie the second image) we are forcing the wkWebView to cancel all navigation (ie stop loading) in the decidePolicyFor navigationResponse delegate. See the line with decisionHandler(.cancel). This is what is stopping the crash.
  3. This leads us to receive the callback from the wkwebview delegate WebView didFail navigation. At this point we want load our motion/pi url again and start the loading process again.
  4. We must then reset our counter so we can repeat this process until someone else comes up with a better solution.

    import UIKit
    import WebKit
    
    class ViewController: UIViewController, WKNavigationDelegate  {
    
        // Memeber variables
        var m_responseCount = 0; /* Counter to keep track of how many loads the webview has done.
                        this is a complete hack to get around the webkit crashing on
                        the second image load */
        let m_urlRequest = URLRequest(url: URL(string: "http://192.168.2.75:6789")!) //Enter your pi ip:motionPort
        var m_webView:WKWebView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            m_webView = WKWebView(frame: self.view.frame)  // Create our webview the same size as the viewcontroller
            m_webView.navigationDelegate = self            // Subscribe to the webview navigation delegate
        }
    
        override func viewDidAppear(_ animated: Bool) {
            m_webView.load(m_urlRequest)                    // Load our first request
            self.view.addSubview(m_webView)                 // Add our webview to the view controller view so we can see it
        }
    
        // MARK: - WKNavigation Delegates
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            print("decidePolicyFor navigationAction")
            print(navigationAction.request) //This is the request to connect to the motion/pi server http::/192.168.2.75:6789
            decisionHandler(.allow)
        }
    
        func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
            print("decidePolicyFor navigationResponse")
            print(navigationResponse.response) // This is HTML from the motion/rpi
    
        /* We only want to load the html header and the first image
           Loading the second image is causing the crash
             m_responseCount = 0 - Header
             m_responseCount = 1 - First Image
             m_responseCount >= 2 - Second Image
        */
            if(m_responseCount < 2)
            {
                decisionHandler(.allow)
            }
            else{
                decisionHandler(.cancel) // This leads to webView::didFail Navigation Delegate to be called
            }
    
            m_responseCount += 1;  // Incriment our counter
    
        }
    
        func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        /*
         We have forced this failure in webView decidePolicyFor navigationResponse
         by setting decisionHandler(.cancel)
         */
            print("didFail navigation")
    
            m_webView.load(m_urlRequest) //Lets load our webview again
            m_responseCount = 0     /*  We need to reset our counter so we can load the next header and image again
                                    repeating the process forever
                                */
        }
    
        func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
        // If you find your wkwebview is still crashing break here for
        // a stack trace
            print("webViewWebContentProcessDidTerminate")
    
        }
    }
    

Note: You are also required to add the following to your info.plist file due to the motion/pi server response being http and not https

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoadsInWebContent</key>
    <true/>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

I encourage you to use this basic example and modify it to suit your application requirements. I also encourage you to post any findings of your own because I am having the exact same problem using the exact same hardware as you and this is a hack more than a solution.



来源:https://stackoverflow.com/questions/52789184/how-do-i-keep-a-wkwebview-object-from-crashing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!