Using environmentObject in watchOS
You can use type erasure, AnyView
in the case of SwiftUI View
.
I would refactor WKHostingController
to return AnyView
.
This seems to compile fine on my end.
class HostingController : WKHostingController<AnyView> {
override var body: AnyView {
return AnyView(ContentView().environmentObject(DataModel()))
}
}
How to inject .environmentObject() in watchOS6
The workaround from the link uses AnyView
, which is a very bad idea. It has been explained in several other questions and tweets from Apple engineers, that AnyView should only be used on leaf views, as there is a heavy performance hit otherwise.
As for the second option (put the environmentObject
inside ContentView
), it works fine. Here you have an example:
class UserData: ObservableObject {
@Published var show: Bool = true
}
struct ContentView: View {
@State var model = UserData()
var body: some View {
SubView().environmentObject(model)
}
}
struct SubView: View {
@EnvironmentObject var model: UserData
var body: some View {
VStack {
Text("Tap Me!").onTapGesture {
self.model.show.toggle()
}
if self.model.show {
Text("Hello World")
}
}
}
}
How does SwiftUI pass EnvironmentObjects between HostingController?
Here is possible approach
class AppState: ObservableObject {
static let shared = AppState() // shared instance
@Published var setting: String = "some"
}
class HostingController: WKHostingController<AnyView> {
override var body: AnyView {
let contentView = ContentView()
.environmentObject(AppState.shared) // << inject !!
return AnyView(contentView)
}
}
class HostingController2: WKHostingController<AnyView> {
override var body: AnyView {
let contentView = ContentView2()
.environmentObject(AppState.shared) // << inject !!
return AnyView(contentView)
}
}
EnvironmentObject refresh/hand over problems using NavigationLink
One of the solutions is to create a variable controlling whether to display a navigation stack.
class AppStateControl: ObservableObject {
...
@Published var isDetailActive = false // <- add this
}
Then you can use this variable to control the first NavigationLink
by setting isActive
parameter. Also you need to add .isDetailLink(false)
to all subsequent links.
First link in stack:
NavigationLink(destination: DetailView1().environmentObject(appStateControl), isActive: self.$appStateControl.isDetailActive) {
Text("Show Detail View 1")
}
.isDetailLink(false)
All other links:
NavigationLink(destination: DetailView2().environmentObject(appStateControl)) {
Text("Show Detail View 2")
}
.isDetailLink(false)
Then just set isDetailActive
to false
to pop all your NavigationLinks and return to the main view:
Button(action: {
self.appStateControl.callView = "AppMain"
self.appStateControl.isDetailActive = false // <- add this
}) {
Text("Go to main App")
}
WatchOS Using ObservableObject in Conditional in View Causing Runtime 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:)
1 Define a property for selection
@State private var selection = 0
2 Update TabView
From
TabView {
// content
}
To
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).
How do I access my model (or other state in the App struct) from a ComplicationController when using SwiftUI lifecycle?
As SomeModel
is a single state object of the app, then you can just make it shared and access explicitly, like shown below
class SomeModel: ObservableObject {
static let shared = SomeModel()
// ... other code
so
@main
struct SomeApp: App {
@StateObject var model = SomeModel.shared // << here
...
and
class ComplicationController: NSObject, CLKComplicationDataSource {
var model = SomeModel.shared // << here
...
Note: EnvironmentObject
works only in SwiftUI views, so in ComplicationController
it is useless (or even harmful)
Related Topics
What's the Difference Between a Required Initializer and a Designated Initializer
Swift Only -- Reading from Nsinputstream
Why Is There a Memory Leak at String Creation in Swift
Swift - Converting from Unsafepointer<Uint8> with Length to String
How to Define an Enum as a Subset of Another Enum's Cases
Vapor 3 Beta Example Endpoint Request
Passing Data Between Two Nsoperations
Finding the Difference in Time Between Two Nsdates Swift
How to Pass Structure by Reference
Subclassing VS Extension in Swift
How to Implement a Spritekit Timer
Case Insensitive Dictionary in Swift
Proper Model for Multiple Alamofire Requests for Multiple Websites
Calculating Angle Between Two Points on Edge of Circle Swift Spritekit
Giving Physics to Tiles of Sktilemapnode in Xcode 8
Create PDF of Dynamic Size with Typography Using Uiview Template(S)