Encodable JSON dictionary in Swift 4 - json

With the help of #matt. I was able to solve it and the finished code is below for anyone who will encounter similer issues in the near future.
Given the below JSON
{
"restaurants": [
{
"id": 1,
"restaurantName": "Chuks",
"restaurantPicture": "restaurantImages/benards.jpg",
"status": "Working",
"workingDays": "Tuesday to Sunday",
"workingHours": "3pm to 10pm"
},
{
"id": 3,
"restaurantName": "Chuks",
"restaurantPicture": "restaurantImages/benards.jpg",
"status": "Working",
"workingDays": "Tuesday to Sunday",
"workingHours": "3pm to 10pm"
},
{
"id": 4,
"restaurantName": "Chuks",
"restaurantPicture": "restaurantImages/benards.jpg",
"status": "Working",
"workingDays": "Tuesday to Sunday",
"workingHours": "3pm to 10pm"
},
{
"id": 5,
"restaurantName": "Chuks",
"restaurantPicture": "restaurantImages/benards.jpg",
"status": "Working",
"workingDays": "Tuesday to Sunday",
"workingHours": "3pm to 10pm"
},
{
"id": 6,
"restaurantName": "Chuks",
"restaurantPicture": "restaurantImages/benards.jpg",
"status": "Working",
"workingDays": "Tuesday to Sunday",
"workingHours": "3pm to 10pm"
}
]
}
And I am trying to access the arrays within the Dictionary with this code into a table. So I have made a Struct. Below is the content of my Structfile.
struct Response: Codable {
let restaurants: [Restaurant]
struct Restaurant: Codable {
let id: Int
let restaurantName: String
let restaurantPicture: String
let status: String
let workingDays: String
let workingHours: String
}
}
This is my main View Controller
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tabelView: UITableView!
var restaurants = [Response]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
downloadJSON {
self.tabelView.reloadData()
}
tabelView.delegate = self
tabelView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return restaurants.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = restaurants[indexPath.row].restaurantName.capitalized
return cell
}
func downloadJSON(completed: #escaping ()->()){
let url = URL(string: "http://localhost:8888/tableview/tableView.php")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil{
do{
let downloadedRestaurants = try JSONDecoder().decode(Response.self, from: data!)
print(downloadedRestaurants.restaurants.map{$0.restaurantName})
self.restaurant.append(downloadedRestaurants)
DispatchQueue.main.async {
completed()
}
}catch{
print(error)
}
}
}.resume()
}
}
Thanks #matt. I hope this piece becomes useful to other newbies or anyone who is having similar issues
Cheers!

The problem is that you are trying to decode as [RestaurantStats]. But look at your JSON! It is not an array of RestaurantStats. It is not an array at all. It is a dictionary with a single key, "restaurants". You need to create that struct and decode as that.
EDIT Since I gave my answer, you have revised your code to declare an outer struct Response. And that's correct — and is exactly what I said you should do. So decode as Response and you're all set.

Related

Can't Decode With JSONDecoder

