My problem is the following: I have an application that uses the MenuBarExtra()
scene type, which was introduced in macOS 13. But I want to have a version of the app for macOS 12, too, just without that scene.
I've looked into all sorts of permuations using @available
and if #available
but haven't been able to come up with something that compiles.
In principle, this is what I would like:
@main
struct MyApp: App {
@ObservedObject public var userSettings: UserSettings
@ObservedObject public var viewModel: ViewModel
/* more variables and initializer declarations */
var body: some Scene {
WindowGroup("Pipelines") {
PipelineListView(model: viewModel, settings: userSettings)
}
.commands {
AppCommands()
}
Settings {
SettingsView(settings: userSettings)
}
if #available(macOS 13.0, *) {
MenuBarExtra() {
MenuBarExtraContent(model: viewModel)
} label: {
MenuBarExtraLabel(model: viewModel)
}
}
}
}
This code doesn't compile, though. The compiler complains that "Closure containing control flow statement cannot be used with result builder 'SceneBuilder'".
Any ideas what I could try?
CodePudding user response:
You should be able to move the MenuBarExtra
code into a separate function, e.g.
func menBarExtra() -> some Scene {
if #available(macOS 13.0, *) {
return MenuBarExtra() {
MenuBarExtraContent(model: viewModel)
} label: {
MenuBarExtraLabel(model: viewModel)
}
}
}
and then
@main
struct MyApp: App {
@ObservedObject public var userSettings: UserSettings
@ObservedObject public var viewModel: ViewModel
/* more variables and initializer declarations */
var body: some Scene {
WindowGroup("Pipelines") {
PipelineListView(model: viewModel, settings: userSettings)
}
.commands {
AppCommands()
}
Settings {
SettingsView(settings: userSettings)
}
menBarExtra()
}
}
CodePudding user response:
SceneBuilder
doesn't allow conditionals, and there's no AnyScene
type eraser, so you can't have a single MyApp
type that returns a different Scene
definition depending on the macOS version.
Instead, create two types conforming to App
: one for macOS 13 and later, and one for older systems. Do not attach the @main
annotation to either type. Factor out the common stuff to a third type that conforms to Scene
.
Here's the common Scene
:
struct CommonScene: Scene {
@ObservedObject var userSettings: UserSettings
@ObservedObject var viewModel: ViewModel
var body: some Scene {
WindowGroup("Pipelines") {
PipelineListView(model: viewModel, settings: userSettings)
}
.commands {
AppCommands()
}
Settings {
SettingsView(settings: userSettings)
}
}
}
The App
for old systems just wraps CommonScene
:
struct OldSystemApp: App {
@StateObject var userSettings = UserSettings()
@StateObject var viewModel = ViewModel()
var body: some Scene {
CommonScene(
userSettings: userSettings,
viewModel: viewModel
)
}
}
The App
for macOS 13 adds the MenuBarExtra
:
struct App13: App {
@StateObject var userSettings = UserSettings()
@StateObject var viewModel = ViewModel()
var body: some Scene {
CommonScene(
userSettings: userSettings,
viewModel: viewModel
)
MenuBarExtra() {
MenuBarExtraContent(model: viewModel)
} label: {
MenuBarExtraLabel(model: viewModel)
}
}
}
Finally, write your own little type with a static void main()
that runs the appropriate App
type's main
depending on whether macOS 13 is available, and add @main
to that type:
@main
struct MyMain {
static void main() {
if #available(macOS 13.0, *) {
App13.main()
} else {
OldSystemApp.main()
}
}
}