How to pass a value when opening a window in visionOS

We already covered how to open a new window by id. Now let’s expand on that by passing a value along with the id.

openWindow(id: , value:)

Set up a window group to accept a value

We start at the App level by adding a new Window Group. The id is set to FlowerWindow and we can specify the value type like so: for: FlowerItem.self . In the content closure, we get the value–which is passed as an optional binding–and pass it to our DetailView.

WindowGroup(id: "FlowerWindow", for: FlowerItem.self, content: { $value in
    DetailView(item: $value)
})

import SwiftUI

struct FlowerItem: Identifiable, Codable, Hashable {
    var id: UUID = UUID()
    let name: String
    let flower: String
}

@MainActor
@Observable
class FlowerData {
    var items: [FlowerItem] = [
        FlowerItem(name: "Lotus", flower: "🌸"),
        FlowerItem(name: "Tulip", flower: "🌷"),
        FlowerItem(name: "Daisy", flower: "🌼"),
        FlowerItem(name: "Rose", flower: "🌹"),
        FlowerItem(name: "Sunflower", flower: "🌻")
    ]
}

The content view renders this data as a list, with a button to open a window for each item.

List(flowerData.items) { item in
    HStack {
        Button(action: {
            openWindow(id: "FlowerWindow", value: item)
        }, label: {
           // button content ...
        })
    }
}

Passing the value to openWindow

In the ContentView, we can call openWindow by passing an id and a FlowerItem.

Button(action: {
    openWindow(id: "FlowerWindow", value: item)
}, label: {
    HStack {
        Text(item.flower)
        Text(item.name)
    }
    .font(.largeTitle)
})

The DetailView expects an optional binding of type FlowerItem. In this example it simply renders the specified flower.

struct DetailView: View {

    // We will pass a Flower Item when opening a window
    @Binding var item: FlowerItem?

    var body: some View {
        if let item = item {
            // Get the flower from the item and lay out 6 copies of it in a circle
            GeometryReader { geometry in
                let radius = min(geometry.size.width, geometry.size.height) / 4
                let centerX = geometry.size.width / 2
                let centerY = geometry.size.height / 2
                let flowers = Array(repeating: item.flower, count: 6)

                ForEach(0..<6) { index in
                    let angle = Angle.degrees(Double(index) / Double(flowers.count) * 360)
                    let xOffset = radius * cos(angle.radians)
                    let yOffset = radius * sin(angle.radians)

                    Text(flowers[index])
                        .font(.extraLargeTitle2)
                        .position(x: centerX + xOffset, y: centerY + yOffset)
                }
            }

        } else {
            Text("No Flowers Found!")
                .font(.title)
                .padding()
        }
    }
}

When we the click the button in a list view row, a new window will be opened for our item. These windows are sort of like “document-based” windows on macOS. We can open a single unique window for each combination of an id and a value. Calling openWindow(id: "FlowerWindow", value: item) will bring the window to the foreground. This happens when there is an open window with the matching id and value.

Note: In the example for this garden, we used FlowerItem as the value type. We can use any type we like as long as they conform to Codable and Hashable. We could also use record IDs instead of passing the item itself. Then fetch the item by ID in our view.

Video demo showing buttons in a main window opening new windows. Each new window is related to a value display on each button.

Dismissing windows by value works much the say way. I’ll explore that in more detail later.

Sample code for this post is available in Garden04 in Step Into Examples on GitHub

Support our work so we can continue to bring you new examples and articles.

Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?