๐ Swift 6 Strict Concurrency Migration Guide
Fixing Sendable, Actor Isolation & @MainActor Errors in Production iOS Apps

Swift 6 Strict Concurrency Migration Guide
๐ Swift 6 Strict Concurrency Migration Guide
Fixing Sendable, Actor Isolation & @MainActor Errors in Production iOS Apps
You upgrade your project to Swift 6.
Everything compilesโฆ for a moment.
Then suddenly your build explodes with errors like:
โ โType does not conform to Sendableโ โ โMain actor-isolated property cannot be referencedโ โ โCapture of non-sendable type in @Sendable closureโ โ โActor-isolated instance method cannot be usedโ
Sound familiar? ๐
If youโve recently migrated your iOS project and your compiler suddenly became very angry, youโre not alone.
Swift 6 introduces Strict Concurrency Checking, which means the compiler is now much stricter about thread safety.
And yesโฆ your Swift 5 code might break.
But hereโs the good news:
These errors are actually protecting your app from data races, unpredictable crashes, and concurrency bugs. ๐
In this guide, weโll walk through:
๐ What Strict Concurrency in Swift 6 actually means ๐ Why your Swift 5 code now fails ๐ How to fix Sendable errors ๐ How to resolve Actor isolation issues ๐ When to use @MainActor, nonisolated, and @unchecked Sendable ๐ A production-safe migration strategy
This is not theory.
This is real-world migration guidance for production iOS apps. ๐จโ๐ป
๐ What Is Swift 6 Strict Concurrency?
When Apple introduced async/await and actors in Swift 5.5, concurrency became much easier.
But many safety rules were only warnings.
Developers could ignore them.
In Swift 6, those warnings are now fully enforced rules.
The compiler now guarantees:
๐งต No data races ๐ก Proper actor isolation ๐ Safe cross-thread communication ๐ฆ Strict Sendable enforcement
Which means something important:
Code that compiled fine before may now fail at build time.
And honestly?
Thatโs a good thing.
Because concurrency bugs are some of the hardest bugs to debug in production.
Swift 6 stops them before your app even runs. ๐
โ ๏ธ Problem 1: โType Does Not Conform to Sendableโ
One of the most common errors during Swift 6 migration.
Why It Happens
In Swift 6, any value that crosses concurrency boundaries must conform to Sendable.
Example:
class User {
var name: String
init(name: String) {
self.name = name
}
}
func fetchUser() async -> User {
return User(name: "Pramod")
}
You may now see this error:
Type 'User' does not conform to Sendable
Why? ๐ค
Because User is a class (reference type).
Reference types can be shared and mutated across threads, which can create race conditions.
Swift 6 wants to prevent that.
โ Fix Option 1: Make It a Struct (Best Option)
The safest solution is to convert it into a value type.
struct User: Sendable {
let name: String
}
Why this works:
โ Structs are copied instead of shared โ Immutable properties improve safety โ Swift can guarantee thread safety
In most cases, this is the cleanest and safest solution.
โ Fix Option 2: Conform Manually (If Thread Safe)
Sometimes you must keep a class.
In that case:
final class User: Sendable {
let name: String
}
But only do this if:
โ Properties are immutable (let)
โ No shared mutable state exists
โ You fully understand the thread safety implications
Otherwise, you may introduce subtle bugs.
โ ๏ธ Dangerous Option: @unchecked Sendable
You might see this in some legacy codebases:
final class User: @unchecked Sendable {
var name: String
}
This tells the compiler:
โDonโt worryโฆ I know what Iโm doing.โ ๐
The compiler stops checking thread safety.
Use this only when absolutely necessary, such as:
- Wrapping legacy frameworks
- Working with APIs you fully control
- Temporary migration fixes
Otherwise, avoid it.
โ ๏ธ Problem 2: Actor Isolation Errors
Swift 6 enforces actor isolation boundaries much more strictly.
Example:
actor UserManager {
var users: [User] = []
func add(user: User) {
users.append(user)
}
}
let manager = UserManager()
manager.add(user: User(name: "Pramod")) // โ Error
Swift will complain:
Actor-isolated instance method cannot be referenced
Why?
Actors protect their internal state from simultaneous access across threads.
You must interact with them asynchronously.
โ Correct Usage
await manager.add(user: User(name: "Pramod"))
Because when you call an actor method from outside the actor, it becomes implicitly async.
This ensures:
โ Serialized access โ No race conditions โ Safe shared state
Actors are one of the most powerful tools in modern Swift concurrency. ๐
โ ๏ธ Problem 3: MainActor Violations
UI updates must always occur on the main thread.
Swift 6 enforces this rule very strictly.
Example:
class ViewModel {
var title: String = ""
func load() async {
title = "Loaded"
}
}
Swift will complain:
Main actor-isolated property cannot be mutated
Because Swift cannot guarantee this code runs on the main thread.
โ Fix With @MainActor
The solution is to isolate UI logic to the Main Actor.
@MainActor
class ViewModel {
var title: String = ""
func load() async {
title = "Loaded"
}
}
Now Swift guarantees:
๐ฅ UI updates always run on the main thread.
No crashes.
No undefined behavior.
โ ๏ธ But Donโt Overuse @MainActor
This is a very common mistake.
Bad example:
@MainActor
class NetworkService {
func fetchData() async { }
}
This forces network requests to run on the main thread.
Which is a performance disaster. ๐ฌ
Rule of thumb:
๐จ UI logic โ @MainActor
๐ Networking โ Background threads
โ ๏ธ Problem 4: Capture of Non-Sendable Type in @Sendable Closure
Example:
class DataManager {
var cache: [String] = []
}
let manager = DataManager()
Task.detached {
manager.cache.append("Hello") // โ Error
}
Swift error:
Capture of non-sendable type in @Sendable closure
Why?
Task.detached requires the closure to be Sendable.
But DataManager is not thread-safe.
โ Fix Options
You have a few options:
โ Convert to an actor
โ Replace Task.detached with Task
โ Refactor architecture
The best modern solution:
actor DataManager {
var cache: [String] = []
}
Actors automatically prevent race conditions.
Which is exactly what Swift 6 wants.
๐ Production Migration Strategy (Step-by-Step)
Migrating a large iOS app to Swift 6 can feel overwhelming.
But following a structured approach makes it manageable.
Step 1: Enable Strict Concurrency in Warnings Mode
In Build Settings:
Set:
Strict Concurrency Checking โ Complete
But start with warnings first.
Fix issues gradually instead of breaking the entire build.
Step 2: Convert Mutable Shared Services to Actors
Best candidates include:
๐ฆ Cache managers ๐ค Session managers ๐ก Repositories ๐ Global state handlers
Actors protect shared data automatically.
Step 3: Replace Classes with Structs Where Possible
Value types reduce:
โ Retain cycles โ Thread safety issues โ Sendable errors
Structs are often the cleanest solution.
Step 4: Audit All Detached Tasks
Search your project for:
Task.detached
In many codebases, these are used incorrectly.
Prefer structured concurrency:
Task {
await work()
}
This keeps concurrency predictable.
Step 5: Use nonisolated When Needed
Example:
actor Logger {
nonisolated func logVersion() {
print("v1.0")
}
}
Use nonisolated only when the method does not access actor state.
Otherwise, you break actor safety guarantees.
๐ง Key Swift 6 Concurrency Keywords (SEO Boost)
This article covers:
๐ Swift 6 strict concurrency ๐ Swift 6 Sendable errors ๐ Actor isolation in Swift ๐ @MainActor best practices ๐ Swift async/await migration ๐ Fix non-sendable type errors ๐ Swift 6 concurrency migration guide
These topics are currently highly searched by iOS developers.
๐ฏ Final Thoughts
Swift 6 doesnโt make your code harder.
It makes your code correct.
Yes โ migration can feel painful at first.
But once your app compiles cleanly under strict concurrency:
โ You eliminate data races โ You gain compiler-level thread safety โ Your architecture becomes cleaner โ Your app becomes more predictable
In modern iOS development, concurrency is no longer optional.
Itโs foundational.
And mastering it will make you a much stronger Swift developer. ๐
Migrating to Swift 6 too?
๐ Clap if this helped ๐ฌ Share your toughest concurrency error below ๐ Follow Apple Community for more advanced Swift guides.
๐ง Shared via Apple Community โ๏ธ By Pramod Kumar โ iOS Developer | SwiftUI Advocate ๐ LinkedIn โข Portfolio โข GitHub
๋ฉํ๋ฐ์ดํฐ
- post_id
- 7d6922a1227d
- slug
- swift-6-strict-concurrency-migration-guide-7d6922a1227d
- url
- https://medium.com/applecommunity/swift-6-strict-concurrency-migration-guide-7d6922a1227d
- canonical_url
- https://medium.com/applecommunity/swift-6-strict-concurrency-migration-guide-7d6922a1227d
- author_url
- https://medium.com/@pramod-kumar-ios
- status
- ok
- fetched_at
- 2026-08-02 13:24:08