How can I decode this JSON by using JSONDecoder? I'm trying, but I always ended up crashing. I have another post talking about it but the error was bigger before, now I'm stucked only on that. I also tried by using JSONSerialization, but I think using JSONDecoder is more clean.
I got this error message when the compiler pass by JSONDecoder part:
Could not get API data. typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil)), The data couldn’t be read because it isn’t in the correct format.
This is how I'm trying to parse all my data:
import Foundation
//typealias AudiobookJSON = [[String: Any]]
struct APIClient {
static func getAudiobooksAPI(completion: #escaping ([Audiobook]?) -> Void) {
let url = URL(string: "https://alodjinha.herokuapp.com/categoria")
let session = URLSession.shared
guard let unwrappedURL = url else { print("Error unwrapping URL"); return }
let dataTask = session.dataTask(with: unwrappedURL) { (data, response, error) in
guard let unwrappedDAta = data else { print("Error unwrapping data"); return }
do {
let posts = try JSONDecoder().decode([Audiobook].self, from: unwrappedDAta)
print(posts)
completion(nil)
} catch {
print("Could not get API data. \(error), \(error.localizedDescription)")
}
}
dataTask.resume()
}
}
Struct that I'm using:
import Foundation
struct Data : Decodable {
let data : [Audiobook]
}
struct Audiobook: Decodable {
let id : Int?
let descricao : String?
let urlImagem : String?
// init(dictionary: Audiobook) {
// self.descricao = dictionary["descricao"] as! String
// self.urlImagem = dictionary["urlImagem"] as! String
//
// }
}
JSON to be parsed:
{
"data": [
{
"id": 1,
"descricao": "Games",
"urlImagem": "http:\/\/39ahd9aq5l9101brf3b8dq58.wpengine.netdna-cdn.com\/wp-content\/uploads\/2013\/06\/3D-Gaming.png"
},
{
"id": 2,
"descricao": "Livros",
"urlImagem": "http:\/\/4.bp.blogspot.com\/-6Bta1H9d22g\/UJAIJbqcHhI\/AAAAAAAAKi4\/hvgjWrlFc64\/s1600\/resenha-missiologia.png"
},
{
"id": 3,
"descricao": "Celulares",
"urlImagem": "http:\/\/pt.seaicons.com\/wp-content\/uploads\/2015\/11\/Mobile-Smartphone-icon.png"
},
{
"id": 4,
"descricao": "Inform\u00e1tica",
"urlImagem": "http:\/\/portal.ifrn.edu.br\/campus\/ceara-mirim\/noticias\/ifrn-oferece-curso-de-informatica-basica-para-pais-dos-estudantes\/image_preview"
},
{
"id": 5,
"descricao": "Eletrodom\u00e9stico",
"urlImagem": "http:\/\/classificados.folharegiao.com.br\/files\/classificados_categoria\/photo\/8\/sm_4d5ed3beb0f31b61cb9a01e46ecd0cf9.png"
},
{
"id": 6,
"descricao": "TVs",
"urlImagem": "http:\/\/i.utdstc.com\/icons\/256\/terrarium-tv-android.png"
},
{
"id": 7,
"descricao": "Filmes e S\u00e9ries",
"urlImagem": "https:\/\/pbs.twimg.com\/profile_images\/801033586438733824\/91Y_N91t_reasonably_small.jpg"
},
{
"id": 8,
"descricao": "M\u00f3veis e Decora\u00e7\u00f5es",
"urlImagem": "https:\/\/image.flaticon.com\/icons\/png\/128\/148\/148188.png"
},
{
"id": 9,
"descricao": "Moda, Beleza e Perfumaria",
"urlImagem": "http:\/\/icon-icons.com\/icons2\/196\/PNG\/128\/fashion_23852.png"
},
{
"id": 10,
"descricao": "Papelaria",
"urlImagem": "http:\/\/esen.pt\/in\/images\/stories\/skills_256.png"
}
]
}
You're trying to decode it as [AudioBook] when the data is actually a dictionary wrapping the array, as in your Data structure. Just change it to:
let data = try JSONDecoder().decode(Data.self, from: unwrappedDAta)
and you should be good to go.

Repeated values in UITable cells from api json

