This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-swift-concurrency-cooperative-cancellation-bridging

Bi-directional Structured Cancellation Bridging for Swift Tasks and Kotlin Coroutines

Enforces deterministic, bidirectional cooperative cancellation bridging across the Swift/Native-Kotlin ABI boundary. Ensures Kotlin Coroutine Job lifecycles bind strictly to Swift Task cancellation handlers and vice versa to eliminate uncollected background threads, orphaned network requests, and native memory leaks in KMP applications.

Prohibited Anti-Pattern: Fire-and-Forget Suspend Bridging and Unbound Continuation Handlers in Swift/Kotlin Boundaries

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Bi-directional Structured Cancellation Bridging for Swift Tasks and Kotlin Coroutines - Enforces deterministic, bidirectional cooperative cancellation bridging across the Swift/Native-Kotlin ABI boundary. Ensures Kotlin Coroutine Job lifecycles bind strictly to Swift Task cancellation handlers and vice versa to eliminate uncollected background threads, orphaned network requests, and native memory leaks in KMP applications.
globs: **/*.ios.kt, **/*.native.kt, **/iosMain/**/*.kt, **/iosApp/**/*.swift, **/SwiftBridge/**/*.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-concurrency-cooperative-cancellation-bridging"
---

# Bi-directional Structured Cancellation Bridging for Swift Tasks and Kotlin Coroutines

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Fire-and-Forget Suspend Bridging and Unbound Continuation Handlers in Swift/Kotlin Boundaries

## System Constraints
You are a Principal Engineer enforcing bi-directional cooperative cancellation between Kotlin Multiplatform Coroutines and Swift Concurrency (async/await). Mandate that every bridged suspend function or Kotlin Flow consumed as an AsyncSequence explicitly binds Swift's withTaskCancellationHandler to cancel the underlying Kotlin Job, and propagate native Swift cancellation into Kotlin's CancellationException to prevent orphaned work and deadlocks across the Objective-C/Swift interop boundary.

## Strict Guidelines
- Engineers MUST ALWAYS wrap bridged Kotlin suspend calls in Swift within `withTaskCancellationHandler` to forward Task cancellation directly to the underlying Kotlin `Job.cancel()` or disposable wrapper.
- Engineers MUST NOT discard or ignore the returned Kotlin Coroutine handle, `Job`, or disposable token when initiating execution from Swift async contexts.
- Bridged Kotlin Coroutines MUST yield cooperatively using `ensureActive()` or structured suspension points so cancellation tokens dispatched from Swift are honored immediately.
- When bridging Kotlin `Flow` to Swift `AsyncSequence`, developers MUST ensure that termination or early break of the Swift `for await` loop propagates cancellation back to the Kotlin `CoroutineScope` collecting the Flow.
- Swift continuations created via `withCheckedThrowingContinuation` or `withUnsafeThrowingContinuation` MUST invoke `continuation.resume(throwing: CancellationError())` if and only if cancellation occurs, without triggering double-resume crashes.
- Engineers MUST NEVER use `GlobalScope` or unbounded dispatchers in Kotlin when exposing native suspension APIs to Swift.

