Swift Concurrency review and remediation for Swift 6.2+. Use when asked to review Swift Concurrency usage, improve concurrency compliance, or fix Swift concurrency compiler errors.
From ios-swift-skillsnpx claudepluginhub patrickserrano/skillsThis skill uses the workspace's default tool permissions.
Review and fix Swift Concurrency issues in Swift 6.2+ codebases by applying actor isolation, Sendable safety, and modern concurrency patterns with minimal behavior changes.
@MainActor, actor, nonisolated)Prefer edits that preserve existing behavior while satisfying data-race safety.
Common fixes:
| Issue | Fix |
|---|---|
| UI-bound types | Annotate the type or members with @MainActor |
| Protocol conformance on main actor types | Make conformance isolated: extension Foo: @MainActor SomeProtocol |
| Global/static state | Protect with @MainActor or move into an actor |
| Background work | Use @concurrent async function on a nonisolated type |
| Sendable errors | Prefer immutable/value types; add Sendable only when correct |
Swift 6.2 stays single-threaded by default until you choose to introduce concurrency:
// In Swift 6.2 with approachable concurrency enabled,
// this no longer produces a data race error
@MainActor
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else {
return nil
}
// Safe - runs on main actor by default
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
Conformances that need main actor state are now supported:
protocol Exportable {
func export()
}
// Isolated conformance - safe because compiler ensures
// it's only used on the main actor
extension StickerModel: @MainActor Exportable {
func export() {
photoProcessor.exportAsPNG()
}
}
// Protect with @MainActor
@MainActor
final class StickerLibrary {
static let shared: StickerLibrary = .init()
}
// Or enable main-actor-by-default mode for the whole project
Use @concurrent to explicitly run code on the concurrent thread pool:
nonisolated struct PhotoProcessor {
@concurrent
func process(data: Data) async -> ProcessedPhoto? {
// Runs on background thread
}
}
// Caller adds await
processedPhotos[item.id] = await PhotoProcessor().process(data: data)
swift.org/migration@MainActor
final class ViewModel {
var items: [Item] = []
func load() async {
items = try await service.fetchItems()
}
}
nonisolated struct ImageProcessor {
@concurrent
static func resize(_ image: UIImage, to size: CGSize) async -> UIImage {
// Heavy work runs off main actor
}
}
actor Cache {
private var storage: [String: Data] = [:]
func get(_ key: String) -> Data? {
storage[key]
}
func set(_ key: String, value: Data) {
storage[key] = value
}
}
Enable in Xcode under Swift Compiler - Concurrency:
SWIFT_STRICT_CONCURRENCY = completeOr in Package.swift:
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]