Concurrency in Swift

Concurrency in Swift has built-in support for writing asynchronous and parallel code in a structured way. Asynchronous code can be suspended and resumed later, although only one piece of the program executes at a time.
Suspending and resuming code in your program lets it continue to make progress on short-term operations like updating its UI while continuing to work on long-running operations like fetching data over the network or parsing files.

Asynchronous Functions

An asynchronous function or asynchronous method is a special kind of function or method that can be suspended while it’s partway through execution.

func yourClassFunction(inGallery name: String) async -> [String] {
    await Task.sleep(2 * 1_000_000_000)  // Two seconds
    return ["sample1", "sample2", "sample3"]
}
  • The code starts running from the first line and runs up to the first await. It calls the yourClassFunction function and suspends execution while it waits for that function to return.
  • While this code’s execution is suspended, some other concurrent code in the same program runs.
  • After yourClassFunction returns, this code continues execution starting at that point. It assigns the value that was returned to photoNames.
  • The lines that define sortedNames and name are regular, synchronous code. Because nothing is marked await on these lines, there aren’t any possible suspension points.
  • The next await marks the call to the yourClassSecondFunction function. This code pauses execution again until that function returns, giving other concurrent code an opportunity to run.
  • After yourClassSecondFunction returns, its return value is assigned to photo and then passed as an argument when calling show(_:).

Asynchronous Sequences

The yourClassFunction function in the previous section asynchronously returns the whole array at once, after all of the array’s elements are ready. Another approach is to wait for one element of the collection at a time using an asynchronous sequence.

import Foundation

let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}

Asynchronous Functions in Parallel

Calling an asynchronous function with await runs only one piece of code at a time. While the asynchronous code is running, the caller waits for that code to finish before moving on to run the next line of code.

let productPhoto1 = await downloadPhoto(named: photoNames[0])
let productPhoto2 = await downloadPhoto(named: photoNames[1])
let productPhoto3 = await downloadPhoto(named: photoNames[2])

To call an asynchronous function and let it run in parallel with code around it, write async in front of let when you define a constant, and then write await each time you use the constant.

async let productPhoto1 = downloadPhoto(named: photoNames[0])
async let productPhoto2 = downloadPhoto(named: photoNames[1])
async let productPhoto3 = downloadPhoto(named: photoNames[2])

let productPhotos = await [productPhoto1, productPhoto2, productPhoto3]

#Steps

  • Call asynchronous functions with await when the code on the following lines depends on that function’s result. This creates work that is carried out sequentially.
  • Call asynchronous functions with async-let when you don’t need the result until later in your code. This creates work that can be carried out in parallel.
  • Both await and async-let allow other code to run while they’re suspended.
  • In both cases, you mark the possible suspension point with await to indicate that execution will pause, if needed, until an asynchronous function has returned.

Tasks and Task Groups

A task is a unit of work that can be run asynchronously as part of your program. All asynchronous code runs as part of some task.

await withTaskGroup(of: Data.self) { taskGroup in
    let photoNames = await listPhotos(inGallery: "Summer Vacation")
    for name in photoNames {
        taskGroup.addTask { await downloadPhoto(named: name) }
    }
}

Unstructured Concurrency

Unlike tasks that are part of a task group, an unstructured task doesn’t have a parent task. You have complete flexibility to manage unstructured tasks in whatever way your program needs

let newProductPhoto = // ... some photo data ...
let handle = Task {
    return await add(newProductPhoto, toGalleryNamed: "someproducts")
}
let result = await handle.value

Task Cancellation

Swift concurrency uses a cooperative cancellation model. Each task checks whether it has been canceled at the appropriate points in its execution, and responds to cancellation in whatever way is appropriate.

  • Throwing an error like CancellationError
  • Returning nil or an empty collection
  • Returning the partially completed work

Happy Multithreaded or Concurrency in Swift Coding 🙂


Discover more from CODE t!ps

Subscribe to get the latest posts sent to your email.

Scroll to Top

Discover more from CODE t!ps

Subscribe now to keep reading and get access to the full archive.

Continue reading