parse data in tableview by alamofire in Swift3 - json

I want to parse data into tableviewcontroller but it doesn't display anything
this is the webservice data:
I want to access the title, img_url and price_formatted inside the key "listings"
the user enters a city name and type of home which he is searching for and I save these values using userdefaults and receive them in PropertySearchViewController.
This is my code using almofire to display these values:
I have PropertySearchViewController which I display the values inside it, PropertyTableViewCell and public model Property
1-
class PropertySearchViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let URL_Get_Data = "https://api.nestoria.co.uk/api?"
#IBOutlet weak var tableViewProperty: UITableView!
var properties = [Property]()
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return properties.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PropertyTableViewCell
let property :Property
property = properties[indexPath.row]
cell.propertyTitle.text = property.title
cell.propertyPrice.text = property.price
if property.imageUrl != nil {
Alamofire.request(property.imageUrl!).responseImage { response in
debugPrint(response)
if let image = response.result.value {
cell.propertyImage.image = image
}
else{
print("no image")
}
}}
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
//fetching data from web api
//recieve data
let city :String = UserDefaults.standard.string(forKey: "city")!
let type :String = UserDefaults.standard.string(forKey: "typeP")!
print("search values are :",city,type)
let params: [String: String] = ["encoding": "json", "pretty": "1", "action": "search_listings", "country": "uk", "listing_type": type, "place_name": city]
//end
//
Alamofire.request(URL_Get_Data, method: .get, parameters: params, encoding: URLEncoding.default, headers: nil).validate(statusCode: 200..<600).responseJSON {
// Alamofire.request(URL_Get_Data).responseJSON {
response in
// response in
//getting json
if let json = response.result.value {
print(type(of: json))
//converting json to NSArray
let propertyArray = json as! NSDictionary
//traversing through all elements of the array
for i in 0..<propertyArray.count{
//adding hero values to the hero list
self.properties.append(Property(
title: (propertyArray[i] as AnyObject).value(forKey: "title") as? String,
price: (propertyArray[i] as AnyObject).value(forKey: "price_formatted") as? String,
imageUrl: (propertyArray[i] as AnyObject).value(forKey: "imageUrl") as? String
))
}
//displaying data in tableview
self.tableViewProperty.reloadData()
}
}
}}
//end of PropertySearchViewController
2-
class PropertyTableViewCell: UITableViewCell {
#IBOutlet weak var propertyImage: UIImageView!
#IBOutlet weak var propertyTitle: UILabel!
#IBOutlet weak var propertyPrice: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
//end
3-
class Property{
var title: String?
var price: String?
var imageUrl: String?
init(title: String?, price: String?, imageUrl: String?) {
self.title = title
self.price = price
self.imageUrl = imageUrl
}
}
Thanks in advance

(1) Install pod SwiftyJSON
(2) import SwiftyJSON in PropertySearchViewController
(3) Add let reuseIdentifierResultTable = "cell" below let URL_Get_Data = "url"
(4) Add in viewDidLoad() tableViewProperty.register(PropertyTableViewCell.self, forCellReuseIdentifier: reuseIdentifierResultTable)
tableViewProperty.delegate = self
tableViewProperty.dataSource = self
(5) Replace everyting in .responseJSON{} with
response in
if let data = response.data {
let json = String(data: data, encoding: String.Encoding.utf8)
//print(json)
if let dataFromString = json?.data(using: .utf8, allowLossyConversion: false) {
let json2 = JSON(data: dataFromString)
print("Response: \(json2)")
print("json status code: \(json2["response"]["status_code"])")
if json2["response"]["status_code"] == "200" && !(json2.null != nil){
print("json request count: \(json2["request"].count)")
print("json response count: \(json2["response"].count)")
print("json response listings count: \(json2["response"]["listings"].count)")
for i in 0...json2["response"]["listings"].count-1 {
print("\(i). \(json2["response"]["listings"][i]["title"])")
self.properties.append(Property(
title: json2["response"]["listings"][i]["title"].rawString()!,
price: json2["response"]["listings"][i]["price_formatted"].rawString()!,
imageUrl: json2["response"]["listings"][i]["img_url"].rawString()!
))
}
}
}
self.tableViewProperty.reloadData()
}
(6) Replacepublic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if properties.count < 1 {
return 0
}
return properties.count
}
(7) Replace
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PropertyTableViewCell
with
let cell = Bundle.main.loadNibNamed("PropertyTableViewCell", owner: self, options: nil)?.first as! PropertyTableViewCell
(8) Replace
cell.propertyTitle.text = property.title!
cell.propertyPrice.text = property.price!

