EDITED: I edited my code because it was a mess and confusing, so I am trying again.
My overall goal is to get a users location and then provide directions to a lat/lon on the map.
The code below creates directions between two points but how can I make the starting point the users location?
Also.. I dont seem to be able to get the users location blue dot to appear, even though I am getting their location.
import SwiftUI
import MapKit
import CoreLocation
@Observable
class NewLocationManager {
var location: CLLocation? = nil
private let locationManager = CLLocationManager()
func requestUserAuthorization() async throws {
locationManager.requestWhenInUseAuthorization()
}
func startCurrentLocationUpdates() async throws {
for try await locationUpdate in CLLocationUpdate.liveUpdates() {
guard let location = locationUpdate.location else { return }
self.location = location
print(location)
print("location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
let lat = location.coordinate.latitude
let long = location.coordinate.longitude
}
}
}
struct ContentView: View {
@State var newlocationManager = NewLocationManager()
@State private var selectedResult: MKMapItem?
@State private var route: MKRoute?
private let startingPoint = CLLocationCoordinate2D(
latitude: 38.941382,
longitude: -122.484136
)
private let destinationCoordinates = CLLocationCoordinate2D(
latitude: 37.94479,
longitude: -122.50959
)
var body: some View {
VStack {
Text("New location manager: \(newlocationManager.location?.description ?? "No Location Provided!")")
}
Map(selection: $selectedResult) {
// Adding the marker for the starting point
Marker("Start", coordinate: self.startingPoint)
// Show the route if it is available
if let route {
MapPolyline(route)
.stroke(.blue, lineWidth: 5)
}
}
.onChange(of: selectedResult){
getDirections()
}
.task {
try? await newlocationManager.requestUserAuthorization()
try? await newlocationManager.startCurrentLocationUpdates()
// remember that nothing will run here until the for try await loop finishes
}
.onAppear {
CLLocationManager().requestWhenInUseAuthorization()
self.selectedResult = MKMapItem(placemark: MKPlacemark(coordinate: self.destinationCoordinates))
}
}
func getDirections() {
self.route = nil
// Check if there is a selected result
guard let selectedResult else { return }
// Create and configure the request
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: self.startingPoint))
request.destination = self.selectedResult
// Get the directions based on the request
Task {
let directions = MKDirections(request: request)
let response = try? await directions.calculate()
route = response?.routes.first
}
}
}
#Preview {
ContentView()
}
To ....show users location and draw a polyline to a predetermined lat, lon
, try this approach
using @State private var destinationCoordinates...
, an additional .onChange(of: newlocationManager.location)
and a new getDirections2()
to get the
polyline route from the startingPoint
to the use current location stored in the
destinationCoordinates
.
You will have to adjust the logic/parameters in startCurrentLocationUpdates
to update the location only if there is a significant change in user location.
@Observable
class NewLocationManager {
var location: CLLocation? = nil
private let locationManager = CLLocationManager()
func requestUserAuthorization() async throws {
locationManager.requestWhenInUseAuthorization()
}
func startCurrentLocationUpdates() async throws {
for try await locationUpdate in CLLocationUpdate.liveUpdates() {
// adjust the logic/parameters as you require
let oldLocation = self.location == nil ? CLLocation() : self.location!
guard let newLocation = locationUpdate.location else { return }
if !oldLocation.isClose(to: newLocation, withinDistance: 50.0) {
self.location = newLocation
}
}
}
}
struct ContentView: View {
@State var newlocationManager = NewLocationManager()
@State private var selectedResult: MKMapItem?
@State private var route: MKRoute?
// for my tests, Tokyo garden
private let startingPoint = CLLocationCoordinate2D(latitude: 35.661991, longitude: 139.762735)
@State private var destinationCoordinates = CLLocationCoordinate2D(latitude: 35.67, longitude: 139.763) // <--- here @State
var body: some View {
Map(selection: $selectedResult) {
UserAnnotation()
// Adding the marker for the starting point
Marker("Start", coordinate: startingPoint)
// Show the route if it is available
if let route {
MapPolyline(route)
.stroke(.blue, lineWidth: 5)
}
}
.onChange(of: newlocationManager.location) { // <--- here
if let coord = newlocationManager.location?.coordinate {
destinationCoordinates = coord
getDirections2()
}
}
.task {
try? await newlocationManager.requestUserAuthorization()
try? await newlocationManager.startCurrentLocationUpdates()
// remember that nothing will run here until the for try await loop finishes
}
.onAppear {
CLLocationManager().requestWhenInUseAuthorization()
selectedResult = MKMapItem(placemark: MKPlacemark(coordinate: destinationCoordinates))
}
}
// --- here
func getDirections2() {
route = nil
// Create and configure the request
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: startingPoint))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: destinationCoordinates))
// Get the directions based on the request
Task {
let directions = MKDirections(request: request)
let response = try? await directions.calculate()
route = response?.routes.first
}
}
}
extension CLLocation {
func isClose(to otherLocation: CLLocation, withinDistance distance: CLLocationDistance) -> Bool {
return self.distance(from: otherLocation) <= distance
}
}
Note, you also need to enable the appropriate privacy settings/info.plist of your App for location usage.