mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
* security fix * more security fixes * fixes * pr feedback * Restore remote URL loading in native-shell-ios and native-shell-android Remove bundled-asset-only loading and SHA-256 integrity checks from both native shell packages. WebViews now load directly from the remote URL (default: https://self-app-alpha.vercel.app) over HTTPS, matching the pattern already implemented in kmp-sdk and self-sdk-swift. Also fixes ObjC selector mismatch in self-sdk-swift WebViewProviderImpl for configureRemoteLoading. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Restore remote URL loading in kmp-sdk and self-sdk-swift Remove bundled-asset-only loading from kmp-sdk AndroidWebViewHost and self-sdk-swift WebViewProviderImpl. Both now load directly from the remote URL (default: https://self-app-alpha.vercel.app) over HTTPS. Adds remoteWebAppBaseUrl to SelfSdkConfig and pipes it through IosWebViewHost via the new configureRemoteLoading protocol method. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * coderabbit comments * lint * coderabbit comments --------- Co-authored-by: seshanthS <seshanth@protonmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
121 lines
4.0 KiB
Swift
121 lines
4.0 KiB
Swift
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
import Foundation
|
|
|
|
final class MessageRouter {
|
|
private var handlers: [BridgeDomain: BridgeHandler] = [:]
|
|
private let sendToWebView: (String) -> Void
|
|
|
|
init(sendToWebView: @escaping (String) -> Void) {
|
|
self.sendToWebView = sendToWebView
|
|
}
|
|
|
|
func register(handler: BridgeHandler) {
|
|
handlers[handler.domain] = handler
|
|
}
|
|
|
|
func onMessageReceived(rawJson: String, isTrustedSource: Bool) {
|
|
guard isTrustedSource else {
|
|
return
|
|
}
|
|
|
|
guard let data = rawJson.data(using: .utf8) else {
|
|
return
|
|
}
|
|
|
|
let request: BridgeRequest
|
|
do {
|
|
request = try JSONDecoder().decode(BridgeRequest.self, from: data)
|
|
} catch {
|
|
return
|
|
}
|
|
|
|
guard request.version == 1 else {
|
|
let response = BridgeResponse.failure(
|
|
request: request,
|
|
code: "UNSUPPORTED_VERSION",
|
|
message: "Protocol version \(request.version) is not supported"
|
|
)
|
|
sendResponse(response)
|
|
return
|
|
}
|
|
|
|
guard let handler = handlers[request.domain] else {
|
|
let response = BridgeResponse.failure(
|
|
request: request,
|
|
code: "UNKNOWN_DOMAIN",
|
|
message: "No handler for domain: \(request.domain.rawValue)"
|
|
)
|
|
sendResponse(response)
|
|
return
|
|
}
|
|
|
|
let params = request.params?.mapValues { $0.value }
|
|
dispatchRequest(request, handler: handler, params: params)
|
|
}
|
|
|
|
private func dispatchRequest(_ request: BridgeRequest, handler: BridgeHandler, params: [String: Any]?) {
|
|
Task {
|
|
do {
|
|
let result = try await handler.handle(method: request.method, params: params)
|
|
let response = BridgeResponse.success(request: request, result: result)
|
|
await MainActor.run { sendResponse(response) }
|
|
} catch let error as BridgeHandlerError {
|
|
let response = BridgeResponse.failure(
|
|
request: request,
|
|
code: error.code,
|
|
message: error.errorDescription ?? "Unknown error"
|
|
)
|
|
await MainActor.run { sendResponse(response) }
|
|
} catch {
|
|
let response = BridgeResponse.failure(
|
|
request: request,
|
|
code: "HANDLER_ERROR",
|
|
message: error.localizedDescription
|
|
)
|
|
await MainActor.run { sendResponse(response) }
|
|
}
|
|
}
|
|
}
|
|
|
|
func pushEvent(domain: BridgeDomain, event: String, data: Any?) {
|
|
let eventDict: [String: Any] = [
|
|
"type": "event",
|
|
"version": 1,
|
|
"domain": domain.rawValue,
|
|
"event": event,
|
|
"data": data as Any,
|
|
"timestamp": Date().timeIntervalSince1970 * 1000
|
|
]
|
|
|
|
guard let jsonData = try? JSONSerialization.data(withJSONObject: eventDict),
|
|
let jsonStr = String(data: jsonData, encoding: .utf8) else {
|
|
return
|
|
}
|
|
|
|
let escaped = escapeForJs(jsonStr)
|
|
let js = "window.SelfNativeBridge._handleEvent('\(escaped)')"
|
|
sendToWebView(js)
|
|
}
|
|
|
|
private func sendResponse(_ response: BridgeResponse) {
|
|
guard let data = try? JSONEncoder().encode(response),
|
|
let jsonStr = String(data: data, encoding: .utf8) else {
|
|
return
|
|
}
|
|
|
|
let escaped = escapeForJs(jsonStr)
|
|
let js = "window.SelfNativeBridge._handleResponse('\(escaped)')"
|
|
sendToWebView(js)
|
|
}
|
|
|
|
private func escapeForJs(_ str: String) -> String {
|
|
str.replacingOccurrences(of: "\\", with: "\\\\")
|
|
.replacingOccurrences(of: "'", with: "\\'")
|
|
.replacingOccurrences(of: "\n", with: "\\n")
|
|
.replacingOccurrences(of: "\r", with: "\\r")
|
|
.replacingOccurrences(of: "\u{2028}", with: "\\u2028")
|
|
.replacingOccurrences(of: "\u{2029}", with: "\\u2029")
|
|
}
|
|
}
|