Related

JSON Parsing issue: "Expected to decode Array<Any> but found a dictionary instead."

I'm facing an error with parisng a JSON.
I can see that I have type mismatch however I'm not sure how to fix it:
typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
var dataModel = [Model]()
This is my model:
import Foundation
struct Model: Codable {
let basepath: String
let items: [Item]
}
struct Item: Codable {
let title: String
let abstract: String
let thumbnail: String
}
And this is my root view controller within which I'm parsing JSON.
import UIKit
class ViewController: UIViewController {
private let hostingView = UIView()
let cellID = "cell"
var dataModel = [Country]()
let tableView: UITableView = {
let table = UITableView()
table.rowHeight = 100
table.translatesAutoresizingMaskIntoConstraints = false
return table
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.showSpinner(on: hostingView)
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
configureConstraints()
tableView.delegate = self
tableView.dataSource = self
tableView.register(TableViewCell.self, forCellReuseIdentifier: cellID)
performRequest()
}
private func configureView(){
view.backgroundColor = .white
title = "Game Of Thrones"
navigationController?.navigationBar.prefersLargeTitles = true
}
private func configureConstraints(){
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
private func performRequest(){
let url = "https://gameofthrones.fandom.com/api/v1/Articles/Top?expand=1&category=Articles&limit=75"
let urlString = URL(string: url)
guard urlString != nil else {return}
print(String("URL: \(urlString)"))
let session = URLSession(configuration: .default)
let dataTask = session.dataTask(with: urlString!) { (data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([Country].self, from: data!)
self.dataModel = decodedData
print(self.dataModel)
DispatchQueue.main.async {
self.removeSpinner()
self.tableView.reloadData()
}
} catch {
DispatchQueue.main.async {
print(error)
self.performRequestError()
}
}
}
}
dataTask.resume()
}
private func performRequestError(){
let ac = UIAlertController(title: "Error", message: "Cannot load data", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(ac, animated: true)
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if dataModel.count > 0 {
// return dataModel[0].items.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! TableViewCell
cell.textLabel?.text = "title"
return cell
}
}

How to decode custom type inside dictionary value with JSON?

my JSON:
https://www.cbr-xml-daily.ru/daily_json.js
my code:
struct CoinData: Decodable {
let Valute: [String: CoinInfo]
}
struct CoinInfo: Decodable {
let Name: String
let Value: Double
}
if let safeData = data {
if let coinData = self.parseJSON(safeData) {
print(coinData)
}
}
func parseJSON(_ data: Data) -> [String: CoinInfo]? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(CoinData.self, from: data)
return decodedData.Valute
} catch {
delegate?.didFailWithError(error: error)
return nil
}
}
In debug console following gets printed:
["PLN": CurrencyConverter.CoinInfo(Name: "X", Value: 19.6678), ...]
This way I can't reach Name and Value properties of a coin. What's wrong?
I am going to do for-loop to check if a key contains certain symbols. If it does - I will need to be able to access to both Name and Value
You don't actually need a for loop. Since coinData is a dictionary, you can use its subscript, together with optional binding to do this. For example, to check if the key "PLN" exists, and access its name and value:
if let coinInfo = coinData["PLN"] {
print(coinInfo.Name)
print(coinInfo.Value)
} else {
// "PLN" does not exist
}
StoyBoard
Code
import UIKit
import Alamofire
// MARK: - CoinData
struct CoinData: Codable {
let date, previousDate: String
let previousURL: String
let timestamp: String
let valute: [String: Valute]
enum CodingKeys: String, CodingKey {
case date = "Date"
case previousDate = "PreviousDate"
case previousURL = "PreviousURL"
case timestamp = "Timestamp"
case valute = "Valute"
}
}
// MARK: - Valute
struct Valute: Codable {
let id, numCode, charCode: String
let nominal: Int
let name: String
let value, previous: Double
enum CodingKeys: String, CodingKey {
case id = "ID"
case numCode = "NumCode"
case charCode = "CharCode"
case nominal = "Nominal"
case name = "Name"
case value = "Value"
case previous = "Previous"
}
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
var getCoinData = [CoinData]()
var coinNameArr = [String]()
var coinDataArr = [Valute]()
#IBOutlet weak var tblDataList: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getData()
}
func getData()
{
let url = "https://www.cbr-xml-daily.ru/daily_json.js"
AF.request(url, method: .get, encoding: URLEncoding.default).responseJSON { response in
let json = response.data
do{
let decoder = JSONDecoder()
self.getCoinData = [try decoder.decode(CoinData.self, from: json!)]
let response = self.getCoinData[0]
if response.valute.count != 0 {
self.coinNameArr.removeAll()
self.coinDataArr.removeAll()
for (coinName, coinData) in response.valute {
self.coinNameArr.append(coinName)
self.coinDataArr.append(coinData)
}
self.tblDataList.reloadData()
} else {
}
}catch let err{
print(err)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return coinDataArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:coinTblCell = tableView.dequeueReusableCell(withIdentifier: "CellID", for: indexPath as IndexPath) as! coinTblCell
cell.accessoryType = .disclosureIndicator
cell.tintColor = .black
let rowData = coinDataArr[indexPath.row]
cell.lblName.text = rowData.name
cell.lblValue.text = String(rowData.value)
return cell
}
}
class coinTblCell: UITableViewCell {
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var lblValue: UILabel!
}

swift 4 Parse JSON without keys with Alamofire

Guys i want to get all names from JSON (screenshot below) and put them to tableView. The problem is...i got dictionary with this code. Now, how i can get each name value and put them on tableView.
func getDataFromApi(){
Alamofire.request("https://api.coinmarketcap.com/v2/listings/").responseJSON{ response in
if let locationJSON = response.result.value{
let locationObject: Dictionary = locationJSON as! Dictionary<String, Any>
for (key, value) in locationObject {
print("id:\(key), value:\(value)")
}
}
}
}
I would suggest convert the dictionaries response to a Currency object:
class Currency: NSObject {
var id: Int!
var name: String!
var symbol: String!
var websiteSlug: String!
init(id: Int, name: String, symbol: String, websiteSlug: String) {
super.init()
self.id = id
self.name = name
self.symbol = symbol
self.websiteSlug = websiteSlug
}
}
Then under the variables' section define the currencies array:
var currencies = [Currency]()
Finaly change the getDataFromApi implementation to this:
func getDataFromApi() {
Alamofire.request("https://api.coinmarketcap.com/v2/listings/").responseJSON{ response in
if let locationJSON = response.result.value as? [String: Any] {
let data = locationJSON["data"] as! [[String: Any]]
for dataItem in data {
let currency = Currency(id: dataItem["id"] as! Int,
name: dataItem["name"] as! String,
symbol: dataItem["symbol"] as! String,
websiteSlug: dataItem["website_slug"] as! String)
self.currencies.append(currency)
}
print(self.currencies)
}
}
}
I always suggest model the responses to objects because it allows you to do a better managing of the data you need to display on screen and keep your code structure organised.
Now you can easily show the data in a UITableView object from the currencies array.
I would suggest convert the Array In dictionaries response un a Currency object:
var dataArray = NSArray()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a
nib.
self.getDataFromApi()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getDataFromApi(){
Alamofire.request("https://api.coinmarketcap.com/v2/listings/").responseJSON{ response in
if let locationJSON = response.result.value{
let locationObject: Dictionary = locationJSON as! Dictionary<String, Any>
self.dataArray = locationObject["data"]as! NSArray
self.tableView.reloadData()
// for (key, value) in locationObject {
// print("id:\(key), value:\(value)")
// }
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:"cell") as! UITableViewCell
cell.textLabel?.text = (dataArray.object(at:indexPath.row) as! NSDictionary).value(forKey:"name") as! String
cell.detailTextLabel?.text = (dataArray.object(at:indexPath.row) as! NSDictionary).value(forKey:"symbol") as! String
return cell
}
var nameArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
getData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! tableCell
cell.nameLabel.text = nameArray[indexPath.row]
return cell
}
func alamofire() {
Alamofire.request("https://api.coinmarketcap.com/v2/listings/", method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
guard let json = response.result.value as! [String:Any] else{ return}
guard let data = ["data"] as! [[String: Any]] else { return}
for item in data {
if let name = item["name"] as? String {
self.nameArray.append(name)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
break
case .failure(_):
print(response.result.error as Any)
break
}
}
}

How to show Json data in Offline using core data

Here i have created table view with labelName and labelUsername,
and i have downloaded Json data and saving it in core data entity called Details which contains attributes name and username..
here table view showing its data in online...
but how can i show fetched data in table view while in offline..
please help me in the code...
import UIKit
import CoreData
struct JsonData {
var nameS: String
var usernameS: String
init(name: String, username: String) {
self.nameS = name
self.usernameS = username
}
}
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var iteamsArray = [JsonData]()
override func viewDidLoad() {
downloadJson()
}
func downloadJson(){
let urlStr = "https://jsonplaceholder.typicode.com/users"
let url = URL(string: urlStr)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
guard let respData = data else {
return
}
guard error == nil else {
print("error")
return
}
do{
let jsonObj = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [[String: Any]]
for items in jsonObj {
let nameJson = items["name"] as? String
let usernameJson = items["username"] as? String
let coreData = NSEntityDescription.insertNewObject(forEntityName: "Details", into: self.context) as! Details
coreData.name = nameJson
coreData.username = usernameJson
self.iteamsArray.append(JsonData(name: nameJson!, username: usernameJson!))
}
try self.context.save()
//fetching from core data
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
let details = try self.context.fetch(fetchRequest)
if details.count > 0 {
for detail in details as [NSManagedObject] {
let nameCore = detail.value(forKey: "name")
let usernameCore = detail.value(forKey: "username")
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print("catch error")
}
}).resume()
}
}
// MARK: - TableView
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return iteamsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "JsonCell", for: indexPath) as! JsonTableViewCell
let aData = iteamsArray[indexPath.row]
cell.labelName.text = aData.nameS
cell.labelUsername.text = aData.usernameS
cell.selectionStyle = .none
return cell
}
}
First of all forget the custom struct. Use the NSManagedObject class as data source array.
var iteamsArray = [Details]()
In viewDidLoad first fetch the data, if the array is empty load it from the web service
override func viewDidLoad() {
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
do {
iteamsArray = try self.context.fetch(fetchRequest)
if iteamsArray.isEmpty {
downloadJson()
} else {
self.tableView.reloadData()
}
} catch { print(error) }
}
In downloadJson() replace
self.iteamsArray.append(JsonData(name: nameJson!, username: usernameJson!))
with
self.iteamsArray.append(coreData)
and remove these lines
//fetching from core data
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
let details = try self.context.fetch(fetchRequest)
if details.count > 0 {
for detail in details as [NSManagedObject] {
let nameCore = detail.value(forKey: "name")
let usernameCore = detail.value(forKey: "username")
}
}
In cellForRow get the values directly from the NSManagedObject objects
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "JsonCell", for: indexPath) as! JsonTableViewCell
let aData = iteamsArray[indexPath.row]
cell.labelName.text = aData.name
cell.labelUsername.text = aData.userName
cell.selectionStyle = .none
return cell
}

