Im trying to parse some data from the following link: https://feeds.divvybikes.com/stations/stations.json.
func getData() {
let url = URL(string:"https://feeds.divvybikes.com/stations/stations.json")
// Submit a request
let task = URLSession.shared.dataTask(with: url!) { (data,response,error) in
// print error if there is one
if error != nil {
print(error!)
self.alert(messageTitle: "Error", messageM: "An error occured")
return
} // end if
// if there is no error, fetch the json
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options:JSONSerialization.ReadingOptions.mutableContainers ) as AnyObject
if let stationName = json["stationBeanList"] as? [String: AnyObject] {
for station in stationName{
if let lat1 = ["latitude"] as? CGFloat, let long1 = ["longitude"] as? CGFloat{
print(lat1,long1)
}
}
}
}
catch {
print(error)
self.alert(messageTitle: "Error", messageM: "An error occured")
}
}//end if
}//end task
task.resume()
//endqueue
}
I would like to grab the longitude and latitude from the link above and plot them on a mapview embedded in swift but I cant seem to get even the longitude and latitude to print out or store properly. Any help would be much appreciated.
i do call the getData function on load()
and nothing shows up in the debugger when I run the application.
In the code you are considering the latitude and longitude as String. But its value is not type String.
So print(lat1,long1) will never be called.
Solution: Consider it as CGFloat instead of String, it will work I think so
let url = URL(string:"https://feeds.divvybikes.com/stations/stations.json")
// Submit a request to get the JASON data
let task = URLSession.shared.dataTask(with: url!) { (data,response,error) in
// if there is an error, print the error and do not continue
if error != nil {
print(error!)
self.alert(messageTitle: "Error", messageM: "An error occured")
return
} // end if
// if there is no error, fetch the json formatted content
if let content = data {
do {
let jsonObject = try JSONSerialization.jsonObject(with: content, options: [] ) as AnyObject
if let JSON = jsonObject["stationBeanList"] as? [[String:AnyObject]] {
for stationInfo in JSON {
if let longitude = stationInfo["longitude"] as? Float64, let latitude = stationInfo["latitude"] as? Float64 {
print(longitude,latitude)
// self.objects.append(stationInfo(latitude:latitude,longitude:longitude))
}//end if
}//end if let
}//end do
}//end do
catch {
print(error)
self.alert(messageTitle: "Error", messageM: "An error occured")
}
}//end if
}//end task
task.resume()
}//endqueue
}
After going through your code what I found is you made a just minor mistake to get lat-long from the call of web service.
if you want to fetch value you need to go according to this after the for loop of stationName.
Here is sample snippet for more clarification.
for station in stationName{
print(station["latitude"] as! Double)
print(station["longitude"] as! Double)
}
Hope this helps you :)
There are many issues.
The main issue is that the value for stationBeanList is an array not a dictionary.
.mutableContainers is completely useless in Swift, omit the parameter.
A Swift 3+ JSON dictionary is [String:Any], an array is [[String:Any]].
The unspecified JSON type in Swift 3+ is Any, never AnyObject.
A standard JSON floating point value is Double.
Never cast a JSON result to Any if the expected type is more specific.
if let json = try JSONSerialization.jsonObject(with: content) as? [String: Any] {
if let stationBeanList = json["stationBeanList"] as? [[String: Any]] {
for stationInfo in stationBeanList {
let lat = stationInfo["latitude"] as! Double
let lng = stationInfo["longitude"] as! Double
print(lat, lng)
}
}
}
Related
I do not know how to access the 'duration' value within my nested Optional NSSingleObjectArrayI that is constructed from a JSON response. How do I access the nested values within this data structure?
When I call print(firstRow["elements"]), I get the following output:
Optional(<__NSSingleObjectArrayI 0x60000120f920>(
{
distance = {
text = "1.8 km";
value = 1754;
};
duration = {
text = "5 mins";
value = 271;
};
"duration_in_traffic" = {
text = "4 mins";
value = 254;
};
status = OK;
}
))
I have tried string indexing (firstRow['elements']['duration']) but am getting errors.
fetchData { (dict, error) in
if let rows = dict?["rows"] as? [[String:Any]]{
if let firstRow = rows[0] as? [String:Any]{
print("firstRow is")
print(firstRow["elements"])
// Trying to access duration within firstRow['elements'] here
}
}
}
For reference, this is the fetchData function:
func fetchData(completion: #escaping ([String:Any]?, Error?) -> Void) {
let url = getRequestURL(origin: "test", destination: "test")!;
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
if let array = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any]{
completion(array, nil)
}
} catch {
print(error)
completion(nil, error)
}
}
task.resume()
}
A sample HTTP JSON request is here:
https://maps.googleapis.com/maps/api/distancematrix/json?destinations=77%20Massachusetts%20Ave,%20Cambridge,%20MA&departure_time=now&key=AIzaSyB65D4XHv6PkqvWJ7C-cFvT1QHi9OkqGCE&origins=428%20Memorial%20Dr,%20Cambridge,%20MA
Seeing your output, your firstRow["elements"] is Optional, so you need to unwrap it. And it actually is an NSArray with a single element, where the only element is a Dictionary, with 4 entries -- "distance", "duration", "duration_in_traffic" and "status". You may need to cast the element to a Dictionary to access each entry.
You may use Optional binding with as?-casting for this purpose:
fetchData { (dict, error) in
if let rows = dict?["rows"] as? [[String: Any]] {
if let firstRow = rows.first {
print("firstRow is")
print(firstRow["elements"])
//Unwrap and cast `firstRow["elements"]`.
if let elements = firstRow["elements"] as? [[String: Any]] {
//The value for "duration" is a Dictionary, you need to cast it again.
if let duration = elements.first?["duration"] as? [String: Any] {
print(duration["text"] as? String)
print(duration["value"] as? Int)
}
}
}
}
}
Or too deeply nested ifs are hard to read, so someone would like it as:
fetchData { (dict, error) in
if
let rows = dict?["rows"] as? [[String: Any]],
let firstRow = rows.first,
let elements = firstRow["elements"] as? [[String: Any]],
let duration = elements.first?["duration"] as? [String: Any]
{
print(duration["text"] as? String)
print(duration["value"] as? Int)
}
}
Or using guard may be a better solution.
Or else, if you can show us the whole JSON text in a readable format, someone would show you how to use Codable, which is a modern way to work with JSON in Swift.
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.
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 am trying to check the version of my app with the iTunes lookup api. I have problems in parsing the response. Please find the code
static func needsUpdate() -> Bool
{
do {
let infoDictionary = Bundle.main.infoDictionary
let appID = infoDictionary?["CFBundleIdentifier"]
let url:URL = URL(string: "http://itunes.apple.com/lookup?bundleId=\(appID!)")!
let data = try Data(contentsOf: url)
let lookup = try JSONSerialization.jsonObject(with:data, options: []) as! [String:AnyObject]
print(lookup)
let resultCount:Int = lookup["resultCount"] as! Int
if (resultCount == 1)
{
var results = lookup["results"] as! [String:AnyObject] // ***Error***
if results.isEmpty
{
print(results)
}
}
} catch
{
}
return true
}
Please let me know how can i parse this response
The error message clearly reveals that the value for results is an array.
let results = lookup["results"] as! [[String:Any]]
And consider that a JSON dictionary is [String:Any] in Swift 3
While fetching data from api I can get response either array of products or dictionary with error for e.g.
If everything went right api sends array of products as:
[
"Product1":
{
name = "someting",
price = 100,
discount = 10%,
images = [image1,image2]
},
"Product2":
{
name = "someting",
price = 100,
discount = 10%,
images = [image1,image2]
}
]
But if some error occur it sends dictionary with error message and code as:
{
error_message = "message"
error_code = 202
}
I am using this code to convert JSON data to array:
do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray{
//Some code....
} catch let error as NSError {
print("JSON Error: \(error.localizedDescription)")
}
but if I get error as dictionary it crash.
Problems:
1. How to know whether received data is an array or dictionary ?
2. Some time even key or value can be missing so checking for value it becomes very lengthy code like:
if let productsArray = jsonObject as? NSArray{
if let product1 = productsArray[0] as? NSDictionary{
if let imagesArray = product1["image"] as? NSArray{
if let imageUrl = imagesArray[0] as? String{
//Code ....
}
}
}
}
I read about guard keyword to reduce if condition but I don't have clear idea how to use here.
Problem 1:
For try catch , add an if let for casting the object as NSDictionary or NSArray like :
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
if let jsonDict = jsonObject as? NSDictionary {
// Do smthg.
}
if let jsonArray = jsonObject as? NSArray {
// Do smthg.
}
}catch {
//...
}
For Problem 2:
I think guard won't help you . It needs smthg like return / break in its else statement. If you don't want to throw your methods if one of your values isn't available you have to use this lengthy if let code style.
Maybe in your case best practice would be setting up a Data Model for Product with optional properties.
Class product {
var name:String?
var image:[NSData]? // maybe UIImage or smthg.
var price:Int?
var discount:Int?
init(jsonDic:NSDictionary){
// if it's not there it would be nil
self.name = jsonDic["name"] as? String
self.image = jsonDic["image"] as? NSArray
self.discount = jsonDic["discount"] as? Int
self.price = jsonDic["price"] as? Int
}
}
Now you can load those models with your data without the if let etc..
But if you wanna read those values you have to use the if let for checkin if its not nil.
For init in your case it should be something like this:
Add this into the if let statement of the do catch block ( ... as? NSArray // DO smthg. )
for item in jsonArray {
guard let jsonDic = item as? NSDictionary else { return }
// if you dont know every key you can just iterate through this dictionary
for (_,value) in jsonDic {
guard let jsonDicValues = value as? NSDictionary else { return }
productArray.append(Product(jsonDic: jsonDicValues)
}
}
As i said , know you got the whole if let stuff when reading from the model an not when writing ( reading the json )
You have a few things going on here, one, I would analyze your server's http response status code and only attempt to process data if you received a status code indicating you will have good data
// In practical scenarios, this may be a range
if statusCode != 200 {
// Handle a scenario where you don't have good data.
return
}
Secondly, I'd guard against the response, it looks like you have named it "data" like so:
guard let receivedData = data else {
return
}
From this point on, you can use the receivedData constant.
Here'd I'd attempt to use NSJSONSeralization, like you do, but by casting it into a Swift dictionary, like so:
if let responseDictionary = try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? [String:AnyObject] {
// Here you can try to access keys on the response
// You can try things like
let products = responseDictionary?["products"] as? [[String:AnyObject]]
for product in products {
let productName = product["name"] as? String
if productName == nil {
continue
}
let newProduct = Product(name: productName)
// Do something with newly processed data
}
}
I tried to be general and also show you a guard example.
first of all I recommend using SwiftyJSON pod or the class straight into your Xcode, it works like a charm and you won't need to cast things down to figure whether you have a string or a dictionary or whatever. It is gold.
Once you've got your JSON, you can use this recursive function I created that does exactly what you need. It turns any Json into a dictionary. I mostly use it to save data into Firebase, without having to parse everything.
After you have imported SwiftyJSON into your project and added import SwiftyJSON to your Swift file you can:
//JSON is created using the awesome SwiftyJSON pod
func json2dic(_ j: JSON) -> [String:AnyObject] {
var post = [String:AnyObject]()
for (key, object) in j {
post[key] = object.stringValue as AnyObject
if object.stringValue == "" {
post[key] = json2dic(object) as AnyObject
}
}
return post
}
let json = JSON(value) // Value is the json structure you received from API.
var myDictionary = [String:AnyObject]()
myDictionary = json2dic(json)
You can catch the class of your response. If your response is kind of class dictionary, assign it as dictionary else if your response is kind of class array, assign it to array. Good luck.