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

KMP 2.1 Swift Export Cross-Runtime ARC/GC Lifetime Synchronization

Enforces strict memory boundary management between Kotlin/Native's modern concurrent GC and Swift ARC when using KMP 2.1 Swift Export. Prohibits cross-runtime strong reference cycles caused by bidirectional delegation, long-lived observer callbacks, and unbounded native view bindings. Mandates explicit teardown protocols, unowned/weak Swift proxies, and CoroutineScope lifecycle anchoring to prevent cross-language heap leaks.

Prohibited Anti-Pattern: Cross-Language Retain Cycles via Strong Delegation and Unbounded Coroutine Bridge Observers

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP 2.1 Swift Export Cross-Runtime ARC/GC Lifetime Synchronization - Enforces strict memory boundary management between Kotlin/Native's modern concurrent GC and Swift ARC when using KMP 2.1 Swift Export. Prohibits cross-runtime strong reference cycles caused by bidirectional delegation, long-lived observer callbacks, and unbounded native view bindings. Mandates explicit teardown protocols, unowned/weak Swift proxies, and CoroutineScope lifecycle anchoring to prevent cross-language heap leaks.
globs: **/iosApp/**/*.swift, **/shared/src/iosMain/**/*.kt, **/shared/src/commonMain/**/*ViewModel.kt, **/shared/build.gradle.kts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-export-arc-lifetime-synchronization"
---

# KMP 2.1 Swift Export Cross-Runtime ARC/GC Lifetime Synchronization

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Cross-Language Retain Cycles via Strong Delegation and Unbounded Coroutine Bridge Observers

## System Constraints
You are a Principal Mobile Architect enforcing Kotlin Multiplatform 2.1 Swift Export ARC Lifetime Synchronization. The target platform couples Swift's Automatic Reference Counting with Kotlin/Native's mark-and-sweep GC. Because cross-runtime reference loops cannot be collected automatically by either garbage collector alone, you must enforce explicit weak/unowned references on the Swift interface boundary, cancellation of CoroutineScopes tied to UI lifecycles, and deterministic disposable observer patterns. Reject any Swift Export interface that passes non-weak Swift closures or stateful retainers directly into long-lived Kotlin singletons or ViewModels.

## Strict Guidelines
- Engineers MUST ALWAYS declare Swift callback listeners passed into Kotlin components as weak or wrap them in an intermediate WeakBox/delegate to prevent pinning Kotlin GC nodes to Swift ARC allocations.
- Engineers MUST NOT capture strong instances of UIViewController, SwiftUI View state holders, or Swift reference classes inside long-lived Kotlin Coroutine flows or Job listeners.
- Code SHOULD enforce deterministic lifecycle termination by requiring Swift view boundaries to call a dedicated `detach()` or `dispose()` method in `viewDidDisappear` / `onDisappear`.
- Services MUST scope CoroutineScope instances to platform lifecycle boundaries (e.g., matching iOS screen presence) rather than static global singletons when interacting with Swift Export types.
- Components NEVER store direct strong bi-directional links (Swift View -> Kotlin Presenter -> Swift View) without making the Kotlin-to-Swift reverse reference weakly typed or wrapped in a WeakReference abstraction.

## Bad vs Good Implementations
### Prevent Cross-Language Retain Cycle in Swift Export ViewModel Observation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin's ProfileViewModel stores the closure in its CoroutineScope or callback list. The Swift closure strongly captures ProfileViewController, while ProfileViewController strongly owns ProfileViewModel. Neither Kotlin GC nor Swift ARC can resolve this cross-runtime cycle, leaking the entire ViewController and its hierarchy.
```swift
import SwiftUI
import SharedKit

// ANTI-PATTERN: Direct bidirectional strong reference between Swift UI and KMP ViewModel.
// Kotlin hold strong reference to Swift closure; Swift view retains Kotlin ViewModel.
final class ProfileViewController: UIViewController {
    private let viewModel = ProfileViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        viewModel.observeState { [self] state in
            // Captures 'self' strongly across the KMP boundary
            self.render(state: state)
        }
    }

    private func render(state: ProfileState) {
        // UI updates
    }
}
```

#### Production Standard (Correct)
> **Standard**: The Swift observer references `self` weakly via Swift Concurrency AsyncSequence/Task. Furthermore, deterministic teardown is invoked during view dismissal (`viewModel.clear()`), cancelling ongoing Kotlin coroutines and severing any residual GC-ARC bridges.
```swift
import SwiftUI
import SharedKit

// PRODUCTION INVARIANT: Weak capture in Swift observer, explicit lifecycle teardown
final class ProfileViewController: UIViewController {
    private let viewModel = ProfileViewModel()
    private var observationTask: Task<Void, Never>?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Bridge using explicit weak reference to prevent ARC pinning
        observationTask = Task { [weak self] in
            for await state in self?.viewModel.stateFlow ?? [] {
                guard let self = self else { return }
                self.render(state: state)
            }
        }
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if isMovingFromParent || isBeingDismissed {
            observationTask?.cancel()
            viewModel.clear()
        }
    }

    private func render(state: ProfileState) {
        // UI updates
    }
}
```

