Reputation: 6448
I'm new to requesting routes and following an article from hackingwithswift (https://www.hackingwithswift.com/example-code/location/how-to-find-directions-using-mkmapview-and-mkdirectionsrequest) I am able to get an alternative route to the route I just tracked, when tracking a new one. My goal is to input a [CLLocation]
get a route from MKDirection
. The problem is that when tracking it, I do get an alternative route, but when requesting it from a saved route ( the same I just tracked ) I get a nil response
with the error message :
direction error : Error Domain=MKErrorDomain Code=1 "Indicazioni stradali non disponibili" UserInfo={NSLocalizedFailureReason=Le informazioni sull'itinerario non sono attualmente disponibili., MKErrorGEOError=-12, MKErrorGEOErrorUserInfo={ NSDebugDescription = "mapItem cannot be nil"; }, MKDirectionsErrorCode=3, NSLocalizedDescription=Indicazioni stradali non disponibili}
Route is the same, so identical starting and ending. Can you see what I'm doing wrong here? As always many thanks.
func repositionLocation(route: [CLLocation]) -> [CLLocation] {
var repositioned: [CLLocation] = []
let request = MKDirections.Request()
request.requestsAlternateRoutes = false
request.transportType = .walking
let directions = MKDirections(request: request)
let a = route.first?.coordinate
let b = route.last?.coordinate
print("a is: \(String(describing: a)), b is \(String(describing: b))") // prints correct CLLocationCoordinate2D
request.source = MKMapItem(placemark: MKPlacemark(coordinate: a!))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b!))
directions.calculate { [unowned self] response, error in
if let err = error {
print("direction error : \(err)")
}
guard let unwrappedResponse = response else {print("no suggested routes available"); return } // here always returns
guard let coord = unwrappedResponse.routes.first?.steps else {return}
for location in coord {
let point: CLLocation = CLLocation(latitude: location.polyline.coordinate.latitude, longitude: location.polyline.coordinate.longitude)
repositioned.append(point)
}
}
return repositioned
}
I'm narrowing down the problem being either I make too many requests(but I'm making only one) and servers stop responding, or, being the response asynchronous, the function exits before it could actually get a valid response as I'm calling it from a TableView.
How would I wait for the response in cellForRow
?
After precious suggestions it's now requesting route and getting responses, from which I create a new CLLocation
for every step, and append it to the repositioned
array that gets returned on completion.
I actually see that the new CLLocation
gets created correctly inside the for
loop as I print it, but the array doesn't increment in size, and the returned array will only have the first append from the input route.
The newer version of the function is:
func repositionLocation(route: [CLLocation], completion: @escaping ([CLLocation]) -> Void) {
var pos = 0
var repositioned = [CLLocation]()
repositioned.append(route.first!)
guard route.count > 4 else {print("Reposision Location failed, not enough positions");return}
let request = MKDirections.Request()
request.requestsAlternateRoutes = false
request.transportType = .walking
while pos < route.count - 4 {
let a = repositioned.last!.coordinate
let b = route[pos + 4].coordinate
request.source = MKMapItem(placemark: MKPlacemark(coordinate: a))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b))
let directions = MKDirections(request: request)
directions.calculate { [unowned self] response, error in
if let err = error {
print("direction error : \(err)")
}
guard let unwrappedResponse = response else {print("no suggested routes available"); return }
print("Response is: \(unwrappedResponse.debugDescription)")
guard let coord = unwrappedResponse.routes.first?.steps else {print("No coordinates");return}
print("coord is: \(coord)")
for location in coord {
let point: CLLocation = CLLocation(latitude: location.polyline.coordinate.latitude, longitude: location.polyline.coordinate.longitude)
print("point is: \(point)") // prints a correct CLLocation with coordinates
repositioned.append(point)
print("repositioned in for loop is : \(repositioned)") // prints just first appended location CLLocation with coordinates
}
}
print("repositioned in while loop is : \(repositioned)")
pos += 5
}
// last segment.
// let a = repositioned.last?.coordinate
// let b = route.last?.coordinate
//
// request.source = MKMapItem(placemark: MKPlacemark(coordinate: a!))
// request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b!))
//
// let directions = MKDirections(request: request)
//
// directions.calculate { [unowned self] response, error in
// if let err = error {
// print("direction error : \(err)")
// }
// guard let unwrappedResponse = response else {print("no suggested routes available"); return }
// print("Response is: \(unwrappedResponse.debugDescription)")
// guard let coord = unwrappedResponse.routes.first?.steps else {print("No coordinates");return}
// for location in coord {
// let point: CLLocation = CLLocation(latitude: location.polyline.coordinate.latitude, longitude: location.polyline.coordinate.longitude)
// repositioned.append(point)
// }
print("repositioned at completion is : \(repositioned)")
completion(repositioned)
// }
}
Don't look at the commented out portion, that will take care of the last bit of the input route that exceeds the part taken care of inside while
loop .
Upvotes: 0
Views: 145
Reputation: 2132
There are several issues:
First of all, you are trying to build a walking route through the whole of USA - this problem cannot be solved by conventional methods. I recommend setting the coordinates of the points at a closer distance. I have tested your code at points closer and the route appears.
Secondary, you can use this code:
func repositionLocation(route: [CLLocation], completion: @escaping ([CLLocation]) -> Void) {
var repositioned = [CLLocation]()
let request = MKDirections.Request()
request.requestsAlternateRoutes = false
request.transportType = .walking
let a = route.first?.coordinate
let b = route.last?.coordinate
request.source = MKMapItem(placemark: MKPlacemark(coordinate: a!))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b!))
let directions = MKDirections(request: request)
print("a is: \(String(describing: a)), b is \(String(describing: b))") // prints correct CLLocationCoordinate2D
request.source = MKMapItem(placemark: MKPlacemark(coordinate: a!))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b!))
directions.calculate { response, error in
if let err = error {
print("direction error : \(err)")
}
guard let unwrappedResponse = response else {print("no suggested routes available"); return } // here always returns
for route in unwrappedResponse.routes {
self.mapView.addOverlay(route.polyline)
self.mapView.setVisibleMapRect(route.polyline.boundingMapRect, animated: true)
}
guard let coord = unwrappedResponse.routes.first?.steps else {return}
for location in coord {
let point: CLLocation = CLLocation(latitude: location.polyline.coordinate.latitude, longitude: location.polyline.coordinate.longitude)
repositioned.append(point)
}
completion(repositioned)
}
}
You set let directions = MKDirections(request: request)
, but only then configure request.source
and request.destination
. Other than that, you get the result asynchronously, so I added completion
for result.
For testing you can call this:
repositionLocation(route: [CLLocation(latitude: 40.7127, longitude: -74.0059), CLLocation(latitude: 40.79, longitude: -74.011)]) { result in
print(result)
}
UPD2:
@Vincenzo as I see, you incorrectly use completion handler for closure, I'd recommend to read about async and closure at all.
I've recommend use this code:
func repositionLocation(route: [CLLocation], completion: @escaping ([CLLocation]) -> Void) {
var pos = 0
var repositioned = [CLLocation]()
repositioned.append(route.first!)
guard route.count > 4 else {print("Reposision Location failed, not enough positions");return}
let request = MKDirections.Request()
request.requestsAlternateRoutes = false
request.transportType = .walking
while pos < route.count - 4 {
let a = repositioned.last!.coordinate
let b = route[pos + 4].coordinate
request.source = MKMapItem(placemark: MKPlacemark(coordinate: a))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b))
let directions = MKDirections(request: request)
directions.calculate { [unowned self] response, error in
if let err = error {
print("direction error : \(err)")
}
guard let unwrappedResponse = response else {print("no suggested routes available"); return }
print("Response is: \(unwrappedResponse.debugDescription)")
guard let coord = unwrappedResponse.routes.first?.steps else {print("No coordinates");return}
print("coord is: \(coord)")
for location in coord {
let point: CLLocation = CLLocation(latitude: location.polyline.coordinate.latitude, longitude: location.polyline.coordinate.longitude)
print("point is: \(point)") // prints a correct CLLocation with coordinates
repositioned.append(point)
print("repositioned in for loop is : \(repositioned)") // prints just first appended location CLLocation with coordinates
}
completion(repositioned)
}
print("repositioned in while loop is : \(repositioned)")
pos += 5
}
}
Upvotes: 0