Home > Software engineering >  Problem with passing entity between views (CoreData)
Problem with passing entity between views (CoreData)

Time:12-10

I have two views: List of trips and detail view. There is my coreData entity Relationship images

There is list if cities:

struct TripView: View {
    @EnvironmentObject var viewRouter: ViewRouter
    @FetchRequest(entity: Trip.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Trip.startDate, ascending: true)], predicate: nil, animation: .linear) var trips: FetchedResults<Trip>
    @State var tappedTrip = 0
    var body: some View {
        VStack(spacing: 20) {
            ForEach(trips, id: \.self) { trip in
                TripRow(trip: trip)
                    .environmentObject(viewRouter)
            }
        }
    }
struct TripRow: View {
    @EnvironmentObject var viewRouter: ViewRouter
    var trip: Trip
    var body: some View {
    ...
    .fullScreenCover(isPresented: $viewRouter.isShowingDetailTripView) {
            DetailTripView(trip: trip)
                .environmentObject(viewRouter)
    }
    .onTapGesture {
            viewRouter.isShowingDetailTripView.toggle()
    }

And detail view:

struct DetailTripView: View {
    var trip: Trip 
    var body: some View {...}

But when I tap on any city in detail view every time I have the first one List view image

For example "Берлингтон"
Detail view image

How I can get the corresponding value of Trip in detail view?

Tried to debug but without any results :c

CodePudding user response:

Each of your rows is monitoring $viewRouter.isShowingDetailTripView for presentation of a fullScreenCover - so when that boolean value toggles, in theory they could all be responding to it. In practice your first row is doing the job.

To correctly represent your state in the view router, it doesn't only need to know whether to show a full screen trip - it also needs to know which full screen trip to display.

The usual way is to use an Optional object in place of the boolean.

class ViewRouter: ObservableObject {
  // ...
  @Published var detailTrip: Trip?
}

When this optional value is nil, the full screen won't display.

In your row, change your action to set this value:

// in TipRow
.onTapGesture {
  viewRouter.detailTrip = trip
}

In terms of the modifier to display the full screen, I wouldn't put that in the row itself, but move it up to the list, so that you only have the one check on that value.

// in TipView
VStack {
  ForEach(...) {
  }
}
.fullScreenCover(item: $viewRouter.detailTrip) { trip in 
  DetailTripView(trip: trip)
}

This may cause an issue if you want to persist ViewRouter between sessions for state preservation, as it'll now contain a Trip object that it doesn't own. But this approach should get you much closer to how you want your UI to work.

  • Related