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

Kotlin Multiplatform 2.1 Concurrent GC & Swift ARC Boundary Guardrail

Enforces strict memory isolation and lifecycle management across the Kotlin/Native Concurrent Mark-Sweep (CMS) GC and Apple Swift Automatic Reference Counting (ARC) boundary. Prevents cross-runtime cyclic retain graphs, dangling C-bridge pointers, and cross-thread deallocation panics during asynchronous state observation. Mandates explicit boundary nullification, weak bridge references, and MainActor-confined listener disposal.

Prohibited Anti-Pattern: Bidirectional Strong References Across KMP-Swift Bridge Causing Silent Native Heap Exhaustion

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform 2.1 Concurrent GC & Swift ARC Boundary Guardrail - Enforces strict memory isolation and lifecycle management across the Kotlin/Native Concurrent Mark-Sweep (CMS) GC and Apple Swift Automatic Reference Counting (ARC) boundary. Prevents cross-runtime cyclic retain graphs, dangling C-bridge pointers, and cross-thread deallocation panics during asynchronous state observation. Mandates explicit boundary nullification, weak bridge references, and MainActor-confined listener disposal.
globs: shared/src/commonMain/**/*.kt, shared/src/iosMain/**/*.kt, iosApp/**/*.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-arc-concurrent-gc-memory-guardrail"
---

# Kotlin Multiplatform 2.1 Concurrent GC & Swift ARC Boundary Guardrail

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Bidirectional Strong References Across KMP-Swift Bridge Causing Silent Native Heap Exhaustion

## System Constraints
You are a Principal Mobile Architect specializing in Kotlin Multiplatform and Swift runtime interop. Your mission is to eliminate native heap leaks and cross-thread invalid pointer panics caused by incompatible memory models between Kotlin/Native 2.1 Concurrent GC and Swift ARC. You must enforce explicit unbinding contracts, weak callback captures, and structured lifecycle bridges on all cross-boundary interactions.

## Strict Guidelines
- Engineers MUST NEVER pass strong Swift closure references or strong delegate implementations into Kotlin long-lived state holders without a corresponding explicit teardown/detach lifecycle.
- Engineers MUST ALWAYS mark Swift delegates held in Kotlin multiplatform modules as weak or store them using `kotlin.native.ref.WeakReference` on native targets.
- Swift clients observing Kotlin Coroutine Flows or StateFlows via bridged callbacks MUST ALWAYS capture context using `[weak self]` in closures and explicitly cancel `Job` or `Closeable` references upon view deinitialization.
- Engineers MUST NOT invoke Swift ARC objects from arbitrary Kotlin background GC worker threads; all cross-boundary notifications MUST be dispatched to the Main Dispatcher (`Dispatchers.Main.immediate` or `@MainActor`).
- Services exposing KMP components to iOS MUST implement `AutoCloseable` or provide an idempotent `dispose()`/`clear()` method that cleans all bridged callback references and cancels internal CoroutineScopes.
- Components NEVER rely on Kotlin/Native GC to reclaim Swift objects, nor on Swift ARC to reclaim Kotlin objects across cyclic reference graphs; ARC cannot inspect the Kotlin tracing heap, and Kotlin GC cannot trace ARC reference counters.

## Bad vs Good Implementations
### Swift-Kotlin Flow Subscription Lifecycle Binding

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin Native holds an exported closure retain handle while Swift holds the Kotlin Store and Cancellable. Since Swift ARC cannot traverse Kotlin's GC roots and Kotlin GC cannot alter Swift ref-counts, this forms an uncollectable cross-runtime cycle that permanently pins both objects in native heap memory.
```swift
import SwiftUI
import SharedKit

final class UserProfileViewModel: ObservableObject {
    @Published var state: UserState = .initial
    private let store: UserStore = UserStore()
    private var cancellableJob: KmpCloseable?

    func startObserving() {
        // CRITICAL BUG: Strong reference cycle across KMP/ARC boundary.
        // The Kotlin store's internal listener retains this closure (capturing strong self),
        // while self retains the store. ARC and Kotlin GC cannot break cross-runtime cycles.
        cancellableJob = store.watchState { state in
            self.state = state
        }
    }
    // cancellableJob is not cleared in deinit, causing silent native heap retention
}
```

#### Production Standard (Correct)
> **Standard**: Breaks the retain cycle by employing `[weak self]` within Swift structured concurrency (`Task`), and guarantees boundary teardown via structured cancellation and explicit invocation of `store.close()` upon `deinit` on `@MainActor`.
```swift
import SwiftUI
import SharedKit

@MainActor
final class UserProfileViewModel: ObservableObject {
    @Published private(set) var state: UserState = .initial
    private let store: UserStore
    private var observationTask: Task<Void, Never>?

    init(store: UserStore = UserStore()) {
        self.store = store
    }

    func startObserving() {
        stopObserving()
        observationTask = Task { [weak self] in
            // Consume Kotlin SKIE / AsyncSequence with weak capture
            for await nextState in self?.store.stateFlow ?? [] {
                guard let self else { return }
                self.state = nextState
            }
        }
    }

    func stopObserving() {
        observationTask?.cancel()
        observationTask = nil
    }

    deinit {
        // Explicitly cancel native bridging job
        observationTask?.cancel()
        store.close()
    }
}
```

