I have this function that finds the closest location from an array of lat,long's to a users location.
func getClosest(){
let LRK = CLLocation(latitude: 37.944804, longitude: -122.509066)
let SAU = CLLocation(latitude: 37.856315, longitude: -122.478723)
let SFO = CLLocation(latitude: 37.795346, longitude: -122.392711)
//let TEST = CLLocation(latitude: 37.947112, longitude: -122.490452)
let coordinates = [LRK, SAU, SFO]
let userLocation = CLLocation(latitude: 37.944804, longitude: -122.509066)
@State var closest = coordinates.min(by:
{ $0.distance(from: userLocation) < $1.distance(from: userLocation) })
print("Closest from Function", (closest))
}
In the main swift file I am trying to use that location as the destination and then drawing a polyline between the two.
import SwiftUI
import MapKit
@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
}
}
}
}
func getClosest(){
let LRK = CLLocation(latitude: 37.944804, longitude: -122.509066)
let SAU = CLLocation(latitude: 37.856315, longitude: -122.478723)
let SFO = CLLocation(latitude: 37.795346, longitude: -122.392711)
//let TEST = CLLocation(latitude: 37.947112, longitude: -122.490452)
let coordinates = [LRK, SAU, SFO]
let userLocation = CLLocation(latitude: 37.944804, longitude: -122.509066)
@State var closest = coordinates.min(by:
{ $0.distance(from: userLocation) < $1.distance(from: userLocation) })
print("Closest from Function", (closest))
}
struct ContentView: View {
@State var newlocationManager = NewLocationManager()
@State private var selectedResult: MKMapItem?
@State private var route: MKRoute?
private let destination = CLLocationCoordinate2D(latitude: 38.944804, longitude: -122.509066)
@State private var myLocation = 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: destination)
// 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 {
myLocation = 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: myLocation))
}
}
// --- here
func getDirections2() {
route = nil
// Create and configure the request
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: destination))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: myLocation))
// 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
}
}
#Preview {
ContentView()
}
What I cannot do is get the "userLocation" into the function and then get the closest variable result from that function to be the "destination" for drawing the polyline. Any ideas?
This should do the trick. I also improved your code a bit to make it cleaner. I don't really know what your code is supposed to do so let me know if it works.
import SwiftUI
import MapKit
import CoreLocation
// Define the function to get the closest location
func getClosest(userLocation: CLLocation, locations: [CLLocation]) -> CLLocation? {
return locations.min(by: { $0.distance(from: userLocation) < $1.distance(from: userLocation) })
}
// Observable Location Manager class
class NewLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var location: CLLocation? = nil
private let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let newLocation = locations.last else { return }
self.location = newLocation
}
}
// SwiftUI View
struct ContentView: View {
@StateObject var newLocationManager = NewLocationManager()
@State private var selectedResult: MKMapItem?
@State private var route: MKRoute?
@State private var destination: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 37.944804, longitude: -122.509066) // Default value
let locations = [
CLLocation(latitude: 37.944804, longitude: -122.509066), // LRK
CLLocation(latitude: 37.856315, longitude: -122.478723), // SAU
CLLocation(latitude: 37.795346, longitude: -122.392711) // SFO
]
var body: some View {
Map {
if let userLocation = newLocationManager.location {
Marker("Start", coordinate: destination)
Marker("User", coordinate: userLocation.coordinate)
if let route = route {
MapPolyline(route)
.stroke(.blue, lineWidth: 5)
}
}
}
.onChange(of: newLocationManager.location) {
if let userLocation = newLocationManager.location {
if let closestLocation = getClosest(userLocation: userLocation, locations: locations) {
destination = closestLocation.coordinate
getDirections()
}
}
}
}
func getDirections() {
guard let userLocation = newLocationManager.location else { return }
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: userLocation.coordinate))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: destination))
Task {
let directions = MKDirections(request: request)
do {
let response = try await directions.calculate()
route = response.routes.first
} catch {
print("Error getting directions: \(error)")
}
}
}
}
extension CLLocation {
func isClose(to otherLocation: CLLocation, withinDistance distance: CLLocationDistance) -> Bool {
return self.distance(from: otherLocation) <= distance
}
}
#Preview {
ContentView()
}