To show my the content from my root view after successful login, I tried with ObservedObject
, and with EnvironmentObject
, to no avail.
E.g. as follows:
struct RootView: View {
@EnvironmentObject var loginManager: LoginManager
var body: some View {
Group {
if loginManager.isLoggedIn {
SegmentedView()
}
else {
WelcomeView()
}
}
}
}
class LoginManager: ObservableObject {
static let shared = LoginManager()
var cancellable = Set<AnyCancellable>()
@Published var isLoggedIn = false
...
func login(...) {
...
// on success
self.isLoggedIn = true
}
The LoginManager
is retained in the SceneDelegate
and put into the environment:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var loginManager = LoginManager.shared
...
// the view passed to window.rootUiewController via UIHostingController
let contentView = RootView().environmentObject(loginManager)
After logging in, it goes right back to my WelcomeView
. What am I missing?
EDIT
Here is a new aspect. I have a view model for the LoginView
to manage the data in date fields. When the login button is pressed, I call a login()
method in this view model.
I need to two .sink
callbacks in the view model, because I have to dismiss the loading indicator by setting a loading
flag to false.
So I cannot call self.isLoggedIn = true
directly because I am in the view model, not the LoginManager
. Instead I call
self.loginManager.isLoggedIn = true
and I suspect that this line is not working.
The connection between view model and login manager is done like this
@ObservedObject var loginManager = LoginManager.shared
However, after shifting this to the LoginManager
, I am indeed calling self.isLoggedIn
from there. It is still not working.
I have two theses:
It could be that the view is not set up correctly with Group
etc. I also tried to use @ViewBuilder
etc, no difference.
It could be that somehow there are two instances of LoginManager
, or the RootView
somehow get's reinitialised with a new instance where isLoggedIn
is false. But I have been creating Swift singletons like this for ages:
static let shared = LoginManager()
and never had any problems.
As mentioned in the comments, there is another error I encountered when switching all to @EnvironmentObject
:
Fatal error: No ObservableObject of type LoginManager found. A View.environmentObject(_:) for LoginManager may be missing as an ancestor of this view.: file SwiftUI, line 0
I think I got the error.
I assumed .sink(receiveCompletion:
always has an error because the template to which I was referring to it. But the completion block is called no matter what.
Unfortunately, if you insert a parameter error in
it will never be nil
but contain something like
Combine.Subscribers.Completion<module.CustomError>.finished
So I guess, you are not supposed to check for errors there again.