# How Decompose Integrates with SwiftUI for iOS Development

> Learn how Decompose integrates with SwiftUI for iOS development. Seamlessly bridge Kotlin Multiplatform components and handle navigation with a custom @StateValue property wrapper and StackView adapter.

- Repository: [Arkadii Ivanov/decompose](https://github.com/arkivanov/decompose)
- Tags: how-to-guide
- Published: 2026-02-25

---

**Decompose integrates with SwiftUI by exposing Kotlin Multiplatform components through observable `Value` properties bridged via a custom `@StateValue` property wrapper, while navigation is handled through a `StackView` adapter that synchronizes Decompose's `ChildStack` with SwiftUI's `NavigationStack` or a UIKit fallback.**

Decompose is a Kotlin Multiplatform library that enforces separation between business-logic components and UI layers. When targeting iOS, the component logic compiles to a native framework while the user interface is built with SwiftUI. This article explains the exact integration patterns used in the `arkivanov/decompose` repository, including state observation, navigation bridging, and the required helper files.

## State Propagation with Value and StateValue

Decompose components expose mutable state through `Value<T>` (or `MutableValue<T>`) interfaces defined in the Kotlin codebase. Since SwiftUI cannot directly observe Kotlin classes, the integration relies on a bridging mechanism.

### The StateValue Property Wrapper

The sample application includes a [`StateValue.swift`](https://github.com/arkivanov/decompose/blob/main/StateValue.swift) file that defines a property wrapper converting Decompose's `Value` into an `ObservableObject` that SwiftUI can observe:

```swift
@propertyWrapper struct StateValue<T : AnyObject>: DynamicProperty {
    @ObservedObject private var obj: ObservableValue<T>
    var wrappedValue: T { obj.value }
    init(_ value: Value<T>) { obj = ObservableValue(value) }
}

```

When you apply `@StateValue` to a property in a SwiftUI view, the view automatically re-renders whenever the underlying Kotlin `Value` changes. This pattern appears throughout the iOS sample code, particularly in [`CountersView.swift`](https://github.com/arkivanov/decompose/blob/main/CountersView.swift).

## Navigation Integration with ChildStack and StackView

Decompose models navigation using `ChildStack<Configuration, Child>`, which maintains a stack of child components with associated configurations. SwiftUI's `NavigationStack` requires `Identifiable` data and specific binding patterns that differ from Decompose's API.

### Adapting to SwiftUI NavigationStack

The [`StackView.swift`](https://github.com/arkivanov/decompose/blob/main/StackView.swift) helper bridges these APIs by reading the current `ChildStack` via `@StateValue` and constructing a `NavigationStack` whose path is bound to the stack items (excluding the root). According to the source implementation in [`StackView.swift`](https://github.com/arkivanov/decompose/blob/main/StackView.swift), this adapter handles the synchronization between Decompose's navigation state and SwiftUI's declarative navigation interface.

### UIKit Fallback for Older iOS Versions

For iOS versions prior to 16.1, [`StackView.swift`](https://github.com/arkivanov/decompose/blob/main/StackView.swift) provides a fallback implementation using a UIKit `UINavigationController` wrapped in `UIViewControllerRepresentable`. This ensures that Decompose navigation works across the supported iOS version range while still allowing SwiftUI developers to declare navigation declaratively.

## Complete Implementation Example

A typical SwiftUI screen integrates both state observation and navigation. The following example from the sample project demonstrates a counter view that observes a Kotlin component:

```swift
struct CounterView: View {
    private let counter: Counter          // Kotlin component
    @StateValue private var state: CounterState

    init(_ counter: Counter) {
        self.counter = counter
        _state = StateValue(counter.state)   // subscribe to Decompose Value
    }

    var body: some View {
        VStack(spacing: 8) {
            Text(state.value.text)
            Button(action: counter.increment) { Text("Increment") }
        }
    }
}

```

For navigation, the [`CountersView.swift`](https://github.com/arkivanov/decompose/blob/main/CountersView.swift) file demonstrates using `StackView` with a `ChildStack`:

```swift
struct CountersView: View {
    private let counters: CountersComponent
    @StateValue private var stack: ChildStack<AnyObject, CounterComponent>

    init(_ counters: CountersComponent) {
        self.counters = counters
        _stack = StateValue(counters.stack)
    }

    var body: some View {
        StackView(
            stackValue: stack,
            getTitle: { $0.title },
            onBack: { index in counters.onBack(to: index) }
        ) { child in
            CounterView(child.instance)
        }
    }
}

```

## Setting Up DecomposeHelpers in Your Project

Due to limitations in publishing Kotlin/Native code that depends on SwiftUI (see issue #206 in the repository), Decompose cannot distribute the SwiftUI helpers as a binary framework. Instead, you must manually copy the helper files into your iOS project.

Copy the following files from the `DecomposeHelpers` folder of the sample app into your Xcode project:

- [`StateValue.swift`](https://github.com/arkivanov/decompose/blob/main/StateValue.swift) – The property wrapper for observing `Value<T>`
- [`StackView.swift`](https://github.com/arkivanov/decompose/blob/main/StackView.swift) – The navigation adapter for `ChildStack`
- [`ObservableValue.swift`](https://github.com/arkivanov/decompose/blob/main/ObservableValue.swift) – Internal helper used by `StateValue`

These files establish the bridge between your Kotlin components and SwiftUI views.

## Summary

- Decompose exposes state through `Value<T>` interfaces that require bridging for SwiftUI consumption.
- The `@StateValue` property wrapper in [`StateValue.swift`](https://github.com/arkivanov/decompose/blob/main/StateValue.swift) converts Kotlin `Value` instances into `ObservableObject` for SwiftUI reactivity.
- Navigation uses `ChildStack` managed by Decompose, rendered in SwiftUI through the `StackView` adapter which supports both `NavigationStack` (iOS 16+) and UIKit fallback.
- SwiftUI helpers must be copied manually into projects due to Kotlin/Native publishing limitations (issue #206).

## Frequently Asked Questions

### Can Decompose publish SwiftUI helpers as a binary framework?

No. Due to current limitations in the Kotlin/Native toolchain regarding publishing code that depends on SwiftUI, Decompose cannot distribute the SwiftUI integration helpers as a pre-built binary. According to issue #206 in the repository, you must manually copy the `DecomposeHelpers` folder containing [`StateValue.swift`](https://github.com/arkivanov/decompose/blob/main/StateValue.swift) and [`StackView.swift`](https://github.com/arkivanov/decompose/blob/main/StackView.swift) into your iOS project.

### How does StateValue handle memory management between Kotlin and Swift?

The `StateValue` property wrapper creates an `ObservableValue` object that holds a reference to the Kotlin `Value<T>`. This intermediate object conforms to `ObservableObject` and manages the subscription lifecycle. When the SwiftUI view is destroyed, the `ObservableValue` is deallocated, which properly cleans up the subscription to the Kotlin state, preventing memory leaks across the language boundary.

### What iOS versions are supported for Decompose SwiftUI integration?

The `StackView` implementation provides two rendering paths. For iOS 16.1 and later, it uses the native SwiftUI `NavigationStack` with proper path binding. For earlier iOS versions, it falls back to a `UINavigationController` wrapped via `UIViewControllerRepresentable`. This ensures compatibility with iOS versions predating SwiftUI's modern navigation APIs while leveraging the latest features on newer devices.

### Where can I find the complete sample implementation?

The complete integration examples are located in the `sample/app-ios` directory of the `arkivanov/decompose` repository. Key files include [`CountersView.swift`](https://github.com/arkivanov/decompose/blob/main/CountersView.swift) which demonstrates navigation with `StackView`, and the `DecomposeHelpers` folder containing [`StateValue.swift`](https://github.com/arkivanov/decompose/blob/main/StateValue.swift) and [`StackView.swift`](https://github.com/arkivanov/decompose/blob/main/StackView.swift). These files serve as the reference implementation for integrating Decompose components with SwiftUI.