I build an little task in SWIFT-XCode where I am trying to fetch and print some JSON Data, but the console doesn´t show me any error´s or result. Maybe someone can help me with the problem?. Here´s my code:
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(lat),\(long)&destination=\(lat+auflong),\(long+auflat)&key=**************")
let task = URLSession.shared.dataTask(with: url!) { (data:Data?, response:URLResponse?, error:Error?) in
if let data = data {
do {
// Convert the data to JSON
let jsonSerialized = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
if let json = jsonSerialized, let url = json["url"], let explanation = json["explanation"] {
print(url)
print(explanation)
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else if let error = error {
print(error.localizedDescription)
}
}
task.resume()
If you debug-step through the code, you see the result in the debug area: watch area.
In my test (using incorrect co-ordinates), when I put a breakpoint at the line starting `let jsonSerialized = '..., return 3 dictionary keys:
status
error_message
routes
But you were expecting url and explantion...
If you change the inner like:
let jsonSerialized = ...
if let json = jsonSerialized
{
print(json)
if let url = json["url"], let explanation = json["explanation"] {
print(url)
print(explanation)
}
}
} catch ...
you'll be able to print the returned json. (Replaced some code with ... for easy reading.)
My output:
["status": REQUEST_DENIED, "error_message": The provided API key is invalid., "routes": <__NSArray0 0x600000003910>(
)
]
Related
Currently I able to show full out out from GET method which return json data.
but I am unable to show individual object . i.e values of description or engine . but I can print the whole json data.
my code
let url = URL(string: "https://mylink/last")!
var request = URLRequest(url: url)
request.allHTTPHeaderFields = [
"Content-Type": "application/json",
"Session": "b14549"
]
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print (response)
}
if let data = data {
print (data)
do {
let jsonresult = try JSONSerialization.jsonObject(with: data, options: [])
// This works
print (jsonresult)
// Bellow does not work , Give Error Value of type 'any' has no subscripts
print (jsonresult["device_id"])
print (jsonresult["engine"])
} catch {
print(error)
}
}
}.resume()
}
I looked at other solution , tried bellow not working not sure if its related to the data type I am getting. I have posted the out put of jsonresult bellow.
JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
out put of Json result : -
(
{
"device_id" = "3aff273f-7f5f-49ef-81a6-50e2fcc2f69f”;
engine = 0;
"last_timestamp" = "2019-10-25 17:33:45";
},
{
"device_id" = "44b0ab5f-5289-4c56-b864-ce4899c2fcb8”;
engine = 0;
"last_timestamp" = "2019-10-25 17:33:40";
},
{
"device_id" = "c5639e8b-7f56-4021-9925-828ed735f527";
engine = 0;
}
)
The result is clearly an array, please note the () in the output.
You have to cast the result to the expected type
if let jsonresult = try JSONSerialization.jsonObject(with: data) as? [[String:Any]] {
for item in jsonresult {
print(item["device_id"])
print(item["engine"])
}
}
I'm following this tutorial: https://www.raywenderlich.com/160517/mapkit-tutorial-getting-started
These questions are in regard to swift (whatever the latest version of xcode uses), JSON and PHP. The tutorial works as is, but I want to make several modifications. I've done everything else but I'm stuck on the following questions.
There are several differences between the tutorial code and what I'm trying to get the app to do. Mainly, the JSON format in the tutorial is different from what my PHP page is spitting out.
I have several questions.
1) How do I modify the code to:
a) use the JSON data from a URL, not from the PublicArt.json file as used in the tutorial, and
b) how do I modify the code from the tutorial to accept the JSON format I'm receiving from a PHP file on my server?
The above question is in reference to the following 2 piece of code (original code in the tutorial):
init?(json: [Any]) {
// 1
self.title = json[16] as? String ?? "No Title"
self.locationName = json[12] as! String
self.discipline = json[15] as! String
// 2
if let latitude = Double(json[18] as! String),
let longitude = Double(json[19] as! String) {
self.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
} else {
self.coordinate = CLLocationCoordinate2D()
}
}
and this code (original code in the tutorial):
func loadInitialData() {
// 1
guard let fileName = Bundle.main.path(forResource: "PublicArt", ofType: "json")
else { return }
let optionalData = try? Data(contentsOf: URL(fileURLWithPath: fileName))
guard
let data = optionalData,
// 2
let json = try? JSONSerialization.jsonObject(with: data),
// 3
let dictionary = json as? [String: Any],
// 4
let works = dictionary["data"] as? [[Any]]
else { return }
// 5
let validWorks = works.flatMap { Artwork(json: $0) }
artworks.append(contentsOf: validWorks)
}
The JSON format used in the tutorial is this:
[ 55, "8492E480-43E9-4683-927F-0E82F3E1A024", 55, 1340413921, "436621", 1340413921, "436621", "{\n}", "Sean Browne", "Gift of the Oahu Kanyaku Imin Centennial Committee", "1989", "Large than life-size bronze figure of King David Kalakaua mounted on a granite pedestal. Located at Waikiki Gateway Park.", "Waikiki Gateway Park", "http://hiculturearts.pastperfect-online.com/34250images/002/199103-3.JPG", "1991.03", "Sculpture", "King David Kalakaua", "Full", "21.283921", "-157.831661", [ null, "21.283921", "-157.831661", null, false ], null ]
The format from my PHP file that I want to use is this:
{"id":1,"placeid":"1","lat":"25.4432","long":"-153.2345","location_title":"Sample Location","location_subtitle":"Sample Subtitle","log_status":"success"}
{"id":2,"placeid":"2","lat":"25.4543","long":"-153.2345","location_title":"Sample Location 2","location_subtitle":"Sample Subtitle 2","log_status":"success"}
{"id":3,"placeid":"3","lat":"25.4632","long":"-153.2345","location_title":"Sample Location 3","location_subtitle":"Sample Subtitle 3","log_status":"success"}
The tutorial uses file PublicArt.json and I use url htttp//www.samplesite.com/json.php (not really the url I use but you get it, the url I actually use produces working JSON code)
2) Finally, the tutorial uses a callout accessory as an info button to open up the Maps app to give directions to a location. Instead, how can I use this button to create a segue to a different ViewController when the button is clicked? I think this is the part of the code from the tutorial the opens the Maps app:
let mapsButton = UIButton(frame: CGRect(origin: CGPoint.zero,
size: CGSize(width: 30, height: 30)))
mapsButton.setBackgroundImage(UIImage(named: "Maps-icon"), for: UIControlState())
rightCalloutAccessoryView = mapsButton
Thanks for your help. It's very appreciated!
Edit:
Ok, so here's my code for getting information from "my PHP file". In this case you don't even need var1 and var2, because json.php is made to spit out all the data anyway. With my code I can just us responseJSON["whatever"] to get values out of the response string. I'm confused about the formatting in the tutorial.
let url = "https://www.samplesite.com/json.php"
let var1 = self.textForm1.text!
let var2 = self.textForm2.text!
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
let postString = "var=\(var1)&var2=\(var2)"
print(postString)
request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
guard error == nil && data != nil else {
print("error=\(String(describing: error))")
return
}
do {
if let responseJSON = try JSONSerialization.jsonObject(with: data!) as? [String:AnyObject]{
// If successful
if ((responseJSON["log_status"]!) as! String == "success") {
// do stuff here if log_status response is success
}
} else {
//If there is an error do stuff here
}
}
}
catch {
print("Error -> \(error)")
}
}
task.resume()
How do I modify the code from the tutorial to be able to parse the data in a similar way? This is the code from the tutorial:
func loadInitialData() {
// 1
guard let fileName = Bundle.main.path(forResource: "PublicArt", ofType: "json")
else { return }
let optionalData = try? Data(contentsOf: URL(fileURLWithPath: fileName))
guard
let data = optionalData,
// 2
let json = try? JSONSerialization.jsonObject(with: data),
// 3
let dictionary = json as? [String: Any],
// 4
let works = dictionary["data"] as? [[Any]]
else { return }
// 5
let validWorks = works.flatMap { Artwork(json: $0) }
artworks.append(contentsOf: validWorks)
}
I am working with Swift on Xcode and I try to parse a JSON file to retrieve some data about nearby stores.
My source code is the following:
import GooglePlaces
import SwiftyJSON
class Place {
let name: String
let coordinates: CLLocationCoordinate2D
init(diction:[String : Any])
{
let json = JSON(diction)
name = json["name"].stringValue //as! String
let lat = json["geometry"]["location"]["lat"].doubleValue as CLLocationDegrees
let long = json["geometry"]["location"]["lng"].doubleValue as CLLocationDegrees
coordinates = CLLocationCoordinate2DMake(lat, long)
}
}
class ViewController: UIViewController, MKMapViewDelegate, SceneLocationViewDelegate {
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?"
urlString += "&location=51.507514,-0.073603"
urlString += "&radius=1500" //meters
urlString += "&name=Specsavers"
urlString += "&key=**************************"
guard let url = URL(string: urlString) else {return}
var places = [Place]()
var request = URLRequest(url:url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
print("HEREurlSession")
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(json) // json results are printed fine here
if let results = json["results"] as? [[String : Any]] {
for place in results {
places.append(Place(diction: place))
}
}
else {
print("return")
}
}
catch{
}
}
}
task.resume()
let size = places.count
print("HERE: ", size)
}
The build is successful but the output is size = 0 which means that I do not retrieve the data and the variable places is empty.
I do not know if it is exactly relevant but I get the following warning: Cast from 'MDLMaterialProperty?!' to unrelated type '[[String : Any]]' always fails for the line if let results = json["results"] as? [[String : Any]] in my source code.
Why I do not parse the JSON file correctly and I do not retrieve the data the I want to?
URLSession.shared.dataTask(with:) is asynchronous. This means, it runs in the background. You are executing
let size = places.count
print("HERE: ", size)
while the dataTask is still working.
Instead, you should use your result in the completion handler:
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
print("HEREurlSession")
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(json)
if let results = json["results"] as? [[String : Any]] {
for place in results {
places.append(Place(diction: place))
}
}
else {
print("return")
}
}
catch{
}
}
// Use your result here
let size = places.count
useResultSize(size)
}
task.resume()
func useResultSize(_ size: Int) {
// Use your result here
print("HERE: ", size)
}
UPDATE
It seems, that you are missing what asynchronous execution actually means. Let me try to explain.
Lets mark the execution order in the code:
First, the red parts of your code are executed. Program execution starts at the top, then moves to the bottom red box and only after that (once the network request is finished) the green part is executed.
That means, that you can only use the result of the network request in the green part of the code. Outside of the green part, the result is not guaranteed to be available.
If you follow my initial advice, than everything should work. Please see the successful execution in my playground:
Im trying to access the variable pic after the request is made but its in a closure, thats why print(pic) doesn't work.
How would someone go about accessing this?
guard let url = URL(string: "myurl") else{ return }
var pic = ""
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
pic = parseJSON["picture"] as! String
print(json!)
}
} catch {
print(error)
}
}
}.resume()
print(pic)
}
Assuming pic is an image you'll be loading into a UIImageView:
You can add an Activity Indicator to your ImageView. Then when you call your function to download the pic simply add:
guard let url = URL(string: "myurl") else{ return }
activityIndicator.isHidden = false
activityIndicator.startAnimating()
The user will know a download is occurring. When complete,
DispatchQueue.main.async {
activityIndicator.isHidden = true
activityIndicator.stopAnimating()
myImageView.image = UIImage(named: "pic")
}
}.resume
Dispatching on the main que will update the UI immediately.
I am currently trying to download, parse and print JSON from an URL.
So far I got to this point:
1) A class (JSONImport.swift), which handles my import:
var data = NSMutableData();
let url = NSURL(string:"http://headers.jsontest.com");
var session = NSURLSession.sharedSession();
var jsonError:NSError?;
var response : NSURLResponse?;
func startConnection(){
let task:NSURLSessionDataTask = session.dataTaskWithURL(url!, completionHandler:apiHandler)
task.resume();
self.apiHandler(data,response: response,error: jsonError);
}
func apiHandler(data:NSData?, response:NSURLResponse?, error:NSError?)
{
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
catch{
print("API error: \(error)");
}
}
My problem is, that the data in
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
remains empty.
When I debug,the connection starts successfully, with the given url as a parameter. But my jsonData variable doesn't get printed. Instead the catch block throws the error, stating that there is no data in my variable:
API error: Error Domain=NSCocoaErrorDomain Code=3840 "No value."
Can someone please help me with this?
What am I missing?
Thank you all very much in advance!
[Edited after switching from NSURL Connection to NSURLSession]
Here's an example on how to use NSURLSession with a very convenient "completion handler".
This function contains the network call and has the "completion handler" (a callback for when the data will be available):
func getDataFrom(urlString: String, completion: (data: NSData)->()) {
if let url = NSURL(string: urlString) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (data, response, error) in
// print(response)
if let data = data {
completion(data: data)
} else {
print(error?.localizedDescription)
}
}
task.resume()
} else {
// URL is invalid
}
}
You can use it like this, inside a new function, with a "trailing closure":
func apiManager() {
getDataFrom("http://headers.jsontest.com") { (data) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
if let jsonDict = json as? NSDictionary {
print(jsonDict)
} else {
// JSON data wasn't a dictionary
}
}
catch let error as NSError {
print("API error: \(error.debugDescription)")
}
}
}