Last active
January 20, 2024 08:54
-
-
Save yycking/e01cf2d73533c5fb752ffdfa511723fa to your computer and use it in GitHub Desktop.
add fetch, console.log and Promise.then/catch to JavaScriptCore on iOS/Mac
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import JavaScriptCore | |
extension JSContext { | |
subscript(key: String) -> Any { | |
get { | |
return self.objectForKeyedSubscript(key) as Any | |
} | |
set{ | |
self.setObject(newValue, forKeyedSubscript: key as NSCopying & NSObjectProtocol) | |
} | |
} | |
} | |
@objc protocol JSConsoleExports: JSExport { | |
static func log(_ msg: String) | |
} | |
class JSConsole: NSObject, JSConsoleExports { | |
class func log(_ msg: String) { | |
print(msg) | |
} | |
} | |
class JSPromise: NSObject { | |
enum JSPromiseResult { | |
case success(Any) | |
case failure(Any) | |
} | |
private var result: JSPromiseResult? { | |
didSet {result.map(report)} | |
} | |
private var callbacks: [(JSPromiseResult) -> Void] = [] | |
func observe(using callback: @escaping (JSPromiseResult) -> Void) { | |
if let result = result { | |
return callback(result) | |
} | |
callbacks.append(callback) | |
} | |
private func report(result: JSPromiseResult) { | |
callbacks.forEach { $0(result) } | |
callbacks = [] | |
} | |
convenience init(executor: @escaping (@escaping(Any)->Void, @escaping(Any)->Void) -> Void) { | |
self.init() | |
executor {[weak self] resolve in | |
self?.result = .success(resolve) | |
} _: {[weak self] reject in | |
self?.result = .failure(reject) | |
} | |
} | |
override init() { | |
} | |
} | |
@objc protocol JSPromiseExports: JSExport { | |
func then(_ resolve: JSValue) -> JSPromise | |
func `catch`(_ reject: JSValue) -> JSPromise | |
} | |
extension JSPromise: JSPromiseExports { | |
func then(_ block: JSValue) -> JSPromise { | |
let weakBlock = JSManagedValue(value: block, andOwner: self) | |
let promise = JSPromise() | |
observe { result in | |
switch result { | |
case .success(let value): | |
let next = weakBlock?.value.call(withArguments: [value]) as Any | |
promise.result = .success(next) | |
case .failure(let error): | |
promise.result = .failure(error) | |
} | |
} | |
return promise | |
} | |
func `catch`(_ block: JSValue) -> JSPromise { | |
let weakBlock = JSManagedValue(value: block, andOwner: self) | |
let promise = JSPromise() | |
observe { result in | |
switch result { | |
case .success(let value): | |
promise.result = .success(value) | |
case .failure(let error): | |
let next = weakBlock?.value.call(withArguments: [error]) as Any | |
promise.result = .failure(next) | |
} | |
} | |
return promise | |
} | |
} | |
extension JSContext { | |
static var plus:JSContext? { | |
let jsMachine = JSVirtualMachine() | |
guard let jsContext = JSContext(virtualMachine: jsMachine) else { | |
return nil | |
} | |
jsContext.evaluateScript(""" | |
Error.prototype.isError = () => {return true} | |
""") | |
jsContext["console"] = JSConsole.self | |
jsContext["Promise"] = JSPromise.self | |
let fetch:@convention(block) (String) -> JSPromise = { link in | |
return JSPromise{ resolve, reject in | |
if let url = URL(string: link) { | |
URLSession.shared.dataTask(with: url){ (data, response, error) in | |
if let error = error { | |
reject(error.localizedDescription) | |
} else if | |
let data = data, | |
let string = String(data: data, encoding: String.Encoding.utf8) { | |
reject(string) | |
} else { | |
reject("\(url) is empty") | |
} | |
}.resume() | |
} else { | |
reject("\(link) is not url") | |
} | |
} | |
} | |
jsContext["fetch"] = unsafeBitCast(fetch, to: JSValue.self) | |
return jsContext | |
} | |
} | |
jsContext.evaluateScript(""" | |
fetch("https://github.com") | |
.then(data=>{ | |
console.log(data) | |
}) | |
.catch(e=>console.log(e)) | |
""") |
@jigneshD11 These is not work in playground.
guard let context = JSContext.plus else {
fatalError("Could not create JSContext")
}
context.evaluateScript("""
(function () {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((data) => {
console.log(data);
})
.catch((e) => console.log(e));
})();
""")
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There is some error