Programmatically load background javascript in WKWebView

We currently have one of the most popular extensions in across all browsers. We reuse a majority of our codebase to support them all. Switching to the safari app extension would require us to rewrite all of our background scripts to support only a small percentage of our users. Instead we would like to leverage a WKWebView and run our current background scripts inside it, with swift acting as a bridge between the WKWebView javascript and our content scripts. Is this possible and how would this WKWebView be implemented? Currently we're running into this issue - https://stackoverflow.com/questions/31169884/wkwebkit-javascript-execution-when-not-attached-to-a-view-hierarchy

Replies

Not sure you solved your problem, anyway giving my reply so that others can use it.


here is content of Background.swift file.

==============================



import Foundation

import WebKit


class Background : NSObject, WKScriptMessageHandler

{

private var _webView: WKWebView?;

public static let shared = Background();

private var _bSetupInvoked = false;


override init() {

super.init();

}


private func readFile(_ url: URL) -> String {

do {

return try String(contentsOf: url, encoding: .utf8)

}

catch {

let message = "Could not load file at: \(url)"

fatalError(message)

}

}


public func setup ()

{

if(self._bSetupInvoked == true)

{

return;

}


let webConfiguration = WKWebViewConfiguration()

let startScript = Bundle(for: Background.self).url(forResource: "bg_script", withExtension: "js")!

let scripts = readFile(startScript)

let script = WKUserScript(source: scripts, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: true)


let contentController: WKUserContentController = WKUserContentController()

contentController.addUserScript(script)


contentController.add(self, name: "backgroundListener")

webConfiguration.userContentController = contentController


self._webView = WKWebView(frame: .zero, configuration: webConfiguration)

self._webView!.customUserAgent = "my-Bridge"

let html : String = """

<html>

<head></head>

<body></body>

</html>

""";

self._webView!.loadHTMLString(html, baseURL: nil)


self._bSetupInvoked = true;

}

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


//Here we will recieve data from background js script


let val1 = message.name;

let val2 = message.body as! String;

NSLog(val1);

NSLog(val2);

}


func sendMessage(payload : String)

{

self._webView?.evaluateJavaScript("handleMessage('\(payload)');", completionHandler: { result, error in

if let val = result as? String {

NSLog(val)

}

else {

NSLog("result is NIL")

}

});

}

}


----------------------------------------------------------------------------------------------------------------------------------------------------------------

Here is content of bg_script.js

========================



window.addEventListener("load", async ()=>{

webkit.messageHandlers.backgroundListener.postMessage("Background webview - javascript loaded successfully");

});


function handleMessage(data) {

webkit.messageHandlers.backgroundListener.postMessage("Reply from background script : " + data);

}


----------------------------------------------------------------------------------------------------------------------------------------------------------------

Here is the content of SafariExtensionHandler.swift

========================================



class SafariExtensionHandler: SFSafariExtensionHandler {



override init() {

super.init()


//Initializing necessary classes

Background.shared.setup()

}

....

....

....

}


----------------------------------------------------------------------------------------------------------------------------------------------------------------

below code sends data to background script loaded in WKView

---------------------------------------------------------------------------


//Sending message to background page

Background.shared.sendMessage( payload: DateFormatter.localizedString(from: Date() as Date, dateStyle: .medium, timeStyle: .medium) );