thiezn
thiezn

Reputation: 2034

How to avoid sharing state on WindowGroup / multiscreen macOS with SwiftUI?

I am building a multiplatform app for iOS/iPadOS/macOS and would like to be able to open multiple windows of the same application on macOS.

This feature comes out of the box when you use WindowGroup and I'm able to open a new window from the status bar. However, I'm having trouble with separating state that should be shared and state that should be local to the windows.

Here's a stripped down version of my current app entry point. When opening a new window, I would like to ensure that the api state object is shared across all opened windows, but the navManager object should maintain it's own state per opened Window.


import SwiftUI

@Observable class NavManager {
    enum NavItem {
        case a
        case b
    }
    
    var currentNav: NavItem = .a
}

@Observable class Api {
    var someProperty: String = "Hello World"
}


struct NavigationView: View {
    @State private var navManager: NavManager = NavManager()
    
    var body: some View {
        switch navManager.currentNav {
        case .a:
            Text("Navigated to a")
        case .b:
            Text("Navigated to b")
        }
        Button {
            if navManager.currentNav == .a {
                navManager.currentNav = .b
            } else {
                navManager.currentNav = .a
            }
        } label: {
            Text("Toggle navigation")
        }
        Text("Some subview hierarchy that needs navManager")
            .environment(navManager)
    }
}

@main
struct MPFApp: App {
    
    @State private var api: Api = Api()
    
    var body: some Scene {
        WindowGroup {
            NavigationView()
                .environment(api)
        }
    }
}

Unfortunately with this code the state maintained in navManager is still shared between both windows, meaning navigation in one, will automatically navigate in the other window.

I couldn't find a clear description for this on the apple website or their WWDC talks. Any help would be much appreciated!

Kind regards, Thiezn

UPDATE: This functionality is now working properly. Ensure not to initialize the NavManager in your App view, but initialize it inside of NavigationView.

Upvotes: 3

Views: 273

Answers (2)

thiezn
thiezn

Reputation: 2034

This functionality is now working properly. Ensure not to initialize the NavManager in your App view, but initialize it inside of NavigationView.

Upvotes: 0

hanstar17
hanstar17

Reputation: 90

For anyone encountering the same issue, I also filed a bug report to Apple.

Meanwhile, I was able to work around the issue using non-default WindowGroup initializer:

@main
struct ExampleApp: App {
    var body: some Scene {
        // A dummy type to force root-view instantiation per window:
        WindowGroup(for: UUID.self) { _ in
            Test()
    }
}
struct Test: View {
    init() {
        Logger().debug("Test init")
    }
    
    var body: some View {
        Text("Test")
    }
}

In this way, I see "Test init" printed per each new window, which was not true with default WindowGroup { ... } initializer.

Upvotes: 2

Related Questions