Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save NghiaTranUIT/336816eb7e82bf277904836cb64d99a4 to your computer and use it in GitHub Desktop.
Save NghiaTranUIT/336816eb7e82bf277904836cb64d99a4 to your computer and use it in GitHub Desktop.
add fetch, console.log and Promise.then/catch to JavaScriptCore on iOS/Mac
import JavaScriptCore
extension JSContext {
subscript(key: String) -> Any {
get {
return self.objectForKeyedSubscript(key)
}
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)
}
}
@objc protocol JSPromiseExports: JSExport {
func then(_ resolve: JSValue) -> JSPromise?
func `catch`(_ reject: JSValue) -> JSPromise?
}
class JSPromise: NSObject, JSPromiseExports {
var resolve: JSValue?
var reject: JSValue?
var next: JSPromise?
var timer: Timer?
func then(_ resolve: JSValue) -> JSPromise? {
self.resolve = resolve
self.next = JSPromise()
self.timer?.fireDate = Date(timeInterval: 1, since: Date())
self.next?.timer = self.timer
self.timer = nil
return self.next
}
func `catch`(_ reject: JSValue) -> JSPromise? {
self.reject = reject
self.next = JSPromise()
self.timer?.fireDate = Date(timeInterval: 1, since: Date())
self.next?.timer = self.timer
self.timer = nil
return self.next
}
func fail(error: String) {
if let reject = reject {
reject.call(withArguments: [error])
} else if let next = next {
next.fail(error: error)
}
}
func success(value: Any?) {
guard let resolve = resolve else { return }
var result:JSValue?
if let value = value {
result = resolve.call(withArguments: [value])
} else {
result = resolve.call(withArguments: [])
}
guard let next = next else { return }
if let result = result {
if result.isUndefined {
next.success(value: nil)
return
} else if (result.hasProperty("isError")) {
next.fail(error: result.toString())
return
}
}
next.success(value: result)
}
}
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
let promise = JSPromise()
promise.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) {timer in
timer.invalidate()
if let url = URL(string: link) {
URLSession.shared.dataTask(with: url){ (data, response, error) in
if let error = error {
promise.fail(error: error.localizedDescription)
} else if
let data = data,
let string = String(data: data, encoding: String.Encoding.utf8) {
promise.success(value: string)
} else {
promise.fail(error: "\(url) is empty")
}
}.resume()
} else {
promise.fail(error: "\(link) is not url")
}
}
return promise
}
jsContext["fetch"] = unsafeBitCast(fetch, to: JSValue.self)
return jsContext
}
}
guard let jsContext = JSContext.plus else {exit(-1)}
jsContext.exceptionHandler = { context, exception in
print("JS Error: \(String(describing: exception))")
}
jsContext.evaluateScript("""
fetch("https://github.com")
.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