I'm using Google Places in one of my apps where given coordinates, I need to find nearby places. Places' iOS API can either use current place or you can use place picker. I don't want either of these but to pass coordinates and get nearby places as I'm getting these coordinates from an image.
When Cinema pass that place you should get type like restaurant,bars etc
Swift
func fetchPlacesNearCoordinate(coordinate: CLLocationCoordinate2D, radius: Double, name : String){
let apiServerKey = "API-Key"
let urlString :URLRequest = URLRequest.init(url: URL.init(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=\(apiServerKey)&location=\(currentlatitude),\(currentlongitude)&radius=\(radius)&types=\(Cinema)&rankby=prominence&sensor=true&name=\(Cinemas)")!)
print(urlString)
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let placesTask = URLSession.shared.dataTask(with: urlString) {data, response, err in guard let data = data, err == nil else{
print("error ==>\(err)")
return
}
let responsestring = String(data: data, encoding: .utf8)
do {
let jsondata1 = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! NSDictionary
print("Final data Convert to JSON =>\(jsondata1)")
let results = jsondata1["results"] as! NSArray
print("Results ==> \(results)")
}
catch{
print("error ==>\(err)")
}
}
placesTask.resume()
}
This will return you nearby places by passing coordinates . Just call this function and pass your coordinates
func getPlacesUsingCoordinates(coordinate: CLLocationCoordinate2D, radius: Double, name : String){
//Pass name of place you want to search nearby in name
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=\(apiServerKey)&location=\(coordinate.latitude),\(coordinate.longitude)&radius=\(radius)&rankby=prominence&sensor=true"
urlString += "&name=\(name)"
urlString = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
placesTask = session.dataTaskWithURL(NSURL(string: urlString)!) {data, response, error in
if let json = NSJSONSerialization.JSONObjectWithData(data, options:nil, error:nil) as? NSDictionary {
if let results = json["results"] as? NSArray {
if let name = results["name"] as? String {
print(name) //Place Name
}
if let location = results["geometry"]
print(location) //Geometry
}
}
}
self.placesTask.resume()
}
}
If you want further place details use :
CLGeocoder().reverseGeocodeLocation(yourLocation) { (placeDetails, error) in
// This provide every info related to location. Just pass yourLocation here
}
Hope this is what you are looking out for.
Related
I'm new to Swift, and I want to 1) run a function that extracts a value from a JSON array (this part works) and 2) pass that variable into another function which will play that URL in my audio player.
My issue: I can't access that string stored in a variable outside the first function. Luckily, there's a bunch of questions on this (example), and they say to establish a global variable outside the function and update it. I have tried this like so:
var audio = ""
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://www.example.json")
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
let json: Any?
do{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch{
return
}
guard let data_list = json as? [[String:Any]] else {
return
}
// here's the important part
if let foo = data_list.first(where: {$0["episode"] as? String == "Special Episode Name"}) {
// do something with foo
self.audio = (foo["audio"] as? String)!
} else {
// item could not be found
}
}).resume()
print(audio) // no errors but doesn't return anything
I have confirmed the JSON extraction is working -- if I move that print(audio) inside the function, it returns the value. I just can't use it elsewhere.
I originally tried it without the self. but returned an error.
Is there a better way to store this string in a variable so I can use it in another function?
EDIT: Trying new approach based on Oleg's first answer. This makes sense to me based on how I understand didSet to work, but it's still causing a thread error with the play button elsewhere.
var audiotest = ""{
didSet{
// use audio, start player
if let audioUrl = URL(string: audiotest) {
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
//let url = Bundle.main.url(forResource: destinationUrl, withExtension: "mp3")!
do {
audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)
} catch let error {
print(error.localizedDescription)
}
} // end player
}
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://www.example.com/example.json")
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
let json: Any?
do{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch{
return
}
guard let data_list = json as? [[String:Any]] else {
return
}
if let foo = data_list.first(where: {$0["episode"] as? String == "Houston Preview"}) {
// do something with foo
self.audiotest = (foo["audio"] as? String)!
} else {
// item could not be found
}
print(self.audiotest)
}).resume()
The request for the data is asynchronous so the code that is inside the completionHandler block happens some time later (depending on the server or the timeout) , that’s why if you try to print outside the completionHandler actually the print func happens before you get the data.
There are couple of solution:
1. Add property observer to your audio property and start playing when it is set:
var audio = “”{
didSet{
// use audio, start player
}
}
2. Wrapping the request with a method that one of its parameters is a completion closure:
// the request
func fetchAudio(completion:(String)->()){
// make request and call completion with the string inside the completionHandler block i.e. completion(audio)
}
// Usage
fetchAudio{ audioString in
// dispatch to main queue and use audioString
}
Try this code. No need to take global variable if it is not being used in multiple function. you can return fetched URL in completion handler.
func getAudioUrl(completionHandler:#escaping ((_ url:String?) -> Void)) {
let url = URL(string: "http://www.example.json")
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
let json: Any?
do{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch{
return
}
guard let data_list = json as? [[String:Any]] else {
return
}
// here's the important part
if let foo = data_list.first(where: {$0["episode"] as? String == "Special Episode Name"}) {
// do something with foo
let audio = (foo["audio"] as? String)!
completionHandler(audio)
} else {
// item could not be found
completionHandler(nil)
}
}).resume()
}
func useAudioURL() {
self.getAudioUrl { (url) in
if let strUrl = url {
// perform your dependant operation
print(strUrl)
}else {
//url is nil
}
}
}
I am new to swift and Maps. I am facing problem with displaying user live location. I have to display user location like Uber and Ola. I am getting array of coordinates from server.
This is the way i am fetching coordinates from server. I want to show moving user location. see following my code.
func SetUpMapsUI()
{
AdminAPIManager.sharedInstance.getAdminRunningStatusFromURL(){(resignationsJson)-> Void in
let swiftyJsonVar = JSON(resignationsJson)
let status = swiftyJsonVar["status"].rawString() as! String
print("status",status)
let message = swiftyJsonVar["message"].rawString()
if status.isEqual("0"){
if (message?.isEqual("No trips done so far."))!
{
self.mapViewBottomCons.constant = 0
}else
{
self.mapViewBottomCons.constant = 70
}
self.Bottom_view.isHidden = true
Toast.short(message: message as! String)
return
}
let busVar = swiftyJsonVar["bus_details"].rawString()!
let jsonData = busVar.data(using: .utf8)!
let LocationArray = try? JSONSerialization.jsonObject(with: jsonData, options: []) as! Array< Any>
for data in LocationArray!
{
let dic = data as! NSDictionary
guard let lat = dic.value(forKey: "latitude") as? Double else {
return
}
print("latlatlatlat",lat)
guard let lon = dic.value(forKey: "longitude") as? Double else {
return
}
print("longitude",lon)
self.arrayMapPath.append(NewMapPath(lat: Double(lat), lon: Double(lon)))
}
if self.arrayMapPath.count > 0
{
self.drawPathOnMap()
}
}
here is library that does that functionality it is written for both swift and ObjC..
ARCarMovement
here is a Stackoverflow SO answer
Hi I'm trying to get data from a certain JSON API. I can gat a snapshot of all values from the API, which is shown below. But I can't manage to put a specifiek row in a variable. This is the JSON form which I get. I want to print the "Description" value.Can someone help me with this?
And Hier is my code:
func apiRequest() {
let config = URLSessionConfiguration.default
let username = "F44C3FC2-91AF-5FB2-8B3F-70397C0D447D"
let password = "G23#rE9t1#"
let loginString = String(format: "%#:%#", username, password)
let userPasswordData = loginString.data(using: String.Encoding.utf8)
let base64EncodedCredential = userPasswordData?.base64EncodedString()
let authString = "Basic " + (base64EncodedCredential)!
print(authString)
config.httpAdditionalHeaders = ["Authorization" : authString]
let session = URLSession(configuration: config)
var running = false
let url = NSURL(string: "https://start.jamespro.nl/v4/api/json/projects/?limit=10")
let task = session.dataTask(with: url! as URL) {
( data, response, error) in
if let taskHeader = response as? HTTPURLResponse {
print(taskHeader.statusCode)
}
if error != nil {
print("There is an error!!!")
print(error)
} else {
if let content = data {
do {
let array = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(array)
if let items = array["items"] {
if let description = items["Description"] as? [[String:Any]]{
print(description as Any)
}
}
}
catch {
print("Error: Could not get any data")
}
}
}
running = false
}
running = true
task.resume()
while running {
print("waiting...")
sleep(1)
}
}
First of all the array is not an array and not AnyObject, it's a dictionary which is [String:Any] in Swift 3.
let dictionary = try JSONSerialization.jsonObject(with: content) as! [String:Any]
print(dictionary)
I don't know why all tutorials suggest .mutableContainers as option. That might be useful in Objective-C but is completely meaningless in Swift. Omit the parameter.
The object for key itemsis an array of dictionaries (again, the unspecified JSON type in Swift 3 is Any). Use a repeat loop to get all description values and you have to downcast all values of a dictionary from Any to the expected type.
if let items = dictionary["items"] as? [[String:Any]] {
for item in items {
if let description = item["Description"] as? String {
print(description)
}
}
}
Looks like items is an array that needs to be looped through. Here is some sample code, but I want to warn you that this code is not tested for your data.
if let items = array["items"] as? [[String: AnyObject]] {
for item in items {
if let description = item["Description"] as? String{
print("Description: \(description)")
}
}
}
This code above, or some variation of it, should get you on the right track.
use the SwiftyJSON and it would be as easy as json["items"][i].arrayValue as return and array with items Values or json["items"][i]["description"].stringValue to get a string from a row
I'm using google maps in my iOS swift project. I want to draw a path between two locations on the map (Not straight line). Any idea how to do that ?
To draw polyline between two locations on Google Map in Swift.
//Pass your source and destination coordinates in this method.
func fetchRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
let session = URLSession.shared
let url = URL(string: "http://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)&sensor=false&mode=driving")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
guard error == nil else {
print(error!.localizedDescription)
return
}
guard let jsonResponse = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] else {
print("error in JSONSerialization")
return
}
guard let routes = jsonResponse["routes"] as? [Any] else {
return
}
guard let route = routes[0] as? [String: Any] else {
return
}
guard let overview_polyline = route["overview_polyline"] as? [String: Any] else {
return
}
guard let polyLineString = overview_polyline["points"] as? String else {
return
}
//Call this method to draw path on map
self.drawPath(from: polyLineString)
})
task.resume()
}
To draw polyline on map .
func drawPath(from polyStr: String){
let path = GMSPath(fromEncodedPath: polyStr)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 3.0
polyline.map = mapView // Google MapView
}
Showing Multiple-routes between two locations in google maps in swift 3.0 with camera zoom:
let origin = "\(startLocation.coordinate.latitude),\(startLocation.coordinate.longitude)"
let destination = "\(destinationLocation.coordinate.latitude),\(destinationLocation.coordinate.longitude)"
let urlString = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving&key=API_KEY"
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: {
(data, response, error) in
if(error != nil){
print("error")
}else{
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
let routes = json["routes"] as! NSArray
self.mapView.clear()
OperationQueue.main.addOperation({
for route in routes
{
let routeOverviewPolyline:NSDictionary = (route as! NSDictionary).value(forKey: "overview_polyline") as! NSDictionary
let points = routeOverviewPolyline.object(forKey: "points")
let path = GMSPath.init(fromEncodedPath: points! as! String)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 3
let bounds = GMSCoordinateBounds(path: path!)
self.mapView!.animate(with: GMSCameraUpdate.fit(bounds, withPadding: 30.0))
polyline.map = self.mapView
}
})
}catch let error as NSError{
print("error:\(error)")
}
}
}).resume()
Above all answers can draw routes but there is a way that you can draw accurate route using directions API.Here is the code hopefully it will help for you.
func getRouteSteps(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
let session = URLSession.shared
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)&sensor=false&mode=driving&key=\(Your_API_Key)")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
guard error == nil else {
print(error!.localizedDescription)
return
}
guard let jsonResult = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] else {
print("error in JSONSerialization")
return
}
guard let routes = jsonResult["routes"] as? [Any] else {
return
}
guard let route = routes[0] as? [String: Any] else {
return
}
guard let legs = route["legs"] as? [Any] else {
return
}
guard let leg = legs[0] as? [String: Any] else {
return
}
guard let steps = leg["steps"] as? [Any] else {
return
}
for item in steps {
guard let step = item as? [String: Any] else {
return
}
guard let polyline = step["polyline"] as? [String: Any] else {
return
}
guard let polyLineString = polyline["points"] as? String else {
return
}
//Call this method to draw path on map
DispatchQueue.main.async {
self.drawPath(from: polyLineString)
}
}
})
task.resume()
}
And then
//MARK:- Draw Path line
func drawPath(from polyStr: String){
let path = GMSPath(fromEncodedPath: polyStr)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 3.0
polyline.map = mapView // Google MapView
let cameraUpdate = GMSCameraUpdate.fit(GMSCoordinateBounds(coordinate: sourceLocationCordinates, coordinate: destinationLocationCordinates))
mapView.moveCamera(cameraUpdate)
let currentZoom = mapView.camera.zoom
mapView.animate(toZoom: currentZoom - 1.4)
}
Note: I have added sourcesLocationCordinates and destinationLocationCordinates variables.Don't forget to replace them with your source and destination.Hopefully this will help you making perfect route line.
Create a new Swift file copy this code, that's it call then drawPolygon() method from map view for polygon line.
import GoogleMaps
private struct MapPath : Decodable{
var routes : [Route]?
}
private struct Route : Decodable{
var overview_polyline : OverView?
}
private struct OverView : Decodable {
var points : String?
}
extension GMSMapView {
//MARK:- Call API for polygon points
func drawPolygon(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D){
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
guard let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)&sensor=false&mode=driving") else {
return
}
DispatchQueue.main.async {
session.dataTask(with: url) { (data, response, error) in
guard data != nil else {
return
}
do {
let route = try JSONDecoder().decode(MapPath.self, from: data!)
if let points = route.routes?.first?.overview_polyline?.points {
self.drawPath(with: points)
}
print(route.routes?.first?.overview_polyline?.points)
} catch let error {
print("Failed to draw ",error.localizedDescription)
}
}.resume()
}
}
//MARK:- Draw polygon
private func drawPath(with points : String){
DispatchQueue.main.async {
let path = GMSPath(fromEncodedPath: points)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 3.0
polyline.strokeColor = .red
polyline.map = self
}
}
}
This piece of code will work right for you. Don't forget to change your API key and mode (walking, driving).
func draw(src: CLLocationCoordinate2D, dst: CLLocationCoordinate2D){
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(src.latitude),\(src.longitude)&destination=\(dst.latitude),\(dst.longitude)&sensor=false&mode=walking&key=**YOUR_KEY**")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
} else {
do {
if let json : [String:Any] = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] {
let preRoutes = json["routes"] as! NSArray
let routes = preRoutes[0] as! NSDictionary
let routeOverviewPolyline:NSDictionary = routes.value(forKey: "overview_polyline") as! NSDictionary
let polyString = routeOverviewPolyline.object(forKey: "points") as! String
DispatchQueue.main.async(execute: {
let path = GMSPath(fromEncodedPath: polyString)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 5.0
polyline.strokeColor = UIColor.green
polyline.map = mapView
})
}
} catch {
print("parsing error")
}
}
})
task.resume()
}
So I have a problem where I call save after putting some items in an object. I go to an API and I download some info and then I save it to another system for temporary use, however the .save() seems to only save 2 items, with no special pattern in which it selects. Can someone explain what the problem is?
let url = URL(string: link)
let spots = PFObject(className:"spot")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
}
else{
if let urlContent = data{
do{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers)
let results = (jsonResult as! NSDictionary)["results"]
let venueList = (results as! NSArray)
//print(jsonResult)
var i = 0
while i < venueList.count{
let venues = venueList[i] as! NSDictionary
let name = venues["name"] as! String
let geometry = venues["geometry"] as! NSDictionary
let location = geometry["location"] as! NSDictionary
let cLat = location["lat"] as! Double
let cLon = location["lng"] as! Double
let vPoint = PFGeoPoint(latitude: cLat, longitude: cLon)
//print(name," ", vPoint)
spots["venue"] = name
spots["name"] = vPoint
do{
try HotSpots.save()
print("Saved! ",name," ", vPoint)
}
catch{
print("Save Error")
}
i+=1
}
}
catch{
print("JSON Error")
}
}
}
}
task.resume()
}
The issue is that you're saving the two values always in the same PFObject instance.
Move the line let spots = PFObject(className:"spot") in the loop.
PS: Use Swift native types. For example this is more efficient
let location = geometry["location"] as! [String:Double]
let cLat = location["lat"]!
let cLon = location["lng"]!