It’s unclear how to use if #available(macOS 15.0, *) { without duplicating a scene for ClientApp: App
var body: some Scene {
Window("Obscura", id: WindowIds.RootWindowId) {
Group {}
}
// https://developer.apple.com/documentation/swiftui/scene/defaultlaunchbehavior(_:)
.defaultLaunchBehavior(.suppressed)
}
How do I use defaultLaunchBehavior condtionally when it is available (macOS 15+)?
I found Availability for modifiers - #4 by tera and Simplifying Backwards Compatibility in Swift | Dave DeLong , but these are from 2021. Is there a better way to do it in 2025? I’m in the process of implementing the same thing but it seems I can’t just backport a modifier like defaultLaunchBehavior but rather I have to create helper functions like launchSuppressed because the type SceneLaunchBehavior itself is restricted to macOS 15.
Also, I need to use SceneBuilder not ViewBuilder, so now I am getting errors like
Closure containing control flow statement cannot be used with result builder 'SceneBuilder
Conditional statements in a SceneBuilder can contain an if statement but not an else statement, and the condition can only perform a compiler check for availability, like in the following code:
var body: some Scene {
if #available(iOS 16, *) {
WindowGroup {
ContentView()
}
}
}
So you would have to put the two branches in separate if #available and if #unavailable blocks.
var body: some Scene {
if #available(macOS 15.0, *) {
return Window("Obscura", id: "1") {
Group {}
}
.defaultLaunchBehavior(.suppressed)
} else {
return Window("Obscura", id: "1") {
Group {}
}
}
}
I am not entirely sure why "return" is needed here compared to normal Swift control flow where you won't need it.
If you want a DRY-er version this also works:
var body: some Scene {
let window = Window("Obscura", id: "1") {
Group {}
}
if #available(macOS 15.0, *) {
return window.defaultLaunchBehavior(.suppressed)
} else {
return window
}
}
Apple provides very limited support for conditionals in SceneBuilder. In your case, it might be easier to hoist the conditional out of the body. Start by moving the “Obscura” scene to its own type:
struct ObscuraScene: Scene {
var body: some Scene {
Window("Obscura", id: "obscura") {
// ...
}
}
}
Then define an App-conforming type that applies the defaultLaunchBehavior modifier:
@available(macOS 15, *)
struct App15: App {
var body: some Scene {
ObscuraScene()
.defaultLaunchBehavior(.suppressed)
}
}
and another App-conforming type that doesn't apply the modifier:
struct AppLegacy: App {
var body: some Scene {
ObscuraScene()
}
}
Note that you do not apply the @main attribute to either of those App types. Instead, write your own main function that calls the main of the appropriate App type:
extension Backport where Content: View {
@ViewBuilder func test() -> some View {
if #available(iOS 15, *) {
// return is not needed here
content.focusable() // for test
} else {
content
}
}
}
What would maintainers of SceneBuilder have to modify should they want SceneBuilder working the same way?
Just remove the application of @SceneBuilder altogether. As the warning is telling you, the result builder has no effect, and your code isn’t even calling SceneBuilder.buildLimitedAvailability(:_) here but instead you end up getting the effect of SE-0360Opaque result types with limited availability, which is much better than that!
Hey guys really thankful for the education. Seems like I solved my original bug by relaunching the app when no window exists? The original bug being that when our app is launched at login, our intended behaviour was that there is no window shown, however our status menu “open window” feature wouldn’t work on macOS 15+, which is why I was exploring ways to fix the issue. One way was to conditionally compile with default launch behaviour, however a simpler solution ended up being using our existing URI protocol to relaunch the app when there are no windows to bring to front which worked as desired.
I’m not sure if I should create a new thread for what the best practice is to implement such a feature. Again, thank you all for the learning. We’re really trying to build our apps to prevent future macOS versions from causing our code to become buggy, so I’m also exploring simply using NSWindow and then hosting a SwiftUI Group inside that.