This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-swift-export-arc-boundary-governor

Kotlin Multiplatform 2.1 Swift-Export ARC & Coroutine Lifecycle Boundary Governor

Governs interop memory safety across Kotlin/Native GC and Swift ARC when consuming stateful asynchronous flows and coroutines via Swift-Export. Enforces explicit lifecycle detachment, structured cancellation propagation, and weak closure binding to prevent cross-runtime retain cycles and unbounded coroutine execution.

Prohibited Anti-Pattern: Unbounded Coroutine Scope Rooting and Cross-Runtime Strong Retain Cycles Across Swift-Export Boundaries

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform 2.1 Swift-Export ARC & Coroutine Lifecycle Boundary Governor - Governs interop memory safety across Kotlin/Native GC and Swift ARC when consuming stateful asynchronous flows and coroutines via Swift-Export. Enforces explicit lifecycle detachment, structured cancellation propagation, and weak closure binding to prevent cross-runtime retain cycles and unbounded coroutine execution.
globs: iosApp/**/*.swift, shared/src/iosMain/**/*.kt, shared/src/commonMain/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-export-arc-boundary-governor"
---

# Kotlin Multiplatform 2.1 Swift-Export ARC & Coroutine Lifecycle Boundary Governor

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Unbounded Coroutine Scope Rooting and Cross-Runtime Strong Retain Cycles Across Swift-Export Boundaries

## System Constraints
You are a Principal Mobile Architect enforcing Kotlin Multiplatform 2.1 Swift-Export memory safety and lifecycle synchronization. Kotlin/Native GC roots and Swift ARC references operate under distinct memory reclamation mechanics. When exposing asynchronous primitives (Coroutines, StateFlow, SharedFlow) across Swift-Export, you must strictly prevent retain cycles where Swift closures capture Kotlin objects strongly while Kotlin coroutines retain Swift completion handlers. Enforce deterministic cancellation, structured Swift Tasks, and explicit lifecycle de-registration.

## Strict Guidelines
- Engineers MUST ALWAYS enforce deterministic teardown of Kotlin coroutine subscriptions when the Swift consumer deinitializes or dismounts from the view hierarchy.
- Engineers MUST NOT expose unbounded Kotlin CoroutineScope roots to Swift without an associated Cancellable or Disposable token returned to Swift ARC.
- Swift consumers MUST wrap Swift-Export Flow collectors inside structured Swift Tasks that guarantee task cancellation propagates to the underlying Kotlin Job.
- Kotlin-to-Swift callback closures MUST NEVER capture strong references to Swift instances (such as UIViewController, ObservableObject, or SwiftUI View models) within long-lived coroutine scopes.
- Engineers MUST utilize explicit DisposableHandle or Closeable abstractions across the boundary rather than relying on Kotlin GC finalizers to release Swift ARC references.
- Swift-Exported ViewModel or Presenter contracts MUST implement a mandatory cancel() or close() lifecycle method invoked during Swift deinit.

## Bad vs Good Implementations
### Swift-Export Flow Observation Retain Cycle vs. Structured Lifecycle Cancellation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The Swift closure strongly captures 'self', while the Kotlin repository's internal coroutine retains the bridged closure as an external reference. Because ARC waits for Kotlin to release the closure, and Kotlin's active CoroutineScope never terminates, neither object can ever be deallocated, creating a persistent native memory and thread leak.
```swift
import SwiftUI
import SharedKit

@MainActor
final class UserProfileViewModel: ObservableObject {
    @Published private(set) var state: ProfileState = .idle
    private let repository: ProfileRepository // Swift-Exported Kotlin class

    init(repository: ProfileRepository) {
        self.repository = repository
        // ANTI-PATTERN: Strong capture of self in unmanaged callback collection.
        // ProfileRepository's coroutine scope roots this closure in Kotlin GC,
        // creating an uncollectible ARC <-> GC retain cycle that survives view deinit.
        repository.observeProfile {
            self.state = $0
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Encapsulating the cross-boundary observation in a native Swift Task with [weak self] ensures that ARC can deallocate UserProfileViewModel. The deinit hook cleanly cancels the task, signaling the bridged Kotlin stream adapter to abort its collection Job, freeing both Swift closures and Kotlin GC allocations.
```swift
import SwiftUI
import SharedKit

@MainActor
final class UserProfileViewModel: ObservableObject {
    @Published private(set) var state: ProfileState = .idle
    private let repository: ProfileRepository
    private var observationTask: Task<Void, Never>?

    init(repository: ProfileRepository) {
        self.repository = repository
        self.startObservation()
    }

    private func startObservation() {
        // INVARIANT FIX: Structured Swift Task with weak self capture.
        // Cancellation propagates across Swift-Export, terminating the Kotlin coroutine.
        observationTask = Task { [weak self] in
            for await updatedState in self?.repository.profileStateStream() ?? AsyncStream { $0.finish() } {
                guard !Task.isCancelled, let self else { break }
                self.state = updatedState
            }
        }
    }

