Vincenzo
Vincenzo

Reputation: 6448

function returning array doesn't get the appends inside the for loop Swift

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
}

Update :

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?

Update 2:

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 forloop 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

Answers (1)

Vadim Nikolaev
Vadim Nikolaev

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

Related Questions