Search code examples
iosswiftwebrtcrtcmulticonnection

WKWebView using WebRTC quit broadcasting when screen goes off in Swift


I'm making an app that has voice chat via WKWebVew in Swift.

The voice you speak should be heard by other users even when the screen is off.

However, if the screen of the device is turned off while using WKWebView, the broadcast ends and WKWebView is refreshed.

import UIKit
import WebKit

class WebViewController: UIViewController {
    @IBOutlet var webView: WKWebView!

    override func loadView() {
        super.loadView()

        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.ignoresViewportScaleLimits = true
        webConfiguration.allowsInlineMediaPlayback = true
        webConfiguration.allowsAirPlayForMediaPlayback = true
        webConfiguration.allowsPictureInPictureMediaPlayback = true
        webConfiguration.mediaTypesRequiringUserActionForPlayback = []

        webView = WKWebView(frame: self.view.bounds, configuration: webConfiguration)
        webView.uiDelegate = self
        webView.navigationDelegate = self

        self.view = self.webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        webView.scrollView.bounces = false

        let localFilePath = Bundle.main.url(forResource: "www/test", withExtension: "html")
        let request = URLRequest(url: localFilePath!)
        webView.load(request)
    }
}

The following is handling of alert window and avoiding duplicate reload.

extension WebViewController: WKUIDelegate {
    //alert
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo,
                 completionHandler: @escaping () -> Void) {
        let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .default,
                                                handler: { (action) in completionHandler() }))
        self.present(alertController, animated: true, completion: nil)
    }

    //confirm
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo,
                 completionHandler: @escaping (Bool) -> Void) {

        let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in completionHandler(true) }))
        alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in completionHandler(false) }))
        self.present(alertController, animated: true, completion: nil)
    }

    //prompt
    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String,
                 defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        let alertController = UIAlertController(title: "", message: prompt, preferredStyle: .alert)
        alertController.addTextField { (textField) in textField.text = defaultText }
        alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
            if let text = alertController.textFields?.first?.text {
                completionHandler(text)
            } else {
                completionHandler(defaultText)
            }
        }))

        alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
            completionHandler(nil) }))

        self.present(alertController, animated: true, completion: nil)
    }

    // href="_blank"
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        if navigationAction.targetFrame == nil {
            webView.load(navigationAction.request)
        }
        return nil
    }
}

extension WebViewController: WKNavigationDelegate {
    // prevent reload
    public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
        webView.reload()
    }
}

It is said that WKWebView stops working when the screen goes off in WKWebView due to view hierarchy.

Even if the screen is turned off through Background Mode, is there a way to fix the view?

In Android, you can Intent WebView through Foreground Service, so the WebView works even when the screen is turned off. I wonder if there is no such way in Swift.


Solution

  • No, there is no such way. When the screen is off. i.e the device is locked you will not be able to use functionality of the WKWebView. Even if you app is in Background mode. You will have to find another solution to leave the microphone on and send it to the remote server. Read about the different background modes that iOS supports. You can ask to leave the microphone on for some reasons. Record voice in background(when i tapped home button) using AVAudioRecorder