Reading local JSON file and using it to populate UITableView - json

I am writing an app that needs to look at a local JSON file, then compare it's version to one I have hosted on a website. If they don't match, download the one from the web and save it locally. If they do match, then continue on and use the local JSON file. This version info is in the JSON file itself.
Previously, my app would simply parse the online data and use that directly. It would then populate the UITableView using the JSON data. Now that I am using my local file, the UITableView isn't getting populating, and I'm not certain how to fix it. From reading the new function, I think my issue is that I'm not using JSONDecoder(), and instead using JSONSerialization(), and therefore I can't point it at the specific metadata I want.
26 Jun 18 Edit (Below is my BonusListViewController.swift file):
//
// BonusListViewController.swift
// Tour of Honor
//
// Created by Tommy Craft on 6/6/18.
// Copyright © 2018 Tommy Craft. All rights reserved.
//
import UIKit
import os.log
import Foundation
class BonusListViewController: UITableViewController {
var bonuses = [JsonFile.JsonBonuses]()
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
// MARK: Data Structures
// Settings Struct
struct Constants {
struct RiderData {
let riderNumToH = "riderNumToH"
let pillionNumToH = "pillionNumToH"
}
struct RallyData {
let emailDestinationToH = "emailDestinationToH"
}
}
//MARK: Check for updated JSON file
checkJSON()
//MARK: Trigger JSON Download
/*
downloadJSON {
print("downloadJSON Method Called")
}
*/
}
// MARK: - Table View Configuration
// MARK: Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
print("Found \(bonuses.count) sections.")
return bonuses.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Found \(bonuses.count) rows in section.")
return bonuses.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = bonuses[indexPath.section].name.capitalized
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: self)
}
// MARK: - Table View Header
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return bonuses[section].state
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 3
}
// MARK: Functions
// MARK: - Download JSON from ToH webserver
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "http://tourofhonor.com/BonusData.json")
URLSession.shared.dataTask(with: url!) { [weak self] (data, response, error) in
if error == nil {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data!)
DispatchQueue.main.async {
completed()
}
print("Downloading Updated JSON (Version \(posts.meta.version))")
print(posts.bonuses.map {$0.bonusCode})
print(posts.bonuses.map {$0.state})
self?.bonuses = posts.bonuses
self?.defaults.set("downloadJSON", forKey: "jsonVersion") //Set version of JSON for comparison later
DispatchQueue.main.async {
//reload table in the main queue
self?.tableView.reloadData()
}
} catch {
print("JSON Download Failed")
}
}
}.resume()
}
func checkJSON() {
//MARK: Check for updated JSON file
let defaults = UserDefaults.standard
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
var hostedJSONVersion = ""
let jsonData = try! Data(contentsOf: jsonURL!)
let jsonFile = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String : Any]
let metaData = jsonFile["meta"] as! [String : Any]
hostedJSONVersion = metaData["version"] as! String
let localJSONVersion = defaults.string(forKey: "jsonVersion")
if localJSONVersion != hostedJSONVersion {
print("L:\(localJSONVersion!) / H:\(hostedJSONVersion)")
print("Version Mismatch: Retrieving lastest JSON from server.")
updateJSONFile()
} else {
//Retrieve the existing JSON from documents directory
print("L:\(localJSONVersion!) / H:\(hostedJSONVersion)")
print("Version Match: Using local file.")
let fileURL = defaults.url(forKey: "pathForJSON")
do {
let localJSONFileData = try Data(contentsOf: fileURL!, options: [])
let myJson = try JSONSerialization.jsonObject(with: localJSONFileData, options: .mutableContainers) as! [String : Any]
//Use my downloaded JSON file to do stuff
print(myJson)
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
func updateJSONFile() {
print("updateJSONFile Method Called")
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
let itemName = "BonusData.json"
let defaults = UserDefaults.standard
do {
let directory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = directory.appendingPathComponent(itemName)
let jsonData = try Data(contentsOf: jsonURL!)
let jsonFile = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String : Any]
let metaData = jsonFile!["meta"] as! [String : Any]
let jsonVersion = metaData["version"]
print("JSON VERSION ", jsonVersion!)
try jsonData.write(to: fileURL, options: .atomic)
defaults.set(fileURL, forKey: "pathForJSON") //Save the location of your JSON file to UserDefaults
defaults.set(jsonVersion, forKey: "jsonVersion") //Save the version of your JSON file to UserDefaults
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? BonusDetailViewController {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
}
And here is the JsonFile.swift:
import Foundation
struct JsonFile: Codable {
struct Meta: Codable {
let fileName: String
let version: String
}
struct JsonBonuses: Codable {
let bonusCode: String
let category: String
let name: String
let value: Int
let city: String
let state: String
let flavor: String
let imageName: String
}
let meta: Meta
let bonuses: [JsonBonuses]
}
Is this related to not using JSONDecoder() in my updated version or am I going down the wrong path there? Also, how do I get this new data to work with the UITableView?

First of all, you are parsing JSON values incorrectly. You need to first understand your JSON format. You go to your JSON file link, and analyze it. If it starts with a "{", then it is a Dictionary, if it starts with a "[", then it is an Array. In your case, it is a Dictionary, then there come the keys which are Strings ("meta", "bonuses"). So, we know our keys are Strings. Next, we look at our values. For "meta" we have a Dictionary of String : String; for "bonuses" we have an Array of Dictionaries.
So, our JSON format is [String : Any], or it can be written Dictionary<String, Any>.
Next step, is accessing those values in the Dictionary.
func updateJSONFile() {
print("updateJSONFile Method Called")
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
let itemName = "BonusData.json"
let defaults = UserDefaults.standard
do {
let directory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = directory.appendingPathComponent(itemName)
let jsonData = try Data(contentsOf: jsonURL!)
let jsonFile = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String : Any]
let metaData = jsonFile!["meta"] as! [String : Any]
let jsonVersion = metaData["version"]
print("JSON VERSION ", jsonVersion!)
try jsonData.write(to: fileURL, options: .atomic)
defaults.set(fileURL, forKey: "pathForJSON") //Save the location of your JSON file to UserDefaults
defaults.set(jsonVersion, forKey: "jsonVersion") //Save the version of your JSON file to UserDefaults
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
Then, when you access your locally saved file, again, you have to parse the JSON to check the versions:
func checkJSON() {
//MARK: Check for updated JSON file
let defaults = UserDefaults.standard
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
var hostedJSONVersion = ""
let jsonData = try! Data(contentsOf: jsonURL!)
let jsonFile = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String : Any]
let metaData = jsonFile["meta"] as! [String : Any]
hostedJSONVersion = metaData["version"] as! String
let localJSONVersion = defaults.string(forKey: "jsonVersion")
if localJSONVersion != hostedJSONVersion {
print("\(localJSONVersion) : \(hostedJSONVersion)")
updateJSONFile()
} else {
//Retrieve the existing JSON from documents directory
print("\(localJSONVersion) : \(hostedJSONVersion)")
print("Local JSON is still the latest version")
let fileUrl = defaults.url(forKey: "pathForJSON")
do {
let localJSONFileData = try Data(contentsOf: fileUrl!, options: [])
let myJson = try JSONSerialization.jsonObject(with: localJSONFileData, options: .mutableContainers) as! [String : Any]
//Use my downloaded JSON file to do stuff
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
Don't forget to allow arbitrary loads in your Info.plist file, because your JSON file is hosted on a website without https.

Related

Swift parsing JSON into Table View doesnt work

Hi I have problems to read the Json Data into the TableView.
Can anybody help me, its my first time working with it.
I know I am doing something wrong, but I cant finde the Solution for my JSON File cause in the Internet they use simple ones..
Here is my JSON:
{
"data":[
{
"title": "Brot",
"desc":[
{
"Name": "Roggenschrot- und Roggenvollkornbrot",
"Menge": "Gramm",
"Kalorie": "2",
"Energiedichte": "Gelb"
},
{
"Name": "Weizenschrot- und Weizenvollkornbrot",
"Menge": "Gramm",
"Kalorie": "2",
"Energiedichte": "Gelb"
},
{
"Name": "Weizenschrot- und Weizenvollkornbrot",
"Menge": "Gramm",
"Kalorie": "2",
"Energiedichte": "Gelb"
},
]
}
]
}
Here is my tableView
import UIKit
class EseenTagebuchTableViewController: UITableViewController {
var result: Result?
var resultItem: ResultItem?
override func viewDidLoad() {
super.viewDidLoad()
parseJSON()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return result?.data.count ?? 0
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return result?.data[section].title
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if let result = result {
return result.data.count
}
return 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let text = resultItem?.desc?[indexPath.section].Name?[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = text
return cell
}
private func parseJSON() {
guard let path = Bundle.main.path(forResource: "Ernahrungstagebuch", ofType: "json") else {
return
}
let url = URL(fileURLWithPath: path)
do {
let jsonData = try Data(contentsOf: url)
result = try JSONDecoder().decode(Result.self, from: jsonData)
return
}
catch {
print("Error: \(error)")
}
}
}
And here are my Model 1 and after my Model 2
import Foundation
struct Result: Codable {
let data: [ResultItem]
}
struct ResultItem: Codable {
let title: String
let desc: [descItems]?
}
Model 2
import Foundation
struct descItems: Codable {
let Name: String?
let Menge: String?
let Kalorie: Int?
let Energiedichte: String?
}
What am I doing wrong?
First your json data in your file is not correct, there is an extra "," comma after
the last "Energiedichte": "Gelb" , remove that from your file.
Second your model is not correct, you should have:
struct Result: Codable {
let data: [ResultItem]
}
struct ResultItem: Codable {
let title: String
let desc: [DescItems]?
}
struct DescItems: Codable {
let Name: String?
let Menge: String?
let Kalorie: String? // <-- here not Int?
let Energiedichte: String?
}
Using your code parseJSON() I was able to parse the data without errors.
EDIT-1: if you really want kalories to be Int, try:
struct DescItems: Codable {
let name: String?
let menge: String?
let kalorie: Int? // <-- here
let energiedichte: String?
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
menge = try container.decode(String.self, forKey: .menge)
energiedichte = try container.decode(String.self, forKey: .energiedichte)
let calorie = try container.decode(String.self, forKey: .kalorie) // <-- here
kalorie = Int(calorie) // <-- here
}
enum CodingKeys: String, CodingKey {
case name = "Name"
case menge = "Menge"
case kalorie = "Kalorie"
case energiedichte = "Energiedichte"
}
}
Note the change of case, as per common practice.
you can also use json parse below code:
func parseJSON() {
let path = Bundle.main.path(forResource: "Ernahrungstagebuch", ofType: "json")
let jsonData = try? NSData(contentsOfFile: path!, options: NSData.ReadingOptions.mappedIfSafe)
let jsonResult: NSDictionary = try! (JSONSerialization.jsonObject(with: jsonData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary)!
if let data = jsonResult["data"] as? [[String:Any]] {
self.arrPlan = data.map{Day(JSON: $0)!}
// print(arrPlan)
}
}
Try to this code for get data array object and after reload tableview Data.
You can use this extension to parse json.
import Foundation
extension Bundle {
func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = dateDecodingStrategy
decoder.keyDecodingStrategy = keyDecodingStrategy
do {
return try decoder.decode(T.self, from: data)
} catch DecodingError.keyNotFound(let key, let context) {
fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
} catch DecodingError.typeMismatch(_, let context) {
fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
} catch DecodingError.valueNotFound(let type, let context) {
fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)")
} catch DecodingError.dataCorrupted(_) {
fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON")
} catch {
fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
}
}
}
And use it like this:
let incomingData = Bundle.main.decode([Your_Model].self, from: "Your_Json_File_name.json")

parsing json xcode swift 4

Problem. I want to parse the json from here and show on a table view, i am using swift 4 and Decodable. but i am getting a type mismatch error. Link to json:
https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=50fb91212a47432d802b3b1ac0f717a3
My Struct looks like this.
struct Root : Decodable {
let status : String
// let totalResults : Int
let articles : [Article]
}
struct Article : Decodable {
let source: Source
let author, title, description: String
let url: URL
// let publishedAt: Date
let urlToImage: String
}
struct Source: Decodable {
let id, name: String
}
My ViewDidLoad Looks like this :
var articles : [Article]? = []
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
fetchArticles()
}
func fetchArticles(){
let jsonURLString = "https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=50fb91212a47432d802b3b1ac0f717a3"
guard let url = URL(string: jsonURLString) else { return }
URLSession.shared.dataTask(with: url) { (data,response,err) in
guard let data = data else { return }
self.myArticles = [Article]()
do{
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = .base64
let root = try decoder.decode(Root.self, from: data)
self.myArticles = root.articles
DispatchQueue.main.async {
self.tableview.reloadData()
}
} catch let error{
print(error)
}
}.resume()
}
My cellForRowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "articleCell", for: indexPath) as! ArticleCell
let article = self.myArticles[indexPath.row]
myCell.title.text = article.title
myCell.body.text = article.description
myCell.author.text = article.author
myCell.imgView.downloadImage(from: ("https://tctechcrunch2011.files.wordpress.com/2017/04/uber-vs-waymo.png"))
return myCell
}
Error i am getting.
No errors, nothing loads to the table view.
The error is clear. You are going to decode an array but the object is a dictionary.
This decodes the JSON including the special decoding to URL and Date.
The root object is a dictionary, the key articles contains the articles and source is a dictionary, not an array
struct Root : Decodable {
let status : String
let articles : [Article]
}
struct Article : Decodable {
let source: Source
let author, title, description: String
let url: URL
let publishedAt: Date
let urlToImage: String
}
struct Source: Decodable {
let id, name: String
}
var articles = [Article]()
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let root = try JSONDecoder().decode(Root.self, from: data)
self.articles = root.articles
DispatchQueue.main.async {
self.tableview.reloadData()
}
} catch { print(error) }
There is no need to use classes inheriting from NSObject
And do not declare the data source object as optional and get the same item a couple of times in cellForRow and it's indexPath.row
...
let article = self.articles[indexPath.row]
myCell.title.text = article.title
myCell.body.text = article.description
myCell.author.text = article.author
...

How to reload data in UITableview after making second Json call Swift 3

I am making JSON request and if its completed so I am navigating page with JSON data to my table view controller and everything works fine but when I am making second call to load more cells I am not able to reload data and here is the code of my table view controller there I am making second call.
var listData:[String] = []
var videoIDData:[String] = []
var valueKeyData:[String] = []
var nextPageToken:String?
var titleNamee:[String] = []
var videoIDD :[String] = []
var valueKeyy :[String] = []
var PrevPageToken:String?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell", for: indexPath) as! VideoTableViewCell
cell.videoTitle.text = listData[indexPath.row]
let url = NSURL(string: valueKeyData[indexPath.row])
let dataaa = NSData(contentsOf: url! as URL)
let image = UIImage(data: dataaa! as Data)
cell.videoThumnailImageView.image = image
if (indexPath.row == listData.count - 1)
{
makeGetCall()
}
return cell
}
func makeGetCall() {
// Set up the URL request
var pageToken:String = (pageToken)
let todoEndpoint: String = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50\(pageToken)&playlistId=\(id)_ItOZ8WBF5_SI_SrSN3_F&\(key)"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let jsonObject = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String : AnyObject] else {
print("error trying to convert data to JSON")
return
}
self.PrevPageToken = jsonObject["prevPageToken"] as? String
self.nextPageToken = jsonObject["nextPageToken"] as? String
if let itemsArray = jsonObject["items"] as? [[String:AnyObject]]{
for snippetArray in itemsArray{
if var snippet = snippetArray["snippet"] as? [String : AnyObject]{
if let titleItems = snippet["title"] as? String{
self.titleNamee += [titleItems]
}
if let thumbnail = snippet["thumbnails"] as? [String : AnyObject]{
if let highValue = thumbnail["high"] as? [String : AnyObject]{
if let urlValueKey = highValue ["url"] as? String{
self.valueKeyy += [urlValueKey]
}
}
}
if let resource = snippet["resourceId"] as? [String : AnyObject]{
if let videoId = resource["videoId"] as? String{
// self.videoIDD.append(videoId)
self.videoIDD += [videoId]
}
}
}
}
}
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
}
You are reloading tableView at wrong place you need to reload it inside completion block after the for loop because completion block will call async, so remove your current reload code of tableView and put it after the for loop.
for snippetArray in itemsArray{
if var snippet = snippetArray["snippet"] as? [String : AnyObject]{
if let titleItems = snippet["title"] as? String{
self.titleNamee += [titleItems]
}
if let thumbnail = snippet["thumbnails"] as? [String : AnyObject]{
if let highValue = thumbnail["high"] as? [String : AnyObject]{
if let urlValueKey = highValue ["url"] as? String{
self.valueKeyy += [urlValueKey]
}
}
}
if let resource = snippet["resourceId"] as? [String : AnyObject]{
if let videoId = resource["videoId"] as? String{
// self.videoIDD.append(videoId)
self.videoIDD += [videoId]
}
}
}
}
//Reload your table here
DispatchQueue.main.async{
self.tableView.reloadData()
}
Note: Instead of creating multiple array that you are doing currently what you need to do is make one custom class or struct with all these properties and make array of that custom class or struct objects.
Edit: I haven't looked at that you are calling this method in cellForRowAt don't do that as of cell will be reused and that is the reason you UI getting stuck so remove that calling code from cellForRowAt, if you want to make something like pagination than you can use scrollViewDelegate method like this way.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if((scrollView.contentOffset.y + scrollView.frame.size.height) == scrollView.contentSize.height)
{
self.makeGetCall()
}
}
Also it its better that you show some process indicator while you are making API request.

