Closed shirakaba closed 5 months ago
There are two good options here:
NodePromise
yourself: this is quite similar to the approach you'd take in JS. Something like#NodeModule(exports: [
"doHeavyTaskWithArgument": try NodeFunction { (arg: String) in
return NodePromise { deferred in
SCShareableContent.getExcludingDesktopWindows(true, onScreenWindowsOnly: true) { content, error in
guard let content = content else {
deferred(.failure(error!))
return
}
let output = // turn content into something compatible with JS
deferred(.success(output))
}
}
},
])
async
world in Swift: NodeSwift converts async
Swift functions into JS functions that return promises. Fortunately, SCShareableContent
already appears to provide async APIs. So you can do something like#NodeModule(exports: [
"doHeavyTaskWithArgument": try NodeFunction { (arg: String) async throws in
let content = try await SCShareableContent.excludingDesktopWindows(true, onScreenWindowsOnly: true)
let output = // ...
return output
},
])
Thanks so much for providing both examples; I'll definitely have a use for both!
I'll try it out now. Will either come back with follow-up questions or be able to close the issue altogether. 🙇
Tried out the second pattern and it worked great, thanks!
Follow-up question: if we define a NodeFunction
, is it always exposed to JavaScript as a Promise, or only if the Swift function is marked as async
here:
try NodeFunction { (arg: String) async throws in
---------------------------------^^^^^
It's exposed as a Promise if and only if Swift resolves the function as async
. This means either explicitly using async
in the method signature and/or if there's an await
in the method body.
I'll definitely have a use for both
FWIW both of these patterns are mostly equivalent. If you're reliant on a function that uses the callback-passing style, you can bridge it to the async
world by using withChecked[Throwing]Continuation
. For example, if the async
overload of excludingDesktopWindows
didn't exist, you could instead do
#NodeModule(exports: [
"doHeavyTaskWithArgument": try NodeFunction { (arg: String) async throws in
let content = try await withCheckedThrowingContinuation { continuation in
SCShareableContent.getExcludingDesktopWindows(true, onScreenWindowsOnly: true) { content, error in
guard let content = content else {
continuation.resume(throwing: error!)
return
}
continuation.resume(returning: content)
}
}
let output = // ...
return output
},
])
One way to think about this is that withChecked[Throwing]Continuation
is basically Swift's equivalent of the Promise constructor/util.promisify
, so this is almost like a mix of the explicit NodePromise
approach and the implicit async
approach.
It's exposed as a Promise if and only if Swift resolves the function as async. This means either explicitly using async in the method signature and/or if there's an await in the method body.
Got it!
And thanks so much! I was struggling with withChecked[Throwing]Continuation
but this code sample makes a lot of sense. Apart from helping me use node-swift
, this has all been really helpful for my Swift 😅
I'm trying to figure out how to use NodeFunctions and NodePromises, but there are few examples in the code and tests. I'm also rather novice at Swift, so could really use a documented example.
What I'd like to do, on the JS side:
In other words, I want to call a function with this interface:
How would I express that in Swift? I can see how to accept an argument, but not how to resolve or reject a NodePromise.