Yeah, this is kinda where I am too…
I lead a team at the Swift 1.2 to 2.0 transition, and then (a bit more painfully) with the Swift 2.0 to 3.0 transition, but I still felt in charge of the language. That stopped post Swift 4-ish.
Swift 6 becomes a nightmare and sometimes fixing annotations (and workarounds) makes Swift far from initial simple & clean programming language.
Next Swift version should simplify the things, defaults main actor as possible, … hey I just want to create an “object” without problems and lot of annotations.
Thank you!
And while I have very mixed feelings about the change, it does look like MainActor-by-default is coming.
Very interesting!
Would it also apply to global static variables with an option to opt out or opt into a different isolation?
It's still in pitch form, but you can check it out.
Yes, would apply to may things, including globals! It doesn't affect your ability to control isolation statically, only what the compiler infers when there's nothing else to go on.
I think the best is to provide a switch to enable/disable strict concurrency. that can ease the learning curve.
Perhaps you are referring to something else, but there are a number of switches available, both for the checking level as well as some pretty fine-grained control over various concurrency-related features.
I also think, unfortunately, that we have a fairly strong body of evidence at this point that the existence of these switches and their defaults resulted in pretty wide-spread misunderstandings/problems.
Then NSLock. For the task you were referring to ("the app has a global config object. The object is mutable, but it’s typically mutated a single-digit number of times ever, during app startup") locks of any flavour would be quite faster than actors.
Indeed. It's pretty easy to replicate OSAllocatedUnfairLock
yourself.
// Number of Apple engineers who insisted on inspecting this: 5
/// An `os_unfair_lock` wrapper.
final class UnfairLock: Lock, @unchecked Sendable {
private let unfairLock: os_unfair_lock_t
init() {
unfairLock = .allocate(capacity: 1)
unfairLock.initialize(to: os_unfair_lock())
}
deinit {
unfairLock.deinitialize(count: 1)
unfairLock.deallocate()
}
fileprivate func lock() {
os_unfair_lock_lock(unfairLock)
}
fileprivate func unlock() {
os_unfair_lock_unlock(unfairLock)
}
}
It's even possible to backportMutex
(though I haven't tried it).
I did one of those backports too: GitHub - CharlesJS/SyncPolyfill: A polyfill to use Synchronization types on older macOS / iOS versions
I had it only use my rewritten implementation on Darwin platforms though. Elsewhere, I just have it typealias Mutex
to Synchronization.Mutex
, since platforms that don't have the Swift libraries packaged into the system don't have as pressing a need to support older versions.

it’s not really reasonable to expect Evolution proposers to scrutinize every piece of Swift documentation that has ever been written and update them, especially since the existing documentation is scattered across multiple websites and repositories
I agree, and that is why I also think that it’s crucial that there should only be one source of truth for the documentation, which should live in the same repository as the implementation of the thing that it documents.

but until Apple contracts, hires, or reassigns someone internally to work on Writing Documentation as their Actual Job
Apple can write whatever documentation it wants on its own site, but that should not be treated as an excuse for the project to not have its own complete documentation, just like it would if it were any other open-source language. You don’t see the Rust project merging undocumented features and expecting a company to write the documentation for them later.
I personally feel if not too far but definitely Too Fast.
Even seasoned Swift developers are finding the syntax and semantics overwhelming. Take One example is the Task
initializer, which requires understanding multiple modifiers like @escaping
, @isolated
, and async
—just for one parameter
I hope the language slows down on features and work on making it simple.
Assuming all the access during startup is single-threaded, nonisolated(unsafe)
is even faster.
Actually things have noticeably improved since I started this topic. Seems like Swift 6.1 was a step in the right direction. It might be that I understand Swift's structured concurrency better now, or things really have improved, or both, but my most recently started project was almost seamless and pain free.
One area where I'd be most confused previously was having "pure" functions with no side effects callable from anywhere. Somehow it would always get problematic but now with 6.1 I know exactly how to achieve it.
I'm curious, do other developers feel the same about Swift 6.1, that it got better?
I‘ve been writing a SwiftNIO application with Swift 6.2 snapshots which relies heavily on multiple managed tasks etc and had close to 0 unexpected concurrency complexity, just one case where I knew beforehand it would be hard to handle due to the design, so it really got more easier/consistent.
@crontab - It seems to be project dependent. For example, I recently tried doing static let regex = <regex-literal>
inside a top-level class, and the compiler complained (as regexes cannot be immutable, apparently - I only vaguely understood the problem).
I quickly gave up, and just moved the regex into the only method that uses it. But what if I needed to use the regex in a bunch of methods? I don't know how to do that (without binding it to every instance).
In short, it seems to be purely a matter of luck: Sometimes, Swift 6 is easy to work around. Other times it's not.
P.S. I really think we have to stop pretending Swift is a good language for beginners.
Although I am not a fan of Swift's concurrency model, I don't really find it hard to understand (because I have enough experience.)
It is just that the whole subject is poorly documented. There is still no sign of a unified and coherent
official documentation.

For example, I recently tried doing
static let regex = <regex-literal>
inside a top-level class, and the compiler complained (as regexes cannot be immutable, apparently - I only vaguely understood the problem).
Interesting, it's probably because the underlying library is written in C and say it can't guarantee immutability, something like that. The annoying part here is that you can't "convert" something to immutable with just let
like you would do with const
in C++.
Swift Regex is all Swift (you can read the code) but it does have mutable internal state to compile the regex.