1

UIViewRepresentable.updateUIView is not called when an ObservableObject is changed that this view and its parent view depend on.

I have a GameView that has "@State var locationManager: LocationManager" which is passed to my MapView as a binding. The MapView conforms to UIViewRepresentable protocol. LocationManager conforms, among other things, to ObservableObject and has the following conformance-related code:

var didChange = PassthroughSubject<locationmanager, Never>()  


    var lastKnownLocation: CLLocation {  
        didSet {  

            // Propagate the update to the observers.  
            didChange.send(self)  
            print("Finished propagating lastKnownLocation to the observers.")  
        }  
}

I suppose that GameView and consequently MapView must be updated every time LocationManager.lastKnownLocation changes. In practice I only see MapView.updateUIView() called when I exit the app per home button. At this point control gets in updateUIView() and when I open the app again (not compile-install-run), I get the update. This also happens once short after the GameView() was presented.

Is my understanding of how SwiftUI works wrong or is it some bug? How do I get it right?

struct GameView: View { 
    @State var locationManager: LocationManager 


    var body: some View { 
        MapView(locationManager: $locationManager)
    }
}

struct MapView: UIViewRepresentable {

    @Binding var locationManager: LocationManager

   func makeUIView(context: Context) -> GMSMapView{ ... }

   func updateUIView(_ mapView: GMSMapView, context: Context) { ... }

}

class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {

    var didChange = PassthroughSubject<LocationManager, Never>()

    var lastKnownLocation: CLLocation {
        didSet {


            // Propagate the update to the observers.
            didChange.send(self)
            print("Finished propagating lastKnownLocation to the observers.")
        }
    }

    ...

}

I expect that every tim LocationManager.lastKnownLocation changes, MapView.updateUIView() is called. Practically the call only happens when I exit the app per home button (and enter again)

Roman Reimche
  • 143
  • 1
  • 13

1 Answers1

0

change MapView like this

struct MapView: UIViewRepresentable {
  @State var locationManager = LocationManager()

  func makeUIView(context: Context) -> GMSMapView{
    let view = GMSMapView(frame: .zero)
    ...
    willAppear(context)
    return view
  }

  func updateUIView(_ mapView: GMSMapView, context: Context) { ... }

  func makeCoordinator() -> MapView.Coordinator {
    return Coordinator()
  }

  static func dismantleUIView(_ uiView: GMSMapView, coordinator: MapView.Coordinator) {
    coordinator.cancellable.removeAll()
  }

  class Coordinator {
    var cancellable = Set<AnyCancellable>()
  }
}

extension MapView {
  func willAppear(_ context: Context) {
    locationManager.didChange.sink{
      print("location: \($0)")
    }.store(in: &context.coordinator.cancellable)

    locationManager.startUpdating()
  }
}

I think this method locationManager.startUpdating(), locationManager.stopUpdating() need call another class.

Hun
  • 3,652
  • 35
  • 72