/Sidewalk

Proof-of-concept `WKWebview.evaluateJavaScript(...)` replacement for WebSocket-based Javascript execution.

Primary LanguageSwiftMIT LicenseMIT

Sidewalk

Proof-of-concept WKWebview.evaluateJavaScript(...) replacement for WebSocket-based Javascript execution.

Latest version

Github tag

Goals

Notes

  • The project is experimental and full of TODOs !
  • Use it for your own risk, test it carefully !
  • To send through big amount of data, you need to use sidewalkJavaScript(data: Data, ...) method and handle the data in Javascript by yourself. (customMessageHandler)
  • There is no HTTPS/WSS support yet. If you inject Sidewalk into a page with HTTPS base URL, the connection will fail due to mixed content error
  • There is no completionHandler implementation yet ---> not possible to access JS execution result
  • There is no bidirectional communication yet ---> you can only execute Javascript
  • To receive messages from Javascript, you still have to use WKScriptMessageHandler

TODOs

  • add HTTPS/WSS support
  • add bidirectional messaging
  • add completion handler for script evaluation results (async/await with message ID on top of bidirectional messaging)
  • add reconnection logic in case of exceptional socket close
  • here will be the collection of TODOs from the code

Sample project

This project highlights the problem with WKWebview.evaluateJavaScript(...) and shows how to overcome the issue with Sidewalk. https://github.com/Danesz/SidewalkExample.git

How to use

  1. Install the Swift package
...
dependencies: [
    .package(url: "https://github.com/Danesz/Sidewalk.git", from: "0.0.2"),
],
...
  1. A) Attach it to the webview before it loads the page
import Sidewalk
...

Sidewalk.shared().attachToWebView(webview, when: .atDocumentStart)
webview.loadHTMLString(...)
  1. B) Or, inject the Javascript manually when needed on the already loaded page
import Sidewalk
...

Sidewalk.shared().injectNow(webview)
  1. Execute Javascript via Sidewalk on your WKWebview instance. (Sidewalk defines and extension on the WKWebview class)
import Sidewalk
...

//old way
//webview.evaluateJavaScript("console.log('I am here')")

//new way
webview.sidewalkJavaScript("console.log('I am here')")
  1. Tell to Sidewalk to forget your webview once you don't need the webview anymore. (to avoid memory leaks)
import Sidewalk
...

Sidewalk.shared().forgetWebview(webview)
  1. (optional) Use custom messaging between JS and Swift. In this example we send JSON messages to Javascript and inside the webview we can decide how to react based on the message type.

In Swift:

import Sidewalk
...

struct SidewalkSocketDirectBodyUpdateMessage: Codable {
    let type: String = "bodyUpdate"
    var content: String
}

class SidewalkSocketMessageHandlerJSON: SidewalkSocketMessageHandler {
        
    func didReceive(message: WebSocketMessage, onSocket socket: WebSocket) {
        fatalError("not supported")
    }
    
    func send(data: Data, onSocket socket: WebSocket) {
        fatalError("not supported")
    }
    
    func send(text: String, onSocket socket: WebSocket) {
        do {
            let data = try JSONEncoder().encode(SidewalkSocketDirectBodyUpdateMessage(content: text))
            if let message = String(data: data, encoding: String.Encoding.utf8) {
                socket.send(text: message)
            }
        } catch let error {
            print("SidewalkSocketMessageHandlerJSON error", error.localizedDescription)
        }
    }
    
    func send(message: WebSocketMessage, onSocket socket: WebSocket) {
        fatalError("not supported")
    }

}

And receive it in Javascript:

Sidewalk.customMessageHandler = function(event){
    let parsed = JSON.parse(event.data);
    if (parsed.type === "bodyUpdate") {
        document.body.innerHTML = parsed.content;
    }
}

Contribution

Any contribution is welcome!