This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-kotlin-2-1-swift-export-memory-ownership

Kotlin 2.1 Direct Swift Export Memory Ownership & ARC/GC Boundary Standard

Prevents cross-runtime retain cycles and runtime deadlocks across Apple ARC and Kotlin/Native GC during Kotlin 2.1 Direct Swift Export. Mandates explicit ownership contracts, non-retaining delegate patterns, and deterministic dispatch isolation across foreign function boundaries.

Prohibited Anti-Pattern: Bidirectional Strong Retain Cycles and Blocking Swift Finalizers Across ARC-GC Boundaries

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin 2.1 Direct Swift Export Memory Ownership & ARC/GC Boundary Standard - Prevents cross-runtime retain cycles and runtime deadlocks across Apple ARC and Kotlin/Native GC during Kotlin 2.1 Direct Swift Export. Mandates explicit ownership contracts, non-retaining delegate patterns, and deterministic dispatch isolation across foreign function boundaries.
globs: **/*.kt, **/*.swift, **/build.gradle.kts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-kotlin-2-1-swift-export-memory-ownership"
---

# Kotlin 2.1 Direct Swift Export Memory Ownership & ARC/GC Boundary Standard

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Bidirectional Strong Retain Cycles and Blocking Swift Finalizers Across ARC-GC Boundaries

## System Constraints
You are a Principal Mobile Architect enforcing strict memory safety and deterministic lifecycle isolation across Apple ARC and Kotlin 2.1 Direct Swift Export. Kotlin/Native GC and Swift ARC operate on fundamentally different memory semantics: ARC relies on reference counts, while Kotlin/Native relies on a tracing garbage collector with distinct finalization timing. Mandate explicit bidirectional ownership contracts, prohibit circular strong references across the export boundary, and guarantee non-blocking disposal lifecycles.

## Strict Guidelines
- Engineers MUST ALWAYS break cross-runtime cycles by capturing Swift contexts as 'weak' inside closures or delegates registered with exported Kotlin types.
- Engineers MUST NOT invoke blocking Kotlin coroutine primitives (`runBlocking`) or await asynchronous exports inside Swift `deinit` finalizers to prevent native thread deadlocks.
- Services MUST expose explicit `close()` or `detach()` lifecycle methods in exported Kotlin classes that maintain listeners or state observers consumed by Swift.
- Components NEVER store direct strong references to Swift callback instances within long-lived Kotlin singletons or un-scoped coroutine scopes.
- Code SHOULD verify Kotlin instance disposal in Swift unit tests by asserting reference deallocation after parent SwiftUI view models or UIKit controllers dismiss.

## Bad vs Good Implementations
### Swift: Cross-Boundary Listener Registration with Weak Scope

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Strong capture of `self` within a closure passed across the Direct Swift Export boundary causes a cyclic retain graph: Swift retains Kotlin, Kotlin GC retains the Swift closure context, preventing ARC deallocation and resulting in a silent native memory leak.
```swift
import Shared

final class UserProfileViewModel {
    private let presenter: UserProfilePresenter

    init(presenter: UserProfilePresenter) {
        self.presenter = presenter
        // Leak: Swift self is strongly captured by Kotlin presenter callback,
        // and self holds the presenter strongly. Kotlin GC cannot track ARC cycle.
        self.presenter.subscribe { state in
            self.handleStateChange(state)
        }
    }

    private func handleStateChange(_ state: UserState) {
        // Update UI state
    }
}
```

#### Production Standard (Correct)
> **Standard**: Using `[weak self]` breaks the ARC retain cycle, allowing the Swift class to deallocate normally. The explicit `detach()` in `deinit` signals the Kotlin presenter to release native listener wrappers immediately.
```swift
import Shared

final class UserProfileViewModel {
    private let presenter: UserProfilePresenter

    init(presenter: UserProfilePresenter) {
        self.presenter = presenter
        // Invariant: Weak capture prevents ARC from being anchored by Kotlin GC
        self.presenter.subscribe { [weak self] state in
            self?.handleStateChange(state)
        }
    }

    deinit {
        // Invariant: Deterministic detachment breaks cross-boundary subscriptions
        presenter.detach()
    }

    private func handleStateChange(_ state: UserState) {
        // Update UI state safely
    }
}
```

