Monday, November 3, 2025

Swift 6.2: Approachable Concurrency

Holly Borla:

Swift 6.2 lowers the barrier to concurrent programming with a set of changes designed to reduce boilerplate and let you write safe concurrent code more naturally:

  • Single-threaded by default: Run your code on the main thread without explicit @MainActor annotations using the new option to isolate code to the main actor by default. This option is ideal for scripts, UI code, and other executable targets.
  • Intuitive async functions: Write async code without concurrent access to mutable state. Previously, nonisolated async methods always switched to the global executor that manages the concurrent thread pool, which made it difficult to write async methods for class types without data-race safety errors. In Swift 6.2, you can migrate to an upcoming feature where async functions run in the caller’s execution context, even when called on the main actor.
  • Opting into concurrency with @concurrent: Introduce code that runs concurrently using the new @concurrent attribute. This makes it clear when you want code to remain serialized on actor, and when code may run in parallel.

Donny Wals:

With Swift 6.2, Apple has made a several improvements to Swift Concurrency and its approachability. One of the biggest changes is that new Xcode projects will now, by default, apply an implicit main actor annotation to all your code. This essentially makes your apps single-threaded by default.

I really like this change because without this change it was far too easy to accidentally introduce loads of concurrency in your apps.

In this post I’d like to take a quick look at how you can control this setting as well as the setting for nonisolated(nonsending) from Xcode 26’s build settings menu.

Donny Wals:

Xcode 26 allows developers to opt-in to several of Swift 6.2’s features that will make concurrency more approachable to developers through a compiler setting called “Approachable Concurrency” or SWIFT_APPROACHABLE_CONCURRENCY. In this post, we’ll take a look at how to enable approachable concurrency, and which compiler settings are affected by it.

Matt Massicotte:

“Approachable Concurrency” is just a big group of settings. It is completely independent of making the default isolation MainActor.

Matt Massicotte:

In the Swift 6 language mode, there are only two flags “Approachable Concurrency” changes: InferIsolatedConformances and NonisolatedNonsendingByDefault. It switches on more stuff in 5 mode, but those will not affect this material.

Paul Hudson (Mastodon, Hacker News):

SE-0466 introduces the ability for code to opt into running on a single actor by default – to effectively go back to being a single-threaded program, where most code runs on the main actor until you say otherwise.

[…]

SE-0470 resolves a small but important concurrency problem by making it possible to restrict a protocol conformance to a specific global actor.

[…]

SE-0472 introduces a new way to create tasks so they start immediately if possible, rather than the existing behavior that only allows tasks to be queued to run at the next opportunity.

[…]

SE-0461 adjusts the way nonisolated async functions are called so that they run on the same actor as their caller. This sounds like a really abstract change, but it is important so I would recommend you spend the time to understand what’s changing and why.

[…]

SE-0371 introduces the ability to mark the deinitializers of actor-isolated classes as being isolated, which allows them to safely access data elsewhere in the class.

[…]

SE-0462 introduces the ability for tasks to detect when their priority has been escalated, and also for us to manually escalate task priority if needed.

[…]

SE-0469 introduces a useful change to the way we create tasks and child tasks: we can now give them names, which is ideal for debugging when one particular task goes rogue.

Matt Massicotte:

As we started getting closer to the release of Swift 6.0, I had this bright idea. I decided to write about every evolution proposal related to concurrency that would ship with that release. This resulted in 12 posts and let me tell you, it was a lot of work.

[…]

I don’t think I can pull off a post for each proposal this year. But, I’m not sure that’s even necessary anyways. Many of them are minor things. But not all.

[…]

I wanted to go on this little side-quest because people don’t find understanding isolation easy. Adding the distinction between static vs dynamic just makes it that much harder. And I really wanted to find a way to help explain this, because this is the Big Idea you need to get to understand what’s changing.

[…]

Setting the default has the potential to completely remove the need to think about concurrency. This could be an enormous win in many situations. For example, it would make the Swift 6 language mode a more friendly choice for true beginner programmers.

[…]

Right now, though, I see myself sticking with a nonisolated default. I need more experience trying this out with real systems before I make up my mind.

Steve Troughton-Smith:

As I understand it, the whole point of Swift 6 is to add syntactic sugar all over your projects to try to codify ahead of time that you know which thread everything will run on. The ‘approachable concurrency’ changes in 6.2 feels like an admission that nobody is actually going to do that, because it’s too hard, if not impossible, for most apps. It’s a major lift and a lot of work that, at best, leaves your app the same as before you started 😅