Showing JSON results after parsing it in Swift 3

I'm trying to parse a JSON with the below code and show it in a table view, but this code runs without any problems or errors, but I'm unable to make it show when I run the app in the sim or even see the JSON results in the debug window.
I'm not using Storyboard. Only working on Swift code. For this I have added the following in the AppDelegate.swift
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
import UIKit
class Post: NSObject {
var id: String?
var title: String?
var year: NSNumber?
var quote: String?
var image: String?
var currency: NSNumber?
var desc: String?
var type: String?
}
class FeedController: UITableViewController {
var posts = [Post]()
var numberOfRows = 0
override func viewDidLoad() {
super.viewDidLoad()
if let path = Bundle.main.path(forResource: "jsonFile", ofType: "json") {
do {
let data = try NSData(contentsOfFile: path, options: NSData.ReadingOptions.mappedIfSafe)
let jsonDictionary = try JSONSerialization.jsonObject(with: data as Data, options: .mutableContainers) as! [String: AnyObject]
if let postDictionary = jsonDictionary["post"] as? [String: Any] {
let post = Post()
post.setValuesForKeys(postDictionary)
print(post.mvs_id, post.mvs_plot)
numberOfRows = (jsonDictionary["data"]?.count)!
}
print(jsonDictionary)
} catch let err {
print(err)
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfRows
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I need help to understand what am I doing wrong to make this parsed JSON show when I run the app, or at least show me the results in the debugger? Thanks