# How Vorssaint Separates UI and Business Logic in Its Features

> Learn how Vorssaint separates UI from business logic using MVVM, lazy singletons, and SwiftUI for clean, isolated code. Enhance your feature development.

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

---

**Vorssaint uses a clean MVVM-style architecture with lazy-loaded service singletons, runtime-managed feature bindings, and pure SwiftUI views to keep visual components completely isolated from functional code.**

The `vorssaint/vorssaint-utils` repository demonstrates a disciplined approach to feature modularity. Every capability—from window management to audio routing—follows the same structural pattern: metadata definition, runtime-controlled availability, service-based business logic, and view-only presentation code. This design enables independent testing, on-demand initialization, and graceful feature toggling without side effects.

## The Four-Layer Architecture

Vorssaint's separation strategy rests on four distinct layers that communicate through narrow, well-defined interfaces.

### FeatureCatalog: Pure Metadata Definitions

All features are enumerated in [`Sources/Vorssaint/Core/FeatureCatalog.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Core/FeatureCatalog.swift) as the `AppFeature` enum. This catalog holds **only metadata**—SF Symbols, organizational groups, and user defaults keys—without a single line of UI or business logic.

```swift
// FeatureCatalog.swift (simplified concept)
enum AppFeature: String, CaseIterable {
    case windowLayout = "window.layout"
    case commandBar = "command.bar"
    // metadata properties only: symbolName, group, availabilityKey
}

```

Lines 15-33 of this file establish the complete feature taxonomy. No view imports this file to render buttons; no service imports it to perform work. It exists solely as the system's canonical feature registry.

### FeatureRuntime: Lazy Binding Hub

The runtime layer in [`Sources/Vorssaint/App/FeatureRuntime.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/App/FeatureRuntime.swift) serves as the central coordinator. It tracks which features are *available* based on user preferences and executes **initialization closures only when a feature becomes enabled**.

```swift
// FeatureRuntime.swift – availability check and lazy instantiation
final class FeatureRuntime: ObservableObject {
    static let shared = FeatureRuntime()
    @Published private(set) var revision: Int = 0  // triggers UI refresh
    
    func isAvailable(_ feature: AppFeature) -> Bool {
        // reads UserDefaults via feature.availabilityKey
    }
    
    // bindings dictionary maps features to service initialization closures
    private static var bindings: [AppFeature: () -> Void] = [:]
    
    static func whenAvailable(_ feature: AppFeature, run closure: @escaping () -> Void) {
        bindings[feature] = closure
    }
}

```

Lines 72-85 implement the critical lazy-loading mechanism. When `FeatureRuntime` detects a feature toggle, it executes the registered closure **once**, creating or awakening the corresponding service singleton. UI components can check availability freely—`FeatureRuntime.shared.isAvailable(.windowLayout)`—without triggering expensive initialization.

### Services: Self-Contained Business Logic

Each feature's functional core lives in `Sources/Vorssaint/Services/…` as a `final class` conforming to `ObservableObject`. These singletons encapsulate all side effects: system API calls, hotkey registration, preference synchronization, and state mutation.

```swift
// WindowLayoutService.swift – business logic layer
final class WindowLayoutService: ObservableObject {
    static let shared = WindowLayoutService()
    
    @Published private(set) var lastResult: WindowLayoutResult?
    @Published var currentLayout: WindowLayout = .default
    
    func syncWithPreferences() {
        // reads UserDefaults, registers AX observers, activates hotkeys
        // heavy initialization happens here, not at app launch
    }
    
    func performSnap(to edge: WindowEdge) -> WindowLayoutResult {
        // actual window manipulation via Accessibility APIs
        let result = // ... compute and execute ...
        lastResult = result
        return result
    }
}

```

Lines 23-31 declare the published interface; lines 84-98 implement the window manipulation engine. Critically, this file contains **no SwiftUI imports**. The service knows nothing about buttons, pickers, or toggle states—it only exposes `@Published` properties that the UI may observe.

### UI Layer: Pure SwiftUI Views

All visual components reside in `Sources/Vorssaint/UI/…` as standard SwiftUI structs. These views bind to values provided by `FeatureRuntime` or observe services through `@ObservedObject`/`@EnvironmentObject`, but they **never import service code directly** or invoke business methods.

