| | |

Strict concurrency checking in Swift 5.x and 6.0

Swift 6 aims to detect many concurrency issues at compile time. Fixing these new warnings and errors at compile time should prevent so-called “data-races” from showing up as intermittent run-time errors. This goal is important and even urgent because software developers are increasingly relying on concurrency to utilise multi-core processors and to perform slow tasks like network requests on background threads.

Expressed differently, the Swift 6 compiler forces you (after optin) to follow certain rules that should guarantee that data-races between threads cannot occur. This “cannot occur” involves one or more of these rules:

  • ensuring that the shared data cannot change (“immutability”, e.g. via let)
  • ensuring that the passed data conforms to the sendable protocol (e.g. value types)
  • ensuring that the code is isolated to one actor (concurrency context) and thus can never be used elsewhere
  • providing some soft of serialisation, thus ensuring that a thread is finished with a critical update before another thread can start with the same code

give problems. This “provability” approach can imply that you end up fixing cases that theoretically could happen, but won’t because the compiler is not sure that they cannot occur. But the Swift.org community is working hard at minimising the number of false alarms.

Migration path and ‘optin’ settings

The Swift standard has been working towards this goal for a few years. In recent releases of Swift 5.x new capabilities were added stepwise – aiming at full data-race safety being guaranteed with the release of Swift 6.0 (Summer 2024).

Alternatives for fixing errors/warnings detected by Swift 5.10’s strict or complete concurrency checking (-strict-concurrency=complete):


@MainActor ensures a class or method is only accessible from the main actor (“isolated to a global actor” – doesn’t have to be Main but usually is).
This ensures that the class is only accessible to one thread.
It essentially blocks concurrent access, to achieve data-race safety.


Ensures that the type conforms to Sendable, and is marked final.
The Sendable protocol declares that the class only contain immutable (value type) properties.
Notably let properties are immutable, but so are value types like structs and Int and String. A type may implicitly conform to Sendable if all its properties are Sendable.
final blocks creation of subclasses which could contain new mutable properties.
With Sendable the compiler will check whether the type is indeed provably Sendable.
But this checking does not cross file boundaries (?).
Sendable checking is a bit strict (and may become a bit more flexible in Swift 6 to reduce the number of false positives).
The Sendable feature was added in Swift 5.5 (September 20, 2021).

@unchecked Sendable

This is applied to a type.
This option essentially just disables the compile-time warnings.
It disables checking for all properties in the type, so is a rather blunt instrument.


Used to make functions sendable: you cannot make a function conform to a protocol like Sendable.


Declare a property as nonisolated(unsafe) if you know there can never be a concurrency problem SE-412. Available in Swift 5.10 (March 5, 2024).
This option essentially just disables the warnings.
It can be used on stored properties, local variables, and global/static variables.


This turns off checking in an imported module.
And is meant if you don’t have time yet to fix it. Or don’t have access to the 3rd party source code.
Although the compiler will actually tell you if usage of @preconcurrency wasn’t necessary, this fix should be used sparingly.




Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *