I use an ObservableObject
to keep the state of whether a user is subscribed to my app or not, and based on the subscription status, show different views. This worked fine prior to Xcode 13 and WatchOS 8, but now this is causing a runtime error of runtime: SwiftUI: Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
And, the binding does not update per the error. This occurs on both Xcode 13.1 and 13.2b2
This code below reproduces the error:
struct MultiPageView: View {
@ObservedObject var subscribed = SubscribedModel.shared
var body: some View {
if subscribed.value {
TabView {
ViewOne()
ViewTwo()
ViewThree()
ToggleView()
}
.tabViewStyle(PageTabViewStyle())
} else {
TabView {
ViewOne()
ToggleView()
}
.tabViewStyle(PageTabViewStyle())
}
}
}
struct ToggleView: View {
@ObservedObject var subscribed = SubscribedModel()
var body: some View {
Toggle(isOn: $subscribed.value) {
Text("Subscribed")
}
}
}
class SubscribedModel: ObservableObject {
public static let shared = SubscribedModel.shared
@Published var value: Bool = false
}
I am only listing ViewOne
for brevity, but ViewTwo
and ViewThree
are the same with different text:
struct ViewOne: View {
var body: some View {
Text("View One")
.padding()
}
}
If you navigate to the ToggleView()
, and switch the toggle, the error pops immediately. Any suggestions to fix this?
Update per @LoremIpsum comment:
struct MultiPageView: View {
@StateObject var subscribed = SubscribedModel()
var body: some View {
if subscribed.value {
TabView {
ViewOne()
ViewTwo()
ViewThree()
ToggleView(subscribed: $subscribed.value)
}
.tabViewStyle(PageTabViewStyle())
} else {
TabView {
ViewOne()
ToggleView(subscribed: $subscribed.value)
}
.tabViewStyle(PageTabViewStyle())
}
}
}
struct ToggleView: View {
@Binding var subscribed: Bool
var body: some View {
Toggle(isOn: $subscribed) {
Text("Subscribed")
}
}
}
It is now switching between the TabViews
, but the error still remains, and is showing up immediately. Deleted DerivedData and cleaned build folder. Any thoughts?
I will add that this same basic code is running fine on iOS 15. It is just WatchOS that is popping the error.
I was having the same issue for a long time, and this is still happening on Xcode 13.2.1.
Seems to be an issue with TabView
on watchOS, because if you replace the TabView for another View the error is gone.
The solution is to use the initialiser for TabView
with a selection
value: init(selection:content:)
@State private var selection = 0
TabView {
// content
}
TabView(selection: $selection) {
// content
}
Updating your code would look like this:
struct MultiPageView: View {
@StateObject var subscribed = SubscribedModel()
@State private var selection = 0
var body: some View {
if subscribed.value {
TabView(selection: $selection) {
ViewOne()
ViewTwo()
ViewThree()
ToggleView(subscribed: $subscribed.value)
}
.tabViewStyle(PageTabViewStyle())
} else {
TabView(selection: $selection) {
ViewOne()
ToggleView(subscribed: $subscribed.value)
}
.tabViewStyle(PageTabViewStyle())
}
}
}
Basically just defining a @State
property for TabView.selection
, and using it on both your TabViews (using separated properties would also work).