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

Deterministic Swift-ARC and Kotlin/Native Boundary Lifecycle Management

Prevents cross-runtime cyclic memory deadlocks and untracked retain leaks across Kotlin/Native tracing GC and Swift Automatic Reference Counting (ARC). Mandates zero strong bidirectional references across the Objective-C/Swift bridge, deterministic delegate detachment, and explicit teardown protocols for offline-first enterprise synchronizers.

Prohibited Anti-Pattern: Unmanaged Strong Bidirectional Cross-Runtime Delegates Causing Permanent Retention and ARC Deadlocks

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Deterministic Swift-ARC and Kotlin/Native Boundary Lifecycle Management - Prevents cross-runtime cyclic memory deadlocks and untracked retain leaks across Kotlin/Native tracing GC and Swift Automatic Reference Counting (ARC). Mandates zero strong bidirectional references across the Objective-C/Swift bridge, deterministic delegate detachment, and explicit teardown protocols for offline-first enterprise synchronizers.
globs: iosApp/**/*.swift, shared/src/iosMain/**/*.kt, shared/src/commonMain/**/*Sync*.kt, **/*Bridge*.swift, **/*Delegate*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-arc-deterministic-interop-standard"
---

# Deterministic Swift-ARC and Kotlin/Native Boundary Lifecycle Management

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Unmanaged Strong Bidirectional Cross-Runtime Delegates Causing Permanent Retention and ARC Deadlocks

## System Constraints
You are a Principal Mobile Architect enforcing the Kotlin Multiplatform (KMP) Swift-ARC Deterministic Interop Standard. Kotlin/Native's garbage collector and Apple Swift ARC operate with distinct memory reclamation mechanics; neither runtime can traverse root graphs into the other across the bridge. You must enforce explicit weak referencing, non-capturing delegate bridges, and deterministic lifecycle disposal to prevent catastrophic memory retention in continuous sync engines.

## Strict Guidelines
- Engineers MUST ALWAYS enforce weak references on Swift delegates passed into Kotlin multiplatform components via a weak container or non-retaining wrapper.
- Engineers MUST NOT allow a Kotlin object to maintain a strong reference to an iOS native listener while that iOS native listener holds a strong reference to the Kotlin instance.
- Code MUST expose explicit, idempotent `close()` or `detach()` lifecycle methods on long-lived Kotlin native bridges, and call them from Swift `deinit`.
- Services MUST decouple long-running coroutines from bridge listeners by using cold Kotlin flows or channel-based queues instead of direct imperative callbacks.
- Components NEVER store direct Swift closures inside persistent Kotlin object graphs without an explicit unbind mechanism.

## Bad vs Good Implementations
### Bidirectional Sync Engine Bridge with Weak Wrapper Pattern

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin/Native holds a strong reference to the Swift object through the exported Objective-C protocol pointer. The Swift object simultaneously holds an ARC strong reference to the Kotlin object. Since neither runtime can trace into the other's heap, this causes an invisible cross-runtime cyclic memory leak, resulting in background sync execution continuing indefinitely.
```swift
// BAD: Creates an untracked cross-runtime cycle.
// Kotlin SyncCoordinator retains SyncStatusDelegate, while Swift Coordinator retains Kotlin SyncCoordinator.
import SharedKit

final class IosSyncCoordinator: SyncStatusDelegate {
    private let kotlinEngine: SyncCoordinator

    init() {
        // Direct strong reference assignment across runtime boundary
        self.kotlinEngine = SyncCoordinator()
        self.kotlinEngine.setDelegate(delegate: self)
    }

    func onSyncProgress(progress: Double) {
        print("Progress: \(progress)")
    }
    // Swift ARC cannot deallocate IosSyncCoordinator because Kotlin GC holds a root reference.
    // Kotlin GC cannot collect SyncCoordinator because Swift holds an ARC reference.
}
```

