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

Deterministic KMP Swift-Export ARC Pinning and Cross-Runtime Lifecycle Guardrail

Enforces strict memory boundary invariants between Kotlin/Native GC and Swift ARC during cross-runtime invocations and Swift-Export bindings. Prevents uncollected cross-runtime retain cycles, unsafe cross-thread ARC object releases, and native thread deadlocks when bridging Kotlin Coroutines with Swift Concurrency.

Prohibited Anti-Pattern: Cross-Runtime Retain Cycles and Unpinned Foreign Pointer Deallocation Across Asynchronous Boundaries

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Deterministic KMP Swift-Export ARC Pinning and Cross-Runtime Lifecycle Guardrail - Enforces strict memory boundary invariants between Kotlin/Native GC and Swift ARC during cross-runtime invocations and Swift-Export bindings. Prevents uncollected cross-runtime retain cycles, unsafe cross-thread ARC object releases, and native thread deadlocks when bridging Kotlin Coroutines with Swift Concurrency.
globs: **/*iosMain*/**/*.kt, **/*appleMain*/**/*.kt, **/swiftExport/**/*.kt, iosApp/**/*.swift, **/*Bridge*.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-export-arc-threading-guardrail"
---

# Deterministic KMP Swift-Export ARC Pinning and Cross-Runtime Lifecycle Guardrail

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Cross-Runtime Retain Cycles and Unpinned Foreign Pointer Deallocation Across Asynchronous Boundaries

## System Constraints
You are a Principal Mobile Architect specializing in Kotlin Multiplatform (KMP) and Swift Cinterop. You enforce zero-leak memory invariants and safe cross-language thread models across Kotlin/Native GC and Apple Swift Automatic Reference Counting (ARC). You strictly reject cyclic cross-boundary strong references, unmanaged pointer leaks, and blocking interop calls across concurrent runtimes.

## Strict Guidelines
- Engineers MUST ALWAYS break cross-runtime strong reference cycles by injecting weak wrappers or using explicitly scoped unmanaged lifecycle tokens across Swift-Kotlin boundaries.
- Engineers MUST NOT capture strong Kotlin object references within escaping Swift closures that are registered as listeners or callbacks in long-lived Kotlin singletons.
- Code SHOULD enforce deterministic disposal of Kotlin `StableRef` handles inside Swift `deinit` or defer blocks using `dispose()` exactly once.
- Services MUST route cross-boundary coroutine completions to thread-confined dispatchers (e.g., MainActor / Dispatchers.Main) to prevent ARC deallocation on non-cooperative background worker threads.
- Components NEVER execute synchronous cross-runtime lock acquisitions or `runBlocking` calls on threads managed by Swift GCD/Task executors.

## Bad vs Good Implementations
### Swift Bridge Callback Subscription with Weak Reference & ARC Lifecycle Disconnection

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin's Swift-Export generates wrapper bridges that retain closure arguments via ARC and Kotlin GC tracking. Passing an escaping closure capturing 'self' strongly without explicit token cancellation creates a cross-runtime cycle that neither Kotlin's tracing GC nor Swift's ARC can resolve.
```swift
import SharedCore

final class AccountViewModel: ObservableObject {
    private let accountService: SharedAccountService
    
    init(service: SharedAccountService = SharedAccountService()) {
        self.accountService = service
        // ANTI-PATTERN: Strong capture of Swift 'self' in escaping Kotlin callback,
        // and Kotlin service strongly retains the observer block across the Cinterop boundary.
        // If accountService is retained or single-instanced, AccountViewModel leaks permanently.
        self.accountService.observeAccountUpdates { account in
            self.handleAccount(account)
        }
    }
    
    private func handleAccount(_ account: SharedAccount) {
        // UI mutation
    }
}
```

#### Production Standard (Correct)
> **Standard**: The good pattern breaks the cycle by weakly capturing self, safely marshaling asynchronous emissions to Swift's `@MainActor`, and holding a deterministic cancellation handle that is invalidated during `deinit`, guaranteeing predictable reclamation across both runtimes.
```swift
import SharedCore

@MainActor
final class AccountViewModel: ObservableObject {
    private let accountService: SharedAccountService
    private var observationCancellable: SharedCancellable?
    
    init(service: SharedAccountService = SharedAccountService()) {
        self.accountService = service
        bindObservers()
    }
    
    private func bindObservers() {
        // INVARIANT FIX: Capture weak self and store an explicit Kotlin-managed Cancellable token.
        self.observationCancellable = accountService.observeAccountUpdates { [weak self] account in
            Task { @MainActor [weak self] in
                self?.handleAccount(account)
            }
        }
    }
    
    private func handleAccount(_ account: SharedAccount) {
        // Thread-safe UI update confined to MainActor
    }
    
    deinit {
        // Explicitly tear down the Cinterop bridge handle on object destruction
        observationCancellable?.cancel()
    }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Deterministic KMP Swift-Export ARC Pinning and Cross-Runtime Lifecycle Guardrail

Deterministic KMP Swift-Export ARC Pinning and Cross-Runtime Lifecycle Guardrail defines an industrial-grade behavioral standard for mobile engineering. Enforces strict memory boundary invariants between Kotlin/Native GC and Swift ARC during cross-runtime invocations and Swift-Export bindings. Prevents uncollected cross-runtime retain cycles, unsafe cross-thread ARC object releases, and native thread deadlocks when bridging Kotlin Coroutines with Swift Concurrency. 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 and Unpinned Foreign Pointer Deallocation Across Asynchronous 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 DriftCross-Runtime Retain Cycles and Unpinned Foreign Pointer Deallocation Across Asynchronous 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 strong reference cycles by injecting weak wrappers or using explicitly scoped unmanaged lifecycle tokens across Swift-Kotlin boundaries.

Engineers MUST ALWAYS break cross-runtime strong reference cycles by injecting weak wrappers or using explicitly scoped unmanaged lifecycle tokens across Swift-Kotlin 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 within escaping Swift closures that are registered as listeners or callbacks in long-lived Kotlin singletons.

Engineers MUST NOT capture strong Kotlin object references within escaping Swift closures that are registered as listeners or callbacks in long-lived Kotlin singletons.

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 disposal of Kotlin `StableRef` handles inside Swift `deinit` or defer blocks using `dispose()` exactly once.

Code SHOULD enforce deterministic disposal of Kotlin `StableRef` handles inside Swift `deinit` or defer blocks using `dispose()` exactly once.

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 route cross-boundary coroutine completions to thread-confined dispatchers (e.g., MainActor / Dispatchers.Main) to prevent ARC deallocation on non-cooperative background worker threads.

Services MUST route cross-boundary coroutine completions to thread-confined dispatchers (e.g., MainActor / Dispatchers.Main) to prevent ARC deallocation on non-cooperative background worker threads.

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 execute synchronous cross-runtime lock acquisitions or `runBlocking` calls on threads managed by Swift GCD/Task executors.

Components NEVER execute synchronous cross-runtime lock acquisitions or `runBlocking` calls on threads managed by Swift GCD/Task executors.

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 Deterministic KMP Swift-Export ARC Pinning and Cross-Runtime Lifecycle Guardrail critical for AI coding assistants?

Enforces strict memory boundary invariants between Kotlin/Native GC and Swift ARC during cross-runtime invocations and Swift-Export bindings. Prevents uncollected cross-runtime retain cycles, unsafe cross-thread ARC object releases, and native thread deadlocks when bridging Kotlin Coroutines with Swift Concurrency. 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 and Unpinned Foreign Pointer Deallocation Across Asynchronous 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

Deterministic