Reputation: 3059
I understand that EnvironmentObject
property wrapper can be used to pass around objects to views. I have a session object which I am passing around to my views. Now I have a requirement to pass this into one of my model classes (i.e., non-view). Ideally, this model (receiving the session object) is instantiated as a StateObject
.
struct CreditDetailsView: View {
@EnvironmentObject var session: Session
@StateObject var transactionsModel = TransactionsModel(token: session.token)
The code above will not work (understandably) because:
cannot use instance member 'session' within property initializer; property initializers run before 'self' is available
Any suggestions on how I can pass in the session into TransactionsModel
?
Upvotes: 10
Views: 4435
Reputation: 2373
@Finley's answer leads to an optional property.
@Jason Bobier's answer requires an additional class and still leads to an optional property.
The SwiftUI approach is to inject your StateObject
as an EnvironmentObject
which avoids the problems with the previous answers:
struct ParentView: View {
@EnvironmentObject var session: Session
var body: some View {
CreditDetailsView()
.environmentObject(TransactionsModel(token: session.token))
}
}
struct CreditDetailsView: View {
@EnvironmentObject var session: Session
@EnvironmentObject var transactionsModel: TransactionsModel
}
Upvotes: 0
Reputation: 412
The best way that I've found to do this (because you cannot have an optional StateObject) is:
struct CreditDetailsView: View {
@EnvironmentObject var session: Session
@StateObject var localModel = LocalModel()
var body: some View {
SomeChildView()
.onAppear {
localModel.transactionModel = TransactionModel(token: session.token)
}
}
class LocalModel: ObservableObject {
@Published transactionModel: TransactionModel?
}
}
Upvotes: 1
Reputation: 2762
This is an incorrect answer. Please check out the chosen answer above.
You can access session
object in init
. In this case, transactionsModel
should be done to be already initialized in any ways.
@EnvironmentObject var session: Session
@StateObject var transactionsModel = TransitionalModel(token: "")
init() {
let token = self.session.token
_transactionsModel = StateObject(wrappedValue: TransitionalModel(token: token))
}
Although it's out of the question, I am not sure if it's good way to pass something between them who look like being in different levels in the level of View.
Upvotes: -1
Reputation: 176
Try initializing the StateObject in an .onAppear()
prop to a child view, like this:
struct CreditDetailsView: View {
@EnvironmentObject var session: Session
@StateObject var transactionsModel: TransactionModel?
var body: some View {
SomeChildView()
.onAppear(perform: {
transactionModel = TransactionModel(token: session.token)
})
}
}
This way, the variable is initialized when the view renders on the screen. It doesn't matter much which child view you add the onAppear
prop to, as long as it is rendered as soon as the parent does.
Upvotes: 4