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

Kotlin Multiplatform Concurrent StateFlow and Swift Concurrency Memory Invariant

Enforces strict memory isolation and non-blocking emission boundaries when exposing Kotlin Coroutines StateFlow or SharedFlow instances across the Objective-C/Swift interoperability bridge. Prevents silent thread starvation, deadlocks, and legacy runtime freezing on Darwin platforms by mandating buffered dispatching and MainActor-isolated adapter contracts.

Prohibited Anti-Pattern: Direct Consumption of Unbuffered Cold/Hot Flows in Swift Tasks Leading to Main-Thread Deadlocks

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform Concurrent StateFlow and Swift Concurrency Memory Invariant - Enforces strict memory isolation and non-blocking emission boundaries when exposing Kotlin Coroutines StateFlow or SharedFlow instances across the Objective-C/Swift interoperability bridge. Prevents silent thread starvation, deadlocks, and legacy runtime freezing on Darwin platforms by mandating buffered dispatching and MainActor-isolated adapter contracts.
globs: shared/src/commonMain/kotlin/**/*.kt, shared/src/iosMain/kotlin/**/*.kt, iosApp/**/*.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-concurrent-stateflow-swift-isolation"
---

# Kotlin Multiplatform Concurrent StateFlow and Swift Concurrency Memory Invariant

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Direct Consumption of Unbuffered Cold/Hot Flows in Swift Tasks Leading to Main-Thread Deadlocks

## System Constraints
You are a Principal Mobile Architect enforcing bulletproof Kotlin Multiplatform memory model invariants. Never expose raw Coroutine Flows across the Swift/Obj-C interop surface without deterministic lifecycle termination, buffer isolation, and Swift MainActor execution guarantees.

## Strict Guidelines
- Engineers MUST ALWAYS wrap common StateFlow exposures destined for Swift in an explicit Skie, KMP-NativeCoroutines, or custom CFRunLoop-safe adapter to prevent blocking Darwin cooperative thread pools.
- Engineers MUST NOT synchronously await or collect unbounded Kotlin Flows directly inside Swift View bodies or @MainActor initialization paths without an explicit cancellation token or AsyncSequence bridge.
- Engineers MUST ensure shared StateFlow updates originate from or yield explicitly across non-cooperative threads using Dispatchers.Default, while isolating Swift presentation observation to @MainActor.
- Services MUST explicitly define buffer overflow strategies (e.g., BufferOverflow.DROP_OLDEST) on SharedFlow instances exported to Darwin to prevent backpressure deadlocks when Swift fails to drain the queue immediately.
- Components NEVER bridge raw C-interop completion handlers across Swift Concurrency boundaries without calling withTaskCancellationHandler to cancel the underlying Kotlin Job.

## Bad vs Good Implementations
### StateFlow Interop Between Kotlin and Swift AsyncSequence

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Directly exposing raw StateFlow forces Swift callers to use cumbersome Objective-C callback wrappers or unmanaged long-running tasks. This frequently causes unbounded retention of native Swift closures, silent memory leaks, and deadlocks if the main thread gets blocked awaiting background coroutine contexts.
```kotlin
// shared/src/iosMain/kotlin/UserViewModel.kt
package com.example.shared

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

class UserViewModel {
    private val _uiState = MutableStateFlow<String>("Initial")
    // ANTI-PATTERN: Exposing raw StateFlow to Swift directly.
    // Swift lacks coroutine context; native collectors risk blocking or failing to cancel.
    val uiState: StateFlow<String> = _uiState
}
```

#### Production Standard (Correct)
> **Standard**: Encapsulates the coroutine subscription lifecycle within a managed scope on Dispatchers.Main.immediate. It returns an explicit cancellation closure directly mapping to Swift's Task or deinit lifecycle, preventing deadlock and cross-thread lifecycle retention.
```kotlin
// shared/src/iosMain/kotlin/UserViewModel.kt
package com.example.shared

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

class UserViewModel {
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
    private val _uiState = MutableStateFlow<String>("Initial")
    val uiState: StateFlow<String> = _uiState.asStateFlow()

    // PRODUCTION INVARIANT: Provide a strictly managed disposable observer for Swift
    fun observeUiState(onChange: (String) -> Unit): () -> Unit {
        val job = scope.launch {
            _uiState.collect { value ->
                onChange(value)
            }
        }
        return { job.cancel() }
    }

    fun clear() {
        scope.cancel()
    }
}
```

