0

I have this view which displays the name of a user. ClientViewModel has a method loadClient() which takes a Firestore document reference, loads the user and updates the model, which should update the view. But the view is not being updated. In the loadClient(), displayName is printed correctly, then I update the model, but the view is not being updated. What am I doing wrong?

struct ItemView: View {
    @EnvironmentObject var sessionStore: SessionStore
    @ObservedObject var itemViewModel: ItemViewModel
    @ObservedObject var clientViewModel = ClientViewModel()

    func onAppear() {
        itemViewModel.calcRoute() // this makes my client model null.
        clientViewModel.loadClient(documentReference: itemViewModel.item.ownerReference)
    }

    var body: some View {
        LoadingView(isShowing: $itemViewModel.isAnimating) {
            VStack {
                ItemDetailsView(itemViewModel: self.itemViewModel, clientViewModel: self.clientViewModel)
            }
            .navigationBarItems(trailing: EditButtonView(item: self.itemViewModel.item))
            .onAppear(perform: self.onAppear)
        }
    }
}

struct ItemDetailsView: View {
    @ObservedObject var itemViewModel: ItemViewModel
    @ObservedObject var clientViewModel = ClientViewModel()

    var body: some View {
        Text(clientViewModel.client?.displayName ?? "nope")
         .onAppear() {
            self.clientViewModel.loadClient(documentReference: self.itemViewModel.item.ownerReference)
        }
    }
}

class ClientViewModel: ObservableObject {
    @Published var client: Client?
    @Published var error = ""

    var firestoreService: FirestoreService = FirestoreService()

    func loadClient(documentReference: DocumentReference) {
        documentReference.addSnapshotListener { documentSnapshot, error in
          guard let document = documentSnapshot else {
            self.error = error!.localizedDescription
            return
          }

          guard let data = document.data() else {
            print("Document data was empty.")
            return
          }

            self.client = Client(document: document)
            print(self.client?.displayName) // this prints the name
        }
    }
}


class ItemViewModel: ObservableObject {
    var firestoreService = FirestoreService()

    @Published var item: Item
    @Published var distance: String = "0 km"
    @Published var travelTime = ""
    @Published var route: MKRoute?
    @Published var price: String = ""
    @Published var error: String = ""
    @Published var isAnimating: Bool = true

    init(item: Item) {
        self.item = item
    }

    // kte n servis
    func calcRoute() {
        let sourcePlacemark = MKPlacemark(coordinate: CLLocationCoordinate2D(
            latitude: item.location.coordinate.latitude,
            longitude: item.location.coordinate.longitude
        ))

        let destinationPlacemark = MKPlacemark(coordinate: CLLocationCoordinate2D(
            latitude: item.destination.coordinate.latitude,
            longitude: item.destination.coordinate.longitude
        ))

        let directionRequest = MKDirections.Request()
        directionRequest.source = MKMapItem(placemark: sourcePlacemark)
        directionRequest.destination = MKMapItem(placemark: destinationPlacemark)
        directionRequest.transportType = .automobile

        let directions = MKDirections(request: directionRequest)

        directions.calculate { (response, error) in
            self.isAnimating = false
            guard let directionResponse = response else {
                if let error = error {
                    self.error = error.localizedDescription
                }
                return
            }

            let route = directionResponse.routes[0]
            self.route = route
            self.travelTime = route.expectedTravelTime.asString(style: .abbreviated)
            self.distance = "\((route.distance / 1000).rounded()) km"
            let price = route.distance / 1000 //km
            self.price = "\(price / 10)"
        }
    }
}
4
  • 1
    When I removed all firebase-related code, it worked as expected. Commented Jun 18, 2020 at 22:43
  • But then why do i get the name printed correctly in the method? Commented Jun 18, 2020 at 22:47
  • 1
    Dispatch the update of the client property onto the main queue Commented Jun 19, 2020 at 2:43
  • Not working, the name is still not being displayed in the view. Commented Jun 19, 2020 at 9:50

2 Answers 2

2

Solved by using @StateObject instead of ObservedObject.

@StateObject var clientViewModel = ClientViewModel()
Sign up to request clarification or add additional context in comments.

Comments

1

All @Published properties must be updated on main queue to have UI updated. Here is corrected variant

    func loadClient(documentReference: DocumentReference) {
        documentReference.addSnapshotListener { documentSnapshot, error in
          guard let document = documentSnapshot else {
            DispatchQueue.main.async {                     // << here !!
                self.error = error!.localizedDescription
            }
            return
          }

          guard let data = document.data() else {
            print("Document data was empty.")
            return
          }
            DispatchQueue.main.async {                     // << here !!
                 self.client = Client(document: document)
                 print(self.client?.displayName) // this prints the name
            }
        }
    }

2 Comments

Not working, the name is still not being displayed in the view.
ok I found which line is breaking it. In my ItemView which is the parent of ItemDetailsView, onAppear() Im running this calcRoute() method. If I comment it out, it works, When I took a closer took, first the name displays and then after calcRoute is finished, it becomes nil.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.