Approachable Concurrency, instead of making you annotate everything piece by piece, flips it and just assumes that none of your app is concurrent unless you specify otherwise, which is absolutely what should have been the default for Swift 6. If you were halfway through a Swift 6 port under the previous model, you now… delete all your code and go back to how it was before you annotated it?

Donny Wals:

Finding the solution to the issues I describe above is pretty tedious, and it forces us to explicitly opt-out of concurrency for specific methods and eventually an entire class. This feels wrong. It feels like we’re having to decrease the quality of our code just to make the compiler happy.

In reality, the default in Swift 6.1 and earlier was to introduce concurrency by default. Run as much as possible in parallel and things will be great.

This is almost never true. Concurrency is not the best default to have.

Amy Worrall:

imo the thing they tried to make safe is not the thing that’s actually the problem for most devs, and they did it in such a way that you can’t make a halfway house solution. Especially when you’re interacting with other people’s code, sometimes there’s just no way to architect things how you want.

Matt Massicotte:

I do not think you need to go and read every proposal. I did, and honestly, it was a lot of work. But, something I learned from doing it is many of these new language features were specifically built to help hide details. It’s still pretty early, but I think ultimately this is going to end up largely succeeding. What it all comes down to is how much concurrency you have in your project. (Probably too much)

You cannot progressively disclose the details of features you are actively using. Concurrency touches virtually all existing Swift code so tons of stuff gets shoved in your face all at once.

[…]

But, accidentally running stuff off the main thread is the simplest and most straight-forward concurrency problem you could possibly have it was absolutely pervasive.

[…]

Now there are some well-established patterns that can help avoid many concurrency issues, that one definitely included. But, these are often employed by advanced users that have been practicing for years. I think it is easy for these habits to make “I don’t have this problem” feel like “this is not a problem”.

[…]

Actors are not queues. Let me say it again, actor is an advanced tool. Be wary of any material that makes it seem otherwise. You should be sticking to the binary main/not-main until you are very comfortable with isolation before trying them out.

travisgriggs (Helge Heß):

One of the pitches in the earlier days was “C/Objective-C OK, but you can’t write safe/next level code with it—-Swift will close that gap.”

N years later, it doesn’t feel like there has been a step change in Apple software quality; if anything Apple software feels less solid, and looks cool “look what I did” extension points. I mean, some of the [things] you could do with runtime categories, and runtime prototypes were really cool. Now when I work on my 2 apps that originally happily port to Swift/UIKit, I’m just left confused with how to make things work. I’m happy when it finally works, and don’t ever try to improve the thing, it’s too much work.

Steve Troughton-Smith:

Swift 6 language mode is still a no-go, even with approachable concurrency turned on. I would be surprised if any full-featured UIKit app is able to use it, even now. Toy apps, or something that stays entirely within SwiftUI-land, maybe. It’s a major lift.

Jacob Bartlett (ios_memes):

Is Swift 6 strict concurrency going to be our Python 3 moment?

Sean Heber:

So I now have Tapestry entirely compiling with Swift 6 mode.

That was… fun.

Daniel Jalkut:

I achieved a major development milestone for my biggest app, MarsEdit, today. I can now build against Swift 6 with strict concurrency, and no warnings. It was harder than it should be (though Apple’s working on that), but it feels good knowing I can move forward with concurrent confidence.

Gwendal Roué:

GRDB is in a strange bucket. I think it goes back to the introduction of Swift concurrency.

[…]

Want to use the new MainActor isolation by default? It’s the default for new Xcode 26 projects, after all. Well this new language dialect creates incomprehensible compiler errors, and is so incompatible with the normal language that I’m supposed to rewrite all sample codes and documentation (hint: I won’t, SE-0466 is a sick joke).

Want to await SQLite with non-Sendable types? Well, I’m not sure the language makes it possible to express that.

Nico Reese (forum):

What would I do about this?

“Main actor-isolated property ‘timestamp’ can not be mutated from a Sendable closure”

[…]

It seems to be an issue with Core Data not being 100% ready for these concurrency changes? The workaround gets rid of the warning but does not look very nice.

Dave DeLong:

It’s hard to look at the state of @swiftlang’s concurrency model these days and draw any other conclusion than “this is a hodge podge of conflicting models that are basically inoperable together”.

IMO Swift Concurrency needs a huge “reset” button. Throw it all away and start over.

Ryan Ashcraft:

All I want from Swift is a fast compiler that gives actually useful error messages. Enough syntax sugar. Oh and also, Swift Concurrency has been a disaster.

Kyle Hughes:

