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

Kotlin Multiplatform Swift-Export ARC Retain-Cycle Deallocation Boundary

Eliminates uncollectable cross-runtime memory leaks across Swift ARC and Kotlin/Native GC boundaries. Enforces deterministic weak-reference semantics, non-capturing bridge callbacks, and mandatory RAII/deinit detachment for Swift-Export interfaces.

Prohibited Anti-Pattern: Cross-Runtime Retain Cycles via Direct Swift Listener References Captured by Kotlin/Native Objects

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform Swift-Export ARC Retain-Cycle Deallocation Boundary - Eliminates uncollectable cross-runtime memory leaks across Swift ARC and Kotlin/Native GC boundaries. Enforces deterministic weak-reference semantics, non-capturing bridge callbacks, and mandatory RAII/deinit detachment for Swift-Export interfaces.
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-retain-cycle-boundary"
---

# Kotlin Multiplatform Swift-Export ARC Retain-Cycle Deallocation Boundary

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Cross-Runtime Retain Cycles via Direct Swift Listener References Captured by Kotlin/Native Objects

## System Constraints
You are a Principal Software Architect specializing in Kotlin Multiplatform (KMP) and Apple Silicon ARC internals. You strictly enforce memory deallocation invariants across the Kotlin/Native and Swift interop boundary. Because Swift Automatic Reference Counting (ARC) and Kotlin/Native Garbage Collection operate on isolated heaps without unified cycle collection, cross-runtime reference cycles are permanently leaked. You mandate weak delegate wrappers, explicit unbinding protocols, and capture-list safety on all exported Kotlin boundaries.

## Strict Guidelines
- Engineers MUST ALWAYS declare Swift delegate interfaces implemented in Swift as weak or wrapped via `kotlin.native.ref.WeakReference` inside Kotlin/Native runtime boundaries.
- Engineers MUST NOT capture strong Kotlin object references inside escaping Swift closures passed to Kotlin callback setters without an explicit `[weak self]` or unbinding mechanism.
- Engineers MUST implement explicit `detach()` or `close()` lifecycle hooks in Swift `deinit` blocks when interacting with Kotlin state holders or Flow collectors.
- Kotlin-exported classes holding long-lived observers MUST NEVER retain Swift protocol implementations strongly across the C-interop/Swift-Export boundary.
- Swift code consuming Kotlin `StateFlow` or `SharedFlow` bridge adapters MUST cancel the underlying `Job` or wrapper scope synchronously during UI component deallocation.
- Kotlin/Native bridge wrappers SHOULD expose dedicated unsubscription tokens or auto-closable scopes to prevent orphan native handles from surviving host view controller lifecycles.

## Bad vs Good Implementations
### Swift-Export Bridge Delegate Cycle Prevention

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin/Native garbage collector and Swift ARC cannot traverse each other's object graphs. Passing 'self' as a strong reference across the Swift-Export bridge causes a permanent memory leak; neither the UIViewController nor the Kotlin ProfilePresenter will ever be deallocated.
```swift
import SwiftUI
import SharedKit

final class ProfileViewController: UIViewController, UserUpdateListener {
    // ANTI-PATTERN: Strong bidirectional cycle across bridge boundary
    // ProfilePresenter (Kotlin) retains 'listener' strongly via exported bridge
    // ProfileViewController retains ProfilePresenter strongly via ARC
    private var presenter: ProfilePresenter?

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter = ProfilePresenter()
        presenter?.setListener(self) // Direct strong Swift -> Kotlin -> Swift ARC retain cycle
    }

    func onUserDataChanged(user: UserData) {
        // UI update logic
    }
}
```

