Reputation: 394
I have an EnvironmentObject "User" that is responsible for sign-in and fetching user data. It's shared between all views in my app. Once the user data is loaded, the app changes views from "LoginView" to "HomeView." This User object contains data "userId."
I have another model called "Alerts" that should be initialized in the HomeView. Alerts will fetch alerts from the server, but it needs the userId from User to actually fetch. How can I share this data from User to Alert?
struct HomeView: View {
@EnvironmentObject var user: User
@ObservedObject var alerts: Alerts
init() {
if let id = user.id {
self.alerts = Alerts(userId: id)
}
}
Above I get the error 'self' used before all stored properties are initialized
. I believe there is something fundamental here that I am not understanding about SwiftUI or my model designs are problematic (each model is capable of using an API class to make calls to the server).
Edit: I've found a similar question here:
Swiftui - How do I initialize an observedObject using an environmentobject as a parameter?
But the answer (to create a nested view) seems hacky. Is this what SwiftUI intended or is there a more proper pattern used to accomplish this?
Upvotes: 0
Views: 1674
Reputation: 394
I've found two solutions to my problem and have opted for the second one listed below.
Option 1: Nest views so that "Alerts" is initialized in the body of the parent view
This solution is detailed in this Stack Overflow question.
This solution seems a bit hacky, but maybe I'm misguided in that thinking and someone can correct this sentiment. Regardless, here is a quick code snippet:
struct HomeView: View {
@EnvironmentObject var user: User
var body: some View {
HomeInternalView(Alerts(userId: user.id))
}
}
struct HomeInternalView: View {
@ObservedObject alerts: Alerts
init(alerts) {
self.alerts = alerts
}
var body: some View {
// ...
}
}
Option 2: Make User into a singleton
For my specific case, I realized that the "User" model is needed for every view in the app as well as some models (Alerts). So instead of passing an environment object to every view, I now have a "UserService" and I can access the UserService directly from Alerts.
As per this Stack Overflow question, if a model is needed for every view in the app then using a singleton is acceptable.
class UserService: ObservableObject {
static let shared: UserService = UserService()
private init() {
// initializations ..
}
}
struct HomeView: View {
@ObservedObject var alerts: Alerts
@ObservedObject private var user = UserService.shared
init() {
self.alerts = Alerts()
}
// ...
}
class Alerts: ObservableObject {
@ObservedObject private var user = UserService.shared
// ...
}
Upvotes: 1