Intro

If you’re able to solve this, it means you understand TaskLocal really well.

Riddle

Look at the attached code snippet and guess:

  • What will be printed at each step?
  • Is the order of prints always the same?
class Riddler {
    @TaskLocal static var message = "Hello"

    static func riddleMe() {
        Self.$message
          .withValue("Bye", operation: {
              print("Print 1: \(Self.message)") // ???
              Task {
                  print("Print 2: \(Self.message)") // ???
              }
              Task.detached {
                print("Print 3: \(Self.message)") // ???
              }
          })
        print("Print 4: \(Self.message)") // ???
    }
}

Riddler.riddleMe()

Hint

Do you want to learn more about TaskLocal and Test Scoping in Swift 6.1 first? - Check out my blog post on “Concurrency-Safe Testing in Swift 6.1 with TaskLocal and Test Scoping” HERE.

Answer

class Riddler {
    @TaskLocal static var message = "Hello"

    static func riddleMe() {
        Self.$message
          .withValue("Bye", operation: {
              print("Print 1: \(Self.message)") // Bye
              Task {
                  print("Print 2: \(Self.message)") // Bye
              }
              Task.detached {
                print("Print 3: \(Self.message)") // Hello
              }
          })
        print("Print 4: \(Self.message)") // Hello
    }
}

Riddler.riddleMe()

Console output

Print 1: Bye
Print 2: Bye
Print 3: Hello
Print 4: Hello

Explanation

  • Print 1 - you’re inside the .withValue("Bye") block. The message TaskLocal property is overriden bt “Bye”
  • Print 2 - the non-detached Task { ... } inherits the parent tasks’s context, including TaskLocal override.
  • Print 3 - the detached task runs with a new context - it does not inherit any TaskLocal overrides.
  • Print 4 - outside the withValue block, the override reverts to the default “Hello” value

Is the order of prints always the same? - No

Why?

In the code snippet we spawn 2 tasks: a child Task and just after a dettached Task. Both run concurrently, so we have no control over when they start or complete. This means you can see the different output in the console for each code run.


Thanks for reading. 📖

I hope you found it useful!

If you enjoy the topic don’t forget to follow me on one of my social media - LinkedIn, X, Mastodon, Bluesky or via RSS feed to keep up to speed. 🚀