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

Kotlin Multiplatform Swift Export Weak-Reference Bridging & Retain Cycle Elimination

Defines strict architectural boundaries between Apple Automatic Reference Counting (ARC) and Kotlin Garbage Collection (GC) runtimes across Swift Export. Eliminates cross-runtime bidirectional retain cycles and non-deterministic native deallocation by mandating weak-reference wrappers, explicit lifecycle unbinding, and decoupled observer interfaces.

Prohibited Anti-Pattern: Bidirectional Hard References Between Swift Closures/Delegates and Kotlin GC Objects Inducing Native-to-GC Retain Cycles and Memory Leaks

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform Swift Export Weak-Reference Bridging & Retain Cycle Elimination - Defines strict architectural boundaries between Apple Automatic Reference Counting (ARC) and Kotlin Garbage Collection (GC) runtimes across Swift Export. Eliminates cross-runtime bidirectional retain cycles and non-deterministic native deallocation by mandating weak-reference wrappers, explicit lifecycle unbinding, and decoupled observer interfaces.
globs: **/*.kt, **/*.swift, **/iosMain/**/*.kt, **/shared/**/*.kt, **/nativeInterop/**/*
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-export-weak-reference-bridging"
---

# Kotlin Multiplatform Swift Export Weak-Reference Bridging & Retain Cycle Elimination

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Bidirectional Hard References Between Swift Closures/Delegates and Kotlin GC Objects Inducing Native-to-GC Retain Cycles and Memory Leaks

## System Constraints
You are a Principal Mobile Architect enforcing absolute memory safety and zero memory leaks across Kotlin Multiplatform (KMP) and Swift/Objective-C boundaries. You prevent cross-runtime retain cycles between Apple Automatic Reference Counting (ARC) and Kotlin GC runtime boundaries, especially when using Swift Export or native interop delegate mechanisms. Mandate explicit unbinding, kotlin.native.ref.WeakReference usage, and lifecycle-scoped teardown hooks.

## Strict Guidelines
- Engineers MUST NEVER maintain a strong reference to a Swift class or native Swift callback delegate inside a long-lived Kotlin heap object without using a weak reference wrapper (e.g., `kotlin.native.ref.WeakReference` or native interop weak storage).
- Engineers MUST ALWAYS expose explicit lifecycle termination hooks (`detach()`, `dispose()`, `clearListener()`) on long-lived Kotlin presenters, view models, or domain managers that hold native delegates.
- Swift clients bridging callbacks to Kotlin MUST ALWAYS declare capture lists with `[weak self]` or `[unowned self]` when forwarding Swift closures into Kotlin interfaces.
- Engineers MUST NOT allow native platform objects (such as `UIViewController` or `UIView`) to implement Kotlin observer interfaces directly if the Kotlin object is injected into or owned by that native platform object.
- Kotlin Multiplatform code bridging asynchronous state via callbacks MUST store listener collections using explicit weak reference wrappers or explicitly unregister observers during native deinit.
- When compiling Kotlin Native with Swift Export, engineers MUST ensure any native callback abstraction allows the underlying target to be deallocated independently of the Kotlin GC collection cycle.

## Bad vs Good Implementations
### KMP Delegate Bridge Retain Cycle Prevention

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The Swift object holds a strong ARC reference to `SessionManager`, and `SessionManager` holds a strong GC reference to the Swift-implemented `SessionListener`. Neither ARC nor Kotlin GC can collect this cycle because neither runtime can inspect the internal graph of the other across the interop boundary.
```kotlin
// Kotlin Native - BAD: Direct strong reference to Swift implementation
package com.example.analytics

interface SessionListener {
    fun onSessionExpired()
}

class SessionManager {
    // Strong reference to native iOS implementation bridged via Swift Export
    var listener: SessionListener? = null

    fun notifyExpired() {
        listener?.onSessionExpired()
    }
}

// Swift - BAD: Strong bidirectional retain cycle
// class AnalyticsCoordinator: SessionListener {
//     let manager = SessionManager()
//     init() {
//         manager.listener = self // ARC owns Kotlin object, Kotlin GC roots Swift object!
//     }
// }
```

