Skip to content

How to open a new window in SwiftUI app

Published: at 06:42 AM

When you target and environment that support opening multiple windows, it’s great opportunity to enable opening new window for multitasking purpose. So, recently when developing Spatial Counter for visionOS, I want to show multiple window of counter.

This will be useful for user when they want to count multiple things at once. Also a great opportunity for me to learn how multiple window works outside iOS platform which I usually develop an app.

Open window using its identifier

In typical SwiftUI application, we will have this code as entry point to our application.

import SwiftUI

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

On a platform where multiple window is allowed, we can create multiple WindowGroup inside the scene body.

For example if we want a main window, we can give an identifier to our window group

import SwiftUI

@main
struct Spatial_CounterApp: App {
  var body: some Scene {
    WindowGroup(id: "main-view") {
      ContentView()
    }

    WindowGroup(id: "settings-view") {
      SettingsView()
    }
  }
}

Then in other view, we can use @Environment(\.openWindow) to open specific window

import SwiftUI

@main
struct SomeOtherView: View {
  @Environment(\.openWindow) var openWindow

  var body: some View {
    Button("Open Settings") {
      self.openWindow(id: "settings-view") // open SettingsView() as new window
    }
  }
}

Open window using custom value

We can also open a new window by passing binding of a value. This value should be conform to Hashable and Decodable. This can be useful for case where you want show more detailed information in a new window. You need to pass the value into openWindow(value:) environment


struct Contact: Identifiable {
  var name: String
  var phoneNumber: String
  var address: String
  var profilePictureURL: URL?
}

extension Contact: Codable, Hashable {}

struct ContactDetail: View {
  @Environment(\.openWindow) var openWindow
  @Bindable var contact: Contact

  var body: some View {
    // show contact cell
    Menu {
      Button {
        self.openWindow(value: self.contact)
      } label: {
        Label("Open in New Window", systemImage: "rectangle.fill.on.rectangle.fill")
      }
    } label: {
      Text("Options")
    }
  }
}

Then in your app entrypoint, create a WindowGroup that accept the value

import SwiftUI

@main
struct Spatial_CounterApp: App {
  var body: some Scene {
    WindowGroup(id: "main-view") {
      ContentView()
    }

    WindowGroup(id: "settings-view") {
      SettingsView()
    }
    WindowGroup(for: Contact.self) { contact in
      ContactWindowView(contact: contact)
    }
  }
}

References: