← All writing
HackerNoonMay 2025

What Swift's Playground Won't Tell You About Concurrency

The starting point is a challenge that circulated on LinkedIn: a @TaskLocal value, a withValue block, a Task, a Task.detached, and a question about which print lands first. It looks like a trivia question and turns into a demonstration of why the environment you experiment in shapes the answer you believe.

It sets out what @TaskLocal actually guarantees — a value scoped to a task and its children — derives the expected ordering from that, then runs the code in a Playground and gets a result that does not match. Re-running the same code under XCTest behaves differently again.

From there it digs into task priorities as the actual explanation, repeats the experiment at a lower priority, and lands on the practical point: a Playground is not a scheduler you should be drawing conclusions from.

Along the way it includes a second, simpler @TaskLocal example, separate from the puzzle itself, to establish what the property is actually meant to guarantee before the Playground result complicates the picture. The mismatch ends up being a lesson about the environment rather than about @TaskLocal: a Playground can look consistent and still not schedule the code the way an app or a test target does.

What's inside

  • The originating LinkedIn puzzle itself: a task-local value set inside a scoped block, read from a plain Task and a detached one, with the reader asked to call the print order before running it.
  • A plainer, separate example of @TaskLocal used to establish what the property actually promises, scoping a value to a task and the children it spawns, before the puzzle's own behavior gets confusing.
  • The reasoned-out expected order versus what Playground actually printed, including that Playground itself was not fully consistent from run to run.
  • Moving the identical code into an XCTest target and running it repeatedly to see how it behaves under more realistic conditions than a single console run.
  • Tracing the mismatch to how the two environments default to different task priorities, and confirming the theory by deliberately lowering the priority and watching the ordering move again.
  • The practical takeaway aimed at any Swift developer: verify concurrency-sensitive code in a real app or test target, because Playground's scheduling behavior does not transfer.
The takeaway

The real subject is not the puzzle's answer but the reliability of the tool used to reason about it: @TaskLocal behaves exactly as documented, yet Playground's own scheduling defaults were enough to produce a different console output than a proper test target would. The practical lesson is to stop trusting quick experiments in Playground for anything priority- or ordering-sensitive.

Read the full experiment, priorities and all, on HackerNoon ↗