Reputation: 1235
I have an EnvironmentObject
that I'm using to generate a list
class ActivityViewModel: ObservableObject {
@Published var Activities = [Activity]()
init() {
self.Activities = ActivityService.fetchActivities()
}
}
struct Activity: Codable {
var Id: UUID
var Name: String
var Records: [Record]
init(Name:String) {
self.Id = UUID.init()
self.Name = Name
self.Records = [Record]()
}
}
When I'm using this in the view, I can use a ForEach
to access each value in the Activities
array.
My issue is when I use a NavigationLink to pass this Activity
to another view, it's not then using the EnvironmentObject
My 'parent view' code:
ForEach(0..<activities.Activities.count, id: \.self) { activity in
HStack {
ActivityListItemView(activity: self.activities.Activities[activity])
}
}
My child view code:
struct ActivityListItemView: View {
let activity: Activity
var body: some View {
NavigationLink(destination: ActivityDetail(Activity: activity)) {
HStack {
VStack {
HStack {
Text(activity.Name)
Text("\(activity.Records.count) records")
}
}
Text(">")
}
}
.buttonStyle(PlainButtonStyle())
}
}
If I then update the viewModel, I need this to replicate into the child view. But I can't figure out how to make it use the EnvironmentObject
, rather than just the variable I pass to the view.
Any help would be appreciated.
Edit: Added activity type
Upvotes: 1
Views: 142
Reputation: 14975
This approach is similar to Apple's in this tutorials.
https://developer.apple.com/tutorials/swiftui/handling-user-input
Confirm to Identifiable
and Equatable
.
struct Activity: Codable, Identifiable, Equatable {
var id: UUID
var name: String
// var Records: [Record]
init(Name:String) {
self.id = UUID()
self.name = Name
// self.Records = [Record]()
}
}
Iterate over activity.activities
and pass your view-model
and activity
to ActivityListItemView
ForEach(viewModel.activities) { activity in
HStack {
ActivityListItemView(viewModel: viewModel, activity: activity)
}
}
In ActivityListItemView
, find index of its activity
private var activityIndex: Int? {
viewModel.activities.firstIndex(of: activity)
}
Unwrap activityIndex
and pass $viewModel.activities[index]
to ActivityDetail
var body: some View {
if let index = activityIndex {
NavigationLink(destination: ActivityDetail(activity: $viewModel.activities[index])) {
...
}
...
}
}
Use @Binding
wrapper in ActivityDetail
.
struct ActivityDetail: View {
@Binding var activity: Activity
var body: some View {
...
}
}
A complete working exammple.
class ActivityViewModel: ObservableObject {
@Published var activities = [Activity]()
init() {
self.activities = [Activity(Name: "A"), Activity(Name: "B"), Activity(Name: "C"), Activity(Name: "D"), Activity(Name: "E")]
}
}
struct Activity: Codable, Identifiable, Equatable {
var id: UUID
var name: String
// var Records: [Record]
init(Name:String) {
self.id = UUID()
self.name = Name
// self.Records = [Record]()
}
}
struct ActivityView: View {
@ObservedObject var viewModel = ActivityViewModel()
var body: some View {
Button(action: {
self.viewModel.activities.append(Activity(Name: "\(Date())"))
}, label: {
Text("Button")
})
ForEach(viewModel.activities) { activity in
HStack {
ActivityListItemView(viewModel: viewModel, activity: activity)
}
}
}
}
struct ActivityListItemView: View {
@ObservedObject var viewModel: ActivityViewModel
let activity: Activity
private var activityIndex: Int? {
viewModel.activities.firstIndex(of: activity)
}
var body: some View {
if let index = activityIndex {
NavigationLink(destination: ActivityDetail(activity: $viewModel.activities[index])) {
HStack {
VStack {
HStack {
Text(activity.name)
// Text("\(activity.Records.count) records")
}
}
Text(">")
}
}
.buttonStyle(PlainButtonStyle())
}
}
}
struct ActivityDetail: View {
@Binding var activity: Activity
var body: some View {
Text("\(activity.name)")
}
}
Upvotes: 1