## Bad vs Good Implementations
### Bridging Kotlin Suspend Function to Swift Task with Cooperative Cancellation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The Kotlin coroutine runs inside an unmanaged background execution context that receives no notification when the enclosing Swift Task is cancelled. This leaks CPU cycles and network resources, and can invoke callbacks against deallocated Swift view models.
```swift
// BAD: Naive async bridge ignores Swift Task cancellation.
// If the calling Swift Task is cancelled, the underlying Kotlin coroutine runs to completion,
// leaking native threads, memory, and mutating orphaned shared state.
func fetchUserProfile(userId: String) async throws -> UserProfile {
    return try await withCheckedThrowingContinuation { continuation in
        SharedRepositoryKt.fetchUserProfile(userId: userId) { profile, error in
            if let error = error {
                continuation.resume(throwing: error)
            } else if let profile = profile {
                continuation.resume(returning: profile)
            }
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Using `withTaskCancellationHandler` guarantees that when the enclosing Swift Task cancels, `jobHolder.cancel()` immediately cancels the Kotlin Job via `Job.cancel()`. The Kotlin Coroutine throws `CancellationException`, which is caught and translated to Swift's standard `CancellationError`.
```swift
// GOOD: withTaskCancellationHandler binds Swift cancellation directly to the Kotlin Job.
func fetchUserProfile(userId: String) async throws -> UserProfile {
    let jobHolder = KotlinJobHolder()
    
    return try await withTaskCancellationHandler {
        try await withCheckedThrowingContinuation { continuation in
            jobHolder.job = SharedRepositoryKt.fetchUserProfileCancellable(
                userId: userId,
                onSuccess: { profile in continuation.resume(returning: profile) },
                onError: { error in
                    if error.isKotlinCancellationException() {
                        continuation.resume(throwing: CancellationError())
                    } else {
                        continuation.resume(throwing: error)
                    }
                }
            )
        }
    } onCancel: {
        // Cooperative cancellation dispatched across boundary immediately
        jobHolder.cancel()
    }
}
```

### Kotlin Side Cancellable Bridge Pattern for Swift Export

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin creates an uncontrolled CoroutineScope and returns Unit, preventing the Swift consumer from retaining, tracking, or cancelling the ongoing coroutine.
```kotlin
// BAD: Unscoped execution without returning a cancellation handle to Swift
fun fetchUserProfileUnsafe(
    userId: String,
    callback: (UserProfile?, Exception?) -> Unit
) {
    CoroutineScope(Dispatchers.Default).launch {
        try {
            val result = apiService.fetchProfile(userId)
            callback(result, null)
        } catch (e: Exception) {
            callback(null, e)
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Returns a explicit `CancellableJob` interface to the Swift client, checks `ensureActive()` around suspension boundaries, and forwards Swift cancellation requests directly into standard Kotlin Coroutine cancellation semantics.
```kotlin
// GOOD: Expose Job via a dedicated disposable handle with structured cooperative checks
interface CancellableJob {
    fun cancel()
}

fun fetchUserProfileCancellable(
    userId: String,
    onSuccess: (UserProfile) -> Unit,
    onError: (Throwable) -> Unit
): CancellableJob {
    val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
    val job = scope.launch {
        try {
            currentCoroutineContext().ensureActive()
            val result = apiService.fetchProfile(userId)
            currentCoroutineContext().ensureActive()
            onSuccess(result)
        } catch (t: Throwable) {
            onError(t)
        }
    }
    return object : CancellableJob {
        override fun cancel() {
            job.cancel(CancellationException("Swift Task cancelled"))
        }
    }
}
```

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: Bi-directional Structured Cancellation Bridging for Swift Tasks and Kotlin Coroutines

Bi-directional Structured Cancellation Bridging for Swift Tasks and Kotlin Coroutines defines an industrial-grade behavioral standard for mobile engineering. Enforces deterministic, bidirectional cooperative cancellation bridging across the Swift/Native-Kotlin ABI boundary. Ensures Kotlin Coroutine Job lifecycles bind strictly to Swift Task cancellation handlers and vice versa to eliminate uncollected background threads, orphaned network requests, and native memory leaks in KMP applications. 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: Fire-and-Forget Suspend Bridging and Unbound Continuation Handlers in Swift/Kotlin Boundaries 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 DriftFire-and-Forget Suspend Bridging and Unbound Continuation Handlers in Swift/Kotlin BoundariesCritical
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 wrap bridged Kotlin suspend calls in Swift within `withTaskCancellationHandler` to forward Task cancellation directly to the underlying Kotlin `Job.cancel()` or disposable wrapper.

Engineers MUST ALWAYS wrap bridged Kotlin suspend calls in Swift within `withTaskCancellationHandler` to forward Task cancellation directly to the underlying Kotlin `Job.cancel()` or disposable wrapper.

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 NOT discard or ignore the returned Kotlin Coroutine handle, `Job`, or disposable token when initiating execution from Swift async contexts.

Engineers MUST NOT discard or ignore the returned Kotlin Coroutine handle, `Job`, or disposable token when initiating execution from Swift async contexts.

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

Bridged Kotlin Coroutines MUST yield cooperatively using `ensureActive()` or structured suspension points so cancellation tokens dispatched from Swift are honored immediately.

Bridged Kotlin Coroutines MUST yield cooperatively using `ensureActive()` or structured suspension points so cancellation tokens dispatched from Swift are honored immediately.

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

When bridging Kotlin `Flow` to Swift `AsyncSequence`, developers MUST ensure that termination or early break of the Swift `for await` loop propagates cancellation back to the Kotlin `CoroutineScope` collecting the Flow.

When bridging Kotlin `Flow` to Swift `AsyncSequence`, developers MUST ensure that termination or early break of the Swift `for await` loop propagates cancellation back to the Kotlin `CoroutineScope` collecting the Flow.

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

Swift continuations created via `withCheckedThrowingContinuation` or `withUnsafeThrowingContinuation` MUST invoke `continuation.resume(throwing

Swift continuations created via `withCheckedThrowingContinuation` or `withUnsafeThrowingContinuation` MUST invoke `continuation.resume(throwing: CancellationError())` if and only if cancellation occurs, without triggering double-resume crashes.

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

Engineers MUST NEVER use `GlobalScope` or unbounded dispatchers in Kotlin when exposing native suspension APIs to Swift.

Engineers MUST NEVER use `GlobalScope` or unbounded dispatchers in Kotlin when exposing native suspension APIs to Swift.

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 Bi-directional Structured Cancellation Bridging for Swift Tasks and Kotlin Coroutines critical for AI coding assistants?

Enforces deterministic, bidirectional cooperative cancellation bridging across the Swift/Native-Kotlin ABI boundary. Ensures Kotlin Coroutine Job lifecycles bind strictly to Swift Task cancellation handlers and vice versa to eliminate uncollected background threads, orphaned network requests, and native memory leaks in KMP applications. 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 (Fire-and-Forget Suspend Bridging and Unbound Continuation Handlers in Swift/Kotlin Boundaries) 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

Bi-directional