I would like to ask if it's possible to extend functions or have some way to use them like this. Because I don't want to open task closure for every async function. I couldnt find any Nominal Type for functions in swift to extend.
func doSometingAsync() async -> Void {
}
// Non - desired
Task {
await doSometingAsync()
}
func makeItAsync() {
Task {
await self()
}
}
// Desired
doSometingAsync().makeItAsync()
// Extending those also not working
typealias Function = () -> Void
typealias AsnyFunction = () async -> Any?
Thank you!
You definitely shouldn't create a Task for every async function. If you are, something else is likely wrong. But your desired syntax is definitely impossible. You cannot attach methods to functions. Swift does not support it. It's not logically impossible (Go uses it regularly). It's just not a Swift feature, and I expect it will never be a Swift feature. It's not the direction Swift is evolving.
The following is legal if you want it:
func makeAsync(_ f: #escaping () async -> Void) {
Task { await f() }
}
makeAsync(doSometingAsync)
This is shorter than your proposed solution by a few characters, if that matters to you.
All said, though, if you need this a lot, you probably have an async design problem, and should investigate that.
Related
Is it in Swift, like in TypeScript possible to define a struct where the methods depend on the generics?
protocol UseCase {
associatedtype Request
associatedtype Response
func execute(controller: () throws -> Request) async throws -> Response
}
If Request is Void, the protocol should return a different method signature.
func execute() async throws -> Response
I would write a protocol extension with where Request == Void:
extension UseCase where Request == Void {
func execute() async throws -> Response {
try await execute {}
}
}
When Request is Void, you can still call execute(controller:), but you can also call the parameterless execute().
When you conform to the protocol, you would need to implement execute(controller:), but this isn't a big problem, since you can just ignore the parameter.
Your contract would be a lie, violating the Liskov substitution principle. You can get around it with an underscore and a default parameter, but I recommend against it.
Also, I think you want Never, not Void—that's what Apple does.
struct JuiceCase: UseCase {
func execute(
controller _: () throws -> Never = { fatalError() }
) async throws -> String {
"🧃"
}
}
I'm trying to figure out what Cristik is saying in the comments but don't get it. What I see here is JuiceCase not semantically conforming to UseCase, even though it syntactically does. My interpretation of "substitutability" is that both must be correct—i.e. saying that JuiceCase is a UseCase is a lie, even though the compiler currently has no mechanism to verify that.
let juiceCase = JuiceCase()
try await juiceCase.execute() // "🧃"
let useCase: some UseCase = juiceCase
try await useCase.execute { fatalError() } // "🧃"
I don't understand what's the difference between add(_) and add(_) async method. Like the below code, the MyActor has two add methods and one of them uses async keyword. They can exist at the same time. If I comment out the second add method it will output AAAA. If both add methods exist at the same time, output "BBBBB"。
actor MyActor {
var num: Int = 0
func add(_ value: Int) {
print("AAAAA")
num += value
}
func add(_ value: Int) async {
print("BBBBB")
num += value
}
}
let actor = MyActor()
Task {
await actor.add(200)
print(await actor.num)
}
Supplementary:
With the second add method commented out, I defined let actor = MyActor() outside Task and I noticed the add method signed as add(_:). If move let actor = MyActor() inside Task the add method signed as add(_:) async
The difference emerges inside the actor, for example
actor MyActor {
func addSync(_ value: Int) {
}
func addAsync(_ value: Int) async {
}
func f() {
addSync(0)
}
func g() async {
addSync(0)
await addAsync(0)
}
}
In the actor method f and g you can call addSync synchronously. While outside the actor, you need always call an actor method with the await keyword as if the method is asynchronous:
func outside() async {
let actor = MyActor()
await actor.addSync(0)
}
Async in Swift allows for structured concurrency, which will improve the readability of complex asynchronous code. Completion closures are no longer needed, and calling into multiple asynchronous methods after each other is a lot more readable.
Async stands for asynchronous and can be seen as a method attribute making it clear that a method performs asynchronous work. An example of such a method looks as follows:
func fetchImages() async throws -> [UIImage] {
// .. perform data request
}
The fetchImages method is defined as async throwing, which means that it’s performing a failable asynchronous job. The method would return a collection of images if everything went well or throws an error if something went wrong.
How async replaces closure completion callbacks
Async methods replace the often seen closure completion callbacks. Completion callbacks were common in Swift to return from an asynchronous task, often combined with a Result type parameter. The above method would have been written as followed:
func fetchImages(completion: (Result<[UIImage], Error>) -> Void) {
// .. perform data request
}
Defining a method using a completion closure is still possible in Swift today, but it has a few downsides that are solved by using async instead:
You have to make sure yourself to call the completion closure in each possible method exit. Not doing so will possibly result in an app waiting for a result endlessly.
Closures are harder to read. It’s not as easy to reason about the order of execution as compared to how easy it is with structured concurrency.
Retain cycles need to be avoided using weak references.
Implementors need to switch over the result to get the outcome. It’s not possible to use try catch statements from the implementation level.
These downsides are based on the closure version using the relatively new Result enum. It’s likely that a lot of projects still make use of completion callbacks without this enumeration:
func fetchImages(completion: ([UIImage]?, Error?) -> Void) {
// .. perform data request
}
Defining a method like this makes it even harder to reason about the outcome on the caller’s side. Both value and error are optional, which requires us to perform an unwrap in any case. Unwrapping these optionals results in more code clutter which does not help to improve readability.
I have a callback based API.
func start(_ completion: #escaping () -> Void)
I'd like to write an async/await wrapper on top of that API, deferring the implementation to original callback based API.
func start() async {
let task = Task()
start {
task.fulfill()
}
return await task
}
Obviously, this code doesn't connect - it's not real, there is no method fulfill on Task.
Question: is there a way to use unstructured concurrency in Swift so that it would help me achieve this?
You are looking for continuations.
Here is how to use it in your example:
func start() async {
await withCheckedContinuation { continuation in
start(continuation.resume)
}
}
Consider this code:
func test() {
A()
B()
C()
D()
E()
}
Each function here have their own set of actions like calling API's, parsing them, writing results in file, uploading to servers, etc etc.
I want to run this functions one by one. I read about completion handlers. My problem with completion handlers are:
All examples given to understand completion handlers was having just two methods
I don't want to place this functions in some other function. I want all function calls (A to E) inside Test() function alone
Can someone help on this?
This is easy to do, you just need to add a closure argument to call on completion. For example:
func a(completion: (() -> Void)) {
// When all async operations are complete:
completion()
}
func b(completion: (() -> Void)) {
// When all async operations are complete:
completion()
}
func c(completion: (() -> Void)) {
// When all async operations are complete:
completion()
}
func d(completion: (() -> Void)) {
// When all async operations are complete:
completion()
}
func e(completion: (() -> Void)) {
// When all async operations are complete:
completion()
}
func test() {
a {
b {
c {
d {
e {
// All have now completed.
}
}
}
}
}
}
As you can see, this looks horrible. One problem with multiple async operations, non concurrently is that you end up with this horrible nesting.
Solutions to this do exist, I personally recommend PromiseKit. It encapsulates blocks in easy chaining methods, which is far cleaner.
Take a look at reactive programming and observable chains. RxSwift will be an excellent library for you. Reactive programming is very popular right now and was created exactly to solve problems like yours. It enables you to easily create a process (like an assembly line) that transforms inputs into your desired outputs. For example, your code in Rx would look like:
A()
.flatMap { resultsFromA in
B(resultsFromA)
}
.flatMap { resultsFromB in
C(resultsFromB)
}
.flatMap { resultsFromC in
D(resultsFromC)
}
.flatMap { resultsFromD in
E(resultsFromD)
}
.subscribe(onNext: { finalResult in
//result from the process E, subscribe starts the whole 'assembly line'
//without subscribing nothing will happen, so you can easily create observable chains and reuse them throughout your app
})
This code sample would create a process where you'd transform results from your initial API call (A), into parsed results (B), then write parsed results to a file (C) etc. Those processes can be synchronous or asynchronous. Reactive programming can be understood as Observable pattern on steroids.
Please excuse me for the poor title. Will update it if something better comes in mind.
Tested on: Xcode Version 7.2.1 (7C1002)
I want to take advantage of Swift's lazy stored properties to compute the value of a property while in a "background" queue before accessing it from the main queue.
Here is a class defining a lazy stored property
class Cache {
lazy var heavilyComputational = {
return 42
}()
}
This is an example usage
dispatch_async(dispatch_queue_create("com.qnoid.compute", DISPATCH_QUEUE_CONCURRENT)){
let cache = Cache()
let _ = cache.heavilyComputational
dispatch_async(dispatch_get_main_queue()){
debugPrint(cache.heavilyComputational)
}
}
That works.
Warning aside, IMO the intent is not clear.
A lazy property is lazily computed yes but in this case, the "where", "when" and "why" are equally important and not as clear.
Tried to clarify intent by being "clever" using a struct like so
struct Compute {
var function: () -> Void {
didSet{
function()
}
}
}
dispatch_async(dispatch_queue_create("com.qnoid.compute", DISPATCH_QUEUE_CONCURRENT)){
let cache = Cache()
Compute {
cache.heavilyComputational
}
dispatch_async(dispatch_get_main_queue()){
debugPrint(cache.heavilyComputational)
}
}
but the compiler "outsmart" me by removing the instruction (AFAICT) since the Compute instance isn't used. FWIW, this is on the Debug configuration and I can only assume on Release it will be even more aggressive.
For comparison, this works as expected (*not the didSet call)
dispatch_async(dispatch_queue_create("com.qnoid.compute", DISPATCH_QUEUE_CONCURRENT)){
let cache = Cache()
let compute = Compute {
cache.heavilyComputational
}
compute.function()
dispatch_async(dispatch_get_main_queue()){
debugPrint(cache.heavilyComputational)
}
}
but by that time, the whole purpose is defeated.
1. Have I misunderstood something?
2. Is there a native construct to allow for something like this in Swift?
3. If not, is there a way to rewrite this code so that the intent is clear?
4. Why the didSet is not called?
Sample project: https://github.com/qnoid/compute
It seems you kinda realise the problem. As far as I know, didSet won't be triggered if it's in the init method.
In your case, when you do
Compute {
cache.heavilyComputational
}
It's actually using init method of the struct to set the function property which will not trigger the didSet.
And in your second attempt, since you explicitly call the function() which will then call cache.heavilyComputational, so you trigger the lazy initialization of heavilyComputational manually. It's pretty much equivalent to your original (without the Compute struct) method.
To make your struct works without calling the function, you have to do some hacks.
struct Compute {
var function: () -> Void = {} {
didSet{
function()
}
}
init(function: () -> Void) {
setFunction(function)
}
private mutating func setFunction(function: () -> Void) {
self.function = function
}
}
So then you can just do
Compute {
cache.heavilyComputational
}
For further improvement, I'll have to think about it.
I believe I have found a simple answer to this.
struct Compute {
init(f: () -> Void) {
f()
}
}
Happy with that answer for question 3, the rest remain. Tho, worry I might be bitten by some future compiler optimisation.