简体   繁体   中英

SwiftUI ObservedObject not updating the view

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)"
        }
    }
}

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
            }
        }
    }

Solved by using @StateObject instead of ObservedObject .

@StateObject var clientViewModel = ClientViewModel()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM