
Swift 6: Thay đổi lớn nhất là concurrency
Swift 6, phát hành năm 2024, mang theo strict concurrency checking — compiler bắt buộc kiểm tra data race trước khi compile thành công. Đây là bước tiến lớn nhất từ Swift 5.x, giúp phát hiện race condition sớm thay vì debug thủ công trên thiết bị thật.
Trước Swift 6, Sendable chỉ là warning. Swift 6 biến nó thành error: nếu type không tuân thủ concurrency rules, code sẽ không compile. Thay đổi này ảnh hưởng toàn bộ codebase — đặc biệt là các app dùng async/await, Task, actor.

Data race là gì?
Data race xảy ra khi 2 thread cùng truy cập cùng memory, ít nhất 1 write, không có synchronization — compiler không thể biết thứ tự thực thi nào đúng.
Ví dụ kinh điển:
var balance = 0
Task {
for _ in 0..<1000 { balance += 1 }
}
Task {
for _ in 0..<1000 { balance -= 1 }
}
// balance có thể là -989, 7, 1023 — không predict được
Trên Swift 5, code này compile thành công, chạy lung tung, rất khó debug. Trên Swift 6, compiler báo lỗi ngay: Capture of non-sendable type 'Int' in a @Sendable closure.

Swift 6 concurrency rules
3 rule cốt lõi compiler kiểm tra:
- Sendable conformance: Type có thể truyền qua isolation boundary (task, actor, main actor) phải tuân thủ
Sendableprotocol — value semantics, internal state immutable hoặc protected. - Sendable closures: Closure passed vào
Task,TaskGroup,withTaskGroupphải có@Sendable— capture only Sendable values, no mutable reference. - Isolation domains: Mỗi actor (bao gồm
MainActor) là isolation domain riêng. Không được truy cập state của actor khác từ bên ngoài mà không cóawait.
Sendable types: value vs reference
Value types (struct, enum) tự động Sendable nếu tất cả properties cũng Sendable:
struct User: Sendable { // OK
let id: Int
let name: String
}
Reference types (class) cần đánh dấu final + properties immutable để auto-conformance:
final class Config: Sendable { // OK
let apiKey: String
let timeout: TimeInterval
}
Nếu class có mutable state, dùng actor thay vì class:
actor BankAccount {
private var balance: Int = 0 // mutable, nhưng protected bởi actor isolation
func deposit(_ amount: Int) { balance += amount }
func withdraw(_ amount: Int) -> Bool { ... }
}
Compiler tự động đảm bảo mọi gọi đến BankAccount phải qua await — không ai đọc/ghi balance đồng thời từ bên ngoài.
Global actor: MainActor thay vì DispatchQueue.main
Swift 6 khuyến khích dùng MainActor thay vì DispatchQueue.main.async:
// Swift 5 cũ
DispatchQueue.main.async {
self.label.text = "Updated"
}
// Swift 6
@MainActor
class ProfileViewController: UIViewController {
func updateUI() {
label.text = "Updated" // tự động chạy trên main thread
}
}
@MainActor đảm bảo toàn bộ type chạy trên main thread — không cần gọi DispatchQueue.main thủ công. Swift compiler tự động inject hop sang main thread khi cần.
Nếu chỉ cần 1 function chạy main, dùng @MainActor annotation riêng:
@MainActor
func updateLabel() {
label.text = "Done"
}
TaskGroup: parallel execution an toàn
async let và TaskGroup cho phép parallel task — Swift 6 đảm bảo mỗi task có riêng data, không share mutable state:
func fetchAll() async throws -> [User] {
try await withThrowingTaskGroup(of: User.self) { group in
group.addTask { try await fetchUser(id: 1) }
group.addTask { try await fetchUser(id: 2) }
group.addTask { try await fetchUser(id: 3) }
var results: [User] = []
for try await user in group {
results.append(user) // thread-safe, group quản lý synchronization
}
return results
}
}
Swift 6 còn giới thiệu sending parameter — value được “chuyển ownership” sang task, không còn copy:
func process(_ data: sending Data) async throws {
// data ownership chuyển sang task này — không share với caller
}
Migrate từ Swift 5 sang Swift 6
Bước 1: Bật SWIFT_STRICT_CONCURRENCY = complete trong Xcode build settings. Xcode sẽ highlight toàn bộ Sendable violations.
Bước 2: Fix từng module:
- Struct/enum: thêm
: Sendablenếu properties đều Sendable. - Class mutable: convert sang
actorhoặc@MainActor. - Closure capture: replace mutable captured variable bằng
actorhoặcsendingparameter.
Bước 3: Chạy swift migrate tool — tool tự apply nhiều fix phổ biến (thêm Sendable, chuyển class sang actor).
Bước 4: Test kỹ — đặc biệt là unit test chạy parallel, memory test bằng Thread Sanitizer.
Khi nào cần quan tâm
Swift 6 concurrency không chỉ cho app mới — nó còn quan trọng khi bạn:
- Viết library/framework — consumer sẽ compile với strict checking, nên bạn phải expose Sendable types.
- Maintain legacy codebase — migrate từ Swift 5 dần, tránh tech debt.
- Build real-time app — audio processing, video streaming, game loop — data race là kẻ thù số 1.
