ios-swift6/SKILL.md
Prepare for Swift 6 with strict concurrency, Sendable conformance, data race safety, and migration strategies. Use when migrating to Swift 6, fixing concurrency warnings, making types Sendable, or ensuring data race safety. Triggers on Swift 6, strict concurrency, Sendable, data race, concurrency warning, migration, @preconcurrency, sending, isolation.
npx skillsauth add abanoub-ashraf/manus-skills-import ios-swift6Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in Swift 6 migration. When this skill activates, help prepare code for Swift 6 strict concurrency.
| Swift 5 | Swift 6 | |---------|---------| | Concurrency warnings | Concurrency errors | | Optional Sendable | Required Sendable | | Implicit main actor | Explicit isolation | | Data races possible | Data races prevented |
// 1. Enable strict checking in Swift 5
// Build Settings → Swift Compiler → Upcoming Features
// SWIFT_UPCOMING_FEATURE_FLAGS = StrictConcurrency
// 2. Or in Package.swift
.target(
name: "MyTarget",
swiftSettings: [
.enableUpcomingFeature("StrictConcurrency")
]
)
// 3. Consider Swift 6.2 behavior toggles (default isolation, nonisolated rules)
// 4. Fix warnings incrementally
// 5. Enable Swift 6 mode when ready
In Swift 6.1, nonisolated async functions always hop to the global executor. In Swift 6.2, opt-in settings let nonisolated async functions inherit the caller's isolation (so they can stay on the main actor by default) unless you explicitly force concurrency.
// With NonisolatedNonsendingByDefault enabled:
nonisolated func decode(_ data: Data) async -> Image {
// Runs on caller's isolation
}
@concurrent
nonisolated func decodeOffMain(_ data: Data) async -> Image {
// Always runs on global executor
}
// Structs with Sendable properties are implicitly Sendable
struct User: Sendable {
let id: String
let name: String
let createdAt: Date // Date is Sendable
}
// Arrays/Dictionaries of Sendable are Sendable
struct UserList: Sendable {
let users: [User] // OK - [Sendable] is Sendable
}
// Option 1: Make immutable and final
final class Configuration: Sendable {
let apiKey: String // ✅ Immutable
let environment: String // ✅ Immutable
init(apiKey: String, environment: String) {
self.apiKey = apiKey
self.environment = environment
}
}
// Option 2: Use actor
actor UserStore {
private var users: [String: User] = [:]
func add(_ user: User) {
users[user.id] = user
}
func get(_ id: String) -> User? {
users[id]
}
}
// Option 3: @unchecked Sendable (manual thread safety)
final class ThreadSafeCache: @unchecked Sendable {
private let lock = NSLock()
private var storage: [String: Any] = [:]
func set(_ value: Any, forKey key: String) {
lock.lock()
defer { lock.unlock() }
storage[key] = value
}
}
// Sendable closures
func runInBackground(_ work: @Sendable () async -> Void) {
Task.detached {
await work()
}
}
// Capturing Sendable values
func process() {
let id = "123" // String is Sendable
Task {
await fetchUser(id: id) // ✅ OK
}
}
// ❌ Problem: Capturing non-Sendable
class ViewController {
var data: [String] = [] // Mutable, not Sendable
func loadData() {
Task {
self.data = await fetchData() // ⚠️ Warning
}
}
}
// ✅ Fix: Use @MainActor
@MainActor
class ViewController {
var data: [String] = []
func loadData() {
Task {
self.data = await fetchData() // ✅ OK - same actor
}
}
}
// Entire class on MainActor
@MainActor
class ViewModel: ObservableObject {
@Published var items: [Item] = []
func loadItems() async {
items = await api.fetchItems()
}
}
// Single property
class Service {
@MainActor var uiState: UIState = .idle
}
// Single method
class DataManager {
@MainActor
func updateUI() {
// Guaranteed on main thread
}
}
// Hop to MainActor
func fetchAndUpdate() async {
let data = await fetchData()
await MainActor.run {
updateLabel(data)
}
}
For app targets, setting a default main-actor isolation can reduce migration noise:
// Package.swift
swiftSettings: [
.defaultIsolation(MainActor.self)
]
For libraries or mixed-concurrency modules, consider nil to keep a nonisolated default.
actor DatabaseManager {
private var connection: Connection?
func query(_ sql: String) async throws -> [Row] {
// Isolated - thread-safe access
}
// nonisolated for sync access to immutable data
nonisolated let databasePath: String
// nonisolated async for work that doesn't need isolation
nonisolated func validateSQL(_ sql: String) async throws {
// Can run on any thread
}
}
actor MyActor {
func doWork() {
// Closures inherit isolation by default in Swift 6
Task {
// Still on MyActor
await self.otherMethod()
}
// To escape isolation:
Task.detached {
// Not on MyActor
}
}
}
// ❌ Before: Concurrency issues
class ViewModel: ObservableObject {
@Published var items: [Item] = []
func load() {
Task {
items = await api.fetch() // ⚠️ Not on main actor
}
}
}
// ✅ After: MainActor isolated
@MainActor
class ViewModel: ObservableObject {
@Published var items: [Item] = []
func load() async {
items = await api.fetch() // ✅ On main actor
}
}
// ❌ Before: Protocol not Sendable
protocol DataDelegate: AnyObject {
func didReceive(_ data: Data)
}
// ✅ After: MainActor delegate
@MainActor
protocol DataDelegate: AnyObject {
func didReceive(_ data: Data)
}
// Or Sendable delegate
protocol DataDelegate: AnyObject, Sendable {
func didReceive(_ data: Data)
}
// ❌ Before: Not thread-safe
class Analytics {
static let shared = Analytics()
private var events: [Event] = []
func track(_ event: Event) {
events.append(event) // ⚠️ Data race
}
}
// ✅ After: Actor
actor Analytics {
static let shared = Analytics()
private var events: [Event] = []
func track(_ event: Event) {
events.append(event) // ✅ Thread-safe
}
}
// ❌ Before: Closure escapes to unknown thread
NotificationCenter.default.addObserver(
forName: .userLoggedIn,
object: nil,
queue: nil
) { notification in
self.updateUI() // ⚠️ Which thread?
}
// ✅ After: Explicit main queue
NotificationCenter.default.addObserver(
forName: .userLoggedIn,
object: nil,
queue: .main
) { notification in
self.updateUI() // ✅ On main thread
}
// Or use async sequence
Task { @MainActor in
for await _ in NotificationCenter.default.notifications(named: .userLoggedIn) {
updateUI()
}
}
// Import non-Sendable types from old frameworks
@preconcurrency import OldFramework
// Use non-Sendable type temporarily
func useLegacy() {
let legacy = OldFramework.LegacyClass() // Suppresses warning
}
// Mark your own types for gradual adoption
@preconcurrency
public protocol MyProtocol {
func process() // Caller can be non-Sendable for now
}
// Swift 6: Transfer ownership to callee
func process(sending data: [String]) async {
// data is now owned by this function
// Caller cannot use it after call
}
// Usage
var items = ["a", "b", "c"]
await process(sending: items)
// items can no longer be used here
// Package.swift
swiftSettings: [
.enableUpcomingFeature("StrictConcurrency")
]
@MainActor to ViewModels@MainActor to UI-related delegatesSendable[weak self]nonisolated where appropriate// Package.swift
swiftLanguageVersions: [.v6]
// Or in Xcode
// Build Settings → Swift Language Version → 6
// Package.swift swiftSettings
.enableUpcomingFeature("StrictConcurrency"), // Full strict mode
.enableUpcomingFeature("NonisolatedNonsendingByDefault"),
.defaultIsolation(MainActor.self),
.enableUpcomingFeature("GlobalConcurrency"), // Global actor inference
.enableUpcomingFeature("InferSendableFromCaptures"),
// Or selective warnings
.enableExperimentalFeature("StrictConcurrency=targeted"), // Only new code
.enableExperimentalFeature("StrictConcurrency=complete"), // Everything
// Prefer actors for shared mutable state
actor DataStore { }
// Use @MainActor for UI classes
@MainActor class ViewModel { }
// Make value types Sendable
struct Config: Sendable { }
// Use structured concurrency
async let result = fetchData()
// Don't use @unchecked Sendable without thread safety
final class Unsafe: @unchecked Sendable {
var data: [String] = [] // ❌ Not actually safe!
}
// Don't ignore warnings
// They will become errors in Swift 6
// Don't fight the system
// If something is hard to make Sendable, redesign it
development
Design principles for building polished, native-feeling SwiftUI apps and widgets. Use this skill when creating or modifying SwiftUI views, iOS widgets (WidgetKit), or any native Apple UI. Ensures proper spacing, typography, colors, and widget implementations that look and feel like quality apps rather than AI-generated slop.
data-ai
Design and implement SwiftUI views, components, and app architecture. Use when creating new SwiftUI views, implementing MVVM/TCA patterns, managing state with @Observable, @State, @Binding, or @Environment, designing navigation flows, or structuring iOS app architecture. Triggers on SwiftUI, view model, state management, navigation, coordinator pattern.
development
Implement, review, or improve SwiftUI animations and transitions. Use when adding implicit or explicit animations with withAnimation, configuring spring animations (.smooth, .snappy, .bouncy), building phase or keyframe animations with PhaseAnimator/KeyframeAnimator, creating hero transitions with matchedGeometryEffect or matchedTransitionSource, adding SF Symbol effects (bounce, pulse, variableColor, breathe, rotate, wiggle), implementing custom Transition or CustomAnimation types, or ensuring animations respect accessibilityReduceMotion.
testing
Audit SwiftUI views for accessibility (iOS + macOS) with patch-ready fixes