#### Production Standard (Correct)
> **Standard**: Kotlin stores `SessionListener` inside a `WeakReference`, breaking the native-to-GC retain cycle. Additionally, `clearSessionListener()` provides deterministic unbinding, allowing Swift ARC to freely deallocate the coordinator without waiting for an indeterminate Kotlin GC sweep.
```kotlin
// Kotlin Native - GOOD: Weak reference wrapper with explicit teardown
package com.example.analytics

import kotlin.native.ref.WeakReference

interface SessionListener: Any

class SessionManager {
    private var weakListener: WeakReference<SessionListener>? = null

    fun setSessionListener(listener: SessionListener) {
        weakListener = WeakReference(listener)
    }

    fun clearSessionListener() {
        weakListener = null
    }

    fun notifyExpired(action: (SessionListener) -> Unit) {
        val target = weakListener?.get()
        if (target != null) {
            action(target)
        } else {
            weakListener = null // Stale target cleared
        }
    }
}

// Swift - GOOD: Native deinit guarantees unbinding
// final class AnalyticsCoordinator: SessionListener {
//     private let manager: SessionManager
//     init(manager: SessionManager) {
//         self.manager = manager
//         manager.setSessionListener(listener: self)
//     }
//     deinit {
//         manager.clearSessionListener()
//     }
// }
```

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 Swift Export Weak-Reference Bridging & Retain Cycle Elimination

Kotlin Multiplatform Swift Export Weak-Reference Bridging & Retain Cycle Elimination defines an industrial-grade behavioral standard for mobile engineering. Defines strict architectural boundaries between Apple Automatic Reference Counting (ARC) and Kotlin Garbage Collection (GC) runtimes across Swift Export. Eliminates cross-runtime bidirectional retain cycles and non-deterministic native deallocation by mandating weak-reference wrappers, explicit lifecycle unbinding, and decoupled observer interfaces. 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 Hard References Between Swift Closures/Delegates and Kotlin GC Objects Inducing Native-to-GC Retain Cycles and Memory Leaks 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 Hard References Between Swift Closures/Delegates and Kotlin GC Objects Inducing Native-to-GC Retain Cycles and Memory LeaksCritical
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 maintain a strong reference to a Swift class or native Swift callback delegate inside a long-lived Kotlin heap object without using a weak reference wrapper (e.g., `kotlin.native.ref.WeakReference` or native interop weak storage).

Engineers MUST NEVER maintain a strong reference to a Swift class or native Swift callback delegate inside a long-lived Kotlin heap object without using a weak reference wrapper (e.g., `kotlin.native.ref.WeakReference` or native interop weak storage).

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 expose explicit lifecycle termination hooks (`detach()`, `dispose()`, `clearListener()`) on long-lived Kotlin presenters, view models, or domain managers that hold native delegates.

Engineers MUST ALWAYS expose explicit lifecycle termination hooks (`detach()`, `dispose()`, `clearListener()`) on long-lived Kotlin presenters, view models, or domain managers that hold native delegates.

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 bridging callbacks to Kotlin MUST ALWAYS declare capture lists with `[weak self]` or `[unowned self]` when forwarding Swift closures into Kotlin interfaces.

Swift clients bridging callbacks to Kotlin MUST ALWAYS declare capture lists with `[weak self]` or `[unowned self]` when forwarding Swift closures into Kotlin interfaces.

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 allow native platform objects (such as `UIViewController` or `UIView`) to implement Kotlin observer interfaces directly if the Kotlin object is injected into or owned by that native platform object.

Engineers MUST NOT allow native platform objects (such as `UIViewController` or `UIView`) to implement Kotlin observer interfaces directly if the Kotlin object is injected into or owned by that native platform object.

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

Kotlin Multiplatform code bridging asynchronous state via callbacks MUST store listener collections using explicit weak reference wrappers or explicitly unregister observers during native deinit.

Kotlin Multiplatform code bridging asynchronous state via callbacks MUST store listener collections using explicit weak reference wrappers or explicitly unregister observers during native 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.
6

When compiling Kotlin Native with Swift Export, engineers MUST ensure any native callback abstraction allows the underlying target to be deallocated independently of the Kotlin GC collection cycle.

When compiling Kotlin Native with Swift Export, engineers MUST ensure any native callback abstraction allows the underlying target to be deallocated independently of the Kotlin GC collection cycle.

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 Swift Export Weak-Reference Bridging & Retain Cycle Elimination critical for AI coding assistants?

Defines strict architectural boundaries between Apple Automatic Reference Counting (ARC) and Kotlin Garbage Collection (GC) runtimes across Swift Export. Eliminates cross-runtime bidirectional retain cycles and non-deterministic native deallocation by mandating weak-reference wrappers, explicit lifecycle unbinding, and decoupled observer interfaces. 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 Hard References Between Swift Closures/Delegates and Kotlin GC Objects Inducing Native-to-GC Retain Cycles and Memory Leaks) 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