### Consuming KMP State in Swift via Modern Concurrency

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Ignoring the returned cancellation token leaks the Kotlin Coroutine Job and retains the Swift View context indefinitely across view reappearance, degrading memory and starving runtime event queues.
```swift
// iosApp/iOSApp/ContentView.swift
import SwiftUI
import Shared

struct ContentView: View {
    let viewModel = UserViewModel()
    @State private var text: String = ""

    var body: some View {
        Text(text)
            .onAppear {
                // ANTI-PATTERN: Escaping closure without lifecycle cancellation handling
                _ = viewModel.observeUiState { newText in
                    self.text = newText
                }
            }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Binds observation to @MainActor, guarantees zero retain cycles via [weak self], and ties subscription lifetime deterministically to view appearance and deinitialization through explicit cancellable invocation.
```swift
// iosApp/iOSApp/ContentView.swift
import SwiftUI
import Shared

@MainActor
final class ObservableUserViewModel: ObservableObject {
    @Published private(set) var state: String = ""
    private let viewModel = UserViewModel()
    private var cancellable: (() -> Void)?

    func activate() {
        guard cancellable == nil else { return }
        cancellable = viewModel.observeUiState { [weak self] newState in
            self?.state = newState
        }
    }

    func deactivate() {
        cancellable?()
        cancellable = nil
    }

    deinit {
        cancellable?()
        viewModel.clear()
    }
}

struct ContentView: View {
    @StateObject private var model = ObservableUserViewModel()

    var body: some View {
        Text(model.state)
            .onAppear { model.activate() }
            .onDisappear { model.deactivate() }
    }
}
```

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 Concurrent StateFlow and Swift Concurrency Memory Invariant

Kotlin Multiplatform Concurrent StateFlow and Swift Concurrency Memory Invariant defines an industrial-grade behavioral standard for mobile engineering. Enforces strict memory isolation and non-blocking emission boundaries when exposing Kotlin Coroutines StateFlow or SharedFlow instances across the Objective-C/Swift interoperability bridge. Prevents silent thread starvation, deadlocks, and legacy runtime freezing on Darwin platforms by mandating buffered dispatching and MainActor-isolated adapter contracts. 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: Direct Consumption of Unbuffered Cold/Hot Flows in Swift Tasks Leading to Main-Thread 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 DriftDirect Consumption of Unbuffered Cold/Hot Flows in Swift Tasks Leading to Main-Thread 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 wrap common StateFlow exposures destined for Swift in an explicit Skie, KMP-NativeCoroutines, or custom CFRunLoop-safe adapter to prevent blocking Darwin cooperative thread pools.

Engineers MUST ALWAYS wrap common StateFlow exposures destined for Swift in an explicit Skie, KMP-NativeCoroutines, or custom CFRunLoop-safe adapter to prevent blocking Darwin cooperative thread pools.

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 synchronously await or collect unbounded Kotlin Flows directly inside Swift View bodies or @MainActor initialization paths without an explicit cancellation token or AsyncSequence bridge.

Engineers MUST NOT synchronously await or collect unbounded Kotlin Flows directly inside Swift View bodies or @MainActor initialization paths without an explicit cancellation token or AsyncSequence bridge.

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

Engineers MUST ensure shared StateFlow updates originate from or yield explicitly across non-cooperative threads using Dispatchers.Default, while isolating Swift presentation observation to @MainActor.

Engineers MUST ensure shared StateFlow updates originate from or yield explicitly across non-cooperative threads using Dispatchers.Default, while isolating Swift presentation observation to @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.
4

Services MUST explicitly define buffer overflow strategies (e.g., BufferOverflow.DROP_OLDEST) on SharedFlow instances exported to Darwin to prevent backpressure deadlocks when Swift fails to drain the queue immediately.

Services MUST explicitly define buffer overflow strategies (e.g., BufferOverflow.DROP_OLDEST) on SharedFlow instances exported to Darwin to prevent backpressure deadlocks when Swift fails to drain the queue immediately.

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 bridge raw C-interop completion handlers across Swift Concurrency boundaries without calling withTaskCancellationHandler to cancel the underlying Kotlin Job.

Components NEVER bridge raw C-interop completion handlers across Swift Concurrency boundaries without calling withTaskCancellationHandler to cancel the underlying Kotlin Job.

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 Concurrent StateFlow and Swift Concurrency Memory Invariant critical for AI coding assistants?

Enforces strict memory isolation and non-blocking emission boundaries when exposing Kotlin Coroutines StateFlow or SharedFlow instances across the Objective-C/Swift interoperability bridge. Prevents silent thread starvation, deadlocks, and legacy runtime freezing on Darwin platforms by mandating buffered dispatching and MainActor-isolated adapter contracts. 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 (Direct Consumption of Unbuffered Cold/Hot Flows in Swift Tasks Leading to Main-Thread 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

Kotlin