It doesn’t feel like any plane has been landed in the realm of Apple platform development since the introduction of SwiftUI in 2019. Nothing new introduced fits well together. Everything that is released feels unfinished and gets informally deprecated, only to be replaced with another incomplete solution. It’s a confusing moment to be in—we will likely reflect on this as the time where we should have known what direction we were headed in.

Modern Apple platform development is optimized for building miniature versions of Apple’s own apps or for tackling interesting programming-language puzzles. It is, at present, poorly suited to making strategic, long-term decisions about economically viable software—or to providing the tools needed to make such decisions.

That gap will either get solved or not, but regardless, the end of this narrative arc is likely in the air right now.

Previously:

Update (2025-11-04): Helge Heß:

If you actually understand concurrency, you don’t need all the boilerplate 😬 I think, like with static typing, the key idea is to help people who don’t understand it. And like the former it mostly fails those promises.

It’s like driving with training wheels.

Also that the static checks guarantee valid concurrent code is a huge misconception. Like static typing it just removes a small, mostly irrelevant, class of errors.

Sebastián Benítez:

The walled garden, as it is called, has a door through which the users can easily exit if they find they don’t want it anymore. But for a Mac exclusive developer, it’s a prison. There is no way out other than escaping.

[…]

Yes, [Swift is] great for some things, such as modelling data and has some nice FP in it. But then you have to deal with async functions (colouring) invading all your code, and all the troubles they bring with so much concurrency. That has to be one of the reasons Apple now provides an option to make everything @MainActor.

It’s becoming bloated and complex. At the same time, the standard library is lacking, compared to C++ at least.

Macro implementation is horrible, period. I’m a lisper, so I have a high bar. There is no way Swift macros are as useable as Common Lisp’s macros. I gave up just looking at the documentation on how to implement one.

One last thing about it: so much evolution, so many changes, lack of directed design, makes all code constantly obsolete, bit-rotting. You are pushed to adopt new language features, newer frameworks, since they are updated with the new features, so your hand is sometimes forced. Then there comes all the refactoring or you are left behind.

Previously:

6 Comments RSS · Twitter · Mastodon


I laughed out loud reading these comments. Then I stopped myself b/c I felt bad because Apple is basically abusing developers with this shit.

The C++ enthusiasts have way too much power.
Ya'll need to stop following Apple off every cliff. Apple introduced Swift, port all your ObjC over...well why? What did that do for YOU? Anything? I promise if you would've stuck with ObjC over the last 10 years you'd be in better shape. Maybe you needed Swift just for your widget, fidget, midget feature. But if ya'll would've resisted more instead of bootlicking maybe Apple would still be writing new APIs in the programming language that's actually good?

> I achieved a major development milestone for my biggest app, MarsEdit, today. I can now build against Swift 6 with strict concurrency, and no warnings. It was harder than it should be (though Apple’s working on that), but it feels good knowing I can move forward with concurrent confidence.

Congrats on your milestone. But judging by all the other comments I have my doubts that this is the end the story as it relates to Swift Concurrency. I think it's going to get killed like ObjC GC. It would be great if the entire language went with it...though that probably won't happen...we can dream :)

> All I want from Swift is a fast compiler that gives actually useful error messages. Enough syntax sugar. Oh and also, Swift Concurrency has been a disaster.

The Swift compiler will never be fast because they want the compiler to involved in everything. Just to broadcast a notification they want you to template an object for the compiler to type check because reading shit out of a notification user info dictionary is apparently a widespread problem on the platform. What the fuck is wrong with these people? Compiler warnings/errors are great but they obviously bit off more than they can chew when your build fails and it gives you an error message that nobody can understand...

Just don't fix anything and keep increasing the surface area for new bugs by introducing redesigns and dinky language features nobody needs. WTF CAEF? They need to get their fucking priorities straight.

The old cliche - perfect is the enemy of good. Pretty sure we all would be happier if they went back to ObjC and added more features like lightweight generics/ nullability which may have been the last two meaningful features they added to ObjC. Shit like that can get you most of what you need without destroying the entire world. High level ObjC is safe enough for apps IMO. They could've worked on APIs for doing 'safe' performance critical low-level stuff without having to drop down to raw unsafe C...which you can still do in Swift anyway.


"Intuitive async functions"

So they basically reinvented

dispatch_async(dispatch_get_main_queue(), ^{
	// Magic that happens just after the layout pass
});

Just with a lot more cruft and ugliness.


It amazes me how Apple still fails to learn from the lessons they themselves inflicted on Microsoft.