    deinit {
        observationTask?.cancel()
    }
}
```

### Kotlin Swift-Export Coroutine Wrapper Lifecycle Boundary

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin creates a detached coroutine with no deterministic cancellation mechanism exposed to Swift-Export. When Swift passes a closure, Kotlin Native holds a stable reference to it, leaking the closure and any Swift context it captured until the app process terminates.
```kotlin
package com.example.shared

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// ANTI-PATTERN: Exposing direct raw collection without returning a cancellation handle.
// Kotlin scope outlives Swift callers and retains Swift closures indefinitely.
class ProfileRepository {
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
    private val _profileState = MutableStateFlow(ProfileState.Idle)

    fun observeProfile(onUpdate: (ProfileState) -> Unit) {
        scope.launch {
            _profileState.collect { onUpdate(it) }
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Returning a DisposableHandle gives Swift ARC immediate ownership of the subscription lifecycle. Conforming to AutoCloseable allows Swift to manage repository lifetime deterministically, guaranteeing that all child coroutines and bridged closures are explicitly terminated.
```kotlin
package com.example.shared

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// INVARIANT FIX: Provide an explicit DisposableHandle and scope cancellation lifecycle
class ProfileRepository : AutoCloseable {
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
    private val _profileState = MutableStateFlow(ProfileState.Idle)

    fun observeProfile(onUpdate: (ProfileState) -> Unit): DisposableHandle {
        val job = scope.launch {
            _profileState.collect { state ->
                ensureActive()
                onUpdate(state)
            }
        }
        return DisposableHandle { job.cancel() }
    }

    override fun close() {
        scope.cancel()
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

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

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Kotlin Multiplatform 2.1 Swift-Export ARC & Coroutine Lifecycle Boundary Governor

Kotlin Multiplatform 2.1 Swift-Export ARC & Coroutine Lifecycle Boundary Governor defines an industrial-grade behavioral standard for mobile engineering. Governs interop memory safety across Kotlin/Native GC and Swift ARC when consuming stateful asynchronous flows and coroutines via Swift-Export. Enforces explicit lifecycle detachment, structured cancellation propagation, and weak closure binding to prevent cross-runtime retain cycles and unbounded coroutine execution. 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: Unbounded Coroutine Scope Rooting and Cross-Runtime Strong Retain Cycles Across Swift-Export 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 DriftUnbounded Coroutine Scope Rooting and Cross-Runtime Strong Retain Cycles Across Swift-Export 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 enforce deterministic teardown of Kotlin coroutine subscriptions when the Swift consumer deinitializes or dismounts from the view hierarchy.

Engineers MUST ALWAYS enforce deterministic teardown of Kotlin coroutine subscriptions when the Swift consumer deinitializes or dismounts from the view hierarchy.

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 expose unbounded Kotlin CoroutineScope roots to Swift without an associated Cancellable or Disposable token returned to Swift ARC.

Engineers MUST NOT expose unbounded Kotlin CoroutineScope roots to Swift without an associated Cancellable or Disposable token returned to Swift ARC.

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

Swift consumers MUST wrap Swift-Export Flow collectors inside structured Swift Tasks that guarantee task cancellation propagates to the underlying Kotlin Job.

Swift consumers MUST wrap Swift-Export Flow collectors inside structured Swift Tasks that guarantee task cancellation propagates to the underlying Kotlin Job.

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

Kotlin-to-Swift callback closures MUST NEVER capture strong references to Swift instances (such as UIViewController, ObservableObject, or SwiftUI View models) within long-lived coroutine scopes.

Kotlin-to-Swift callback closures MUST NEVER capture strong references to Swift instances (such as UIViewController, ObservableObject, or SwiftUI View models) within long-lived coroutine scopes.

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 MUST utilize explicit DisposableHandle or Closeable abstractions across the boundary rather than relying on Kotlin GC finalizers to release Swift ARC references.

Engineers MUST utilize explicit DisposableHandle or Closeable abstractions across the boundary rather than relying on Kotlin GC finalizers to release Swift ARC references.

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

Swift-Exported ViewModel or Presenter contracts MUST implement a mandatory cancel() or close() lifecycle method invoked during Swift deinit.

Swift-Exported ViewModel or Presenter contracts MUST implement a mandatory cancel() or close() lifecycle method invoked during Swift deinit.

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 Kotlin Multiplatform 2.1 Swift-Export ARC & Coroutine Lifecycle Boundary Governor critical for AI coding assistants?

Governs interop memory safety across Kotlin/Native GC and Swift ARC when consuming stateful asynchronous flows and coroutines via Swift-Export. Enforces explicit lifecycle detachment, structured cancellation propagation, and weak closure binding to prevent cross-runtime retain cycles and unbounded coroutine execution. 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 (Unbounded Coroutine Scope Rooting and Cross-Runtime Strong Retain Cycles Across Swift-Export 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

Kotlin