import SwiftUI import SwiftData @main @MainActor struct RsyncStudioApp: App { let sharedModelContainer: ModelContainer let startupStorageError: String? @StateObject private var runner: RsyncRunner @StateObject private var automation: BackgroundSyncCoordinator @StateObject private var licensing: LicensingManager init() { let schema = Schema([ SyncJob.self, SyncRun.self, ]) let configuration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) KeychainStore.delete("defaultSSHPassword") _licensing = StateObject(wrappedValue: LicensingManager()) do { let container = try ModelContainer(for: schema, configurations: [configuration]) sharedModelContainer = container startupStorageError = nil let rsyncRunner = RsyncRunner() _runner = StateObject(wrappedValue: rsyncRunner) _automation = StateObject(wrappedValue: BackgroundSyncCoordinator(modelContext: container.mainContext, runner: rsyncRunner)) } catch { let fallbackConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true) do { let fallback = try ModelContainer(for: schema, configurations: [fallbackConfiguration]) sharedModelContainer = fallback startupStorageError = "Rsync Studio could not open its persistent database and is running with temporary in-memory storage for this session. Jobs or history created now will not persist. Original error: \(error.localizedDescription)" let rsyncRunner = RsyncRunner() _runner = StateObject(wrappedValue: rsyncRunner) _automation = StateObject(wrappedValue: BackgroundSyncCoordinator(modelContext: fallback.mainContext, runner: rsyncRunner)) } catch { fatalError("Rsync Studio could not initialise persistent or temporary storage: \(error)") } } } var body: some Scene { WindowGroup { ContentView(startupStorageError: startupStorageError) .frame(minWidth: 1040, minHeight: 680) .environmentObject(runner) .environmentObject(automation) .environmentObject(licensing) .onAppear { licensing.refresh() if licensing.canUseApp { automation.start() } else { automation.stop() } } .onChange(of: licensing.canUseApp) { _, allowed in if allowed { automation.start() } else { automation.stop() } } } .modelContainer(sharedModelContainer) .commands { RsyncStudioCommands() } Window("Rsync Studio Help", id: "help") { HelpView() } .defaultSize(width: 940, height: 780) Settings { SettingsView() .environmentObject(automation) .environmentObject(licensing) } } } private struct RsyncStudioCommands: Commands { @Environment(\.openWindow) private var openWindow var body: some Commands { CommandGroup(replacing: .help) { Button("Rsync Studio Help") { openWindow(id: "help") } .keyboardShortcut("?", modifiers: [.command]) } } }