parsing json array in label swift 4 - json

This is my JSON array
[
{
"dollar":"15000",
"date":"1397-12-12"
}
]
i want to show dollar and date values in two different label but i have this type mismatch error :
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
this is my Struct :
struct Dollar_Date : Codable {
let dollar : String
let date : String
}
and this is my code in view controller :
func dollarLabel() {
let urlString = DOLLAR_DATE_BASE_URL
guard let url = URL(string: urlString) else { return }
// 2
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
do {
// 3
//Decode data
let JSONData = try JSONDecoder().decode(Dollar_Date.self, from: data)
// 4
print(JSONData.dollar)
//Get back to the main queue
DispatchQueue.main.async {
self.main_Price.text = JSONData.dollar
self.data_Label.text = JSONData.date
}
} catch let jsonError {
print(jsonError)
}
// 5
}.resume()
}

Try this:
guard let jsonArray = JSONData as? [[String: Any]] else {
return
}
print(jsonArray)
//Now get title value
guard let title = jsonArray[0]["title"] as? String else { return } print(title)

You get an error because your data contains an array of JSON (dictionary as well), not JSON itself.
So you can't decode with
let JSONData = try JSONDecoder().decode(Dollar_Date.self, from: data)
But you have to do it manually:
EDIT:
guard let json = data as? [[String:Any]] else{return}
Now you can retrieve dollars and date from all the elements of your array:
// These are because you have to access an element of the array
guard let dollar = json[0]["dollar"] as? String else {return}
guard let date = json[0]["date"] as? String else {return}
DispatchQueue.main.async {
self.main_Price.text = dollar
self.data_Label.text = date
}
if your data can contain multiple value (dollars-date couples) you have to loop your array. By the previous example, you should do:
for element in json{
guard let dollar = element["dollar"] as? String else {return}
guard let date = element["date"] as? String else {return}
// do some stuff with each value
}

Related

Fetching data from multi level JSON

I am having issue fetching products "name" from a json file.
and here is my code so far. and it prints complete json. I just want to fetch name of the products from this file.
guard let url = URL(string: "https://URL/get_products.php") else {return}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Response Error")
return }
do{
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
print(jsonResponse)
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
The structure is pretty clear, values next to string keys are dictionaries, next to ordinal numbers are arrays:
if let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse) as? [String:Any],
let data = jsonResponse["data"] as? [[String:Any]] {
for anItem in data {
if let products = anItem["products"] as? [[String:Any]] {
for product in products {
if let name = product["name"] as? String {
print(name)
}
}
}
}
}
A better way is to parse the JSON into structs with JSONDecoder

How parse a json data that has a dictionary key:value pair? I'm trying to extract the symbol and value pair

How to correctly parse the following json data?
My problem is at the line: if let rates = data["rates"] as? NSDictionary...
The json data
{ "valid": true,
"timestamp": 1579683079,
"base": "USD",
"rates": {
"AED": 3.67316,
"AFN": 77.99911,
"ALL": 110.11741,
...
"ZAR": 14.45,
"ZMW": 14.63257
}
}
Code
import UIKit
struct CurrencyRate {
var valid: String
var timestamp: Int
var base: String
var rates: [String:Double] = [:]
}
class ViewController: UIViewController {
var mySymbols:[String] = []
var myValues:[Double] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let jsonUrlString = "https://currencyapi.net/api/v1/rates?key=6b171cc58787d922eb53e3684d97784d165a&base=USD"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
//let dataAsString = String(data: data, encoding: .utf8)
if let rates = data["rates"] as? NSDictionary {
for (key, value) in rates {
self.mySymbols.append((key as? String)!)
self.myValues.append((value as? Double)!)
}
}
}.resume()
}
}
The syntax
for (key, value) in ...
to enumerate a dictionary exists only for native Swift dictionaries.
This is supposed to work, if you cast to specific [String:Double] you even get rid of the ugly type cast of key and value.
if let rates = data["rates"] as? [String:Double] {
for (key, value) in rates {
self.mySymbols.append(key)
self.myValues.append(value)
}
}
However you are encouraged to use the Decodable protocol to parse the JSON. There are only a few slightly changes.
struct CurrencyRate : Decodable {
let valid: Bool // must be Bool
let timestamp: Date
let base: String
let rates: [String:Double]
}
var rates = [String:Double]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let jsonUrlString = "https://currencyapi.net/api/v1/rates?key=6b171cc58787d922eb53e3684d97784d165a&base=USD"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, error) in
guard let error = error else { print(error); return }
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let result = try decoder.decode(CurrencyRate.self, from: data!)
self.rates = result.rates
print(rates)
} catch {
print(error)
}
}.resume()
}
timestamp is decoded as Date
Don't use NSDictionary - use Dictionary type in Swift. Also, you have data and it's not dictionary at all. It's Data type. Use should convert your data to your type. Try use this code:
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
let jsonResult = try? JSONSerialization.jsonObject(with: data)
if let dict = jsonResult as? Dictionary<String, Any>, let rates = dict["rates"] as? Dictionary<String, Double> {
for (key, value) in rates {
print(key, value)
self.mySymbols.append(key)
self.myValues.append(value)
}
}
}.resume()
There is a way with Codable, but I guess it will be the next challenge for you. Also, keep in mind that the code is executed asynchronously and the data in global variables will not appear immediately

