Skip to content

Commit

Permalink
Remove @mainactor attribute from the Bridge and add it to relevant fu…
Browse files Browse the repository at this point in the history
…nctions only.
  • Loading branch information
svara committed Feb 29, 2024
1 parent 454d6c0 commit 5d1cffb
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 50 deletions.
20 changes: 10 additions & 10 deletions Source/Bridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ public enum BridgeError: Error {
case missingWebView
}

@MainActor
protocol Bridgable: AnyObject {
var delegate: BridgeDelegate? { get set }
var webView: WKWebView? { get }
Expand All @@ -18,20 +17,12 @@ protocol Bridgable: AnyObject {

/// `Bridge` is the object for configuring a web view and
/// the channel for sending/receiving messages
@MainActor
public final class Bridge: Bridgable {
public typealias InitializationCompletionHandler = () -> Void
weak var delegate: BridgeDelegate?
weak var webView: WKWebView?

nonisolated public static func initialize(_ webView: WKWebView, completion: InitializationCompletionHandler?) {
Task { @MainActor in
await initialize(webView)
completion?()
}
}

public static func initialize(_ webView: WKWebView) async {
public static func initialize(_ webView: WKWebView) {
if getBridgeFor(webView) == nil {
initialize(Bridge(webView: webView))
}
Expand All @@ -46,24 +37,28 @@ public final class Bridge: Bridgable {

/// Register a single component
/// - Parameter component: Name of a component to register support for
@MainActor
func register(component: String) async throws {
try await callBridgeFunction(.register, arguments: [component])
}

/// Register multiple components
/// - Parameter components: Array of component names to register
@MainActor
func register(components: [String]) async throws {
try await callBridgeFunction(.register, arguments: [components])
}

/// Unregister support for a single component
/// - Parameter component: Component name
@MainActor
func unregister(component: String) async throws {
try await callBridgeFunction(.unregister, arguments: [component])
}

/// Send a message through the bridge to the web application
/// - Parameter message: Message to send
@MainActor
func reply(with message: Message) async throws {
logger.debug("bridgeWillReplyWithMessage: \(String(describing: message))")
let internalMessage = InternalMessage(from: message)
Expand All @@ -79,6 +74,7 @@ public final class Bridge: Bridgable {
// callBridgeFunction("send", arguments: [replyMessage.toJSON()])
// }
@discardableResult
@MainActor
func evaluate(javaScript: String) async throws -> Any? {
guard let webView else {
throw BridgeError.missingWebView
Expand All @@ -95,6 +91,7 @@ public final class Bridge: Bridgable {
/// Evaluates a JavaScript function with optional arguments by encoding the arguments
/// Function should not include the parens
/// Usage: evaluate(function: "console.log", arguments: ["test"])
@MainActor
func evaluate(function: String, arguments: [Any] = []) async throws -> Any? {
try await evaluate(javaScript: JavaScript(functionName: function, arguments: arguments).toString())
}
Expand All @@ -117,6 +114,7 @@ public final class Bridge: Bridgable {
/// The webkit.messageHandlers name
private let scriptHandlerName = "strada"

@MainActor
private func callBridgeFunction(_ function: JavaScriptBridgeFunction, arguments: [Any]) async throws {
let js = JavaScript(object: bridgeGlobal, functionName: function.rawValue, arguments: arguments)
try await evaluate(javaScript: js)
Expand Down Expand Up @@ -156,6 +154,7 @@ public final class Bridge: Bridgable {
// MARK: - JavaScript Evaluation

@discardableResult
@MainActor
private func evaluate(javaScript: JavaScript) async throws -> Any? {
do {
return try await evaluate(javaScript: javaScript.toString())
Expand All @@ -173,6 +172,7 @@ public final class Bridge: Bridgable {
}

extension Bridge: ScriptMessageHandlerDelegate {
@MainActor
func scriptMessageHandlerDidReceiveMessage(_ scriptMessage: WKScriptMessage) {
if let event = scriptMessage.body as? String, event == "ready" {
delegate?.bridgeDidInitialize()
Expand Down
45 changes: 5 additions & 40 deletions Tests/BridgeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,26 @@ import WebKit

@MainActor
class BridgeTests: XCTestCase {
func testInitWithANewWebViewAutomaticallyLoadsIntoWebView() async {
func testInitWithANewWebViewAutomaticallyLoadsIntoWebView() {
let webView = WKWebView()
let userContentController = webView.configuration.userContentController
XCTAssertTrue(userContentController.userScripts.isEmpty)

await Bridge.initialize(webView)
Bridge.initialize(webView)
XCTAssertEqual(userContentController.userScripts.count, 1)
}

func testInitWithTheSameWebViewDoesNotLoadTwice() async {
func testInitWithTheSameWebViewDoesNotLoadTwice() {
let webView = WKWebView()
let userContentController = webView.configuration.userContentController
XCTAssertTrue(userContentController.userScripts.isEmpty)

await Bridge.initialize(webView)
Bridge.initialize(webView)
XCTAssertEqual(userContentController.userScripts.count, 1)

await Bridge.initialize(webView)
Bridge.initialize(webView)
XCTAssertEqual(userContentController.userScripts.count, 1)
}

func testInitWithANewWebViewAutomaticallyLoadsIntoWebView() {
let webView = WKWebView()
let userContentController = webView.configuration.userContentController
XCTAssertTrue(userContentController.userScripts.isEmpty)

let expectation = expectation(description: "Wait for completion.")
Bridge.initialize(webView) {
XCTAssertEqual(userContentController.userScripts.count, 1)
expectation.fulfill()
}

wait(for: [expectation], timeout: .expectationTimeout)
}

func testInitWithTheSameWebViewDoesNotLoadTwice() {
let webView = WKWebView()
let userContentController = webView.configuration.userContentController
XCTAssertTrue(userContentController.userScripts.isEmpty)

let expectation1 = expectation(description: "Wait for completion.")
Bridge.initialize(webView) {
XCTAssertEqual(userContentController.userScripts.count, 1)
expectation1.fulfill()
}

let expectation2 = expectation(description: "Wait for completion.")

Bridge.initialize(webView) {
XCTAssertEqual(userContentController.userScripts.count, 1)
expectation2.fulfill()
}

wait(for: [expectation1, expectation2], timeout: .expectationTimeout)
}

/// NOTE: Each call to `webView.evaluateJavaScript(String)` will throw an error.
/// We intentionally disregard any thrown errors (`try? await bridge...`)
Expand Down

0 comments on commit 5d1cffb

Please sign in to comment.