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

KMP 2.1 Swift Export ARC-to-GC Reference Cycle Guardrail

Defines mandatory reference retention semantics and explicit lifetime unbinding between Kotlin/Native tracing GC and Apple Swift Automatic Reference Counting (ARC) under Kotlin 2.1 Swift Export. Eliminates cross-runtime uncollectible memory leaks caused by bidirectional strong retention across Swift delegates and long-lived Kotlin state holders.

Prohibited Anti-Pattern: Bidirectional Strong Retention Between Swift Observers and Kotlin Exported Services

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP 2.1 Swift Export ARC-to-GC Reference Cycle Guardrail - Defines mandatory reference retention semantics and explicit lifetime unbinding between Kotlin/Native tracing GC and Apple Swift Automatic Reference Counting (ARC) under Kotlin 2.1 Swift Export. Eliminates cross-runtime uncollectible memory leaks caused by bidirectional strong retention across Swift delegates and long-lived Kotlin state holders.
globs: **/*.kt, **/*.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-export-arc-cycle-prevention"
---

# KMP 2.1 Swift Export ARC-to-GC Reference Cycle Guardrail

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Bidirectional Strong Retention Between Swift Observers and Kotlin Exported Services

## System Constraints
You are a Principal Software Architect enforcing the Kotlin Multiplatform 2.1 Swift Export ARC Reference Cycle Prevention Standard. Your mission is to eliminate cross-runtime memory leaks across the Kotlin/Native and Swift boundary. Swift ARC and Kotlin tracing GC run isolated collector mechanisms; when an ARC object strongly retains a Kotlin object that strongly retains the Swift object (directly or via closures), neither engine can identify the cycle as garbage, producing an uncollectible leak. You must mandate non-retaining bridge interfaces, explicit Swift deinitialization cleanup, and disposable token patterns across all exported interfaces.

## Strict Guidelines
- Engineers MUST ALWAYS decouple cross-runtime bidirectional graphs by declaring Swift-side callbacks with weak capture semantics (`[weak self]`) or wrapping Kotlin bridge references in `kotlin.native.ref.WeakReference`.
- Engineers MUST NOT store direct strong references to Swift protocol implementations or closure callbacks inside long-lived Kotlin singletons or application-scoped state holders.
- Kotlin-exported subscription or observation APIs MUST return an explicit `DisposableHandle` or `Cancellable` contract rather than relying on object deallocation across the bridge.
- Swift clients observing Kotlin Multiplatform view models or repositories MUST explicitly cancel tokens within `deinit`, SwiftUI `.onDisappear`, or lifecycle teardown blocks.
- Kotlin bridge interfaces that require observer notifications SHOULD model listeners through non-retaining delegate containers or thread-safe atomic weak-reference lists.
- Swift closures passed across the exported boundary MUST NEVER capture non-local escaping contexts without explicitly guarded weak captures.

## Bad vs Good Implementations
### Cross-Runtime Observer Subscription Pattern

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin/Native stores the Swift closure strongly in its internal state map, while the Swift closure implicitly captures self strongly. Because ARC does not scan Kotlin GC roots and Kotlin GC treats ARC references as root references, this cross-runtime cycle is permanently uncollectible.
```swift
// Swift Observer strongly capturing self and passing strong callback to exported Kotlin Engine
final class ProfileViewController: UIViewController {
    private let viewModel: UserViewModelExported = UserViewModelExported()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // CRITICAL BUG: Kotlin retains callback, callback retains self strongly;
        // UserViewModelExported is retained by self -> Cross-runtime ARC/GC immortal cycle.
        viewModel.observeUserProfile(onUpdate: { profile in
            self.updateUI(profile)
        })
    }
    
    private func updateUI(_ profile: UserProfile) {
        // UI binding logic
    }
}
```

#### Production Standard (Correct)
> **Standard**: Capturing self weakly inside the exported bridge closure ensures ARC can reclaim the UIViewController. Explicit disposal in deinit breaks the Kotlin-side tracing root immediately, ensuring both ARC and Kotlin GC reclaim runtime memory deterministically.
```swift
// Swift Observer utilizing explicit disposable unbinding and weak self captures
final class ProfileViewController: UIViewController {
    private let viewModel: UserViewModelExported = UserViewModelExported()
    private var profileSubscription: DisposableToken? = nil
    
    override func viewDidLoad() {
        super.viewDidLoad()
        profileSubscription = viewModel.observeUserProfile(onUpdate: { [weak self] profile in
            guard let self = self else { return }
            self.updateUI(profile)
        })
    }
    
    deinit {
        // Deterministically break boundary bridge reference on ARC deallocation
        profileSubscription?.dispose()
        profileSubscription = nil
    }
    
    private func updateUI(_ profile: UserProfile) {
        // UI binding logic
    }
}
```