#### Production Standard (Correct)
> **Standard**: By routing calls through a `WeakSyncDelegateProxy`, the strong reference chain from Kotlin back to Swift is broken. Furthermore, implementing deterministic unbinding inside `deinit` ensures that any underlying coroutines or native handles held by the Kotlin engine are severed immediately upon Swift view controller or coordinator disposal.
```swift
// GOOD: Deterministic weak-wrapper lifecycle detachment across the ARC boundary
import SharedKit

private final class WeakSyncDelegateProxy: SyncStatusDelegate {
    private weak var target: SyncStatusDelegate?

    init(target: SyncStatusDelegate) {
        self.target = target
    }

    func onSyncProgress(progress: Double) {
        target?.onSyncProgress(progress: progress)
    }
}

final class IosSyncCoordinator: SyncStatusDelegate {
    private let kotlinEngine: SyncCoordinator
    private var proxy: WeakSyncDelegateProxy?

    init() {
        self.kotlinEngine = SyncCoordinator()
        let proxy = WeakSyncDelegateProxy(target: self)
        self.proxy = proxy
        self.kotlinEngine.setDelegate(delegate: proxy)
    }

    func onSyncProgress(progress: Double) {
        print("Deterministic progress: \(progress)")
    }

    deinit {
        // Explicit cross-runtime unbind prevents dangling finalizers
        kotlinEngine.clearDelegate()
        kotlinEngine.close()
    }
}
```

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: Deterministic Swift-ARC and Kotlin/Native Boundary Lifecycle Management

Deterministic Swift-ARC and Kotlin/Native Boundary Lifecycle Management defines an industrial-grade behavioral standard for mobile engineering. Prevents cross-runtime cyclic memory deadlocks and untracked retain leaks across Kotlin/Native tracing GC and Swift Automatic Reference Counting (ARC). Mandates zero strong bidirectional references across the Objective-C/Swift bridge, deterministic delegate detachment, and explicit teardown protocols for offline-first enterprise synchronizers. 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: Unmanaged Strong Bidirectional Cross-Runtime Delegates Causing Permanent Retention and ARC Deadlocks 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 DriftUnmanaged Strong Bidirectional Cross-Runtime Delegates Causing Permanent Retention and ARC DeadlocksCritical
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 enforce weak references on Swift delegates passed into Kotlin multiplatform components via a weak container or non-retaining wrapper.

Engineers MUST ALWAYS enforce weak references on Swift delegates passed into Kotlin multiplatform components via a weak container or non-retaining wrapper.

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 allow a Kotlin object to maintain a strong reference to an iOS native listener while that iOS native listener holds a strong reference to the Kotlin instance.

Engineers MUST NOT allow a Kotlin object to maintain a strong reference to an iOS native listener while that iOS native listener holds a strong reference to the Kotlin instance.

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 MUST expose explicit, idempotent `close()` or `detach()` lifecycle methods on long-lived Kotlin native bridges, and call them from Swift `deinit`.

Code MUST expose explicit, idempotent `close()` or `detach()` lifecycle methods on long-lived Kotlin native bridges, and call them from Swift `deinit`.

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 decouple long-running coroutines from bridge listeners by using cold Kotlin flows or channel-based queues instead of direct imperative callbacks.

Services MUST decouple long-running coroutines from bridge listeners by using cold Kotlin flows or channel-based queues instead of direct imperative callbacks.

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 Swift closures inside persistent Kotlin object graphs without an explicit unbind mechanism.

Components NEVER store direct Swift closures inside persistent Kotlin object graphs without an explicit unbind 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.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Deterministic Swift-ARC and Kotlin/Native Boundary Lifecycle Management critical for AI coding assistants?

Prevents cross-runtime cyclic memory deadlocks and untracked retain leaks across Kotlin/Native tracing GC and Swift Automatic Reference Counting (ARC). Mandates zero strong bidirectional references across the Objective-C/Swift bridge, deterministic delegate detachment, and explicit teardown protocols for offline-first enterprise synchronizers. 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 (Unmanaged Strong Bidirectional Cross-Runtime Delegates Causing Permanent Retention and ARC Deadlocks) 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