I am having trouble with placing marker of bus station which was near me on my google map application.
Let say I live in New York City,I want all bus station with marker on my google map.But,i really don't know how to fetch all the bus station data of my current city(New York) and place a picker on it.I tried AppCoda tutorial and it gives me only marker of my custom search
Here : http://www.appcoda.com/google-maps-api-tutorial/
Is there any way that i can modified its "MapTask.Swift" code to get all bus station near me and place picker on each bus station?
Here is MapTask.Swift
import UIKit
import CoreLocation
import SwiftyJSON
class MapTask: NSObject {
let baseURLGeocode = "https://maps.googleapis.com/maps/api/geocode/json?"
var lookupAddressResults: Dictionary<NSObject, AnyObject>!
var fetchedFormattedAddress: String!
var fetchedAddressLongitude: Double!
var fetchedAddressLatitude: Double!
override init() {
super.init()
}
func geocodeAddress(address: String!, withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
geocodeURLString = geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let geocodeURL = NSURL(string: geocodeURLString)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let geocodingResultsData = NSData(contentsOfURL: geocodeURL!)
let dictionary: Dictionary<NSObject, AnyObject> = try!NSJSONSerialization.JSONObjectWithData(geocodingResultsData!, options: NSJSONReadingOptions.MutableContainers) as! Dictionary<NSObject, AnyObject>
// Get the response status.
let status = dictionary["status"] as! String
if status == "OK" {
let allResults = dictionary["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
print("The result is : \(dictionary)")
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as! String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status: status, success: true)
}
else {
completionHandler(status: status, success: false)
}
})
}
else {
completionHandler(status: "No valid address.", success: false)
}
}
}
You'll need to call the Place Search with the type set to bus_station to limit the return to that specific type. The response will return results with their details that you can use to set the Markers.
Related
I'm new to firebase and I wanted to try using a realtime database. It's been fairly easy to use until it's time to fetch the data from the database. I'm currently stuck at trying to get the data back into an array to provide my tableView with data.
Each image is saved with a timestamp. I've fetching it in different ways, it ends up printing nil or breaking the app. But when i use a breakpoint, I'm able to see the same data.
func downloadFromFirebase(completion: #escaping (Bool, Error?) -> Void) {
ref.child("ImageDetails/").observeSingleEvent(of: .value) { (snapshot) in
guard let value = snapshot.value as? [String:Any] else { return }
let name = value["username"] as! String
completion(true, nil)
}
}
I don't understand your question so I will write a code in order to retrieve the data from realtime database and to save it in an array.
You said "Each image is saved with a timestamp", so I assume you want to retrieve an array of data containing images(imageURL, etc) right?
we create a structure based on your database
struct dataStructure {
var Description:String?
var imageURL:String?
var Portfolio:String?
var createdAt:String?
var Instagram:String?
var profileImageUrl:String?
var Twitter:String?
var Username:String?
}
// now we create an array of this struct
var arrayOfData = [dataStructure]()
func downloadFromFirebase(completion:#escaping (Bool ,Error?)-> Void) {
ref.child("ImageDetails/").observeSingleEvent(of: .value) { snapshot in
guard let value = snapshot.value as? [[String:Any]] else { return }
// we add each element of value in the arrayOfData
for element in value {
guard let name = value["username"] as! String,
let Description = value["Description"] as! String,
let imageURL = value["imageURL"] as! String,
let Portfolio = value["Portfolio"] as! String,
let creationDate = value["createdAt"] as! String,
let Instagram = value["Instagram"] as! String,
let profileImageUrl = value["profileImageUrl"] as! String,
let Twitter = value["Twitter"] as! String ,
let Username = value["Username"] as! String else {
completion(false)
return
}
arrayOfData.append(dataStructure(name: name,
Description: Description,
imageURL: imageURL,
Portfolio: Portfolio ,
creationDate:creationDate ,
Instagram: Instagram,
profileImageUrl: profileImageUrl,
Twitter: Twitter))
}
completion(true)
}
}
at the end you will have an array with all your data
here is the code if you want to take the data from this function in your completion
func downloadFromFirebase(completion:#escaping (Result<dataStructure, Error>) {
ref.child("ImageDetails/").observeSingleEvent(of: .value) { snapshot in
guard let value = snapshot.value as? [[String:Any]] else { return }
// we add each element of value in the arrayOfData
for element in value {
guard let name = value["username"] as! String,
let Description = value["Description"] as! String,
let imageURL = value["imageURL"] as! String,
let Portfolio = value["Portfolio"] as! String,
let creationDate = value["createdAt"] as! String,
let Instagram = value["Instagram"] as! String,
let profileImageUrl = value["profileImageUrl"] as! String,
let Twitter = value["Twitter"] as! String ,
let Username = value["Username"] as! String else {
completion(.failure(error)
return
}
arrayOfData.append(dataStructure(name: name,
Description: Description,
imageURL: imageURL,
Portfolio: Portfolio ,
creationDate:creationDate ,
Instagram: Instagram,
profileImageUrl: profileImageUrl,
Twitter: Twitter))
}
/* we provide the array with the new data in the completion at the
end of the loop */
completion(.success(arrayOfData))
}
}
Question 1 :
I am using YouTube API to display a playlist of videos in a UITableView but it's not working. It's working fine when I make it for a single video, one video appears in the UITableView.
How can I display a playlist of any YouTube channel? I am using this code in my UITableView.
My UITableView code :
import UIKit
import AVFoundation
class YTViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, AVAudioPlayerDelegate {
#IBOutlet weak var txtSearch: UITextField!
#IBOutlet weak var searchResultTableView: UITableView!
// Set up a network session
let session = URLSession.shared
// ReST GET static String parts
let BASE_URL: String = "https://www.googleapis.com/youtube/v3/"
let SEARCH_VIDEO: String = "channels?part=snippet&q="
let VIDEO_TYPE: String = "&id=UCJIc9yX_3iHE2CfmUqoeJKQ&key="
let API_KEY: String = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#IBAction func btnSearchClicked(_ sender: UIButton) {
}
func getVideoList() {
let methodArguments: [String: AnyObject] = [
"query": txtSearch.text! as AnyObject
]
// Format the search string (video title) for http request
let videoTitle: String = escapedParameters(methodArguments)
// Make the query url
// sample: https://www.googleapis.com/youtube/v3/search?part=snippet&q=werewolf&type=video&key=AIzaSyDDqTGpVR7jxeozoOEjH6SLaRdw0YY-HPQ
let searchVideoByTitle = BASE_URL + SEARCH_VIDEO + videoTitle + VIDEO_TYPE + API_KEY
print("#####################\(searchVideoByTitle)")
if let url = URL(string: searchVideoByTitle) {
let request = URLRequest(url: url)
// Initialise the task for getting the data
initialiseTaskForGettingData(request, element: "items")
}
}
// Array to store all the desired values dictionaries
var videosArray: Array<Dictionary<String, AnyObject>> = [[String: AnyObject]]()
func initialiseTaskForGettingData(_ request: URLRequest, element: String) {
// Initialize task for getting data
// Refer to http://www.appcoda.com/youtube-api-ios-tutorial/
let task = session.dataTask(with: request, completionHandler: {(data, HTTPStatusCode, error) in
// Handler in the case of an error
if error != nil {
print(error as Any)
return
}
else {
// Parse that data received from the service
let resultDict: [String: AnyObject]!
do {
// Convert the JSON data to a dictionary
resultDict = try JSONSerialization.jsonObject(with: data! as Data, options: .allowFragments) as! [String: AnyObject]
print("***************************\(resultDict)")
// Get the first item from the returned items
if let itemsArray = (resultDict as AnyObject).value(forKey: element) as? NSArray {
// Remove all existing video data
self.videosArray.removeAll()
for index in 0..<itemsArray.count {
// Append the desiredVaules dictionary to the videos array
self.videosArray.append(self.unwrapYoutubeJson(arrayToBeUnwrapped: itemsArray, index: index))
}
// Asynchronously reload the data and display on the tableview
DispatchQueue.main.async {
// Reload the tableview
self.searchResultTableView.reloadData()
}
}
} catch let jsonError {
print(jsonError)
}
}
})
// Execute the task
task.resume()
}
func unwrapYoutubeJson(arrayToBeUnwrapped: NSArray, index: Int) -> [String: AnyObject]{
let firstItemDict = arrayToBeUnwrapped[index] as! [String: AnyObject]
// Get the snippet dictionary that contains the desired data
let snippetDict = firstItemDict["snippet"] as! [String: AnyObject]
// Dictionary to store desired video contents for display on tableview
// desired values - "Title", "Description", "Thumbnail"
var desiredValuesDict = [String: AnyObject]()
desiredValuesDict["title"] = snippetDict["title"]
desiredValuesDict["description"] = snippetDict["description"]
// Further unwrap to get the Thumbnail default URL
let thumbnailDict: [String: AnyObject]
thumbnailDict = snippetDict["thumbnails"] as! [String: AnyObject]
let defaultThumbnailDict = thumbnailDict["default"] as! [String: AnyObject]
desiredValuesDict["thumbnail"] = defaultThumbnailDict["url"]
//Get the id dictionary that contains videoId
let idDict = firstItemDict["id"] as? [String: AnyObject]
desiredValuesDict["videoId"] = idDict?["videoId"]
return desiredValuesDict
}
// Helper function: Given a dictionary of parameters, convert to a string for a url
func escapedParameters(_ parameters: [String : AnyObject]) -> String {
var urlVars = [String]()
for (key, value) in parameters {
// Make sure that it is a string value
let stringValue = "\(value)"
// Escape it
let escapedValue = stringValue.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
//Append it
urlVars += [key + "=" + "\(escapedValue!)"]
}
return (!urlVars.isEmpty ? "" : "") + urlVars.joined(separator: "&")
}
// MARK: UITableView method implementation
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SearchResultTableViewCell
let videoSelected = videosArray[indexPath.row]
cell.updateIU(video: videoSelected)
cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
let id = videosArray[indexPath.row]["videoId"] as? String
print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\(id)")
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videosArray.count
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? playerViewController {
if let selectedRowIndexPath = searchResultTableView.indexPathForSelectedRow?.row {
destination.mytitle = videosArray[selectedRowIndexPath]["title"] as! String
destination.mydescript = videosArray[selectedRowIndexPath]["description"] as! String
destination.myvideoId = videosArray[selectedRowIndexPath] ["videoId"] as? String
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
getVideoList()
searchResultTableView.dataSource = self
searchResultTableView.delegate = self
}
}
Question 2 :
When I am trying to play a video using YTPlayerHelper it's not working:
fatal error: unexpectedly found nil while unwrapping an Optional value and the video ID appears as nil.
How can I play the video using the YTPlayerHelper? This is how I am playing the video:
import UIKit
import youtube_ios_player_helper
class playerViewController: UIViewController {
#IBOutlet weak var MyPlayer: YTPlayerView!
#IBOutlet weak var txtTitle: UITextView!
#IBOutlet weak var txtDescript: UITextView!
var mytitle: String!
var mydescript: String!
var myvideoId : String!
override func viewDidLoad() {
super.viewDidLoad()
print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\(myvideoId)")
MyPlayer.load(withVideoId: myvideoId!)
txtTitle.text = mytitle
txtDescript.text = mydescript
}
}
Here is my Alamofire implementation; you have to adjust the names to match yours:
func callAlamo(url : String) {
Alamofire.request(url).responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData : Data) {
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStandard
pageToken = readableJSON["nextPageToken"] as! String
if previousPageButton.isEnabled {
previousPageToken = readableJSON["prevPageToken"] as? String
}
if previousPageToken == nil {
previousPageButton.isEnabled = false
}
if let items = readableJSON["items"] as? [JSONStandard] {
for i in 0..<items.count {
let item = items[i]
var name = String()
var previewURL1 = String()
if let id = item["id"] as? JSONStandard {
let url = id["videoId"] as! String
previewURL1 = url
}
let previewURL = previewURL1
if let snippet = item["snippet"] as? JSONStandard {
let title = snippet["title"] as! String
name = title
if let thumbnails = snippet["thumbnails"] as? JSONStandard {
if let images = thumbnails["high"] as? JSONStandard {
let mainImageURL = URL(string: images["url"] as! String)
imageURL = images["url"] as! String
let mainImageData = NSData(contentsOf: mainImageURL!)
let mainImage = UIImage(data: mainImageData! as Data)
posts.append(post.init(mainImage: mainImage, name: name, previewURL: previewURL, imageURL: imageURL))
self.tableView.reloadData()
nextPageButton.isEnabled = true
}
}
}
}
}
} catch {
print(error)
}
}
Then make a request by using callAlamo(url: yourURL), replacing yourURL with the actual URL.
For the second question, you have a great tutorial here: http://www.appcoda.com/youtube-api-ios-tutorial/
In the tutorial is another way to update UITableView with YouTube videos, but personally I prefer the Alamofire one, as it is much faster and easier to write. I recommend to view just the playing videos part.
I'm making an app like the AppStore with UICollectionView so when downloading the data from url
import Alamofire
import AlamofireImage
class AppCategory: NSObject {
var id: NSNumber?
var title: String?
var apps: [App]?
var type: NSNumber?
static func fetchFeaturedApps(completionHandler: #escaping ([AppCategory]) -> ()) {
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
var appCategories = [AppCategory]()
let sections = OFFERIM["sections"] as! [Dictionary<String, AnyObject>]
for i in 0..<sections.count {
var appCategory = AppCategory()
var apps = [App]()
let sectionTitle = sections[i]["title"] as! String
appCategory.title = sectionTitle
let sectionType = sections[i]["type"] as! NSNumber
appCategory.type = sectionType
let sectionId = sections[i]["id"] as! NSNumber
appCategory.id = sectionId
// for Apps inside each section
let lists = sections[i]["lists"] as! [String: AnyObject]
for j in 0..<lists.count{
let app = App()
let id = lists[j]["id"] as! NSNumber
app.id = id
let thumb = lists[j]["thumb"]! as! String
Alamofire.request(thumb).responseImage { response in
if let image = response.result.value {
app.image = image
print("image downloaded: \(image)")
}
}
let title = lists[j]["title"] as! String
app.title = title
apps.append(app)
}
appCategory.apps = apps
appCategories.append(appCategory)
}
DispatchQueue.main.async(execute: {
completionHandler(appCategories)
})
}catch{
print("JSON Processing Failed")
}
}.resume()
}
}
and I have these classes as models
class App: NSObject {
var id: NSNumber?
var title: String?
var image: UIImage?
}
and I call featuredApps in the viewDidLoad in viewController
what happened is when the view loaded the labels appears but the images not in the collection view .
I need to scroll each row to make the images appear.
and I don't kwon the problem where ???
Keep the url and set the url to image view use AlamofireImage.
imageView.af_setImage(withURL: url)
The below code works fine in Xcode 7.1 but when I upgrade to Xcode 8 and swift 3 I get this error, I had searched on internet for same problem in stackoverflow but none of them answer my question.
func drawRoute() {
clearRoute()
// self.routePolyline = nil
// self.routePolyline.map = nil
let route = mapTk.overviewPolyline!["points"] as String //error here
//var overViewPolyLine = routes![0]["overview_polyline"]["points"].string
let path: GMSPath = GMSPath(fromEncodedPath: route)
routePolyline = GMSPolyline(path: path)
routePolyline.map = viewMap
}
here is the class mapTK for more info:
import Foundation
import UIKit
import CoreLocation
import MapKit
///This Class is designed for the detail methods that contact google maps server like GeoCode, GetDirection and Calculate distance and time
class MapTK: NSObject {
let baseURLGeocode = "https://maps.googleapis.com/maps/api/geocode/json?"
var lookupAddressResults: Dictionary<NSObject, AnyObject>!
var fetchedFormattedAddress: String!
var fetchedAddressLongitude: Double!
var fetchedAddressLatitude: Double!
let baseURLDirections = "https://maps.googleapis.com/maps/api/directions/json?"
var selectedRoute: Dictionary<NSObject, AnyObject>!
var overviewPolyline: Dictionary<NSObject, AnyObject>!
var originCoordinate: CLLocationCoordinate2D!
var destinationCoordinate: CLLocationCoordinate2D!
var originAddress: String!
var destinationAddress: String!
var totalDistanceInMeters: UInt = 0
var totalDistance: String!
var totalDurationInSeconds: UInt = 0
var totalDuration: String!
/* private let errorDictionary = ["NOT_FOUND" : "At least one of the locations specified in the request's origin, destination, or waypoints could not be geocoded",
"ZERO_RESULTS":"No route could be found between the origin and destination",
"MAX_WAYPOINTS_EXCEEDED":"Too many waypointss were provided in the request The maximum allowed waypoints is 8, plus the origin, and destination",
"INVALID_REQUEST":"The provided request was invalid. Common causes of this status include an invalid parameter or parameter value",
"OVER_QUERY_LIMIT":"Service has received too many requests from your application within the allowed time period",
"REQUEST_DENIED":"Service denied use of the directions service by your application",
"UNKNOWN_ERROR":"Directions request could not be processed due to a server error. Please try again"]
*/
override init() {
super.init()
}
///the geocodeAddress is getting information from googlemaps with JSON it receive the geocode information
func geocodeAddress(address: String!, withCompletionHandler completionHandler: #escaping ((_: String, _: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
//geocodeURLString = geocodeURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
let geocodeURL = NSURL(string: geocodeURLString)
// dispatch_Dispatch.Queue.mainasync(dispatch_get_main_queue(), { () -> Void in
DispatchQueue.main.async {
let geocodingResultsData = NSData(contentsOf: geocodeURL! as URL)
// let error: NSError?
do
{
let dictionary = try JSONSerialization.jsonObject(with: geocodingResultsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
// try NSJSONSerialization.JSONObjectWithData(directionsData!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
// Get the response status.
let status = dictionary?["status"]as! String
// let status = dictionary["status"] as! [AnyObject]
if status == "OK" {
let allResults = dictionary?["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"]? as? String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("No valid address.", false)
}
}
///getDirection method is using JSON to receive waypoints for making route for drawing polyline, as its name suggests it get the direciton inforamtion, first it send lat and lng the receive a dictionary and that dictionary give all the infrmation need to draw route like waypoints
func getDirections(origin: String!, destination: String!, waypoints: Array<String>!, travelMode: TravelModes!, completionHandler: #escaping ((_ status: String, _ success: Bool) -> Void)) {
if let originLocation = origin {
if let destinationLocation = destination {
var directionsURLString = baseURLDirections + "origin=" + originLocation + "&destination=" + destinationLocation //+ "&key=AIzaSyDsDqj0EMYZ-C4lGF3tmbntZtzurLl6_J4"
if let routeWaypoints = waypoints {
directionsURLString += "&waypoints=optimize:true"
for waypoint in routeWaypoints {
directionsURLString += "|" + waypoint
}
}
if let _ = travelMode {
var travelModeString = ""
switch travelMode.rawValue {
case TravelModes.walking.rawValue:
travelModeString = "walking"
case TravelModes.bicycling.rawValue:
travelModeString = "bicycling"
default:
travelModeString = "driving"
}
directionsURLString += "&mode=" + travelModeString
}
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
/// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
directionsURLString = directionsURLString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
//let encodedHost = unencodedHost.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
//var url = NSURL(urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()))
//let savePath = (documentDirectory as NSString).stringByAppendingPathComponent("mergeVideo-\(date).mov")
let directionsURL = NSURL(string: directionsURLString)
// DispatchQueue.main.asynchronously(execute: { () -> Void in
DispatchQueue.main.async {
let directionsData = NSData(contentsOf: directionsURL! as URL)
// var error: NSError?
do {
let dictionary = try JSONSerialization.jsonObject(with: directionsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
let status = dictionary?["status"] as! String
if status == "OK" {
self.selectedRoute = (dictionary?["routes"] as! Array<Dictionary<NSObject, AnyObject>>)[0]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<NSObject, AnyObject>
let legs = self.selectedRoute["legs"] as! Array<Dictionary<NSObject, AnyObject>>
let startLocationDictionary = legs[0]["start_location"] as! Dictionary<NSObject, AnyObject>
self.originCoordinate = CLLocationCoordinate2DMake(startLocationDictionary["lat"] as! Double, startLocationDictionary["lng"] as! Double)
let endLocationDictionary = legs[legs.count - 1]["end_location"]as! Dictionary<NSObject, AnyObject>
self.destinationCoordinate = CLLocationCoordinate2DMake(endLocationDictionary["lat"] as! Double, endLocationDictionary["lng"] as! Double)
self.originAddress = legs[0]["start_address"] as! String
self.destinationAddress = legs[legs.count - 1]["end_address"] as! String
self.calculateTotalDistanceAndDuration()
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("Destination is nil.", false)
}
}
else {
completionHandler("Origin is nil", false)
}
}
/// this method below is for the calculation of Distance adn Duration of each location for travel
func calculateTotalDistanceAndDuration() {
let legs = self.selectedRoute["legs"] as! Array<NSDictionary>
totalDistanceInMeters = 0
totalDurationInSeconds = 0
for leg in legs {
totalDistanceInMeters += (leg["distance"] as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
totalDurationInSeconds += (leg["duration"]as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
}
let distanceInKilometers: Double = Double(totalDistanceInMeters / 1000)
totalDistance = "Total Distance: \(distanceInKilometers) Km"
let mins = totalDurationInSeconds / 60
let hours = mins / 60
let days = hours / 24
let remainingHours = hours % 24
let remainingMins = mins % 60
let remainingSecs = totalDurationInSeconds % 60
totalDuration = "Duration: \(days) d, \(remainingHours) h, \(remainingMins) mins, \(remainingSecs) secs"
}
}
Use
Dictionary<String, Any>
as JSON dictionary type.
JSON keys are required to be String anyway and
AnyObject has been changed to Any in Swift 3.
Edit :
I recommend to use a type alias for the JSON dictionary type
typealias JSONObject = [String:Any] // synonym of Dictionary<String, Any>
Then you can write
var lookupAddressResults: JSONObject!
...
var selectedRoute: JSONObject!
var overviewPolyline: JSONObject!
and the parsing code
...
if status == "OK" {
let allResults = dictionary?["results"] as! Array<JSONObject>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as? String
let geometry = self.lookupAddressResults["geometry"] as! JSONObject
self.fetchedAddressLongitude = ((geometry["location"] as! JSONObject)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! JSONObject)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
...
You can also replace
( ... as! NSNumber).doubleValue
with
... as! Double
Is there a way to get the Coordinates from an entered Address string using GMS in iOS Swift? I can find examples of returning an Address from Coordinates (reverse geocoding?), but not the other way around. Does google offer this service? To first parse the entered address string, return the most suitable actual address, and ultimately the coordinates. Please provide a simple example, or point me in the right direction. Regards, Chris
As of June 2015, GMS iOS SDK does not expose this functionality directly. However, there are two ways to get it. First one is using Google Maps Web API.
let baseURLGeocode = "https://maps.googleapis.com/maps/api/geocode/json?"
func geocodeAddress(address: String!, withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
geocodeURLString = geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let geocodeURL = NSURL(string: geocodeURLString)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let geocodingResultsData = NSData(contentsOfURL: geocodeURL!)
var error: NSError?
let dictionary: Dictionary<NSObject, AnyObject> = NSJSONSerialization.JSONObjectWithData(geocodingResultsData!, options: NSJSONReadingOptions.MutableContainers, error: &error) as Dictionary<NSObject, AnyObject>
if (error != nil) {
println(error)
completionHandler(status: "", success: false)
}
else {
// Get the response status.
let status = dictionary["status"] as String
if status == "OK" {
let allResults = dictionary["results"] as Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as String
let geometry = self.lookupAddressResults["geometry"] as Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as Dictionary<NSObject, AnyObject>)["lng"] as NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as Dictionary<NSObject, AnyObject>)["lat"] as NSNumber).doubleValue
completionHandler(status: status, success: true)
}
else {
completionHandler(status: status, success: false)
}
}
})
}
else {
completionHandler(status: "No valid address.", success: false)
}
}
You can find the complete description of this method here: http://www.appcoda.com/google-maps-api-tutorial/
Second way is to use apple native CoreLocation framework
func geoLocate(address:String!){
let gc:CLGeocoder = CLGeocoder()
gc.geocodeAddressString(address, completionHandler: { (placemarks: [AnyObject]!, error: NSError!) -> Void in
let pm = placemarks as! [CLPlacemark]
if (placemarks.count > 0){
let p = pm[0]
var myLatitude = p.location.coordinate.latitude
var myLongtitude = p.location.coordinate.longitude
//do your stuff
}
})
}
EDIT: Swift 4.2
func geoLocate(address:String!){
let gc:CLGeocoder = CLGeocoder()
gc.geocodeAddressString(address) { (placemarks, error) in
if ((placemarks?.count)! > 0){
let p = placemarks![0]
print("Lat: \(p.location?.coordinate.latitude) Lon: \(p.location?.coordinate.longitude)")
}
}
}