I’m trying to create a view style for customizing a custom SwiftUI view that I’m developing. I declared a protocol with a makeBody function, I’m storing the style as a @State property so it can be updated, and I’m calling makeBody from inside a view. This technique closely follows what SwiftUI already does but there’s one thing that I can’t figure out.
Problem
There’s one important behavior that the first-party view styles have that I can’t replicate — that’s using @State, @Environment, or any other SwiftUI property wrapper inside the custom view style.
For example, I can implement the following custom ButtonStyle that just displays the value of the current color scheme as the button’s content.
struct MyButtonStyle: ButtonStyle {
@Environment(\.colorScheme) var colorScheme
func makeBody(configuration: Configuration) -> some View {
Text(String(describing: colorScheme))
}
}
struct MyView: View {
var body: some View {
Button("foo", action: {}).buttonStyle(MyButtonStyle())
}
}
When I preview MyView with a .dark preferred color scheme, the colorScheme property of MyButtonStyle is updated, the makeBody function is called again, and the text reads “dark”. This is all expected.

Now, let’s take a look at the custom view style that I created.
struct MyCustomStyle /* : CustomStyle */ {
@Environment(\.colorScheme) var colorScheme
@ViewBuilder func makeBody(configuration: CustomStyleConfiguration) -> some View {
Text(String(describing: colorScheme))
}
}
struct MyView: View {
@State var style = MyCustomStyle()
var body: some View {
style.makeBody(configuration: .init())
}
}
In the above scenario, when I preview MyView with a .dark preferred color scheme, the colorScheme property is not updated, and the text reads “light” even though the label's foreground color has correctly picked up the change.

Question
How to make @State and @Environment properties work in the custom view style, just as they work in ButtonStyle and in views, without introducing any boilerplate to the implementation of makeBody function?
I'm certain that it's possible because all the first-party view styles already support this, without styles conforming to View, and without them implementing the private _makeView API that manually adds dependencies to SwiftUI's graph (at least according to the module interface of SwiftUI).
What I Tried That Didn't Work
I tried initializing the view style inside a computable body of a view that wraps MyView. That doesn't work either.
struct ContentView: View {
var body: some View {
MyView(style: MyCustomStyle())
}
}
Unacceptable Answers
Wrapping the return value of the custom makeBody function in a separate view, with colorScheme property being part of that view, is a known workaround that results in correct behavior. Answers suggesting this as a workaround will not be accepted because this question is all about avoiding that.
Answers saying that it is impossible to use @Environment or @State outside of a type conforming to View will not be accepted (because it is possible, see ButtonStyle), unless they can prove that private API is what drives this.