How Vorssaint Separates UI and Business Logic in Its Features

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 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.

// 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 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.

// 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.

// 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.

// 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
// 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—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 with its metadata, register an initialization closure in 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →