Skip to content

Commit

Permalink
Work to Blob API
Browse files Browse the repository at this point in the history
  • Loading branch information
theolampert committed Nov 3, 2023
1 parent 21962f5 commit ae20f78
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 62 deletions.
74 changes: 74 additions & 0 deletions Sources/ECMASwift/API/Fetch/Blob.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Foundation
import JavaScriptCore

@objc public protocol BlobExports: JSExport {
func text() -> JSValue?
func arrayBuffer() -> JSValue
}


@objc class Blob: NSObject, BlobExports {
var content: String

weak var context: JSContext?

init(content: String) {
self.content = content
}

func text() -> JSValue? {
guard let context = context else {
fatalError("JSContext is nil")
}

return JSValue(newPromiseIn: context) { (resolve, reject) in
let blobObject = JSValue(object: self.content, in: context)!
resolve?.call(withArguments: [blobObject])
}
}

func arrayBuffer() -> JSValue {
return JSValue(newPromiseIn: context) { [weak self] (resolve, reject) in
guard let data = self?.content.data(using: .utf8) else {
let errorDescription = "Failed to convert blob content to ArrayBuffer"
reject?.call(withArguments: [errorDescription])
return
}

// Convert Data to [UInt8]
let byteArray = [UInt8](data)

// Convert [UInt8] to JavaScript ArrayBuffer
let jsArrayBufferConstructor = self?.context?.evaluateScript("ArrayBuffer")
let jsUint8ArrayConstructor = self?.context?.evaluateScript("Uint8Array")
guard let arrayBuffer = jsArrayBufferConstructor?.construct(withArguments: [byteArray.count]),
let uint8Array = jsUint8ArrayConstructor?.construct(withArguments: [arrayBuffer]) else {
let errorDescription = "Failed to create ArrayBuffer"
reject?.call(withArguments: [errorDescription])
return
}

// Set bytes to ArrayBuffer
for (index, byte) in byteArray.enumerated() {
uint8Array.setValue(byte, at: index)
}

resolve?.call(withArguments: [arrayBuffer])
}
}
}

struct BlobAPI {
func registerAPIInto(context: JSContext) {
let blobClass: @convention(block) (String) -> Blob = { text in
let blob = Blob(content: text)
blob.context = context
return blob
}

context.setObject(
unsafeBitCast(blobClass, to: AnyObject.self),
forKeyedSubscript: "Blob" as NSString
)
}
}
6 changes: 3 additions & 3 deletions Sources/ECMASwift/API/Fetch/Fetch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ public class FetchAPI {
private func createRequest(url: Foundation.URL, options: JSValue?) throws -> URLRequest? {
var request = URLRequest(url: url)

if let options, let requestOptions = options.toDictionary() {
if let body = requestOptions["body"] as? Body {
if let options {
if let body = options.forProperty("body"), let body = try? Body.createFrom(body) {
request.httpBody = body.data()
}
if let method = requestOptions["method"] as? String {
if let method = options.forProperty("method").toString() {
request.httpMethod = method
}
}
Expand Down
58 changes: 1 addition & 57 deletions Sources/ECMASwift/API/Fetch/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,7 @@ enum Body: Codable {
}

return JSValue(newPromiseIn: context) { (resolve, reject) in
// guard let data = self.body?.data() else {
// let errorDescription = JSValue(object: "Unable to extract blob data", in: context)
// reject?.call(withArguments: [errorDescription])
// return
// }
// The Data should be converted to a suitable JSValue or a JS object, depending on your requirements.
// let blobObject = JSValue(object: data.base64EncodedString(), in: context)
let blob = Blob(_text: self.body!.string()!)
let blob = Blob(content: self.body!.string()!)
blob.context = context
resolve?.call(withArguments: [blob])
}
Expand Down Expand Up @@ -182,52 +175,3 @@ struct RequestAPI {
)
}
}

@objc public protocol BlobExports: JSExport {
// var url: String { get }
// var method: String? { get }

func text() -> JSValue?
}


@objc class Blob: NSObject, BlobExports {
var _text: String?

weak var context: JSContext?

init(_text: String? = nil) {
self._text = _text
}

func text() -> JSValue? {
guard let context = context else {
fatalError("JSContext is nil")
}

return JSValue(newPromiseIn: context) { (resolve, reject) in
guard let data = self._text else {
let errorDescription = JSValue(object: "Unable to extract blob data", in: context)
reject?.call(withArguments: [errorDescription])
return
}
let blobObject = JSValue(object: data, in: context)
resolve?.call(withArguments: [blobObject])
}
}
}

struct BlobAPI {
func registerAPIInto(context: JSContext) {
let blobClass: @convention(block) (String) -> Blob = { text in
let blob = Blob(_text: text)
blob.context = context
return blob
}

context.setObject(
unsafeBitCast(blobClass, to: AnyObject.self),
forKeyedSubscript: "Blob" as NSString
)
}
}
4 changes: 2 additions & 2 deletions Sources/ECMASwift/ECMASwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ public struct ECMASwift {
public let context: JSContext = .init()

public init() {
registerAPI()
registerAPIs()
}

private func registerAPI() {
private func registerAPIs() {
// Runtime APIs
BlobAPI().registerAPIInto(context: context)
RequestAPI().registerAPIInto(context: context)
Expand Down
42 changes: 42 additions & 0 deletions Tests/ECMASwiftTests/BlobTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import ECMASwift
import XCTest
import JavaScriptCore

final class BlobAPITests: XCTestCase {
let runtime = ECMASwift()

func testBlobCreation() {
let expectedContent = "Hello, World!"

let blob = runtime.context.evaluateScript("""
Blob('\(expectedContent)');
""")

XCTAssertNotNil(blob, "Blob creation failed")
}

func testBlobTextMethod() async {
let expectedContent = "Hello, World!"
_ = runtime.context.evaluateScript("""
async function getBlob() {
let blob = new Blob(["\(expectedContent)"])
return await blob.text()
}
""")
let result = try! await runtime.context.callAsyncFunction(key: "getBlob")
XCTAssertEqual(result.toString(), expectedContent)
}

func testBlobArrayBufferMethod() async {
let expectedContent = "Hello, World!"
_ = runtime.context.evaluateScript("""
async function getBlob() {
let blob = new Blob(["\(expectedContent)"])
let res = await blob.arrayBuffer()
return res
}
""")
let result = try! await runtime.context.callAsyncFunction(key: "getBlob")
XCTAssertEqual(result.forProperty("byteLength").toNumber(), 13)
}
}

0 comments on commit ae20f78

Please sign in to comment.