Which SwiftUI scene types exist on which platform (Settings, MenuBarExtra, Window, DocumentGroup, WindowGroup(for:)), and how do you share an App?

20.9.1 · L3 · Interview

Predict first

Does Settings compile for iOS?

import SwiftUI
struct A: App {
    @SceneBuilder var body: some Scene {
        Settings { Text("s") }
    }
}
  • Yes
  • No, unavailable in iOS

Predict first

Does Settings compile for macOS?

import SwiftUI
struct A: App {
    @SceneBuilder var body: some Scene {
        Settings { Text("s") }
    }
}
  • Yes
  • No

Predict first

Does WindowGroup(for:) compile for tvOS?

import SwiftUI
struct A: App {
    @SceneBuilder var body: some Scene {
        WindowGroup(for: Int.self) { _ in Text("v") }
    }
}
  • Yes
  • No, unavailable in tvOS

Predict first

Does a plain WindowGroup compile for watchOS?

import SwiftUI
struct A: App {
    @SceneBuilder var body: some Scene {
        WindowGroup { Text("a") }
    }
}
  • Yes
  • No

Mental model. A building with one front door (WindowGroup) and a few extra doors that only exist on some floors: you can ask the architect’s drawing (the compiler) which floor has which door.

Compiling the same scene for each destination gives this map. WindowGroup { } exists everywhere, including tvOS and watchOS. WindowGroup(for:) (a window per value), defaultSize and .commands are unavailable on tvOS and watchOS but compile on iOS, macOS and visionOS. Settings, MenuBarExtra and UtilityWindow are macOS only (“unavailable in iOS”, “in visionOS”, and so on). Window (a single window) is macOS, and on visionOS it needs the newer SDK baseline (“only available in visionOS 26.0 or newer” against a visionOS 1 target). DocumentGroup(newDocument:) with a FileDocument builds on iOS, macOS and visionOS; FileDocument itself is unavailable on tvOS and watchOS. ImmersiveSpace and .windowStyle(.volumetric) exist only on visionOS (Q847).

So one shared App type works only for the lowest common denominator, and platform scenes are added with #if os() inside the @SceneBuilder body. A scalable layout: a shared RootScene (or an App in a package) that returns the WindowGroup and cross-platform scenes; a Mac-only file contributing Settings and MenuBarExtra; a visionOS-only file with spaces; and per-platform @main entry points that compose them. Keep the conditionals at those edges, since a conditional scene is still ordinary @SceneBuilder content. This map is as of the 27.2 SDKs and it moves each release (the visionOS Window is the live example), so regenerate it in CI by building each destination. Not run: how the system presents the scenes (that needs a running app on each platform).

Settings is a macOS scene.

Settings is the Mac preferences window.

Value-based windows do not exist on the TV or the watch.

WindowGroup is the one scene every platform has.

Common traps

  • Putting Settings or .commands in a shared App. The iOS or tvOS build fails with “unavailable”; contribute them from a platform file.
  • Assuming WindowGroup(for:) works on every platform. It is unavailable on tvOS and watchOS.

They might follow up with

  • How do I show different first scenes per platform? Per-platform @main that composes the shared scene.
  • How is a multi-window iPad app wired? Q417 covers scenes and documents on iPad.

Related questions

  • 20.1.1 How do you structure one codebase for several Apple platforms: shared cores, UI leaves, #if os, canImport and…
  • 20.2.1 How do document-based apps and multiple scenes work on iPad, and how should state be organised?
  • 20.3.1 Mac Catalyst, native AppKit, SwiftUI for macOS, or an iPad app on the Mac: how do you choose, and what…
  • 20.9.3 How do windows, volumes and immersive spaces fit together in SwiftUI on visionOS: ornaments…

Which common view modifiers are platform specific… →