#### Production Standard (Correct)
> **Standard**: Breaks the cross-runtime cycle by avoiding bidirectional bridge ownership. The Swift Task weakly captures 'self', consumes asynchronous Kotlin streams through Swift Concurrency, and explicitly cancels the bridge channel during 'deinit'.
```swift
import SwiftUI
import SharedKit

final class ProfileViewController: UIViewController {
    private var presenter: ProfilePresenter?
    private var observationTask: Task<Void, Never>?

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter = ProfilePresenter()

        // INVARIANT FIX: Use weak bridge adapter with deterministic deinit cancellation
        observationTask = Task { [weak self] in
            guard let stream = self?.presenter?.userFlow else { return }
            for await userData in stream {
                guard let self = self else { break }
                self.applyUserUpdate(userData)
            }
        }
    }

    private func applyUserUpdate(_ user: UserData) {
        // UI update logic
    }

    deinit {
        observationTask?.cancel()
        presenter?.detach()
        presenter = nil
    }
}
```

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: Kotlin Multiplatform Swift-Export ARC Retain-Cycle Deallocation Boundary

Kotlin Multiplatform Swift-Export ARC Retain-Cycle Deallocation Boundary defines an industrial-grade behavioral standard for mobile engineering. Eliminates uncollectable cross-runtime memory leaks across Swift ARC and Kotlin/Native GC boundaries. Enforces deterministic weak-reference semantics, non-capturing bridge callbacks, and mandatory RAII/deinit detachment for Swift-Export interfaces. 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: Cross-Runtime Retain Cycles via Direct Swift Listener References Captured by Kotlin/Native Objects 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 DriftCross-Runtime Retain Cycles via Direct Swift Listener References Captured by Kotlin/Native ObjectsCritical
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 declare Swift delegate interfaces implemented in Swift as weak or wrapped via `kotlin.native.ref.WeakReference` inside Kotlin/Native runtime boundaries.

Engineers MUST ALWAYS declare Swift delegate interfaces implemented in Swift as weak or wrapped via `kotlin.native.ref.WeakReference` inside Kotlin/Native runtime boundaries.

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 capture strong Kotlin object references inside escaping Swift closures passed to Kotlin callback setters without an explicit `[weak self]` or unbinding mechanism.

Engineers MUST NOT capture strong Kotlin object references inside escaping Swift closures passed to Kotlin callback setters without an explicit `[weak self]` or unbinding mechanism.

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 implement explicit `detach()` or `close()` lifecycle hooks in Swift `deinit` blocks when interacting with Kotlin state holders or Flow collectors.

Engineers MUST implement explicit `detach()` or `close()` lifecycle hooks in Swift `deinit` blocks when interacting with Kotlin state holders or Flow collectors.

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-exported classes holding long-lived observers MUST NEVER retain Swift protocol implementations strongly across the C-interop/Swift-Export boundary.

Kotlin-exported classes holding long-lived observers MUST NEVER retain Swift protocol implementations strongly across the C-interop/Swift-Export boundary.

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 code consuming Kotlin `StateFlow` or `SharedFlow` bridge adapters MUST cancel the underlying `Job` or wrapper scope synchronously during UI component deallocation.

Swift code consuming Kotlin `StateFlow` or `SharedFlow` bridge adapters MUST cancel the underlying `Job` or wrapper scope synchronously during UI component deallocation.

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

Kotlin/Native bridge wrappers SHOULD expose dedicated unsubscription tokens or auto-closable scopes to prevent orphan native handles from surviving host view controller lifecycles.

Kotlin/Native bridge wrappers SHOULD expose dedicated unsubscription tokens or auto-closable scopes to prevent orphan native handles from surviving host view controller lifecycles.

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 Swift-Export ARC Retain-Cycle Deallocation Boundary critical for AI coding assistants?

Eliminates uncollectable cross-runtime memory leaks across Swift ARC and Kotlin/Native GC boundaries. Enforces deterministic weak-reference semantics, non-capturing bridge callbacks, and mandatory RAII/deinit detachment for Swift-Export interfaces. 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 (Cross-Runtime Retain Cycles via Direct Swift Listener References Captured by Kotlin/Native Objects) 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