### Kotlin/Native Weak Listener Pattern for Swift Delegates

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin's strong reference to `SessionDelegate` creates an unmanaged bridge handle that increments Swift's ARC retain count. If the Swift delegate retains `SessionManager`, a bidirectional dead retain loop is created across runtime barriers.
```kotlin
package com.example.shared

interface SessionDelegate {
    fun onSessionExpired()
}

class SessionManager {
    // CRITICAL BUG: Strong reference to an Objective-C / Swift-backed interface.
    // In Kotlin 2.1 Native, this holds an ARC-retained handle to the Swift instance.
    // If the Swift delegate retains this SessionManager, both leak indefinitely.
    var delegate: SessionDelegate? = null

    fun invalidate() {
        delegate?.onSessionExpired()
    }
}
```

#### Production Standard (Correct)
> **Standard**: Wraps the delegate in a target-specific `WeakReference` on native targets, preventing Kotlin from artificially incrementing the Swift ARC count, and exposes an explicit `AutoCloseable` lifecycle contract.
```kotlin
package com.example.shared

import kotlin.native.ref.WeakReference
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

interface SessionDelegate : Any

expect class WeakSessionDelegate(delegate: SessionDelegate) {
    fun get(): SessionDelegate?
}

class SessionManager : AutoCloseable {
    private var weakDelegate: WeakSessionDelegate? = null

    fun setDelegate(delegate: SessionDelegate) {
        this.weakDelegate = WeakSessionDelegate(delegate)
    }

    suspend fun notifyExpired() = withContext(Dispatchers.Main) {
        // Safely dereference weak reference on Main thread
        weakDelegate?.get()?.let { activeDelegate ->
            // Dispatch event to active Swift instance
        }
    }

    override fun close() {
        // Explicitly sever reference boundary
        weakDelegate = null
    }
}
```

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 2.1 Concurrent GC & Swift ARC Boundary Guardrail

Kotlin Multiplatform 2.1 Concurrent GC & Swift ARC Boundary Guardrail defines an industrial-grade behavioral standard for mobile engineering. Enforces strict memory isolation and lifecycle management across the Kotlin/Native Concurrent Mark-Sweep (CMS) GC and Apple Swift Automatic Reference Counting (ARC) boundary. Prevents cross-runtime cyclic retain graphs, dangling C-bridge pointers, and cross-thread deallocation panics during asynchronous state observation. Mandates explicit boundary nullification, weak bridge references, and MainActor-confined listener disposal. 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 References Across KMP-Swift Bridge Causing Silent Native Heap Exhaustion 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 References Across KMP-Swift Bridge Causing Silent Native Heap ExhaustionCritical
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 NEVER pass strong Swift closure references or strong delegate implementations into Kotlin long-lived state holders without a corresponding explicit teardown/detach lifecycle.

Engineers MUST NEVER pass strong Swift closure references or strong delegate implementations into Kotlin long-lived state holders without a corresponding explicit teardown/detach lifecycle.

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 ALWAYS mark Swift delegates held in Kotlin multiplatform modules as weak or store them using `kotlin.native.ref.WeakReference` on native targets.

Engineers MUST ALWAYS mark Swift delegates held in Kotlin multiplatform modules as weak or store them using `kotlin.native.ref.WeakReference` on native targets.

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 clients observing Kotlin Coroutine Flows or StateFlows via bridged callbacks MUST ALWAYS capture context using `[weak self]` in closures and explicitly cancel `Job` or `Closeable` references upon view deinitialization.

Swift clients observing Kotlin Coroutine Flows or StateFlows via bridged callbacks MUST ALWAYS capture context using `[weak self]` in closures and explicitly cancel `Job` or `Closeable` references upon view deinitialization.

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

Engineers MUST NOT invoke Swift ARC objects from arbitrary Kotlin background GC worker threads

Engineers MUST NOT invoke Swift ARC objects from arbitrary Kotlin background GC worker threads; all cross-boundary notifications MUST be dispatched to the Main Dispatcher (`Dispatchers.Main.immediate` or `@MainActor`).

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

Services exposing KMP components to iOS MUST implement `AutoCloseable` or provide an idempotent `dispose()`/`clear()` method that cleans all bridged callback references and cancels internal CoroutineScopes.

Services exposing KMP components to iOS MUST implement `AutoCloseable` or provide an idempotent `dispose()`/`clear()` method that cleans all bridged callback references and cancels internal CoroutineScopes.

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

Components NEVER rely on Kotlin/Native GC to reclaim Swift objects, nor on Swift ARC to reclaim Kotlin objects across cyclic reference graphs

Components NEVER rely on Kotlin/Native GC to reclaim Swift objects, nor on Swift ARC to reclaim Kotlin objects across cyclic reference graphs; ARC cannot inspect the Kotlin tracing heap, and Kotlin GC cannot trace ARC reference counters.

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 Concurrent GC & Swift ARC Boundary Guardrail critical for AI coding assistants?

Enforces strict memory isolation and lifecycle management across the Kotlin/Native Concurrent Mark-Sweep (CMS) GC and Apple Swift Automatic Reference Counting (ARC) boundary. Prevents cross-runtime cyclic retain graphs, dangling C-bridge pointers, and cross-thread deallocation panics during asynchronous state observation. Mandates explicit boundary nullification, weak bridge references, and MainActor-confined listener disposal. 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 References Across KMP-Swift Bridge Causing Silent Native Heap Exhaustion) 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