# How Vorssaint Manages Feature State Using Combine

> Learn how Vorssaint manages feature state with Combine's ObservableObject and @Published properties. Achieve reactive synchronization across your SwiftUI app and background services.

- Repository: [vorssaint/vorssaint-utils](https://github.com/vorssaint/vorssaint-utils)
- Tags: how-to-guide
- Published: 2026-09-06

---

**Vorssaint uses Combine's `ObservableObject` pattern with a singleton `FeatureRuntime` class that publishes state changes via `@Published`, enabling reactive synchronization across SwiftUI views and background services.**

Vorssaint's feature-state architecture centers on reactive programming through Apple's Combine framework. The implementation in `vorssaint/vorssaint-utils` eliminates manual state polling by broadcasting changes through a centralized runtime, ensuring UI components and services respond instantly to configuration updates.

## The Core Architecture: FeatureRuntime

The `FeatureRuntime` class in [[`Sources/Vorssaint/App/FeatureRuntime.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/App/FeatureRuntime.swift)](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/App/FeatureRuntime.swift) serves as the single source of truth for all feature availability.

**Key implementation details:**

- **Singleton pattern**: Accessed via `FeatureRuntime.shared`
- **`ObservableObject` conformance**: Required for Combine integration
- **`@Published private(set) var revision`**: The reactive trigger that increments on every state mutation

When any feature's availability changes, the `finishAvailabilityChange()` method increments `revision`. This single property drives the entire reactive pipeline.

```swift
// Conceptual runtime structure as implemented in FeatureRuntime.swift
final class FeatureRuntime: ObservableObject {
    static let shared = FeatureRuntime()
    
    @Published private(set) var revision: UInt = 0
    
    private init() {}
    
    func setAvailable(_ feature: AppFeature, _ available: Bool) {
        // Update UserDefaults, execute binding closures, then:
        finishAvailabilityChange()
    }
    
    private func finishAvailabilityChange() {
        revision += 1
    }
}

```

## Observing State in SwiftUI Views

SwiftUI views consume the runtime through `@ObservedObject`, creating automatic subscriptions to `revision` changes.

The [[`Sources/Vorssaint/UI/Settings/MonitorSettings.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/Settings/MonitorSettings.swift)](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/Settings/MonitorSettings.swift) implementation demonstrates this pattern for the "Combine usage and temperature" toggle and similar controls:

```swift
struct FeatureHubView: View {
    @ObservedObject private var runtime = FeatureRuntime.shared

    var body: some View {
        List {
            ForEach(AppFeature.allCases) { feature in
                Toggle(feature.title,
                       isOn: Binding(
                         get: { feature.isAvailable },
                         set: { runtime.setAvailable(feature, $0) }))
            }
        }
    }
}

```

The `@ObservedObject` wrapper establishes a Combine subscriber. When `revision` increments, SwiftUI invalidates the view and triggers a rebuild—no explicit `.onChange` handler required for basic refresh behavior.

## Binding Features to UserDefaults

Feature state persists through `@AppStorage` keys defined in [[`Sources/Vorssaint/Core/Defaults.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Core/Defaults.swift)](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Core/Defaults.swift). The runtime maintains a static `bindings` dictionary mapping features to reactive closures.

The flow works as follows:

1. User toggles a feature in UI
2. `setAvailable()` writes to the corresponding `UserDefaults` key
3. The binding closure executes, calling `syncWithPreferences()` on the relevant service
4. `finishAvailabilityChange()` increments `revision`
5. All subscribers receive the update

```swift
// Triggering a state change programmatically
FeatureRuntime.shared.setAvailable(.monitorCPU, true)

```

This single call cascades through: preference storage → service synchronization → UI refresh.

## Service-Level Combine Integration

Background services in Vorssaint import Combine directly and subscribe to runtime changes. Services like `WindowMaximizer` and `AutoQuitService` implement `syncWithPreferences()` methods that react to the `revision` stream.

A typical service implementation:

```swift
final class WindowMaximizer {
    static let shared = WindowMaximizer()
    private var cancellable: AnyCancellable?

    private init() {
        cancellable = FeatureRuntime.shared.$revision
            .sink { [weak self] _ in
                self?.syncWithPreferences()
            }
    }

    func syncWithPreferences() {
        // Read current FeatureRuntime state and UserDefaults
        // Start or stop maximizer logic based on availability
    }
}

```

The `$revision` syntax accesses the `Published` wrapper's `Publisher` interface, creating a type-erased `AnyCancellable` subscription that survives for the service's lifetime.

## Command-Bar Synchronization

The [[`Sources/Vorssaint/App/CommandBarService.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/App/CommandBarService.swift)](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/App/CommandBarService.swift) demonstrates cross-cutting reactive behavior. After each `revision` increment, `CommandBarService.shared.noteHubChange()` re-evaluates available feature rows using internal Combine publishers.

This ensures the command palette—often detached from the main view hierarchy—remains synchronized with ground-truth state without direct view-to-view communication.

## Why Combine Over Alternatives

Vorssaint's Combine-based approach provides specific advantages for feature state:

| Aspect | Implementation |
|--------|---------------|
| **Propagation speed** | Synchronous publisher delivery |
| **Memory safety** | Automatic cancellation via `AnyCancellable` |
| **Threading** | Receive on `RunLoop.main` for UI updates |
| **Composability** | Operators chain for complex derived state |

The `@Published` property wrapper abstracts away `PassthroughSubject` or `CurrentValueSubject` boilerplate while maintaining full Combine compatibility for services requiring manual subscription control.

## Summary

- **`FeatureRuntime`** acts as the centralized `ObservableObject` with a `@Published revision` counter
- **SwiftUI views** use `@ObservedObject` for automatic refresh on state changes
- **Services** subscribe directly to `$revision` via `sink` to trigger background work
- **UserDefaults** provides persistence, with reactive bindings triggering service updates
- **Single revision increment** propagates changes instantly across all system layers

## Frequently Asked Questions

### What triggers a UI refresh in Vorssaint's feature system?

The `@Published var revision` in `FeatureRuntime` triggers refreshes. Any SwiftUI view observing the runtime via `@ObservedObject` automatically rebuilds when `revision` increments, which occurs after every `setAvailable()` call through `finishAvailabilityChange()`.

### How do background services know when to start or stop?

Services subscribe to `FeatureRuntime.shared.$revision` using Combine's `sink` operator. When the revision changes, the subscription executes `syncWithPreferences()`, allowing the service to read current availability and adjust its operation accordingly.

### Why use a revision counter instead of `@Published` properties for each feature?

A single revision counter reduces publisher overhead and simplifies subscription management. Services and views rarely need granular per-feature updates; they typically re-scan all relevant preferences when any feature changes. This trade-off minimizes Combine subscription complexity across the codebase.

### Where is feature availability actually stored?

Availability persists in `UserDefaults` via `@AppStorage` keys defined in [`Sources/Vorssaint/Core/Defaults.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Core/Defaults.swift). The runtime reads and writes these keys while maintaining the reactive `ObservableObject` layer for in-memory synchronization.