What it's the recommended way to support multiple windows without sharing state?

Yes, still figuring out a more elegant solution, but basically if you store your "TCA state" on a property @State inside a children of the view hierarchy (and not on the first level), then you can use it for constructing independent stores when instantiating windows.

import SwiftUI
import ComposableArchitecture

@main
struct TCAMultiWindowApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}

struct AppState: Identifiable, Equatable {
  let id: UUID //Only used for demo purposes
  var color: Color = .clear
}

enum AppAction: Equatable {
  case changeColor(Color)
}

struct AppEnvironment {}

let appReducer = Reducer<AppState, AppAction, AppEnvironment> { (state, action, _) in
  switch action {
  case let .changeColor(color):
    state.color = color
    return .none
  }
}

struct ContentView: View {
  
  // This is what will allow an independent state for each window
  @State var appState: AppState? = nil
  
  var body: some View {
    if let state = appState {
      
      let store = Store(
        initialState: state,
        reducer: appReducer,
        environment: AppEnvironment()
      )
      
      WithViewStore(store) { viewStore in
        VStack {
          Text(state.id.description).background(viewStore.color)
          
          Button("Change Color") {
            viewStore.send(.changeColor(.pink))
          }
        }
        .padding()
      }
      
    }
    else {
      Spacer()
        .onAppear {
          appState = AppState(id: UUID())
        }
    }
  }
}

Tried also combined with mathieutozer idea (using IdentifiedArray and ForEachStore) and also works with the same @State on children technic and even if it gives you centralised control on the windows, in my experience its rather easy to enter on recursion.

Note that I've mostly focused on making this work on macOS, so I didn't tested properly yet on iOS.

The key reference for me has being this mention on the WindowGroup documentation:

Every window created from the group maintains independent state. For example, for each new window created from the group the system allocates new storage for any State or StateObject variables instantiated by the scene’s view hierarchy.

2 Likes