Search code examples
swiftgoogle-mapsswift3

Finding Driving/Walking distance between 2 locations in Swift 3 using Google Maps API


I am trying to get the driving/walking distance between two locations using the Google Maps API. I am updating my app to Swift 3, from objective-c and right now I'm stuck on converting this block of code to Swift 3.

NSString *dist;
NSString *strUrl = [NSString stringWithFormat:@"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false&mode=%@", closestLocation.coordinate.latitude,  closestLocation.coordinate.longitude, _currentUserLocation.coordinate.latitude,  _currentUserLocation.coordinate.longitude, @"DRIVING"];
NSURL *url = [NSURL URLWithString:[strUrl stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
if(jsonData != nil)
{
    NSError *error = nil;
    id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
    NSMutableArray *arrDistance=[result objectForKey:@"routes"];
    if ([arrDistance count]==0) {
        NSLog(@"N.A.");
    }
    else{
        NSMutableArray *arrLeg=[[arrDistance objectAtIndex:0]objectForKey:@"legs"];
        NSMutableDictionary *dictleg=[arrLeg objectAtIndex:0];

        dist = [NSString stringWithFormat:@"%@",[[dictleg   objectForKey:@"distance"] objectForKey:@"text"]];
    }
}
else{
    NSLog(@"N.A.");
}

I tried using tools like Swiftify, but keep getting errors everywhere. What I have right now is:

var dist: String = ""
var strUrl: String = "http://maps.googleapis.com/maps/api/directions/json?origin=\(closestLocation!.coordinate.latitude),\(closestLocation!.coordinate.longitude)&destination=\(currentUserLocation!.coordinate.latitude),\(currentUserLocation!.coordinate.longitude)&sensor=false&mode=\("DRIVING")"
var url = URL(string: strUrl)
var jsonData = Data(contentsOf: url!)
    if (jsonData != nil) {
        var error: Error? = nil
        var result: Any? = try? JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.Element.mutableContainers)
        var arrDistance: [Any]? = (result?["routes"] as? [Any])
        if arrDistance?.count == 0 {
            print("N.A.")
        }
        else {
            var arrLeg: [Any]? = ((arrDistance?[0] as? [Any])?["legs"] as? [Any])
            var dictleg: [AnyHashable: Any]? = (arrLeg?[0] as? [AnyHashable: Any])

            dist = "\(dictleg?["distance"]["text"])"
        }
    }
    else {
        print("N.A.")
    }

My errors now are the following:

with the Data(contentsOf: url!) it tells me "Call can throw, but it is not marked with 'try' and the error is not handled

And it doesn't like how I use [Any] for my arrDistance variable.

If anyone knows how to implement this API call in Swift 3, it would be really helpful. Thanks


Solution

  • What compiler is saying that Data(contentsOf:) will throws exception so that you need to handle it with do catch block. Now as suggested in comment you need to use dataTask(with:) instead of Data(contentsOf:) to download data. Also you can make completion block with your code, so make changes in your code like below.

    func getDistance(completion: @escaping(String) -> Void) {
    
        var dist = ""
        let strUrl = "http://maps.googleapis.com/maps/api/directions/json?origin=\(closestLocation!.coordinate.latitude),\(closestLocation!.coordinate.longitude)&destination=\(currentUserLocation!.coordinate.latitude),\(currentUserLocation!.coordinate.longitude)&sensor=false&mode=\("DRIVING")"
        let url = URL(string: strUrl)!
        let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
            guard let data = data, error == nil else {
                print(error?.localizedDescription ?? "")
                completion(dist)
                return
            }
            if let result = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String:Any],
                let routes = result["routes"] as? [[String:Any]], let route = routes.first,
                let legs = route["legs"] as? [[String:Any]], let leg = legs.first,
                let distance = leg["distance"] as? [String:Any], let distanceText = distance["text"] as? String {
    
                dist = distanceText
            }
            completion(dist)
        })
        task.resume()
    }
    

    Now simply call getDistance function like this way.

    self.getDistance { (distance) in
        //Update UI on main thread
        DispatchQueue.main.async {
            print(distance)
            self.label.text = distance
        }
    }