All skills
avdlee avatar

/swift-concurrency

@35095d4

Diagnose Swift Concurrency issues, refactor callback-based code to async/await, and guide Swift 6 migration when working with tasks, actors, @MainActor, Sendable, data races, thread safety, or concurrency-related compiler and linter warnings.

Use this Skill: https://skilld.dev/gh/avdlee/swift-concurrency-agent-skill/swift-concurrency

This session only. Nothing lands on disk.

referenceslinting.md

≈1.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Linting & Concurrency

Use this when:

  • SwiftLint flags async_without_await or other concurrency-related warnings.
  • You need to decide whether to suppress, fix, or reconfigure a concurrency lint rule.

Skip this file if:

  • The issue is a compiler diagnostic, not a lint rule. Use actors.md, sendable.md, or threading.md.

Jump to:

  • SwiftLint Concurrency Rules Overview
  • async_without_await Rule
  • Suppression Strategies

SwiftLint Concurrency Rules Overview

SwiftLint provides several rules targeting async/await and concurrency patterns. Understanding when to fix vs. suppress is critical.

Rule Default Purpose
async_without_await warning Flags async functions that never await
unowned_variable_capture warning Warns about unowned in closures (risky in async)
class_delegate_protocol warning Ensures delegates are class-bound (AnyObject)
weak_delegate warning Delegates should be weak to avoid retain cycles

SwiftLint: async_without_await

  • Intent: A declaration should not be async if it never awaits.
  • Never "fix" by inserting fake suspension (e.g. await Task.yield(), await Task { ... }.value). Those mask the real issue and add meaningless suspension points.
  • Legit use of Task.yield(): OK in tests or scheduling control when you truly need a yield; not as a lint workaround.

Diagnose why the declaration is async

  1. Protocol requirement — the protocol method/property is async.
  2. Override requirement — base class API is async.
  3. @concurrent requirement — stays async even without await.
  4. Accidental/legacy async — no caller needs async semantics.

Preferred fixes (order)

  1. Remove async (and adjust call sites) when no async semantics are needed.
  2. If async is required (protocol/override/@concurrent):
    • Re-evaluate the upstream API if you own it (can it be non-async?).
    • If you cannot change it, keep async and narrowly suppress the rule where appropriate (common for mocks/stubs/overrides).

Suppression examples (keep scope tight)

// swiftlint:disable:next async_without_await
func fetch() async { perform() }

// For a block:
// swiftlint:disable async_without_await
func makeMock() async { perform() }
// swiftlint:enable async_without_await

Quick checklist

  • Confirm if async is truly required (protocol/override/@concurrent).
  • If not required, remove async and update callers.
  • If required, prefer localized suppression over dummy awaits.
  • Avoid adding new suspension points without intent.

Compiler Warnings: Sendable & Isolation

The Swift compiler generates concurrency-related warnings based on strict concurrency checking level.

Common Warning Patterns

"Capture of non-sendable type"

// Warning: Capture of 'self' with non-sendable type 'MyClass' in a `@Sendable` closure
Task {
    self.doWork() // 'self' is non-Sendable
}

Fixes (in order of preference):

  1. Make the type Sendable if it's truly thread-safe
  2. Use @MainActor isolation if it's UI-related
  3. Capture only Sendable values instead of self
  4. Use @unchecked Sendable with documented safety invariant (last resort)

"Non-sendable result returned"

// Warning: Non-sendable type 'MyResult' returned by implicitly async call
let result = await actor.getData() // Returns non-Sendable type

Fixes:

  1. Make the return type Sendable
  2. Return Sendable projections (IDs, copies of data)
  3. Keep processing within the actor's isolation

Actor Isolation Warnings

"Main actor-isolated property accessed from non-isolated context"

// Warning: Main actor-isolated property 'title' cannot be referenced from a non-isolated context
func updateTitle() {
    viewModel.title = "New" // viewModel is @MainActor
}

Fixes:

  1. Mark the calling function @MainActor
  2. Use await MainActor.run { } for one-off access
  3. Reconsider if the property truly needs @MainActor isolation

Suppression Strategies

When to Suppress vs. Fix

Fix when:

  • The warning identifies a real data race risk
  • The fix is straightforward (add Sendable, adjust isolation)
  • The code is new or actively maintained

Suppress when:

  • Protocol/inheritance requires the signature
  • Third-party code forces the pattern
  • Migration is in progress (with tracked ticket)

Suppression Annotations

// Suppress Sendable warnings for legacy imports
@preconcurrency import LegacyFramework

// Suppress for a single declaration
nonisolated(unsafe) var legacyCallback: (() -> Void)?

// Type-level suppression (use sparingly)
struct LegacyWrapper: @unchecked Sendable {
    // Document why this is safe
    private let lock = NSLock()
    private var value: Int
}

Documentation Requirements

When using suppression annotations, document:

  1. Why the suppression is needed
  2. What invariant makes it safe
  3. When it can be removed (link to migration ticket)
/// Thread-safe: Internal lock protects all mutations.
/// TODO: Remove @unchecked when migrated to actor (JIRA-1234)
final class ThreadSafeCache: @unchecked Sendable {
    private let lock = NSLock()
    private var storage: [String: Data] = [:]
}

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides comprehensive instructions and reference material for diagnosing and fixing Swift Concurrency issues. It includes detailed guidance on Swift 6 migration, actor isolation, and task management. No security risks or malicious patterns were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    1/16 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 35095d4. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago
  • swift
  • concurrency
  • async-await
  • actors
  • sendable
  • main-actor
  • swift-6
  • migration
  • data-races
  • thread-safety

README badge

README badge for avdlee/swift-concurrency-agent-skill

Diagnose and fix Swift Concurrency issues including data races, isolation violations, and Sendable conformance problems. Refactors callback-based code to async/await and guides Swift 6 migration by analyzing project settings, determining isolation boundaries, and applying the smallest safe changes.

Generated from the current SKILL.md.

Does this skill help with Swift 6 migration?
Yes. The skill guides Swift 6 migration by analyzing language mode and strict concurrency settings from Package.swift or .pbxproj, then walking through diagnostics related to tasks, actors, @MainActor, Sendable, and data races.
What project settings does this skill check?
The skill analyzes Swift language mode, strict concurrency level, default isolation, and upcoming features from both SwiftPM (Package.swift) and Xcode (.pbxproj) to determine the correct fix for each diagnostic.
Does this skill refactor callback-based code to async/await?
Yes. The skill helps refactor legacy callback patterns to async/await, and routes to references/migration.md for detailed closure-to-async conversion strategies.
Will this skill recommend @MainActor as a blanket fix?
No. The skill requires justification for @MainActor isolation and avoids recommending it unless the code is truly UI-bound; it also favors structured concurrency over unstructured tasks.
Does this skill handle Core Data concurrency issues?
Yes. The skill addresses NSManagedObject sendability warnings and crossing context/actor boundaries, with routes to references/core-data.md for detailed solutions.

Generated from the current SKILL.md. These answers refresh after source changes.