Automatic property tracking (no manual @Published annotations)
Better performance (tracks only accessed properties)
Cleaner syntax
Type-safe key paths
Example:
@MainActor@Observableclass Profile { var name: String // Automatically tracked var icon: String // Automatically tracked @ObservationIgnored private var cache: [String: Data] = [:] // Explicitly excluded}
SwiftUI usage:
struct ProfileView: View { @Environment(Profile.self) private var profile // or @Bindable var profile: Profile var body: some View { Text(profile.name) // Auto-updates when name changes }}
@MainActorfinal class BrowserManager: ObservableObject { @Published var currentTab: Tab? @Published var spaces: [Space] = [] @Published var showTabClosureToast: Bool = false}
SwiftUI usage:
struct BrowserView: View { @EnvironmentObject var browserManager: BrowserManager // or @ObservedObject var browserManager: BrowserManager var body: some View { Text(browserManager.currentTab?.name ?? "") }}
Problem: Can’t pass @Observable models directly to actors (actor isolation errors).Solution: Convert to lightweight Codable snapshots (TabManager.swift:33-82):
struct SnapshotTab: Codable { let id: UUID let urlString: String let name: String let index: Int let spaceId: UUID? let isPinned: Bool // ... all persistent properties}struct Snapshot: Codable { let spaces: [SnapshotSpace] let tabs: [SnapshotTab] let folders: [SnapshotFolder] let state: SnapshotState}
Conversion flow:
// TabManager (MainActor) creates snapshotfunc persistSession() { let snapshot = Snapshot( spaces: spaces.map { /* convert to SnapshotSpace */ }, tabs: tabs.map { /* convert to SnapshotTab */ }, folders: folders.map { /* convert to SnapshotFolder */ }, state: SnapshotState(currentTabID: currentTabId, ...) ) // Send to actor Task { await persistenceActor.persist(snapshot: snapshot, generation: generation) }}
Each Profile owns a unique WKWebsiteDataStore for complete data separation (Profile.swift:82-98):
private static func createDataStore(for profileId: UUID) -> WKWebsiteDataStore { if #available(macOS 15.4, *) { let store = WKWebsiteDataStore(forIdentifier: profileId) if !store.isPersistent { print("⚠️ Data store is not persistent") } return store } else { // Fallback: shared store on older macOS return WKWebsiteDataStore.default() }}
@MainActor@Observableclass BrowserWindowState { var currentTabId: UUID? var currentSpaceId: UUID? var currentProfileId: UUID? // Active tab per space (spaceId → tabId) var activeTabForSpace: [UUID: UUID] = [:]}
Pattern: Same tab/space can be active in one window but not another.
// User switches tab in Window AwindowStateA.currentTabId = newTabId// BrowserManager syncs to TabManagerbrowserManager.setCurrentTab(for: windowStateA, tabId: newTabId)// Window B unaffectedwindowStateB.currentTabId // Still shows previous tab
@MainActor@Observablefinal class SpaceManager { var spaces: [Space] = [] var currentSpace: Space?}struct SpaceView: View { @Environment(SpaceManager.self) private var spaceManager var body: some View { Text(spaceManager.currentSpace?.name ?? "") }}