OverviewFilesCommitsIssuesDocsReleasesPeers

p2pbuilders / ios-app / P2PBuilders / ViewController.swift

91 lines · swift ref:
import UIKit
import WebKit

// ViewController hosts a WKWebView and wires it to the Bare worklet via
// BareBridge. Flow:
//
//   frontend calls `window.webkit.messageHandlers.bare.postMessage(json)`
//       ↓  (WKScriptMessageHandler)
//   ViewController.userContentController(_:didReceive:)
//       ↓
//   BareBridge.sendToWorklet(json) → BareIPC.write
//
//   BareIPC.read → BareBridge.onFrameFromWorklet
//       ↓
//   webView.evaluateJavaScript("window.__bareRx(\(safeString))")
//       ↓
//   transport.js WebKit bridge resolves the pending RPC promise
//
// The webview loads index.html from the app bundle's Resources/ directory.

final class ViewController: UIViewController, WKScriptMessageHandler {
  private var webView: WKWebView!
  private let bridge = BareBridge()

  override func viewDidLoad() {
    super.viewDidLoad()
    setupWebView()
    startBackend()
  }

  private func setupWebView() {
    let contentController = WKUserContentController()
    contentController.add(self, name: "bare")

    let config = WKWebViewConfiguration()
    config.userContentController = contentController
    config.preferences.javaScriptCanOpenWindowsAutomatically = false

    let wv = WKWebView(frame: view.bounds, configuration: config)
    wv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    // Useful during bring-up — remove/gate for release.
    if #available(iOS 16.4, *) {
      wv.isInspectable = true
    }
    view.addSubview(wv)
    self.webView = wv

    // Load index.html from the bundle's Resources directory.
    guard let resourcesUrl = Bundle.main.resourceURL else {
      NSLog("[ViewController] Bundle.main.resourceURL missing")
      return
    }
    let htmlUrl = resourcesUrl.appendingPathComponent("Resources/index.html")
    let baseUrl = resourcesUrl.appendingPathComponent("Resources")
    wv.loadFileURL(htmlUrl, allowingReadAccessTo: baseUrl)
  }

  private func startBackend() {
    bridge.onFrameFromWorklet = { [weak self] line in
      self?.deliverToWebView(line)
    }
    do {
      try bridge.startWorklet()
    } catch {
      NSLog("[ViewController] failed to start backend worklet: %@", String(describing: error))
    }
  }

  // MARK: - WKScriptMessageHandler

  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    guard message.name == "bare" else { return }
    guard let body = message.body as? String else { return }
    bridge.sendToWorklet(body)
  }

  // MARK: - Helpers

  private func deliverToWebView(_ line: String) {
    // Safely encode the line as a JSON string literal for injection.
    guard let data = try? JSONSerialization.data(withJSONObject: line, options: [.fragmentsAllowed]),
          let escaped = String(data: data, encoding: .utf8) else { return }
    let js = "window.__bareRx(\(escaped))"
    webView.evaluateJavaScript(js, completionHandler: nil)
  }

  deinit {
    bridge.shutdown()
  }
}