How to parse this json with Alamofire 4 in Swift 3?

I have the json below but unable to figure out how to parse it in Swift 3. My code is below. The json from the API has an array root. I am using Xcode 8.2.1 with Swift 4 and Alamofire 4.0.
["items": <__NSArrayM 0x608000248af0>(
{
currency = USD;
image = "https://cdn.myDomain.com/image.jpg";
"item_title" = "Antique Table";
"name:" = "";
price = 675;
},
{
currency = USD;
image = "https://cdn.mydomain.com/image2.jpg";
"name:" = "";
price = 950;
...
Here is my code. I have tried to get an array r dictionary from the results but it's always nil.
Alamofire.request(myURL)
.responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData: Data) {
do {
let readableJSON = try JSONSerialization.jsonObject(with: JSONData, options:.mutableContainers) as! [String: Any]
print(readableJSON)
}
catch {
print(error)
}
}
I have tried this let item = readableJSON["items"] as? [[String: Any]] as suggested here but it would not compile with an error [String:Any] has no subscript and let item = readableJSON["items"] as? [String: Any]! compiles with a warning Expression implicitly coerced from string but produces nil. Parsing this json is life or death for me.
Do something like
let responseJSON = response.result.value as! [String:AnyObject]
then you'll be able to access elements in that dictionary like so:
let infoElementString = responseJSON["infoElement"] as! String
This was the parse json function I eventually came up with. The problem for this json data is that it is a dictionary inside an array. I am a noob and most of the answers and how tos I saw would not fit my json data. Here is the function I finally came up with with worked.
var myItems = [[String:Any]]()
then in my view controller class
func loadMyItems() {
Alamofire.request(myItemsURL)
.responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
self.collectionView.reloadData()
})
}
func parseData(JSONData: Data) {
do {
let readableJSON = try JSONSerialization.jsonObject(with: JSONData, options:.allowFragments) as! [String: Any]
let items = readableJSON["items"] as! [[String: Any]]
self.myItems = items
}
catch {
print(error)
}
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as? myCell
let dictionary = myItems[indexPath.row] as [String:Any]
if let item_title = dictionary["item_title"] as? String {
cell!.textLabel.text = item_title
print(item_title)
}
return cell!
}
Alamofire Example in Swift 3
1.First of all Use two cocoapods to your project.Use SwiftyJSON for json parse
pod 'Alamofire'
pod 'SwiftyJSON'
My Json is below
{"loginNodes":[{"errorMessage":"Welcome To Alamofire","name":Enamul Haque,"errorCode":"0","photo":null}]}
It may be done in different way. But I have done below Way. Note if you don't need any parameter to send the server then remove parameter option. It may work post or get method. You can use any way. My Alamofire code is below...which is working fine for me......
Alamofire.request("http://era.com.bd/UserSignInSV", method: .post,parameters:["uname":txtUserId.text!,"pass":txtPassword.text!]).responseJSON{(responseData) -> Void in
if((responseData.result.value != nil)){
let jsonData = JSON(responseData.result.value)
if let arrJSON = jsonData["loginNodes"].arrayObject {
for index in 0...arrJSON.count-1 {
let aObject = arrJSON[index] as! [String : AnyObject]
let errorCode = aObject["errorCode"] as? String;
let errorMessage = aObject["errorMessage"] as? String;
if("0"==errorCode ){
//Database Login Success Action
}else{
// //Database Login Fail Action
}
}
}
}
}
If You use Like table View Or Collection View or so on, you can use like that..
Declare A Array
var arrRes = [String:AnyObject]
Assign the value to array like
if((responseData.result.value != nil)){
// let jsonData = JSON(responseData.result.value)
if((responseData.result.value != nil)){
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["loginNodes"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.tableView.reloadData()
}
}
}
In taleView, cellForRowAt indexPath , Just use
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customCell
cell.errorLabelName.text = arrRes[indexPath.row]["errorMessage"] as? String
Swift 3
Alamofire Example in Swift 3
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
{
var array = [[String:AnyObject]]()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request("http://www.designer321.com/johnsagar/plumbingapp/webservice/list_advertise.php?zip=123456").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil)
{
let swiftyJsonVar = JSON(responseData.result.value!)
print("Main Responce")
print(swiftyJsonVar)
}
if let result = responseData.result.value
{
if let Res = (result as AnyObject).value(forKey: "response") as? NSDictionary
{
if let Hallo = (Res as AnyObject).value(forKey: "advertise_list") as? NSArray
{
print("-=-=-=-=-=-=-")
print(Hallo)
self.array = Hallo as! [[String:AnyObject]]
print(self.array)
}
}
self.tableview.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
var dict = array[indexPath.row]
cell.lbl1.text = dict["address"] as? String
cell.lbl2.text = dict["ad_created_date"] as? String
cell.lbl3.text = dict["phone_number"] as? String
cell.lbl4.text = dict["id"] as? String
cell.lbl5.text = dict["ad_zip"] as? String
let imageUrlString = dict["ad_path"]
let imageUrl:URL = URL(string: imageUrlString as! String)!
let imageData:NSData = NSData(contentsOf: imageUrl)!
cell.img.image = UIImage(data: imageData as Data)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 100
}
}

Error when try to parse json?

I'm trying to parse JSON, but it's not working. I want to get id from the JSON at the URL, but it shows me null value.
var names = [String]()
var SearchURL = "http://ios.khothe.vn/web/gamecards/authenticate/user/dungphiau/pass/829d81d46bad96825dc52a6e1675aab0"
typealias jsonStandard = [String : AnyObject]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
callAlamo(url: SearchURL)
}
func callAlamo(url : String) {
Alamofire.request(url).responseJSON(completionHandler:
{
responds in
self.parseData(JsonData: responds.data!)
})
}
func parseData(JsonData : Data) {
do{
var readableJson = try JSONSerialization.jsonObject(with: JsonData, options: .mutableContainers) as! jsonStandard
let tracks = readableJson["id"] as? jsonStandard
print(tracks)
} catch{
print(error)
}
}
The value for key id is String not [String:AnyObject] aka jsonStandard
let tracks = readableJson["id"] as? String
Consider that in Swift 3 the type of a JSON dictionary is [String:Any]
typealias jsonStandard = [String : Any]