```swift
// WindowGestureControls.swift – view layer
struct WindowEdgeSnapZonePicker: View {
    @Binding var disabledZonesStorage: String  // persisted string, not service reference
    
    var body: some View {
        VStack {
            Toggle("Top Edge", isOn: bindingForZone(.top))
            Toggle("Bottom Edge", isOn: bindingForZone(.bottom))
            // ... additional zones ...
        }
    }
    
    private func bindingForZone(_ zone: SnapZone) -> Binding<Bool> {
        Binding(
            get: { !disabledZonesStorage.contains(zone.rawValue) },
            set: { isEnabled in
                // mutates disabledZonesStorage only
                // FeatureRuntime persists; WindowLayoutService observes independently
            }
        )
    }
}

```

Lines 6-14 and 30-37 of this file demonstrate the strict boundary: the picker manipulates a string representation of user preferences. The actual service (which reads these preferences in `syncWithPreferences()`) remains decoupled. The view does not know `WindowLayoutService` exists.

## Data Flow: UI → Bindings → Runtime → Services

The complete interaction pattern forms a **one-directional pipeline**:

1. **User action in UI** modifies a `@Binding` or `@Published` value
2. **FeatureRuntime** persists the change and increments its `revision` counter
3. **Service singleton** observes relevant preferences and updates internal state
4. **UI refresh** occurs automatically through `ObservableObject` conformance

```swift
// Example: enabling the Window Layout feature
struct FeatureToggleRow: View {
    let feature: AppFeature
    @ObservedObject var runtime = FeatureRuntime.shared
    
    var body: some View {
        Toggle(feature.displayName, isOn: Binding(
            get: { runtime.isAvailable(feature) },
            set: { isOn in
                runtime.setAvailable(feature, to: isOn)
                // triggers bindings[feature]?() if newly enabled
            }
        ))
    }
}

```

When the toggle activates, `FeatureRuntime` executes the pre-registered closure from its `bindings` dictionary. For `windowLayout`, this closure calls `WindowLayoutService.shared.syncWithPreferences()`—initializing the service **for the first time** if needed.

## Benefits of This Separation

- **Lazy loading**: Services initialize only when used, improving cold-start performance
- **Testability**: Business logic tests require no view infrastructure; UI tests use mocked `ObservableObject` conformances
- **Feature flags**: Disabling a feature simply blocks its binding execution; no cleanup code required
- **Compilation isolation**: Changes to `WindowLayoutService` do not recompile UI files, and vice versa

## Summary

- **FeatureCatalog.swift** enumerates features with pure metadata—no UI or logic code
- **FeatureRuntime.swift** manages availability and lazily binds features to services via closure dictionary
- **Service singletons** in `Sources/Vorssaint/Services/` encapsulate all business logic as `ObservableObject` conformances
- **SwiftUI views** in `Sources/Vorssaint/UI/` bind to runtime-provided values and observe services indirectly
- The **UI → bindings → FeatureRuntime → Services** pipeline ensures unidirectional dependency flow

## Frequently Asked Questions

### How does FeatureRuntime prevent services from initializing at app launch?

`FeatureRuntime` stores initialization closures in a static `bindings` dictionary rather than executing them immediately. The closure for each feature runs only when `setAvailable(_:to: true)` is called, which typically happens via user toggle in settings. This lazy evaluation ensures that disabled features consume no memory or CPU resources.

### Can UI components access service methods directly?

Technically possible through `ObservableObject` conformance, but architecturally prohibited. Views should bind to values that `FeatureRuntime` manages or that services publish. Direct service method calls—such as `WindowLayoutService.shared.performSnap(to:)`—would violate separation and complicate testing.

### What triggers UI updates when feature availability changes?

`FeatureRuntime` increments an `@Published var revision` counter whenever availability changes. Views observing this property—like those in [`FeatureVisibilitySupport.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/FeatureVisibilitySupport.swift)—automatically refresh. Additionally, services publish their own state changes through `@Published` properties that bound views observe.

### How does a new feature integrate into this architecture?

Add the feature to [`FeatureCatalog.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/FeatureCatalog.swift) with its metadata, register an initialization closure in [`FeatureRuntime.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/FeatureRuntime.swift) using `whenAvailable(_:run:)`, implement the service singleton with `ObservableObject` conformance, and create pure SwiftUI views that bind to runtime-provided values. No existing files require modification beyond these four integration points.