### Kotlin/Native Swift Export Delegate Interface with Weak Protocol Reference

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: When Swift Export projects `NavigationDelegate` into Swift, an ARC wrapper object is generated. The Kotlin property `delegate` maintains a strong GC handle to this Swift bridge instance, preventing its deallocation even if the Swift owner drops it.
```kotlin
// ANTI-PATTERN: Kotlin interface retaining strong Swift implementation reference
package com.example.shared

interface NavigationDelegate {
    fun onNavigateBack()
}

class FlowCoordinator {
    // Strong reference to Swift delegate will prevent Swift ARC deallocation
    var delegate: NavigationDelegate? = null

    fun completeFlow() {
        delegate?.onNavigateBack()
    }
}
```

#### Production Standard (Correct)
> **Standard**: By using Kotlin/Native's `kotlin.native.ref.WeakReference`, the Kotlin object does not increase the ARC retain count on the Swift implementation wrapper. When Swift releases the conforming class, both ARC and GC cleanly release their resources without pinning.
```kotlin
// PRODUCTION INVARIANT: Weak reference holder for cross-runtime Swift delegate interfaces
package com.example.shared

import kotlin.experimental.ExperimentalNativeApi
import kotlin.native.ref.WeakReference

interface NavigationDelegate : Any

class FlowCoordinator {
    private var delegateRef: WeakReference<NavigationDelegate>? = null

    var delegate: NavigationDelegate?
        get() = delegateRef?.get()
        set(value) {
            delegateRef = value?.let { WeakReference(it) }
        }

    fun completeFlow() {
        delegate?.let { (it as? NavigationDelegateActions)?.onNavigateBack() }
    }

    fun clear() {
        delegateRef = null
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

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

Architectural Specification & Invariant Rationale

Architectural Invariant Design: KMP 2.1 Swift Export Cross-Runtime ARC/GC Lifetime Synchronization

KMP 2.1 Swift Export Cross-Runtime ARC/GC Lifetime Synchronization defines an industrial-grade behavioral standard for mobile engineering. Enforces strict memory boundary management between Kotlin/Native's modern concurrent GC and Swift ARC when using KMP 2.1 Swift Export. Prohibits cross-runtime strong reference cycles caused by bidirectional delegation, long-lived observer callbacks, and unbounded native view bindings. Mandates explicit teardown protocols, unowned/weak Swift proxies, and CoroutineScope lifecycle anchoring to prevent cross-language heap leaks. 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-Language Retain Cycles via Strong Delegation and Unbounded Coroutine Bridge Observers 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-Language Retain Cycles via Strong Delegation and Unbounded Coroutine Bridge ObserversCritical
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 callback listeners passed into Kotlin components as weak or wrap them in an intermediate WeakBox/delegate to prevent pinning Kotlin GC nodes to Swift ARC allocations.

Engineers MUST ALWAYS declare Swift callback listeners passed into Kotlin components as weak or wrap them in an intermediate WeakBox/delegate to prevent pinning Kotlin GC nodes to Swift ARC allocations.

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 instances of UIViewController, SwiftUI View state holders, or Swift reference classes inside long-lived Kotlin Coroutine flows or Job listeners.

Engineers MUST NOT capture strong instances of UIViewController, SwiftUI View state holders, or Swift reference classes inside long-lived Kotlin Coroutine flows or Job listeners.

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

Code SHOULD enforce deterministic lifecycle termination by requiring Swift view boundaries to call a dedicated `detach()` or `dispose()` method in `viewDidDisappear` / `onDisappear`.

Code SHOULD enforce deterministic lifecycle termination by requiring Swift view boundaries to call a dedicated `detach()` or `dispose()` method in `viewDidDisappear` / `onDisappear`.

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

Services MUST scope CoroutineScope instances to platform lifecycle boundaries (e.g., matching iOS screen presence) rather than static global singletons when interacting with Swift Export types.

Services MUST scope CoroutineScope instances to platform lifecycle boundaries (e.g., matching iOS screen presence) rather than static global singletons when interacting with Swift Export types.

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

Components NEVER store direct strong bi-directional links (Swift View -> Kotlin Presenter -> Swift View) without making the Kotlin-to-Swift reverse reference weakly typed or wrapped in a WeakReference abstraction.

Components NEVER store direct strong bi-directional links (Swift View -> Kotlin Presenter -> Swift View) without making the Kotlin-to-Swift reverse reference weakly typed or wrapped in a WeakReference abstraction.

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 Cross-Runtime ARC/GC Lifetime Synchronization critical for AI coding assistants?

Enforces strict memory boundary management between Kotlin/Native's modern concurrent GC and Swift ARC when using KMP 2.1 Swift Export. Prohibits cross-runtime strong reference cycles caused by bidirectional delegation, long-lived observer callbacks, and unbounded native view bindings. Mandates explicit teardown protocols, unowned/weak Swift proxies, and CoroutineScope lifecycle anchoring to prevent cross-language heap leaks. 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-Language Retain Cycles via Strong Delegation and Unbounded Coroutine Bridge Observers) 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