Unable to parse JSON from link - no errors, but the function returns after trying to access the URL

I am new to Swift and trying basic JSON parsing by following tutorials. I want to print a field of a JSON file, but it is not working.
Although the link exists, and I am using the same link I used for a previous tutorial, it returns rather than moved on to accessing the JSON.
I understand there is an "easier" way to do it in Swift4 using Decoder, but I received an error when I did it that way.
Here is the structure I am using:
struct Tester {
var userId: Int
var id: Int
var title: String
var body: String
init(json: [String: Any]){
userId = json["userId"] as? Int ?? -10
id = json["id"] as? Int ?? -400
title = json["title"] as? String ?? ""
body = json["body"] as? String ?? ""
}
}
And here is the code that is trying to access the JSON entries
#IBAction func printIDTitle(_ sender: Any) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
guard let data = data else { return }
do {
print("here 0\n")
guard let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] else {
print(error)
return
}
print("here 0.5\n")
print("here 1\n")
let d = Tester(json: json)
print(d.id)
print(d.title)
print("here 2\n")
} catch let error {
print(error)
}
}.resume()
}
The "here 0" is the only print that shows up.
What could be my issue?
The root is an array so change
guard let json = try JSONSerialization.jsonObject(with: data, options:[]) as? [[String: Any]] else {
print(error)
return
}
Or better
let res = try! JSONDecoder().decode([Root].self, from:data)
struct Root: Codable {
let userId, id: Int
let title, body: String
}

filtering JSON data in swift

I am trying to parse my JSON data and append only those objects into array which meets specified condition. At the moment I have commented out code which fetch all objects from the API and add them into array. However, I would like to limit it so that it only appends objects with "wger.de" value for "license_author" key.
However I am getting error on line:
if eachExercise["license_author"] == "wger.de"
Binary operator '==' cannot be applied to operands of type 'Any?' and 'String'.
However I still wants to keep it as Any object because I would like to fetch both strings and integers data from my API.
This is the code for my parseData() function:
func parseData() {
fetchedExercise = []
let urlPath = "https://wger.de/api/v2/exercise/?format=json&language=2&status=2"
let url = URL(string: urlPath)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error while parsing JSON")
}
else {
do {
if let data = data,
let fetchedData = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String:Any],
let exercises = fetchedData["results"] as? [[String: Any]] {
// WORKING CODE
/*
for eachExercise in exercises
{
let name = eachExercise["name"] as! String
let description = eachExercise["description"] as! String
self.fetchedExercise.append(Exercise(name: name, description: description))
}
*/
// TESTING
for eachExercise in exercises {
if eachExercise["license_author"] == "wger.de" {
let name = eachExercise["name"] as! String
let description = eachExercise["description"] as! String
let id = eachExercise["id"] as! Int
self.fetchedExercise.append(Exercise(name: name, description: description))
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
catch {
print("Error while parsing data.")
}
}
}
task.resume()
}
Use the where clause and optional downcast Any to String
for eachExercise in exercises where eachExercise["license_author"] as? String == "wger.de" { ...
You need cast it to String.
if eachExercise["license_author"] as? String == "wger.de" {
}

Parse JSON with no title Swift 3

I am pulling down a json stream? From a phant server I can pull the data down parse it and print it in xcode. I need to pull out specific values but the json does not have a title and I can not seem to figure it out.
My JSON Data
(
{
lat = "36.123450";
long = "-97.123459";
timestamp = "2017-04-26T05:55:15.106Z";
},
My Current Code in Swift
let url = URL(string: "https://data.sparkfun.com/output/5JDdvbVgx6urREAVgKOM.json")
let task = URLSession.shared.dataTask(with: url!) {(data, response, error) in
if error != nil {
print("error")
} else {
if let content = data {
do {
// JSONArray
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(myJson)
let Coordinates = myJson["lat"] as! [[String:Any]]
print(Coordinates)
} catch {
}
}
}
}
task.resume()
}
Please read the JSON. [] represents an array, {} a dictionary.
The JSON is an array of dictionaries. All keys and values are String.
let url = URL(string: "https://data.sparkfun.com/output/5JDdvbVgx6urREAVgKOM.json")
let task = URLSession.shared.dataTask(with: url!) {(data, response, error) in
if error != nil {
print("error: ", error!)
} else {
do {
let coordinateArray = try JSONSerialization.jsonObject(with: data!) as! [[String:String]]
for coodinate in coordinateArray {
let lat = coodinate["lat"] ?? "n/a"
let long = coodinate["long"] ?? "n/a"
let timestamp = coodinate["timestamp"] ?? "n/a"
print("latitude: \(lat), longitude: \(long), timestamp: \(timestamp)")
}
} catch {
print (error)
}
}
}
task.resume()
As always, .mutableContainers has no effect in Swift but the tutorials which suggests that will never die off.
It looks like your JSON data is an array of objects, so myJson will be an array of dictionaries.
I would try something like this:
if let myJson = try JSONSerialization.jsonObject(with: content, options: []) as? [AnyObject] {
print(myJson)
for obj in myJson {
if let dict = obj as? [String:AnyObject] {
print(dict)
if let lat = dict["lat"] as? String,
let lng = dict["long"] as? String,
let time = dict["timestamp"] as? String {
// do something with stuff
}
}
}
}