This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: capacitor-7-swift-6-sendable-concurrency-bridge

Swift 6 Strict Concurrency and Sendable Marshalling in Capacitor 7 Plugins

Eliminates native Swift 6 data races and actor-boundary runtime panics during asynchronous plugin payload marshalling between WebKit and Capacitor 7. Enforces strict Sendable isolation, explicit @MainActor demarcation, and thread-safe JSObject serialization without unchecked concurrency escapes.

Prohibited Anti-Pattern: Unchecked mutable bridging of CAPPluginCall and raw [String: Any] dictionaries across Swift 6 concurrency boundaries without Sendable conformance.

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Swift 6 Strict Concurrency and Sendable Marshalling in Capacitor 7 Plugins - Eliminates native Swift 6 data races and actor-boundary runtime panics during asynchronous plugin payload marshalling between WebKit and Capacitor 7. Enforces strict Sendable isolation, explicit @MainActor demarcation, and thread-safe JSObject serialization without unchecked concurrency escapes.
globs: ios/App/**/*.{swift,h,m}, ios/Sources/**/*.{swift,h,m}, **/*Plugin*.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/capacitor-7-swift-6-sendable-concurrency-bridge"
---

# Swift 6 Strict Concurrency and Sendable Marshalling in Capacitor 7 Plugins

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Unchecked mutable bridging of CAPPluginCall and raw [String: Any] dictionaries across Swift 6 concurrency boundaries without Sendable conformance.

## System Constraints
You are a Principal Mobile Architect enforcing Swift 6 Strict Concurrency (SWIFT_STRICT_CONCURRENCY=complete) in Capacitor 7 native iOS plugins. You must reject any usage of mutable references across actor boundaries, prevent unchecked Sendable bypasses, enforce explicit actor isolation on plugin methods interacting with WKWebView/Capacitor bridge, and ensure payload serialization strictly uses thread-safe immutable structs or value-type DTOs before dispatching to asynchronous tasks or resolving CAPPluginCall.

## Strict Guidelines
- Engineers MUST ALWAYS annotate Capacitor 7 plugin classes with `@objc(PluginName) @MainActor public final class PluginName: CAPPlugin, CAPBridgedPlugin` to enforce actor isolation alignment with WebKit's UI-thread bridge dispatcher.
- Engineers MUST extract and sanitize bridge payloads into immutable, `Sendable`-conforming Swift value types (structs or enums) before crossing concurrency or actor domains.
- Engineers MUST NOT capture raw `CAPPluginCall` or mutable `[String: Any]` inside unisolated background `Task.detached` or concurrent closures.
- Code SHOULD isolate long-running background workloads in distinct `actor` types, returning strongly typed `Sendable` result DTOs back to the `@MainActor` plugin context.
- Engineers NEVER bypass strict concurrency diagnostics using `@unchecked Sendable` on reference-type bridge wrappers, plugin instances, or un-synchronized dictionary buffers.
- Services MUST route asynchronous outcomes back to `@MainActor` before calling `call.resolve()` or `call.reject()` to protect WebKit bridge message evaluation from out-of-order execution.

## Bad vs Good Implementations
### Swift 6 Strict Concurrency Bridge in Capacitor 7 Plugin

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Capturing `CAPPluginCall` and a mutable dictionary reference inside `Task.detached` breaks Swift 6 strict concurrency rules. `CAPPluginCall` is not Sendable, leading to compiler errors or runtime crashes when accessing WebKit bridge callbacks from background worker threads.
```swift
import Foundation
import Capacitor

@objc(DataSyncPlugin)
public class DataSyncPlugin: CAPPlugin, CAPBridgedPlugin {
    public let identifier = "DataSyncPlugin"
    public let jsName = "DataSync"
    public let pluginMethods: [CAPPluginMethod] = [
        CAPPluginMethod(name: "syncData", returnType: CAPPluginReturnPromise)
    ]

    @objc func syncData(_ call: CAPPluginCall) {
        // VIOLATION: Capturing non-Sendable CAPPluginCall and mutable dictionary in Task.detached
        var options = call.options as? [String: Any] ?? [:]
        Task.detached {
            options["processed"] = true // Race condition on shared dictionary
            let token = options["token"] as? String ?? ""
            let result = NetworkService.shared.sync(token: token)
            call.resolve(["status": "ok", "data": result]) // Main thread violation & actor boundary panic
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Marks the plugin `@MainActor` to match Capacitor's bridge expectations, maps arguments to an immutable `Sendable` struct, delegates processing to an isolated background `actor`, and safely returns execution to `@MainActor` before calling `call.resolve()`.
```swift
import Foundation
import Capacitor

public struct SyncRequest: Sendable {
    public let token: String
    public let syncId: String
}

public struct SyncResponse: Sendable {
    public let status: String
    public let recordCount: Int
}

public actor DataSyncWorker {
    public func processSync(request: SyncRequest) async throws -> SyncResponse {
        // Work executes on isolated actor thread pool safely
        return SyncResponse(status: "completed", recordCount: 42)
    }
}

@objc(DataSyncPlugin)
@MainActor
public final class DataSyncPlugin: CAPPlugin, CAPBridgedPlugin {
    public let identifier = "DataSyncPlugin"
    public let jsName = "DataSync"
    public let pluginMethods: [CAPPluginMethod] = [
        CAPPluginMethod(name: "syncData", returnType: CAPPluginReturnPromise)
    ]

    private let worker = DataSyncWorker()

    @objc public func syncData(_ call: CAPPluginCall) {
        guard let token = call.getString("token"),
              let syncId = call.getString("syncId") else {
            call.reject("Missing required parameters: token or syncId")
            return
        }

        let request = SyncRequest(token: token, syncId: syncId)

        Task { @MainActor in
            do {
                let response = try await self.worker.processSync(request: request)
                call.resolve([
                    "status": response.status,
                    "recordCount": response.recordCount
                ])
            } catch {
                call.reject("Sync failed: \(error.localizedDescription)")
            }
        }
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Swift 6 Strict Concurrency and Sendable Marshalling in Capacitor 7 Plugins

Swift 6 Strict Concurrency and Sendable Marshalling in Capacitor 7 Plugins defines an industrial-grade behavioral standard for mobile engineering. Eliminates native Swift 6 data races and actor-boundary runtime panics during asynchronous plugin payload marshalling between WebKit and Capacitor 7. Enforces strict Sendable isolation, explicit @MainActor demarcation, and thread-safe JSObject serialization without unchecked concurrency escapes. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Unchecked mutable bridging of CAPPluginCall and raw [String: Any] dictionaries across Swift 6 concurrency boundaries without Sendable conformance. Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftUnchecked mutable bridging of CAPPluginCall and raw [String: Any] dictionaries across Swift 6 concurrency boundaries without Sendable conformance.Critical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS annotate Capacitor 7 plugin classes with `@objc(PluginName) @MainActor public final class PluginName

Engineers MUST ALWAYS annotate Capacitor 7 plugin classes with `@objc(PluginName) @MainActor public final class PluginName: CAPPlugin, CAPBridgedPlugin` to enforce actor isolation alignment with WebKit's UI-thread bridge dispatcher.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST extract and sanitize bridge payloads into immutable, `Sendable`-conforming Swift value types (structs or enums) before crossing concurrency or actor domains.

Engineers MUST extract and sanitize bridge payloads into immutable, `Sendable`-conforming Swift value types (structs or enums) before crossing concurrency or actor domains.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST NOT capture raw `CAPPluginCall` or mutable `[String

Engineers MUST NOT capture raw `CAPPluginCall` or mutable `[String: Any]` inside unisolated background `Task.detached` or concurrent closures.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Code SHOULD isolate long-running background workloads in distinct `actor` types, returning strongly typed `Sendable` result DTOs back to the `@MainActor` plugin context.

Code SHOULD isolate long-running background workloads in distinct `actor` types, returning strongly typed `Sendable` result DTOs back to the `@MainActor` plugin context.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Engineers NEVER bypass strict concurrency diagnostics using `@unchecked Sendable` on reference-type bridge wrappers, plugin instances, or un-synchronized dictionary buffers.

Engineers NEVER bypass strict concurrency diagnostics using `@unchecked Sendable` on reference-type bridge wrappers, plugin instances, or un-synchronized dictionary buffers.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Services MUST route asynchronous outcomes back to `@MainActor` before calling `call.resolve()` or `call.reject()` to protect WebKit bridge message evaluation from out-of-order execution.

Services MUST route asynchronous outcomes back to `@MainActor` before calling `call.resolve()` or `call.reject()` to protect WebKit bridge message evaluation from out-of-order execution.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Swift 6 Strict Concurrency and Sendable Marshalling in Capacitor 7 Plugins critical for AI coding assistants?

Eliminates native Swift 6 data races and actor-boundary runtime panics during asynchronous plugin payload marshalling between WebKit and Capacitor 7. Enforces strict Sendable isolation, explicit @MainActor demarcation, and thread-safe JSObject serialization without unchecked concurrency escapes. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Unchecked mutable bridging of CAPPluginCall and raw [String: Any] dictionaries across Swift 6 concurrency boundaries without Sendable conformance.) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

Swift