Microsoft lost the developer battle a long time ago, and if they didn’t already have decades of irreplaceable business software no one was ever gonna rewrite, they would have gone out of business.

When was the last time anyone wrote something new for Windows? Long, long time.

Apple doesn’t seem to want anyone to write proper native Mac apps anymore. They don’t even seem to be able to define for themselves what that actually means.


Where's the success story?

I'm reading some people discovered supposed bugs (which is good), but I don't see why we couldn't just have more static analyzers alert about these kinds of issues.

No matter how much enforcement you throw into the compiler, you're still beholden to your platform vendor not sucking. Look at the Swift+SwiftUI guys who shipped apps that checked every box, compiled, were smug ("whole classes of bugs could not possibly exist in my *safe code*, my *modern UI*"), and passed App review, only to get runtime crashes or wildly different deployment behaviors when Apple screwed up.

Being safe from platform vendor bugs is what I need more than any yearly academic exercises from Swift.

--

Also, I don't trust anyone who says they're good at this language. Everyone's just adding keywords they don't understand until it builds.

> Is Swift 6 strict concurrency going to be our Python 3 moment?

No. Python Foundation financials aren't great, but Apple can fund the horror of Swift, indefinitely.

@ObjC4Life I'm very happy with Obj-C as it is. I'm still very productive with it (and so is the WebKit team). A defer feature might be nice, but I don't want the Leetcode hires or the Swift Masters even looking in its direction. 2 dudes in the 80s made something that mogs an entire community of C++ lovers and its spiritual disciples.


> static analyzers alert about these kinds of issues.
+1

> @ObjC4Life I'm very happy with Obj-C as it is. I'm still very productive with it (and so is the WebKit team). A defer feature might be nice, but I don't want the Leetcode hires or the Swift Masters even looking in its direction.

You're probably right. They might try to Swiftly it and ruin it. So maybe we're better off that they don't touch. A couple little features would be nice but ObjC is the cream of the crop for app development and a complete language already.

---
Just to add, isn't Swift Concurrency "The Feature"... if it fails (and it looks like it is failing) the entire language was basically a waste of time. Without Swift Concurrency what does Swift really offer that makes this effort worthwhile? Otherwise ObjC gives us pretty much everything Swift can without all the downsides (which are many including slow compile times, compiler crashes, nonsensical warnings/errors and many more...).

ObjC already gives us:
-Reasonable memory safety with ARC.
-Reasonable type checking (unless you use id everywhere but who does that?)... even in collections with generics I always NSArray *array; I don't think I ever just write NSArray* without putting the type anymore. You get the warnings when you need them. They aren't cryptic. Yea... no type inference Swift ppl hate having to write the type but enjoy that slow compiler. I like writing my types and reading LTR.
-Drop down to low level C when required for performance (rare).

So Swift Concurrency is the Holy Grail or am I missing something? Guaranteed safety at compile time... all the other features are things that can be classified as:
-Bad features that make the compiler slow.
-Ok/decent features that could easily be bolted on ObjC without all the disruption Swift has caused
-Superficial "features" like "no square brackets!" for dumb people.

And if Apple is so fucking concerned about safety and security they could start paying security bounties. If your name is on a CVE they should at min. send you $100. Entitled assholes think only their time is valuable.


@ObjC4Life I've kind of accepted it'll never change and our comments are dreams.

Swift is built by-and-for corporate teams. It's not for people with good taste who hate complexity -- it's for Corps and people who treat "process" as a final solution.

Swift takes a corporate manager's love of process and codifies it -- literally. When you're managing mostly Leet Code hires, you don't have time to reject 50 code reviews a day while these guys Learn 2 Code. So you punt as much process into the compiler as possible. You want to ensure whole classes of bugs cannot possibly exist if the project builds and a review ping hits your Slack.

That is their mission. It's sold as, "well, it's good for all devs!"

It doesn't matter that the language cannot be held in one person's head, or that the builds are slow, or any of the other issues we have. Almost everything is sacrificed to achieve some baseline of "technically sound" if you can figure out the cluster f language and keep your project building (churn be damned). This is *the* most important thing to them.

Obj-C was built for small teams and individual empowerment. Swift is built for BigCorp empowerment.

Everything we know and mentally invested into doesn't matter to them. The Swift machine is going to paper over it even if it works decently. I doubt Apple gets many interviewees these days who even know what AppKit is. They don't care. As long as you did Leet Code that proves you can follow a process, and corporate dev is more about following process than doing good.

Swift is the 1984 of languages.

Leave a Comment