After I do http call from api I get json file then I loop through it to get specific values:
if let value: AnyObject = response.result.value as AnyObject? {
let json = JSON(value)
for (key, subJson) in json {
let drivers = subJson["driver"]
for (key, subJson) in drivers {
let status = subJson["status"].stringValue
let date = subJson["created_at"].stringValue
let name = subJson["name"].stringValue
let pivot = subJson["pivot"]
for (key, subJson) in subJson["pivot"] {
let type = pivot["type"].stringValue
let data = Driverj(name: name, status: status, created_at: date)
self.data.append(data)
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
}
}
in a separate file I connect the values with objects:
class DriverC: UITableViewCell {
#IBOutlet weak var status : UILabel!
#IBOutlet weak var date : UILabel!
#IBOutlet weak var job : UILabel!
#IBOutlet weak var update : UILabel!
}
And in another file I have the data saved :
import Foundation
import ObjectMapper
class Driverj : Mappable {
var status : String?
var name : String?
var created_at : String?
required init?(map: Map) {
}
required init(name: String , status : String , created_at : String ) {
self.status = status
self.name = name
self.created_at = created_at
}
func mapping(map: Map) {
status <- map["status"]
name <- map["name"]
created_at <- map["created_at"]
}
}
And finally here is how it's displayed
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DriverCell", for: indexPath) as! DriverC
let entry = data[indexPath.row]
cell.date.text = entry.date
cell.job.text = entry.job
cell.status.text = entry.status
cell.update.text = entry.name
return cell
}
My question is when I run my project I get three cells with the same data repeated. Why ?
the json files :
[
{
"id": 4,
"name": null,
"email": "5#5.com",
"status": 0,
"confirmed": false,
"street": "street ",
"niehgborhood": "North",
"city": "Sf",
"national_id": "1009090",
"phone": "9000",
"size_of_house": null,
"created_at": "2016-12-04 13:55:52",
"updated_at": "2017-03-08 14:03:44",
"deleted_at": null,
"driver": [
{
"name": "unknown",
"age": "25",
"Smoker": "No",
"language": "English",
"religion": "Muslim",
"created_at": null,
"updated_at": "2017-03-08 13:48:55",
"status": "تم",
"pic": "http://localhost:8000/images/1488714520.jpg",
"id": 1,
"pivot": {
"user_id": 4,
"driver_id": 1,
"type": "driver"
}
},
{
"name": "Jae",
"age": "30",
"Smoker": "No",
"language": "English",
"religion": "Muslim ",
"created_at": "2017-02-28 09:36:15",
"updated_at": "2017-03-08 08:46:06",
"status": "ok",
"pic": "http://localhost:8000/images/1488714520.jpg",
"id": 2,
"pivot": {
"user_id": 4,
"driver_id": 2,
"type": "driver"

Parse JSON from GoogleApiBooks

I'm trying to parse this JSON but it don't works.
This is the JSON:
{
"kind": "books#volumes",
"totalItems": 1,
"items": [
{
"kind": "books#volume",
"id": "uCHmPQAACAAJ",
"etag": "aBZ3KnoRsq4",
"selfLink": "https://www.googleapis.com/books/v1/volumes/uCHmPQAACAAJ",
"volumeInfo": {
"title": "Psicologia delle folle",
"authors": [
"Gustave Le Bon"
],
"publishedDate": "2004",
"industryIdentifiers": [
{
"type": "ISBN_10",
"identifier": "8850206240"
},
{
"type": "ISBN_13",
"identifier": "9788850206247"
}
],
"readingModes": {
"text": false,
"image": false
},
"pageCount": 251,
"printType": "BOOK",
"categories": [
"Psychology"
],
"maturityRating": "NOT_MATURE",
"allowAnonLogging": false,
"contentVersion": "preview-1.0.0",
"language": "it",
},
"saleInfo": {
"country": "IT",
"saleability": "NOT_FOR_SALE",
"isEbook": false
},
"accessInfo": {
"country": "IT",
"viewability": "NO_PAGES",
"embeddable": false,
"publicDomain": false,
"textToSpeechPermission": "ALLOWED",
"epub": {
"isAvailable": false
},
"pdf": {
"isAvailable": false
},
,
"accessViewStatus": "NONE",
"quoteSharingAllowed": false
}
}
]
}
And this is the code that i'm using:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var url = "https://www.googleapis.com/books/v1/volumes?q=isbn9788850206247&key=my-key"
Alamofire.request(url).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["items"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
print(self.arrRes)
}
if self.arrRes.count > 0 {
self.tableProva.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableProva.dequeueReusableCell(withIdentifier: "cellProva", for: indexPath) as! CellProvaClass
var dict = arrRes[indexPath.row]
cell.titleLabel?.text = dict["title"] as? String
cell.authorLabel?.text = dict["authors"] as? String
return cell
}
How can I solve this problem?
This is your JSON Reponse ( in short format) :
And this is the data structure (Array and dictionary format) :
Now you have to access : title and authors so here is your value in cellforRowAtIndexPath Method , a dictionary having name dict stores values of volumeInfo ( a Dictionary) :
so you have to use this way to access title :
cell.titleLabel?.text = dict["volumeInfo"]["title"] as? String
//To Fetch Author array
if let authorArray = dict["volumeInfo"]["authors"] as? NSArray{
print(authorArray)
}
and then to access author is an array so which index of value you want to show in cell.authorLabel .
In Second Screenshot those which are enclosed with {} is Dictionary and [] is Array
First try to show Title and then same way try to fetch value of author array and set according to your requirement.
Feel Free to comment if any further issue.
When you are parsing the JSON on line
if let resData = swiftyJsonVar["items"].arrayObject {
you will receive an object of NSDictionary which contains all other nested elements & arrays in keys. In your tableView delegate you are directly accessing the value from arrRes by unboxing it to NSDictionary object. All the entries are available in an array named items which further has a nested object volumeInfo which contains the info about title & another array of Authors.
You need to extract the further nested objects from arrRes object to use it in your tableView cellForRowAtIndex delegate function.
Let me know if you need any further help.

Parsing Json Files in Swift 3

I would like to parse a local JSON file but I am unaware of how to do so in Swift 3
My Current Code doesn't seem to work
I keep receiving this error:
'jsonObject' produces 'Any', not the expected contextual result type 'NSArray'
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController
{
var allEntries: String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func LoadAllQuestionsAndAnswers
{
let path = Bundle.main.path(forResource: "content", ofType: "json")
let jsonData : NSData = NSData(contentsOfFile: path!)!
allEntries = JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers, error: nil) as String
NSLog(allEntries)
}
}
I am loading the data from this json file "content.json"
[
{
"id" : "1",
"question": "What is the fastest fish in the ocean?",
"answers": [
"Sailfish",
"Lion Fish",
"Flounder",
"Tiger Shark",
"Swordfish"
],
"difficulty": "1"
},
{
"id" : "2",
"question": "Which animal has the most legs?",
"answers": [
"Millipede",
"Shrimp",
"Octopus",
"Dog",
"Lion"
],
"difficulty": "1"
}
]
let path = Bundle.main.path(forResource: "sample", ofType: "son")
let jsonData = try? NSData(contentsOfFile: path!, options: NSData.ReadingOptions.mappedIfSafe)
print(jsonData!)
let jsonResult: NSDictionary = try! JSONSerialization.jsonObject(with: jsonData as! Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
// save below sample json in a file and name it as sample.json
{
"blogs": [
{
"id": 111,
"url": "http://roadfiresoftware.com/blog/",
"name": "Roadfire Software Blog"
},
{
"id": 345,
"url": "https://developer.apple.com/swift/blog/",
"name": "Swift Developer Blog"
}
]
}
The root object of the JSON is an array
var allEntries = [[String:Any]]()
...
allEntries = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [[String:Any]]
mutableContainers is not needed at all in Swift

Alamofire json swift 2.2

I'm trying to take out some data of facebook place's names with Alamofire but i have no luck.
This is what i've tried and i cant figure out how to make it work.
xcode 7.3
swift2.2
So the result of the graph api is this
{
"data": [
{
"category": "Local business",
"category_list": [
{
"id": "272705352802676",
"name": "Outdoors"
},
{
"id": "115725465228008",
"name": "Region"
}
],
"location": {
"street": "Athens",
"city": "Palai\u00f3n F\u00e1liron",
"state": "",
"country": "Greece",
"zip": "17562",
"latitude": 37.9284637008,
"longitude": 23.6944070162
},
"name": "THE NAME OF THE PLACE",
"id": "THE ID"
}
and i want to go inside "data" and take only the name and the ID of the place which are the 2 last lines.
And this is my code!
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "https://graph.facebook.com/search", parameters: ["q": "", "type": "place", "center": "37.928319,23.7036673", "distance": "10000", "access_token": "ACCESS-TOKEN", "expires_in": "5184000"])
.responseJSON { response in
if let JSON = response.result.value {
print("JSON: \(JSON["data"]!["name"]!)")
}
}
}
and i get this error
fatal error: unexpectedly found nil while unwrapping an Optional value
Any idea why?
Ok this was easy i guess and it didn't worth the question but this is the solution
Alamofire.request(.GET, "https://graph.facebook.com/search", parameters: ["q": "", "type": "place", "center": "37.928319,23.7036673", "distance": "10000", "access_token": "475827182628380|6U-mk-1etGQHwrXRSMF4Ht2zOyc", "expires_in": "5184000"])
.responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["data"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.kati.reloadData()
}
}
}
And at the cell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("checkInCell", forIndexPath: indexPath)
var dict = arrRes[indexPath.row]
cell.textLabel?.text = dict["name"] as? String
//cell.detailTextLabel?.text = dict["email"] as? String
return cell
}
Just dont forget to use SwiftyJSON too!