Tasks are a core feature of Swift Concurrency and are required to go from a synchronous to an asynchronous context. They have ‘hidden’ functionality like inheriting task context and come with additional features like detached tasks and task groups.

In this module, we’ll go into all details related to task and answer questions like:

And much more! But before we do, it’s time for a short introduction to get you started.

What is a Task in Swift Concurrency?

A Task in Swift Concurrency allows you to call async methods inside a synchronous context. Take the following two methods:

func someSynchronousMethod() {
    // Inner logic...
}

func someAsynchronousMethod() async {
    // Inner logic...
}

If we want to call the asynchronous method inside the synchronous one, we can’t just simply call it:

While it’s correct what the compiler suggests, we can fix this by turning the synchronous method into an async one, which is not what we always want to do. The asynchronous chain has to start somewhere.

This is where tasks come in to place:

func someSynchronousMethod() {
    Task {
        await someAsynchronousMethod()
    }
}

This is the simplest example of a Task definition in which we provide a closure containing the work to be performed.

When does a Task start executing?

When working with tasks like URLSessionTask you’re used to calling methods like resume() to start a task. Good news: you no longer have to do that! A task starts executing immediately after creation; you don’t explicitly start or schedule them.

Keeping a reference to a Task instance

You can store a reference to a task if you’d like to interact with it later. For example, you can use the reference to wait for the task to complete or to cancel it:

final class SomeTaskExecutor {

    var currentTask: Task<Void, Never>?

    func execute() {
        /// Store a reference to the task.
        currentTask = Task {
            await someAsynchronousMethod()
        }
    }

    deinit {
        /// Cancel the task when the outer instance is being deinitialized.
        currentTask?.cancel()
    }
}

Don’t worry too much about Task cancellation yet, we’ll cover this in detail later in this module.

Leaving a task hanging isn’t a programming error—you don’t need to keep a reference. A task runs regardless of whether you track it. However, without a reference, you can’t wait for its results or cancel its execution.

Threading and tasks

One of the most interesting parts for me to figure out for this course was threading: on which thread will a task execute?

A common example of using tasks you might see looks as follows:

Task { @MainActor in
    // Some logic to return on the main thread.
}

This is also your first introduction to actors!

Let’s say that it’s simply impossible to explain the concept of threads, tasks, and actors in this relatively short introduction. Take this little paragraph as a promise that you’ll learn more about this in a dedicated module later.

Summary

We’ve had a short look at tasks in Swift Concurrency. This exciting module will take you deep into all the knowledge you need around tasks. Stay tuned for dedicated lessons, one topic per time.

In the next lesson, we’ll go deeper into managing tasks and cancellations.