Update polyline when user change current location

I created a map that show routes and directions from current location to the destination, I want to achieve following:

I want to always change beginning of polyline on the current location, I want to always recalculate if user lost their first path. This is my code:

struct MapView: UIViewRepresentable {
typealias UIViewType = MKMapView

@Binding var directions: [String]

func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator()
}

func makeUIView(context: Context) -> MKMapView {
    let mapView = MKMapView()
    
    mapView.delegate = context.coordinator
    mapView.showsUserLocation = true
    
    let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 43.58393, longitude: 19.52201), span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
    
    mapView.setRegion(region, animated: true)
    
    let locationManager = CLLocationManager()
    locationManager.requestWhenInUseAuthorization()
    
    if CLLocationManager.locationServicesEnabled() {
        locationManager.delegate = context.coordinator
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()
    }
    
    // Prijepolje
    let p2 = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 43.000000, longitude: -75.000000))
    
    let request = MKDirections.Request()
    
    request.source = MKMapItem.forCurrentLocation()
    request.destination = MKMapItem(placemark: p2)
    request.transportType = .automobile
    
    let directions = MKDirections(request: request)
    
    directions.calculate { response, error in
        guard let route = response?.routes.first else {
            return
        }
        mapView.addAnnotations([p2])
        mapView.addOverlay(route.polyline)
        mapView.setVisibleMapRect(route.polyline.boundingMapRect, edgePadding: UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20), animated: true)
        self.directions = route.steps.map { $0.instructions }.filter {
            !$0.isEmpty
        }
    }
    
    return mapView
}

func updateUIView(_ uiView: MKMapView, context: Context) {
    
}

class MapViewCoordinator: NSObject, MKMapViewDelegate, CLLocationManagerDelegate {
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer  = MKPolylineRenderer(overlay: overlay)
        
        renderer.strokeColor = .blue
        renderer.lineWidth = 5
        return renderer
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            // Do something with the updated location if needed
        }
    }
}
}

This is my current behaviour

I'm using Xcode 14.2, and iPhone 11.

Anyone?

Essentially, you need a few things:

  1. A method that builds a polyline
  2. Track whether user location is changed. Once it's changed, call a method from previous step.

To achieve this I wrote a method:

func getDirections(to destination: CLLocationCoordinate2D) {
        let sourcePlaceMark = MKPlacemark(coordinate: locationManager.coordinate!)
        let sourceMapItem = MKMapItem(placemark: sourcePlaceMark)
        
        let destinationPlacemark = MKPlacemark(coordinate: destination)
        let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
        
        let directionsRequest = MKDirections.Request()
        directionsRequest.source = sourceMapItem
        directionsRequest.destination = destinationMapItem
        directionsRequest.transportType = .automobile
        
        let directions = MKDirections(request: directionsRequest)
        directions.calculate { [weak self] response, error in
            guard let self else { return }
            guard let response else { return }
            guard let primaryRoute = response.routes.first else { return }
        
            self.overlays.append(primaryRoute.polyline)
            self.steps = primaryRoute.steps
            self.mapView.removeOverlays(self.overlays)
            self.mapView.addOverlay(primaryRoute.polyline)
            
        }
    }

Then, you can call this method either from core location delegate method (_:didUpdateLocations:) or, I believe in your case from updateUIView of your UIViewRepresentable struct.

I just noticed, that you used UIViewRepresentable, why wouldn't you change it to UIViewControllerRepresantable? Which, I assume, would give you a better control.

One more thing, instead of go outside and test every small change there, consider to use so called GPX files, which are really powerful. However, it is up to you 😉

Update polyline when user change current location
 
 
Q