Last active
January 12, 2022 18:44
-
-
Save helje5/0f8f41ac73c2ea0bf161db81defaa08e to your computer and use it in GitHub Desktop.
Using async/await concurrency on iOS 14 and before
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
// Created by Helge Heß 2021-06-17 | |
import Foundation | |
// They use obfuscated names to hide it from us! | |
import JavaScriptCore | |
/// Setup our async/await runtime. | |
let runtime = JSContext()! | |
/// Ask our async/await runtime to tell us about errors. | |
runtime.exceptionHandler = { _, error in | |
print("Caught exception:", error as Any) | |
} | |
/// Add a global `print` function to our async/await runtime | |
runtime.setObject( | |
{()->@convention(block) (JSValue)->Void in { print($0) }}(), | |
forKeyedSubscript: "print" as NSString | |
) | |
/// We need this protoocol to tell our async/await runtime about URLSession. | |
@objc protocol AsyncURLSession: JSExport { | |
/// Give access to `URLSession.shared` | |
@objc(shared) // overlap w/ `sharedSession` on the ObjC side | |
static var sharedSwift : URLSession { get } | |
/// Our async/await enabled URL fetcher, | |
/// returns an async error or a [ data, response ] tuple. | |
func data(_ url: String) -> JSValue | |
} | |
/// Here we implement our async aware function. | |
@objc extension URLSession: AsyncURLSession { | |
dynamic class var sharedSwift : URLSession { shared } | |
func data(_ url: String) -> JSValue { | |
guard let ctx = JSContext.current() else { fatalError("No runtime") } | |
/// Create our continuation | |
return JSValue(newPromiseIn: ctx) { resolve, reject in | |
guard let url = URL(string: url) else { | |
reject?.call(withArguments: [ "invalidURL" ]); return | |
} | |
self.dataTask(with: URLRequest(url: url)) { data, response, error in | |
RunLoop.main.perform { | |
if let error = error { | |
reject?.call(withArguments: [ error.localizedDescription ]) | |
} | |
else if let data = data, let response = response { | |
resolve?.call(withArguments: [ [ data, response ] ]) | |
} | |
else { | |
reject?.call(withArguments: [ "missingResponse" ]) | |
} | |
} | |
} | |
.resume() | |
} | |
} | |
} | |
runtime.setObject(URLSession.self, forKeyedSubscript: "URLSession" as NSString) | |
runtime.evaluateScript("URLSession.shared = URLSession.shared();") | |
/// Finally, execute our fetch using async/await. | |
runtime.evaluateScript(#""" | |
async function mainActor() { | |
let [ data, res ] = | |
await URLSession.shared.data("https://zeezide.com/#dontTrackMe") | |
print(data) | |
print(res) | |
} | |
mainActor() | |
"""#) | |
/// Keep it alive | |
RunLoop.main.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More information is available in the Async/Await for iOS 14 and before blog post.