June 10, 2026
There was a great Swift Group Lab session recently. And this follows a similar concurrency-specific session which was also wonderful. I think the format is a little challenging for the panelists, but they manage to do a great job regardless. It's so valuable to have real questions answered by the people that built the stuff. And I happen to really enjoy the live, unscripted discussion.
(I was also just delighted to hear the entire panel discussing the utility of non-Sendable types!)
However!
There's a voting system. And that means a number of questions weren't selected. This time I copied those down and I thought it could be fun to take a shot at answering the ones that are more in my area.
Why is the default for a new project Swift 5 in Xcode 27? Should we immediately be changing this to Swift 6 for new projects, or stick with the defaults?
My guess is because there are parts of Apple's SDK that are still quite difficult to use in Swift 6 mode. It absolutely can be done, but some areas will demand significant understanding.
Here's the core question - do you understand what the Swift 6 language mode actually does? I have seen quite a few people turn it on and then get very surprised when crashes start happening because of dynamic actor isolation enforcement.
New projects can be a great time to start digging into this area. But it really depends on your goals. I think it is quite risky to make use of concurrency features with the compiler feedback totally disabled, so the defaults aren't great. But, you really do need to understand your project settings to make sure you are making reasonable choices. They can be extremely consequential.
Can you help me better understand @preconcurrency ? I had to add this before import MusicKit in my app in order for it to build in Swift 6. Does this mean that particular framework is not using the latest concurrency features? Does using this annotation make my app "unsafe"?
In fact, I've written a whole thing on it!
A preconcurency import does not necessarily mean unsafe. What it means is more like "This library is missing annotations, but I'm using it correctly". If that is actually the case, nothing is unsafe. Or at least, not any more unsafe than it was before. However, it does open the door for more accidental mistakes because it is suppressing compiler checking for the entire file. I much prefer targeted opt-outs like nonisolated(unsafe) when possible. But all of these facilities do reduce the amount of compile-time checking that can happen.
(I'm also not familiar with MusicKit specifically, but casually checking, it does look like it has at least some concurrency support today. Perhaps that @preconcurrency import is no longer necessary as of the 27 SDK?)
What's your recommendation on the default actor isolation for Swift apps? Should we try to always use MainActor isolation or would you recommend to use nonisolated for fresh code bases?
Apple's defacto recommendation for new app targets is default MainActor, because that's what the Xcode template does.
In my experience, this mode can be very challenging to use, even for small projects. I'm not saying you should not try it. But it typically demands that you understand the concurrency system more deeply than with a default of nonisolated. I would be careful here, particularly if you do not feel very comfortable with concurrency.
For a func that is not marked as @MainActor, non-isolated or @concurrent explicitly, does the thread get assigned at calling site OR is it by default bg thread?
My interpretation of the question is that it is less about the effects of concurrency annotations themselves, and more about the relationship between static isolation and threads. Because you have to remember that all functions have a well-defined static isolation. As worded, this question rules out like 90% of all functions.
The only functions that are guaranteed to not end up on a background thread are those marked @MainActor.
The only situation where the callsite could matter is for nonisolated functions. And the NonisolatedNonsendingByDefault setting (AKA "Approachable Concurrency") will play an important role here and will influence a more complete answer.
Especially as we move towards a world where NonisolatedNonsendingByDefault is just the way, you should be thinking about nonisolated functions as running in the calling context. All other functions do not.
with migration to swift 6 on a large project and moving to approachable concurrency and @MainActor default isolation we end up having data models or enums that belong to the core data layer of the app be @MainActor isolated which is confusion based on separation of concepts how to handle it ?
Very long story short: you probably need to make them explicitly nonisolated or move them into a nonisolated-default module. Many types do not make sense to be MainActor, and some are actually incompatible with it.
As above, a default of MainActor means you are signing up for the responsibility to recognize the need for nonisolated as well as knowing how and where to apply it appropriately.
When your iPhone/iPad app needs to do BOTH a synchronous network API call AND local on-device operations like image processing together, how do you wrap these blocking C functions with Swift's async? Do you use withTaskGroup to run them in parallel, or a dedicated executor?
I would not reach for a custom executor without a very good reason. And without knowing more, I'm not sure this is it.
I think you could make use of a task group here. There are lots of ways to manage synchronous work. However, I'd really urge caution for synchronous networking, because that could potentially be something that never completes. And work that never finishes violates Swift's forward progress requirement.
With default MainActor isolation enabled, we find ourselves adding nonisolated in many places, especially when a type conforms to a nonisolated Sendable protocol. Has there been any consideration of making types that conform to explicitly nonisolated protocols automatically also become nonisolated?
This is an interesting question. I think the issue here is less the nonisolated part, but the fact that the protocol requires a Sendable conformance. Such a protocol can be incompatible with a MainActor type and actually can suppress an implicit MainActor. But, that depends on the protocol conformance being in the primary declaration of the type or in an extension. I wonder if that was a factor here?
(While it was not the only topic, I did write a bit about this.)
Following on from the UserDefaults Sendable question what's the difference between declaring a subclass-able class as ~Sendable and not declaring any Sendable requirement?
The original question, if I recall, was basically "UserDefaults says it is thread-safe, why isn't it also Sendable?"
And the panel's answer, again if I recall right, is that unfortunately UserDefaults also allows for subclasses. It is not possible for a superclass to guarantee that all subclasses are also Sendable, which is why a checked-Sendable class must be final.
This is part of the reason why ~Sendable was introduced. This mechanism allows you to say "I'm not Sendable, but a subclass is allowed to be!".
When a Sendable conformance is just not there, as far as the compiler is concerned, it is completely unambiguous. The type is not Sendable. But in reality, documentation might say otherwise, as is the case with UserDefaults. You can make use of this information in your own code, but it requires an unsafe opt-out, like nonisolated(unsafe).
Sendable classes and subclassing is extremely tricky.
What is the recommended current and future approach for debugging Swift Concurrency tasks and actors on non-Apple platforms such as Linux and Windows?
I don't know the answer to this one, but it's a great question! If I were faced with a similar problem, I would immediately find people in the Swift server community. They are an extremely competent group and I think they would be happy to help.
Is there any general recommendation about when to use approachable concurrency and when to use strict one? For general iOS apps
You have to keep in mind that "Approachable Concurrency" is a group of upcoming compiler features. That means that eventually they will all be turned on by default in some future language mode.
Strict checking is also a setting, but is independent of "Approachable Concurrency".
But, nonetheless, here's my own recommendation. The settings that are in "Approachable Concurrency" are good and you should use them. This is on by default for new Xcode app projects, and has been since last year.
I think it is very risky to actively use Swift's concurrency features with the compiler feedback disabled. You can do it, and it does not guarantee problems especially when paired with NonisolatedNonsendingByDefault. But I would not recommend it.
This was a fun exercise. I didn't spend too long on it, so some details were short. But I hope it was interesting. If any of these questions were yours, I'd love to know if the answers were helpful.
And if you'd like to get your own questions answered, come join me at Swift Rockies. I'm going to do a full day lab there of stuff just like this.
Did you know that I do consulting for concurrency and Swift 6 migrations? If you think I could help, get in touch.