### Kotlin Side Non-Retaining Listener Bridge

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: When Swift implements SessionListener and passes it to SessionManager, Kotlin/Native creates a strong native root pinning the Swift ARC proxy. If the Swift listener also references the manager, a permanent leak occurs.
```kotlin
package com.example.kmp.bridge

interface SessionListener {
    fun onSessionExpired()
}

// CRITICAL BUG: Direct strong storage of Swift-exported listener implementation
class SessionManager {
    private val listeners = mutableListOf<SessionListener>()
    
    fun addListener(listener: SessionListener) {
        listeners.add(listener)
    }
    
    fun notifyExpired() {
        listeners.forEach { it.onSessionExpired() }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Uses WeakReference to prevent Kotlin from anchoring the Swift bridge instance as an immortal GC root, and returns a DisposableHandle for deterministic removal.
```kotlin
package com.example.kmp.bridge

import kotlin.native.ref.WeakReference

fun interface SessionListener {
    fun onSessionExpired()
}

fun interface DisposableHandle {
    fun dispose()
}

class SessionManager {
    private val listeners = mutableListOf<WeakReference<SessionListener>>()
    
    fun addListener(listener: SessionListener): DisposableHandle {
        val weakRef = WeakReference(listener)
        listeners.add(weakRef)
        return DisposableHandle {
            listeners.removeAll { it.get() == null || it.get() == listener }
        }
    }
    
    fun notifyExpired() {
        listeners.removeAll { it.get() == null }
        listeners.forEach { it.get()?.onSessionExpired() }
    }
}
```

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: KMP 2.1 Swift Export ARC-to-GC Reference Cycle Guardrail

KMP 2.1 Swift Export ARC-to-GC Reference Cycle Guardrail defines an industrial-grade behavioral standard for mobile engineering. Defines mandatory reference retention semantics and explicit lifetime unbinding between Kotlin/Native tracing GC and Apple Swift Automatic Reference Counting (ARC) under Kotlin 2.1 Swift Export. Eliminates cross-runtime uncollectible memory leaks caused by bidirectional strong retention across Swift delegates and long-lived Kotlin state holders. 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: Bidirectional Strong Retention Between Swift Observers and Kotlin Exported Services 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 DriftBidirectional Strong Retention Between Swift Observers and Kotlin Exported ServicesCritical
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 decouple cross-runtime bidirectional graphs by declaring Swift-side callbacks with weak capture semantics (`[weak self]`) or wrapping Kotlin bridge references in `kotlin.native.ref.WeakReference`.

Engineers MUST ALWAYS decouple cross-runtime bidirectional graphs by declaring Swift-side callbacks with weak capture semantics (`[weak self]`) or wrapping Kotlin bridge references in `kotlin.native.ref.WeakReference`.

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 store direct strong references to Swift protocol implementations or closure callbacks inside long-lived Kotlin singletons or application-scoped state holders.

Engineers MUST NOT store direct strong references to Swift protocol implementations or closure callbacks inside long-lived Kotlin singletons or application-scoped state holders.

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

Kotlin-exported subscription or observation APIs MUST return an explicit `DisposableHandle` or `Cancellable` contract rather than relying on object deallocation across the bridge.

Kotlin-exported subscription or observation APIs MUST return an explicit `DisposableHandle` or `Cancellable` contract rather than relying on object deallocation across the bridge.

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

Swift clients observing Kotlin Multiplatform view models or repositories MUST explicitly cancel tokens within `deinit`, SwiftUI `.onDisappear`, or lifecycle teardown blocks.

Swift clients observing Kotlin Multiplatform view models or repositories MUST explicitly cancel tokens within `deinit`, SwiftUI `.onDisappear`, or lifecycle teardown blocks.

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

Kotlin bridge interfaces that require observer notifications SHOULD model listeners through non-retaining delegate containers or thread-safe atomic weak-reference lists.

Kotlin bridge interfaces that require observer notifications SHOULD model listeners through non-retaining delegate containers or thread-safe atomic weak-reference lists.

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 closures passed across the exported boundary MUST NEVER capture non-local escaping contexts without explicitly guarded weak captures.

Swift closures passed across the exported boundary MUST NEVER capture non-local escaping contexts without explicitly guarded weak captures.

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 KMP 2.1 Swift Export ARC-to-GC Reference Cycle Guardrail critical for AI coding assistants?

Defines mandatory reference retention semantics and explicit lifetime unbinding between Kotlin/Native tracing GC and Apple Swift Automatic Reference Counting (ARC) under Kotlin 2.1 Swift Export. Eliminates cross-runtime uncollectible memory leaks caused by bidirectional strong retention across Swift delegates and long-lived Kotlin state holders. 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 (Bidirectional Strong Retention Between Swift Observers and Kotlin Exported Services) 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

KMP