I am Parsing json data from google maps api in swift, am trying to show current location using google maps api, getting current location latitude and longitude using didUpdateLocations. inside the serviceLocationupdate()
posting lat and long values using post method but am not getting the response from the json. how can I get the response(data) from the json.
this is the code
var lat = ""
var long = ""
var latlng:String = ""
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location:CLLocationCoordinate2D = manager.location!.coordinate
self.lat = String(location.latitude)
self.long = String(location.longitude)
latlng = self.lat+"," + self.long
}
func serviceLocationupdate()
{
var request = URLRequest(url: URL(string: "http://maps.googleapis.com/maps/api/geocode/json")!)
let session = URLSession.shared
request.httpMethod = "POST"
----->> let bodyData = "latlng=\(latlng)&sensor=\("true")"
print("bodydata",bodyData)
request.httpBody = bodyData.data(using: String.Encoding.utf8);
let task = session.dataTask(with:request,completionHandler:{(d,response,error)in
do{
if let data = d{
do{
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
if let results = jsonResult["results"] as? [[String : AnyObject]] {
for result in results{
if let addressComponents = result["address_components"] as? [[String : AnyObject]] {
print(addressComponents)
}
}
}
} catch
{
}
}
}
})
task.resume()
}
if i can use this api its working fine getting proper data
var request = URLRequest(url: URL(string:"http://maps.googleapis.com/maps/api/geocode/json?latlng=13.026811,77.593773&sensor=true")!)
I want display the current location using latitude and longitude
Looks like the API works with the url you mentioned (http://maps.googleapis.com/maps/api/geocode/json?latlng=13.026811,77.593773&sensor=true), then you can just use GET instead of POST. Try the following:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location:CLLocationCoordinate2D = manager.location!.coordinate
self.lat = String(location.latitude)
self.long = String(location.longitude)
latlng = self.lat+","+self.long
serviceLocationupdate()
}
func serviceLocationupdate()
{
var request = URLRequest(url: URL(string: "http://maps.googleapis.com/maps/api/geocode/json?latlng=\(latlng)&sensor=true")!)
let task = session.dataTask(with:request,completionHandler:{(d,response,error)in
if let data = d{
do{
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
if let results = jsonResult["results"] as? [[String : AnyObject]] {
for result in results{
if let addressComponents = result["address_components"] as? [[String : AnyObject]] {
print(addressComponents)
}
}
}
} catch
{
}
}
})
task.resume()
}
This works in a playground:
//: Playground - noun: a place where people can play
import UIKit
import XCTest
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
func serviceLocationupdate()
{
let session = URLSession.shared
if let url = URL(string: "http://maps.googleapis.com/maps/api/geocode/json"),
var params = URLComponents(url: url, resolvingAgainstBaseURL: false) {
params.queryItems = [URLQueryItem]()
params.queryItems?.append(URLQueryItem(name: "latlng", value: "33.9250675,-84.339827"))
params.queryItems?.append(URLQueryItem(name: "sensor", value: "true"))
guard let finalURL = params.url else {
print("Failed to create URL")
return
}
var request = URLRequest(url: finalURL)
request.httpMethod = "POST"
let task = session.dataTask(with:request,completionHandler:{(data,response,error)in
if let error = error {
print("Error is \(error)")
return
}
if let resp = response as? HTTPURLResponse {
print("Response status code \(resp.statusCode)")
guard let data = data else {
return
}
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary,
let results = jsonResult["results"] as? [[String : AnyObject]] {
for result in results {
if let addressComponents = result["address_components"] as? [[String : AnyObject]] {
print(addressComponents)
}
}
} else {
print("Could not coerce into a dictionary")
}
} catch _ {
print("An error occurred")
}
} else {
print("Invalid response")
}
})
task.resume()
}
}
serviceLocationupdate()
Related
I need to fetch some quizzes for my application from the server. Unfortunately, it seems that URLSession.DataTask.shared is not working. How do I fix the problem?
This is for Swift 4.
import Foundation
import UIKit
class QuizService {
let baseUrl = "https://iosquiz.herokuapp.com/api/quizzes"
func fetchQuizzes(completion: #escaping (([Quiz]?) -> Void)) -> Void {
if let url = URL(string: baseUrl) {
let request = URLRequest(url: url)
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let resultsList = json as? [String: Any], let results = resultsList["quizzes"] as? [[String: Any]] {
let quizzes = results.map({ json -> Quiz? in
print(json)
if
let title = json["title"] as? String,
let id = json["id"] as? Int,
let level = json["level"] as? Int,
let description = json["description"] as? String,
let category = json["category"] as? String,
let questions = json["questions"] as? [String: Any],
let imageUrl = json["image"] as? String {
let quiz=Quiz(id:id,title:title,descript:description,category:category,level:level,imageUrl:imageUrl,questions:questions)
return quiz
} else {
return nil
}
}).filter { $0 != nil } .map { $0! }
completion(quizzes)
} else {
completion(nil)
}
} catch {
completion(nil)
}
} else {
completion(nil)
}
}
dataTask.resume()
} else {
completion(nil)
}
}
}
My error is that field of quizzes are null, so my code is not working in my view controller.
I took a look at the response here https://iosquiz.herokuapp.com/api/quizzes.
The "questions" should be array of dictionaries instead of dictionary.
so it should works if you replace this line
let questions = json["questions"] as? [String: Any]
with this line
let questions = json["questions"] as? [[String: Any]]
BTW, I prefer to extract the logic of parsing json into another method to keep fetchQuizzes method simple.
Hope this helps!
I created a Networking file for downloading data, and I want to assign the data to another view controller so I can populate a map with annotations. The data download successfully, but I can't get it to assign to the view controller. I can only get it working when I include the networking code in the view controller and use DispatchQueue.main.async. I want to keep the networking file and view controller separate. Any insights would be greatly appreciated. Apologies in advance for the many lines of code.
The networking file is as follows:
import UIKit
class Networking {
static let shared = Networking()
var objects = [Any]()
func getData (_ completionHandler:#escaping (Location?) -> ()) {
//Create the url with NSURL reuqest
let url = URL(string: "http://localhost:3000/locations")
let request = NSMutableURLRequest(url: url! as URL)
//Set HTTP method as GET
request.httpMethod = "GET"
//HTTP Headers
request.addValue("application/json", forHTTPHeaderField: "Accept")
//Create dataTask using the session object to send data to the server
URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
guard let data = data,
let dataStore = String(data: data, encoding: String.Encoding.utf8) else {
print("Could not find network")
completionHandler(nil)
return
}
guard error == nil else {
print("Error calling GET")
completionHandler(nil)
return
}
let HTTPResponse = response as! HTTPURLResponse
let statusCode = HTTPResponse.statusCode
if (statusCode == 200) {
print("Files downloaded successfully. \(dataStore)" )
} else {
completionHandler(nil)
return
}
//Create json object from data
do {
let json = try! JSONSerialization.jsonObject(with: data , options: []) as? [[String: Any]]
let location: [Location] = []
if let array = json {
for i in 0 ..< array.count {
if let data_object = array[i] as? [String: Any] {
if let _id = data_object["_id"] as? String,
let name = data_object["name"] as? String,
let imageID = data_object["imageID"] as? String,
let category = data_object["category"] as? String,
let details = data_object["details"] as? String,
let latitude = data_object["latitude"] as? Double,
let longitude = data_object["longitude"] as? Double {
var dictionary = [_id, name, imageID, category, details, latitude, longitude] as [Any]
dictionary.append(location)
}
}
}
}
}
}.resume()
}
}
The model is as follows:
class Location {
var _id : String
var name : String
var imageID : String
var category : String
var details : String
var latitude : Double
var longitude : Double
init?(_id: String, name: String, imageID: String, category: String, details: String, latitude: Double, longitude: Double) {
self._id = _id
self.name = name
self.imageID = imageID
self.category = category
self.details = details
self.latitude = latitude
self.longitude = longitude
}
}
The view controller is as follows:
class MapViewController: UIViewController, MGLMapViewDelegate, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
Networking.shared.getData { (locations) in
}
populateMap()
}
func populateMap (){
let point = MGLPointAnnotation()
for location in locations {
let coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude )
point.coordinate = coordinate
point.title = location.name
point.subtitle = location.category
self.mapView.addAnnotation(point)
}
}
You are executing completion blocks only in failure cases. Execute the completion block once you have managed to parse the data and pass the array as parameter to closure/block.
import UIKit
class Networking {
static let shared = Networking()
var objects = [Any]()
func getData (_ completionHandler:#escaping ([Location]?) -> ()) {
//Create the url with NSURL reuqest
let url = URL(string: "http://localhost:3000/locations")
let request = NSMutableURLRequest(url: url! as URL)
//Set HTTP method as GET
request.httpMethod = "GET"
//HTTP Headers
request.addValue("application/json", forHTTPHeaderField: "Accept")
//Create dataTask using the session object to send data to the server
URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
guard let data = data,
let dataStore = String(data: data, encoding: String.Encoding.utf8) else {
print("Could not find network")
completionHandler(nil)
return
}
guard error == nil else {
print("Error calling GET")
completionHandler(nil)
return
}
let HTTPResponse = response as! HTTPURLResponse
let statusCode = HTTPResponse.statusCode
if (statusCode == 200) {
print("Files downloaded successfully. \(dataStore)" )
} else {
completionHandler(nil)
return
}
//Create json object from data
do {
let json = try! JSONSerialization.jsonObject(with: data , options: []) as? [[String: Any]]
let location: [Location] = []
if let array = json {
for i in 0 ..< array.count {
if let data_object = array[i] as? [String: Any] {
if let _id = data_object["_id"] as? String,
let name = data_object["name"] as? String,
let imageID = data_object["imageID"] as? String,
let category = data_object["category"] as? String,
let details = data_object["details"] as? String,
let latitude = data_object["latitude"] as? Double,
let longitude = data_object["longitude"] as? Double {
var dictionary = [_id, name, imageID, category, details, latitude, longitude] as [Any]
dictionary.append(location) //am not sure of what this means test your code
completionHandler(location)
}
}
}
}
}
}.resume()
}
}
Few more mistakes in your code :
Your completion block expects Location as a parameter. but in your code you are creating an array of Locations.
let location: [Location] = []
So I have modified the completion block parameters to return array of locations
In your for loop you are creating
var dictionary = [_id, name, imageID, category, details, latitude, longitude] as [Any]
and appending it to dictionary.append(location) I have no idea what this code is. I believe what u actually trying to do is create a location object from the data and then add it to location array
location.append(your_new_location_object)
Hope it helps
I'm trying to pass data from a JSON response to a table view cell. I'm having problems with capturing the response values that I'm extracting in URLSession.shared.dataTask.
func callYouTubeAPIToGetAllVideos() {
let url = URL(string: "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=XYZ&maxResults=50&order=date&key=ABC")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
} else {
if let usableData = data {
let json = try? JSONSerialization.jsonObject(with: usableData, options: [])
if let dictionary = json as? [String: Any?] {
if let array = dictionary["items"] as? [Any] {
for object in array {
if let objectAsDictionary = object as? [String: Any?] {
if let objectWithKindAndVideoId = objectAsDictionary["id"] as? [String: String] {
if let videoId = objectWithKindAndVideoId["videoId"] {
//pass data to table cell
}
}
if let snippet = objectAsDictionary["snippet"] as? [String: Any] {
if let description = snippet["description"] {
//pass data to table cell
}
}
}
}
}
}
}
}
}
task.resume()
}
I tried appending the values to an instance variable but it didn't work.
Sorry about the messy code, this is my 1st time working with JSON in Swift.
First of all never declare a received JSON dictionary as [String:Any?]. A received dictionary value can't be nil.
Declare a custom struct Video.
struct Video {
let videoId : String
let description : String
}
Declare a data source array.
var videos = [Video]()
Parse the JSON into the array and reload the table view on the main thread.
func callYouTubeAPIToGetAllVideos() {
let url = URL(string: "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=XYZ&maxResults=50&order=date&key=ABC")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
} else {
do {
if let dictionary = try JSONSerialization.jsonObject(with: data!) as? [String: Any],
let array = dictionary["items"] as? [[String: Any]] {
for object in array {
if let objectWithKindAndVideoId = object["id"] as? [String: String],
let snippet = object["snippet"] as? [String: Any] {
let videoId = objectWithKindAndVideoId["videoId"] ?? ""
let description = snippet["description"] as? String ?? ""
let video = Video(videoId: videoId, description: description)
self.videos.append(video)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
}
task.resume()
}
In cellForRow assign the values to the text properties
let video = videos[indexPath.row]
cell.textLabel!.text = video.videoId
cell.detailTextLabel?.text = video.description
I try to get information about the weather hourly from the Dark Sky API, but the code stops working at the if let data = hourly["data"] as? [String : AnyObject] line (checked with printing stuff after every line). I want to know what is wrong with my code. I think it could be something with the "data" let, but I don't know for sure.
let Task2 = URLSession.shared.dataTask(with: urlRequestDark) { (data, response, error) in
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let hourly = json["hourly"] as? [String : AnyObject] {
if let data = hourly["data"] as? [String : AnyObject]{
if let hourNum = data["14"] as? [String : AnyObject] {
if let chanceRain = hourNum["precipProbability"] as? Float{
self.chanceHour1 = String(chanceRain)
}
DispatchQueue.main.sync {
self.ChanceRainLabel.text = self.chanceHour1
}
}
}
}
} catch let jsonError {
print(jsonError.localizedDescription)
}
}
}
Task2.resume() test
The strange part is, this does work:
let urlRequestDark = URLRequest(url: URL (string: "https://api.darksky.net/forecast/(API Key)/(coordinates)")!)
let Task = URLSession.shared.dataTask(with: urlRequestDark) { (data, response, error) in
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let currently = json["currently"] as? [String : AnyObject] {
if let chance2 = currently["precipProbability"] as? Float{
print(String(chance2))
self.chance = String(Int(chance2 * 100)) + "%"
self.PreType = currently["precipType"] as? String
}
if let _ = json["error"]{
}
DispatchQueue.main.sync{
self.TypeLabel.text = self.PreType
self.ChanceLabel.text = self.chance
}
}
}catch let jsonError{
print(jsonError.localizedDescription)
}
}
}
Task.resume()
You've made couple mistakes.
First, "data" is an array of dictionaries, so it should be cast to [[String : AnyObject]].
Second, you're trying to subscript array by String, not Int.
Third, using self in escaping closures potentially creates retain cycles.
Let me propose you some fixed and adjusted code.
let task2 = URLSession.shared.dataTask(with: urlRequestDark) { [weak self] (data, response, error) in
guard error == nil else { return }
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject],
let hourly = json["hourly"] as? [String : AnyObject],
let data = hourly["data"] as? [[String : AnyObject]],
data.count > 14,
let chanceRain = data[14]["precipProbability"] as? Float {
self?.chanceHour1 = String(chanceRain)
DispatchQueue.main.sync {
self?.ChanceRainLabel.text = self?.chanceHour1
}
}
} catch let jsonError {
print(jsonError.localizedDescription)
}
}
task2.resume()
Try like this
import UIKit
class WebService: NSObject {
var session = URLSession()
public class var sharedInstance: WebService {
struct Singleton {
static let instance = WebService()
}
return Singleton.instance
}
override init() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30.0
configuration.timeoutIntervalForResource = 60.0
session = URLSession(configuration: configuration)
}
public func weatherData(coordinate:String,APIkey:String,completion:#escaping (_ responsedata:NSDictionary?,_ error:NSError?) -> Void) {
var Baseurl = "https://api.darksky.net/forecast/\(APIkey)/\(coordinate)"
Baseurl = Baseurl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let weatherRequestUrl = URL(string: Baseurl)
let request = NSMutableURLRequest(url: weatherRequestUrl!)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard error == nil && data != nil else {
return
}
if let httpStatus = response as? HTTPURLResponse{
if httpStatus.statusCode != 200 {
print("Something is wrong")
}
}
do {
let WindlocationData = try JSONSerialization.jsonObject(with: data! as Data, options:.allowFragments) as! NSDictionary
print(WindlocationData)
completion(WindlocationData,nil)
}
catch let error as NSError {
completion(nil,error)
}
}
task.resume()
}
}
And call API like this!
func callAPI(latlong:String,APIkeyParm:String) {
WebService.sharedInstance.weatherData(coordinate: latlong,APIkey: APIkeyParm) { (responsData, error) in
if error == nil{
print("Response data is-\(responsData)")
}
}
}
Call the method like this
let latlongStr = "\(latitude),\(longitude)"
self.callAPI(latlong: latlongStr,APIkeyParm: "APIKeyString")
One importent thing you need to pass latlong like this format 23.022504999999999,72.571362100000002
I'm having a problem with the following code. I'm downloading a list of actors in JSON and I want to populate Struct Actor with the received data. Everything works great until I try to flatMap on the received data and try to initialize the struct Actor. When I try to compile the code i get the error: Cannot assign value of type '()' to type [Actor]. The error corresponds to a line in viewDidLoad actorsList = downloadActors() Would anybody have any recommendation who to solve this?
import UIKit
func downloadActors() {
var request = URLRequest(url: URL(string: "url...")!)
request.httpMethod = "POST"
let postString = "actorGroup=\("Superhero")"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async {
guard let data = data, error == nil else {
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
print("error : statusCode should be 200 but is \(httpStatus.statusCode)")
print("response = \(response)")
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: AnyObject]
guard let actorsJSON = json?["response"] as? [[String : AnyObject]] else {
return
}
} catch {
print("catch error")
}
}
}
}
task.resume()
}
func loadActors() -> [Actor] {
if let actors = actorsJSON as? [[String : AnyObject]] {
return actors.flatMap(Actor.init)
}
}
let actorsArray = loadActors()
class MasterViewController: UITableViewController {
var actorsList = [Actor]()
var detailViewController: DetailViewController? = nil
var objects = [Any]()
override func viewDidLoad() {
super.viewDidLoad()
actorsList = downloadActors()
print(actorsList)
Struct Actors is as follows:
struct Job {
let actorGroup: String
let actorName: String
}
extension Actor: JSONDecodable {
init?(JSON: [String : AnyObject]) {
guard let actorGroup = JSON["actorGroup"] as? String, let actorName = JSON["actorName"] as? String else {
return nil
}
self. actorGroup = actorGroup
self. actorName = actorName
}
}
let listActors = actorsJSON as? [[String : AnyObject]] {
Should be:
if let listActors = actorsJSON as? [[String : AnyObject]] {
Edit: For more info I'd like to add Vadian's comment:
Very confusing code. What does the function in the middle of the do block? Why do you type-check actorsJSON twice? The computed property is let listActors... which should be probably an optional binding (if let ... ). Further .mutableContainers is completely nonsense in Swift. And finally a JSON dictionary is [String:Any] in Swift 3.