### Kotlin: Exported Boundary Service with Explicit Lifecycle Detachment

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin collection maintains strong references to Swift closures mapped via Direct Swift Export. Without an explicit detachment method, references are kept alive for the lifetime of the Kotlin object, leading to prolonged ARC heap retention.
```kotlin
package com.example.shared

// Anti-pattern: Exported class retains callbacks indefinitely with no release hook
class UserProfilePresenter {
    private val listeners = mutableListOf<(UserState) -> Unit>()

    fun subscribe(onUpdate: (UserState) -> Unit) {
        listeners.add(onUpdate)
    }

    fun notify(state: UserState) {
        listeners.forEach { it(state) }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Implements `AutoCloseable` and provides an explicit `detach()` method to clear exported listener bridges thread-safely, ensuring Swift wrappers can be released deterministically by ARC.
```kotlin
package com.example.shared

import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized

class UserProfilePresenter : AutoCloseable {
    private val lock = SynchronizedObject()
    private var listeners = mutableListOf<(UserState) -> Unit>()

    fun subscribe(onUpdate: (UserState) -> Unit) {
        synchronized(lock) {
            listeners.add(onUpdate)
        }
    }

    fun notify(state: UserState) {
        val targets = synchronized(lock) { listeners.toList() }
        targets.forEach { it(state) }
    }

    fun detach() {
        synchronized(lock) {
            listeners.clear()
        }
    }

    override fun close() {
        detach()
    }
}
```

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 2.1 Direct Swift Export Memory Ownership & ARC/GC Boundary Standard

Kotlin 2.1 Direct Swift Export Memory Ownership & ARC/GC Boundary Standard defines an industrial-grade behavioral standard for mobile engineering. Prevents cross-runtime retain cycles and runtime deadlocks across Apple ARC and Kotlin/Native GC during Kotlin 2.1 Direct Swift Export. Mandates explicit ownership contracts, non-retaining delegate patterns, and deterministic dispatch isolation across foreign function boundaries. 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 Retain Cycles and Blocking Swift Finalizers Across ARC-GC 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 DriftBidirectional Strong Retain Cycles and Blocking Swift Finalizers Across ARC-GC 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 break cross-runtime cycles by capturing Swift contexts as 'weak' inside closures or delegates registered with exported Kotlin types.

Engineers MUST ALWAYS break cross-runtime cycles by capturing Swift contexts as 'weak' inside closures or delegates registered with exported Kotlin 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.
2

Engineers MUST NOT invoke blocking Kotlin coroutine primitives (`runBlocking`) or await asynchronous exports inside Swift `deinit` finalizers to prevent native thread deadlocks.

Engineers MUST NOT invoke blocking Kotlin coroutine primitives (`runBlocking`) or await asynchronous exports inside Swift `deinit` finalizers to prevent native thread deadlocks.

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

Services MUST expose explicit `close()` or `detach()` lifecycle methods in exported Kotlin classes that maintain listeners or state observers consumed by Swift.

Services MUST expose explicit `close()` or `detach()` lifecycle methods in exported Kotlin classes that maintain listeners or state observers consumed by Swift.

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

Components NEVER store direct strong references to Swift callback instances within long-lived Kotlin singletons or un-scoped coroutine scopes.

Components NEVER store direct strong references to Swift callback instances within long-lived Kotlin singletons or un-scoped 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

Code SHOULD verify Kotlin instance disposal in Swift unit tests by asserting reference deallocation after parent SwiftUI view models or UIKit controllers dismiss.

Code SHOULD verify Kotlin instance disposal in Swift unit tests by asserting reference deallocation after parent SwiftUI view models or UIKit controllers dismiss.

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 2.1 Direct Swift Export Memory Ownership & ARC/GC Boundary Standard critical for AI coding assistants?

Prevents cross-runtime retain cycles and runtime deadlocks across Apple ARC and Kotlin/Native GC during Kotlin 2.1 Direct Swift Export. Mandates explicit ownership contracts, non-retaining delegate patterns, and deterministic dispatch isolation across foreign function boundaries. 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 Retain Cycles and Blocking Swift Finalizers Across ARC-GC 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