Reputation: 840
I have a LoginView that shows a RegisterView if the user is not logged in, and a ContentView if he is logged in:
struct LoginView: View {
@EnvironmentObject var userManager: UserManager
var body: some View {
Group {
if userManager.isRegistered {
ContentView()
} else {
RegisterView()
}
}
}
}
ContentView
have three ObservedObject
properties, that uses combine to fetch content from a server with rest api's.
struct ContentView: View {
@EnvironmentObject var userManager: UserManager
@StateObject var usersStore = UsersStore()
@StateObject var rolesStore = RolesStore()
@StateObject var elementsStore = ElementsStore()
var body: some View {
NavigationView {
ZStack {
Image("stell")
.resizable()
.aspectRatio(contentMode: .fit)
.opacity(0.1)
VStack(alignment: .leading, spacing: 5) {
NavigationLink(destination: UsersView(usersStore: usersStore) ) {
Text("Users")
}
NavigationLink(destination: RolesView(rolesStore: rolesStore)) {
Text("Roles")
}
NavigationLink(destination: ElementsView(elements: $elementsStore.elements)) {
Text("Elements")
}
}.font(.title).padding(20)
}.navigationBarTitle(Text("STELL"))
}
}
}
The problem I have is that I want to reference userManager
from any of the observedObjects, e.g. when the rest api's returns 401 Unauthorized
when the session token has expired. Then I want the ObservedObject to set the isRegistered
flag in userManager
to false
so the RegisterView
is automatically shown. But how can I do that? I can't set a reference to userManager
in any of the ObservedObject property initializers, because the compiler complains about property initializers is run before self
is available.
Upvotes: 8
Views: 2411
Reputation: 30773
As long as you don't mutate any state you can do that from body
, e.g.
struct ContentView: View {
@EnvironmentObject var userManager: UserManager
@StateObject var usersStore = UsersStore()
@StateObject var rolesStore = RolesStore()
@StateObject var elementsStore = ElementsStore()
func configureObjects(){
usersStore.userManager = userManager
rolesStore.userManager = userManager
elementsStore.userManager = userManager
}
var body: some View {
let _ = configureObjects()
NavigationView {
And in these objects you probably want to reset things back to nil in the didSet
of the stores (if the userManger is different object from last time) and then set things up again asynchronously or lazily when a getter is accessed from body. Also you might want to move the @StateObject
into a custom View
that actually accesses its properties, so you have less redundant invalidations of ContentView
.
Upvotes: 0
Reputation: 258441
I would use in this case dependency injection via constructor... below is show possible approach on example of UsersStore
, for others it would be the same
Changes in UsersStore
class UsersStore: ObservableObject {
var manager: UserManager
init(manager: UserManager) { // << inject UserManager via constructor
self.manager = manager
}
...
}
Changes in ContentView
struct ContentView: View {
@EnvironmentObject var userManager: UserManager
@ObservedObject var usersStore: UsersStore
init(usersStore: UsersStore) {
self.usersStore = usersStore // << inject UsersStore via contructor
}
Changes in usage
struct LoginView: View {
@EnvironmentObject var userManager: UserManager
var body: some View {
Group {
if userManager.isRegistered {
// userManager is valid here some UsersStore can be created
ContentView(usersStore: UsersStore(manager: self.userManager))
Upvotes: 8