I have struct in my api
{
"Hall":"Hall",
"Date":20180501,
"Prices":[
{
"Time":1,
"Price":4000
},
{
"Time":2,
"Price":4000
},
{
"Time":3,
"Price":4000
}
]
}
Now I'm stuck and can't pull out the price and time. I understand that there were many question, but I still can't understand, please help.
I use this code:
let url = URL(string: "http://<...>/api/prices?hall=<...>&date=20180501")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if let err = error {
print(err)
} else {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
// ...
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
} catch {
print("error")
}
}
})
task.resume()
}
I'm new with json, and just started learning it. I know it's easy, but I can't figure it out. I also know that i can use codable and decodable, but now I need to get price and time in this implementation.
First of all don't use NSArray / NSDictionary, use native Swift types.
The value for key Prices is an array of [String:Int] dictionaries:
if let jsonResult = try JSONSerialization.jsonObject(with: data!) as? [String:Any],
let prices = jsonResult["Prices"] as? [[String:Int]] {
for price in prices {
print(price["Time"]!, price["Price"]!)
}
}
However I would recommended to decode the JSON into a struct which is very simple in Swift 4
struct Item : Decodable {
let hall : String
let date : Int
let prices : [Price]
private enum CodingKeys : String, CodingKey { case hall = "Hall", date = "Date", prices = "Prices"}
}
struct Price : Decodable {
let time, price : Int
private enum CodingKeys : String, CodingKey { case time = "Time", price = "Price"}
}
do {
let result = try JSONDecoder().decode(Item.self, from: data!)
print(result)
} catch { print(error) }
Product Structure:
struct Product{
let time:Int
let price:Int
init(_ object:[String:Int]){
self.time = object["Time"] ?? 0
self.price = object["Price"] ?? 0
}
}
Class Variable:
var products = [Product]()
JSON parsing:
do{
if let jsonObject = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any] {
//Use Swift dictionary instead of NSDictionary
if let prices = jsonObject["Prices"] as? [[String:Int]]{
for price in prices{
self.products.append(Product(price))
}
}
}
}catch{
print("Error: ",error.localizedDescription)
}
Now, your products Array will contain all Price and Time
Example:
for product in products{
print("Time:",product.time)
print("Price:",product.price)
}
Output:
Time: 1
Price: 4000
Time: 2
Price: 4000
Time: 3
Price: 4000
Note: For better understanding, this is my video series about JSON parsing in swift 4
var timeArray = [String]()
var priceArray = [String]()
if jsonResult.object(forKey:"Prices") as? NSArray != nil
{
let pricesArray = jsonResult.object(forKey:"Prices") as! NSArray
for i in 0..<self.pricesArray.count
{
// Getting Time
if jsonResult.object(forKey:"Time") as? Int != nil
{
self.timeArray.append(jsonResult.object(forKey:"Time") as! Int)
}
else
{
print("Time is not a intrger")
}
// Getting Price
if jsonResult.object(forKey:"Price") as? Int != nil
{
self.priceArray.append(jsonResult.object(forKey:"Price") as! Int)
}
else
{
print("Price is not a intrger")
}
}
}
else
{
print("Empty Array")
}
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any], let pirces = json["prices"] // checking json is formed well.
{
for price in prices { // iterating throught array that you received.
print (price["Price"])
}
}
Related
Needs to get country name from below api call :
https://restcountries.eu/rest/v1/all
My code :
var arrRes = []
func getCountry() {
let Url: String = "https://restcountries.eu/rest/v1/all"
Alamofire.request(Url).responseJSON { (responseData) -> Void in
do {
if let datas = responseData.result.value {
let data = (datas as AnyObject).data(using: .utf8)!
let parseData = try JSONSerialization.jsonObject(with: data, options: [])
for country in parseData {
if let name = country["name"] as? String {
print(name)
}
}
}
}
catch let error as NSError {
print(error)
}
}
}
getting error here : 'Any' is not convertible to 'AnyObject' on below line let data = (datas as AnyObject).data(using: .utf8)!..
I need to get only name and append to my array.Any other idea or solution to achieve that ?
Replace do catch block of statement with this.
do {
if let countries = responseData.result.value as? [[String: Any]] {
for country in countries {
if let name = country["name"] as? String {
print(name)
}
}
}
}
catch let error as NSError {
print(error)
}
Try this, its working fine for me.
let urlStr = "https://restcountries.eu/rest/v1/all"
let setFinalURl = urlStr.addingPercentEncoding (withAllowedCharacters: .urlQueryAllowed)!
var request = URLRequest(url: URL(string: setFinalURl)!)
request.httpMethod = HTTPMethod.get.rawValue
Alamofire.request(request).responseJSON
{ (responseObject) -> Void in
if responseObject.result.isSuccess
{
print(responseObject.result.value!)
if "\(String(describing: responseObject.response!.statusCode))" == "200"
{
let result = responseObject.result.value! as AnyObject
let countryNamesArr = result.value(forKey: "name") as! NSArray
print(countryNamesArr)
}
else
{
// handle error
}
}
if responseObject.result.isFailure
{
let error : Error = responseObject.result.error!
print(error.localizedDescription)
}
}
You can try
struct Root: Codable {
let name: String
}
func getCountry() {
let urlStr = "https://restcountries.eu/rest/v1/all"
Alamofire.request(urlStr).responseData { (data) in
do {
guard let data = data.data else { return }
let res = try JSONDecoder().decode([Root].self,from:data)
print(res)
}
catch {
print(error)
}
}
}
Just remove this line
let data = (datas as AnyObject).data(using: .utf8)!
and in optional binding just assign data, since value is of type Data?, from optional binding you get Data
if let data = responseData.result.value
then don't forget to downcast your json to array [String:Any]
...jsonObject(with: data, options: []) as? [[String:Any]]
... then don't forget to unwrap this array or you wouldn't be able to iterate through it in for each loop
Also note that since there is Codable, you should use it instead of JSONSerialization. Then you can decode your json using JSONDecoder to your own model which conforms to protocol Decodable.
As a simple approach, you could implement getCountry() like this:
func getCountry() {
let url: String = "https://restcountries.eu/rest/v1/all"
Alamofire.request(url).responseJSON { response in
if let resultValue = response.result.value, let countryObjects = resultValue as? [[String: Any]] {
let countryNames = countryObjects.compactMap { $0["name"] as? String }
print(countryNames)
}
}
}
At this point, there is no need to use JSONSerialization to get the country names; According to the API response, responseData.result.value is an array of countries (dictionaries), each dictionary has a "name" value, what you should do is to map the response to an array of string. countryNames should contains what are you looking for.
The benefit of using compactMap is to avoid any nil name, so countryNames should be [String] instead of [String?].
However, if you believe that you would need to transform the whole response objects into a custom objects (instead of dictionaries), I would highly recommend to follow the approach of using Decodable.
My code, its working well for me.
Swift 5
public func getCountry(completion: #escaping ([String]) -> ()) {
let url: String = "https://restcountries.eu/rest/v1/all"
AF.request(url).responseJSON { (responseData) -> Void in
do {
guard let data = responseData.data else { return }
let res = try JSONDecoder().decode([CountryName].self,from:data)
completion(self.getCountryName(countryName: res))
}
catch {
print(error)
}
}
}
struct CountryName: Codable {
let name: String
}
private func getCountryName(countryName:[CountryName]) -> [String]{
var country:[String] = []
for index in 0...countryName.count - 1{
country.append(countryName[index].name)
}
return country
}
Can somebody help with parsing this kind of JSON object?
{
"array":[
{
"title":"",
"desc":""
},
{
"title":"",
"desc":""
},
{
"title":"",
"desc":""
}
]
}
My code doesnt work
let task = self.session.dataTask(with: url) {
data, response, error in
if let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
for case let announcment in json!["array"]{
guard let title = announcment["title"] as? String,
let description = announcment["desc"] as? String,
else{ return }
}
task.resume()
Thank in advance for any help!
Pretty-printing your JSON makes it easier to work through:
{
"array":[
{
"title":"",
"desc":""
},
{
"title":"",
"desc":""
},
{
"title":"",
"desc":""
}
]
}
You need to get the array first. An array of dictionaries is of type [[String: Any]].
let task = self.session.dataTask(with: url) {
data, response, error in
if let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
guard let announcements = json["array"] as? [[String: Any]]
else { return }
announcements.forEach { announcement in
guard let title = announcement["title"] as? String,
let description = announcement["desc"] as? String
else { return }
// Do something with the result
}
}
}
task.resume()
You can structure your data and make it Codable:
struct Root: Codable {
let array: [Announcement]
}
struct Announcement: Codable {
let title: String
let desc: String
}
let data = Data("""
{"array":[{"title":"","desc":""},{"title":"","desc":""},{"title":"","desc":""}]}
""".utf8)
do {
let announcements = try JSONDecoder().decode(Root.self, from: data).array
for announcement in announcements {
print(announcement)
}
} catch {
print(error)
}
This will print
Announcement(title: "", desc: "")
Announcement(title: "", desc: "")
Announcement(title: "", desc: "")
i'm trying to import JSON data from the v2 of coinmarketcap API. I had it working with v1 as it was an array, however the new version is a dictionary and i cant quite get my struct correct.
The API im using is : https://api.coinmarketcap.com/v2/ticker/?convert=AUD
My struct is set up as below:
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
var id: String
var symbol : String
var name : String
var priceAUD : quoteStruct
}
struct quoteStruct{
let aud : priceStruct
}
struct priceStruct{
let price : String
}
My code for fetching the data is:
var coins = [Coin]()
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
My code for fetching the data i have used the same as previously which worked with v1 of the API, however i don't think i made my struct correctly.
Thanks in advance!
Your response Changed i try to configure it by converting it to array of dictionary you will need to change quotes to be [String:priceStruct]
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id,rank,symbol, name, priceAUD = "quotes"
}
var id: Int
var rank: Int
var symbol : String
var name : String
var priceAUD : [String: priceStruct]
}
struct priceStruct : Decodable{
let price : Double
}
func getCoinData() {
var coins = [Coin]()
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], let resultData = json["data"] as? [String:Any] {
let dataObject = try JSONSerialization.data(withJSONObject: resultData.values.map({$0}) , options: .prettyPrinted)
coins = try JSONDecoder().decode([Coin].self, from: dataObject)
print(coins.count)
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
You response in data parameter should be an array rather than a dictionary. You will not be able to iterate a dictionary over undefined keys. It would be good to get your response of API updated first.
But, If you wish to continue with the existing API response, first you need to convert your response in an array and use your Decodable structs as:
struct Coin: Decodable {
var id: String
var symbol : String
var name : String
var priceAUD : QuoteStruct
private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
}
struct QuoteStruct: Decodable {
let aud : PriceStruct
}
struct PriceStruct: Decodable {
let price : String
}
Update your data parsing in API block as:
guard let responseData = data else { return }
do {
let json = try? JSONSerialization.jsonObject(with: responseData, options: [])
if let jsonData = json as? [String: Any], let dataObject = jsonData["data"] as? [Int: Any] {
let coinArray = dataObject.map { $0.1 }
if let jsonData = try? JSONSerialization.data(withJSONObject: coinArray, options: .prettyPrinted) {
coins = try JSONDecoder().decode([Coin].self, from: jsonData)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
} catch {
print("Error is : \n\(error)")
}
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 have response from socket io library with response like this
socket.on("response-names") { data, ack in
print(data)
}
can print this,
[{
"person": [{
"_id":"56512323212132",
"name":"John Smith",
"id":"0000001",
"geolocation":{
"latitude":5.12312323443,
"longitude":101.12312464564354
}
}]
}]
How do you guys access the name, id and the geolocation(lat, long) in swift ?,
As far as I know the data is NSArray. but accessing is with data[0] will produce nil. but i can do data.count which will return with 1.
Thank you
Finally i solved it, just for future reference for others. It looks like you need to know the type before you parsing it. and check it with print.
and the debug code really helping, something like NSCFArray means your data as NSArray and try to read from that.
let dataArray = data as NSArray
let dataString = dataArray[0] as! String
let dataNewNow = dataString.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try NSJSONSerialization.JSONObjectWithData(dataNewNow, options: []) as! [String: AnyObject]
let str = json["person"] as! NSArray
let str2 = str[0] as! NSDictionary
let personName = str2["name"] as! String
let personId = str2["id"] as! String
let personGeoLocation = str2["geolocation"] as! NSDictionary
let personLatitude = personGeoLocation["latitude"] as! NSNumber
let personLongitude = personGeoLocation["longitude"] as! NSNumber
print("personName =", personName)
print("personID =", personId)
print("Person Latitude =", personLatitude)
print("Person Longitude =", personLongitude)
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
in my case this worked
let dataArray = data as NSArray
let dataString = dataArray[0] as! String
let dataNewNow = dataString.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: dataNewNow, options: []) as! [String: AnyObject]
let nMsgType = json["msg_type"] as! String
print(nMsgType)
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
I know it's a very old question but my answer may help someone ... In today's swift 5 it's very easy than before...
Create a SocketParser and use the below code to parse data...
class SocketParser {
static func convert<T: Decodable>(data: Any) throws -> T {
let jsonData = try JSONSerialization.data(withJSONObject: data)
let decoder = JSONDecoder()
do{
let _ = try decoder.decode(T.self, from: jsonData)
}catch{
createLog("CheckError \(error)")
}
return try decoder.decode(T.self, from: jsonData)
}
}
Now Create a simple model for your JSON ... in you case as bellow...
import Foundation
// MARK: - WelcomeElement
struct WelcomeElement: Codable {
let person: [Person]
}
// MARK: - Person
struct Person: Codable {
let id, name, personID: String
let geolocation: Geolocation
enum CodingKeys: String, CodingKey {
case id = "_id"
case name
case personID = "id"
case geolocation
}
}
// MARK: - Geolocation
struct Geolocation: Codable {
let latitude, longitude: Double
}
It's time to use these in response as below...
socket.on("response-names") { data, ack in
guard let data = data.first else{
return
}
if let response : WelcomeElement = try? SocketParser.convert(data: data) {
print(responce)
}