i'm trying to do some simple things here, but i'm struggling a bit as i'm a beginner.
So basically i have a JSON file link to use to populate my tableview with 2 sections. Each cell have a "Favourite button".
i've tried many way to save my array with UserDefault with no luck.
What i wish to achieve:
When the "Favourite button" is pressed, i wish to move the cell to the other section
Save and retrieve those 2 array using UserDefault
I'm open to hear any suggestion also any other differs approach, as i'm sure there is some better one.
I will upload here some code, but also the link for the full project in Git so you can check better (https://github.com/Passeric/UserTableview.git)
I really appreciate any help.
The JSON i'm using:
{
"User": [
{
"Name": "John",
"Age": "34"
},{
"Name": "Sara",
"Age": "19"
}.......]}
My Struct:
class User: Codable {
let user: [UserDetails]
enum CodingKeys: String, CodingKey {
case user = "User"
}
init(user: [UserDetails]) {
self.user = user
}
}
class UserDetails: Codable {
let name: String
let age: String
enum CodingKeys: String, CodingKey {
case name = "Name"
case age = "Age"
}
init(name: String, age: String) {
self.name = name
self.age = age
}
}
And my ViewController:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var arrayDefault = [UserDetails]()
var arrayFavourite = [UserDetails]()
var sec: [Int:[UserDetails]] = [:]
var Default = UserDefaults.standard
#IBOutlet weak var myTableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.myTableview.dataSource = self
self.myTableview.delegate = self
DownloadJSON()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
sec = [0 : arrayFavourite, 1 : arrayDefault]
return (sec[section]?.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dataRef = arrayDefault[indexPath.row]
let cel: MyCell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
cel.NameLabel.text = dataRef.name
cel.AgeLabel.text = dataRef.age
let imgFav = UIImage(systemName: "star")
let b = imgFav?.withRenderingMode(.alwaysOriginal)
cel.FavButton.setImage(b, for: .normal)
return cel
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func DownloadJSON() {
let urlll = URL(string: "https://pastebin.com/raw/Wufivqmq")
guard let downloadURL = urlll else { return }
URLSession.shared.dataTask(with: downloadURL) { data, urlResponse, error in
guard let data = data, error == nil, urlResponse != nil else {
print("something is wrong")
return
}
print("downloaded")
do
{
let decoder = JSONDecoder()
let downloadeduser = try decoder.decode(User.self, from: data)
self.arrayDefault.removeAll()
self.arrayDefault.append(contentsOf: downloadeduser.user)
DispatchQueue.main.async {
print("data saved")
self.myTableview.reloadData()
}
} catch {
print(error)
}
}.resume()
}
#IBAction func RefreshButton(_ sender: Any) {
// Here i wish to refresh the table view (so the current saved array) with the updated JSON
// For example if i update the "Age" in the original JSON
// But i don't want to lose the user preference for the "Favourite"
}
}
As you can see is not a big deal thing, but i can't figure out how to properly save and retrive the array, and then move the cell (by the index path) to the other array.
This is the link for the full project: https://github.com/Passeric/UserTableview.git
Thanks again to anyone who will help.❤️
I would make a few changes:
Clean up your data model. Currently you are storing the data as both two arrays and a dictionary. There are minor pros and cons to each approach, but in the grand scheme of things it doesn't really matter which one you use, just pick one. Personally, as a beginner, I would go with two arrays.
Don't change your data model in the data source. Currently you are creating the sec dictionary in the tableView:numberOfRowsInSection function. If you want to keep using sec then create it as the same time you load the data initially. Or, as mentioned above, just remove sec completely.
Assuming you've made the above changes, moving a user to the favorites section is as simple as removing the user from the default list, adding it to the favorites list, and telling the list that the data has changed:
func makeFavorite(index:IndexPath) {
let user = arrayDefault[index.row]
arrayDefault.remove(at: index.row)
arrayFavourite.append(user)
//This updates the entire list. You might want to use moveRow(at:to:) in order to animate the change
tableView.reloadData()
}
When it comes to converting your data model to/from json, you're already most of the way there by conforming to Codable. To load from json:
if let jsonData = UserDefaults.standard.string(forKey: "userKey").data(using: .utf8) {
let decoder = JSONDecoder()
if let user = try? decoder.decode(User.self, from: jsonData) {
//Do something with user here...
}
}
And to output to json:
let encoder = JSONEncoder()
if let data = try? encoder.encode(user),
let jsonString = String(decoding: data, as: .utf8) {
UserDefaults.standard.setValue(jsonString, forKey: "userKey")
}
Do the same thing with the list of favorites (using a different key).
Related
Now what I am adding an item to a cart it is appearing correctly. But when I am reopening the page, the data is lost. I am trying to store the cartTableView data in userdefaults. I have tried several methods but couldn't turn up.
Here is my code for cartViewController :
import UIKit
class CartViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var arrdata = [jsonstruct]()
var categorydata = [Categories]()
var imgdata = [Images]()
var cartArray = [CartStruct]()
#IBOutlet var cartTableView: UITableView!
#IBOutlet var totalCount: UILabel!
#IBOutlet var subtotalPrice: UILabel!
#IBOutlet var shippingPrice: UILabel!
#IBOutlet var totalPrice: UILabel!
#IBOutlet var proceedBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
cartArray = (UserDefaults.standard.array(forKey: "cartt") as? [CartStruct])!
cartTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cartArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CartCellTableViewCell", for: indexPath) as! CartCellTableViewCell
cell.cartImageView.downloadImage(from: cartArray[indexPath.row].cartItems.images.first?.src ?? "place_holder_image")
cell.productNameCart.text = cartArray[indexPath.row].cartItems.name
cell.prodductDescCart.text = cartArray[indexPath.row].cartItems.categories.first?.type
cell.productPriceCart.text = cartArray[indexPath.row].cartItems.price
cell.addBtn.addTarget(self, action: #selector(add(sender:)), for: .touchUpInside)
cell.addBtn.tag = indexPath.row
let cartQuantity = cartArray[indexPath.row].cartQuantity
cell.prodCount.text = "\(cartQuantity)"
if cartQuantity >= 0 {
cell.subBtn.isUserInteractionEnabled = true;
cell.subBtn.addTarget(self, action: #selector(sub(sender:)), for: .touchUpInside)
cell.subBtn.tag = indexPath.row
} else {
cell.subBtn.isUserInteractionEnabled = false;
}
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
cartArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.reloadData()
}
}
#objc func add(sender: UIButton){
if cartArray[sender.tag].cartQuantity >= 0 {
cartArray[sender.tag].cartQuantity += 1
cartTableView.reloadData()
}
}
#objc func sub(sender: UIButton){
if cartArray[sender.tag].cartQuantity > 0 {
cartArray[sender.tag].cartQuantity -= 1
cartTableView.reloadData()
}
}
}
But here is showing some runtime error : " Thread 1: Swift runtime failure: force unwrapped a nil value "
Please suggest me how can I fix the whole thing.. Thanks in advance!
You can use UserDefaults, But You should use core data or realm for better performance.
how to use this code?
// Save:
CartCache.save([Cart])
// Get data:
let cart = CartCache.get()
struct CartCache {
static let key = "CartCache"
static func save(_ value: [CartStruct]!) {
UserDefaults.standard.set(try? PropertyListEncoder().encode(value), forKey: key)
}
static func get() -> [CartStruct]! {
var cartData: User!
if let data = UserDefaults.standard.value(forKey: key) as? Data {
cartData = try? PropertyListDecoder().decode([User].self, from: data)
return cartData!
} else {
return []
}
}
static func remove() {
UserDefaults.standard.removeObject(forKey: key)
}
}
Yes, we can store in UserDefaults, But it is not flexible as compare to Core Data or other storage for add, updates or delete operations.
Below code supposed to work.
You need to make "CartStruct" as Codable
struct CartStruct : Codable {
var cartItems: jsonstruct
var cartQuantity: Int
}
Next, Add this to insert and store it in tap "addToCartbtnTapped" .(DetailViewController)
func saveCart(data: CartStruct) {
let defaults = UserDefaults.standard
if let cdata = defaults.data(forKey: "cartt") {
var cartArray = try! PropertyListDecoder().decode([CartStruct].self, from: cdata)
cartArray.append(data)
cartCount.text = "\(cartArray.count)"
if let updatedCart = try? PropertyListEncoder().encode(cartArray) {
UserDefaults.standard.set(updatedCart, forKey: "cartt")
}
}
}
#IBAction func addToCartbtnTapped(_ sender: Any) {
if let info = detailInfo {
let cartData = CartStruct(cartItems: info, cartQuantity: 1)
self.saveCart(data: cartData)
showAlert()
(sender as AnyObject).setTitle("Go to Cart", for: .normal)
addToCartbtn.isUserInteractionEnabled = false
}
}
#IBAction func cartTappedToNavigate(_ sender: Any) {
let cart = self.storyboard?.instantiateViewController(withIdentifier: "CartViewController") as? CartViewController
self.navigationController?.pushViewController(cart!, animated: true)
}
Now, Fetch your cart data from where you stored.(CartViewController)
override func viewDidLoad() {
super.viewDidLoad()
self.getCartData()
}
func getCartData() {
let defaults = UserDefaults.standard
if let data = defaults.data(forKey: "cartt") {
cartArray = try! PropertyListDecoder().decode([CartStruct].self, from: data)
cartTableView.reloadData()
}
}
You should use different data saving methods other than user defaults. User defaults mostly used to save small informations like name, username, gender etc. I recommend you to use Core Data instead of user defaults and here is a very useful explanation and example projec of Core Data.
https://www.raywenderlich.com/7569-getting-started-with-core-data-tutorial
Have a great day :)
As many people have mentioned UserDefaults is not really meant for storing this type of data.
From the docs:
The UserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an app to customize its behavior to match a user’s preferences. For example, you can allow users to specify their preferred units of measurement or media playback speed. Apps store these preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults because they’re commonly used to determine an app’s default state at startup or the way it acts by default.
However I feel like this is still a valid question and something that every IOS/MacOS developer should know how to use.
NSUserDefaults is simple and easy to use, but there are some things you have to understand.
Namely you can only store property list items.
A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
This means that CartStruct cannot simply be persisted in UserDefaults, because it is not a NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary or a combination of these.
Now using UserDefaults is simple they have a singleton for the app called standard.
UserDefaults.standard.setValue("Hello Defaults", forKey: "Hello_Key")
let greeting = UserDefaults.standard.value(forKey: "Hello_Key")
print(greeting!)// prints Hello Dafaults
The next question becomes how to turn your struct into data. I'm sure if you ask that question on here and post the code for your struct you'll get a quick and easy solution. Once you have data you can save that in UserDefaults or better yet in the file system. In fact I think that really is the question your trying to ask is how to persist and retrieve your struct data for use in a tableview.
I have my data structure: My Firestore Database
As you'll see I have a "Michael 201A" document as well as a "Michael 201B" the idea is to retrieve the fields from these documents and display them in a tableView. Additionally, i would like the tableView to update automatically based off of any new documents that are added to the "Requests" Collection so the tableView data is always populated wit the most recent additions to the firestore database.
Function to retrieve data from FireStore
#IBOutlet weak var tableView: UITableView!
var db: Firestore!
var requestArray = [Request]()
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
tableView.delegate = self
tableView.dataSource = self
loadData()
}
func loadData() {
db.collection("Requests").whereField("Status", isEqualTo: true).getDocuments() {(querySnapshot, err) in
if let err = err {
print("An error occurred\(err)")
} else{
self.requestArray = querySnapshot!.documents.compactMap({Request(dictionary: $0.data())})
print(self.requestArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
I've added a print statement to get a reading of the value but it returns empty.
My tableView functions
extension ResidentAdvisorViewController: UITableViewDelegate {
func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me")
}
}
extension ResidentAdvisorViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return requestArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let request = requestArray[indexPath.row]
cell.textLabel?.text = "\(request.Name)"
return cell
}
}
My Request Struct
protocol DocumentSerializable {
init?(dictionary:[String:Any])
}
struct Request {
var Name: String
var Dorm: String
var Room: Int
var Status: Bool
var UID: String
var TimeStamp: Date
var dictionary:[String:Any] {
return [
"Name":Name,
"Dorm":Dorm,
"Room":Room,
"Status":Status,
"UID": UID,
"TimeStamp": TimeStamp
]
}
}
extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let name = dictionary["Name"] as? String,
let dorm = dictionary["Dorm"] as? String,
let room = dictionary["Room"] as? Int,
let status = dictionary["Status"] as? Bool,
let uid = dictionary["UID"] as? String,
let timestamp = dictionary["Timestamp"] as? Date
else { return nil}
self.init(Name: name, Dorm: dorm, Room: room, Status: status, UID: uid, TimeStamp: timestamp)
}
}
As a side note i have checked to ensure my cell identifier matches "cell". Also, when i change the cell text to "Hello World" I am able to get it displayed in my tableView. Any assistance is greatly appreciated thank you.
There's not a whole lot wrong with the code but there are two questions within the question.
1) Why is the value empty
2) How to I populate my dataSource intially and then update it when new documents are added.
Let me address 2) first.
To initially load the data and then watch for future changes, we can uyse the .addSnapshotListener function, and then handle the specific change type within the firebase closure.
func observeAllRequests() {
let requestsCollection = self.db.collection("Requests")
let query = requestsCollection.whereField("Status", isEqualTo: true)
query.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let name = diff.document.get("Name") as? String ?? "No Name"
print("added: \(name)") //add to your dataSource
}
if (diff.type == .modified) {
let name = diff.document.get("Name") as? String ?? "No Name"
print("modified: \(name)") //update the request in the dataSource
}
if (diff.type == .removed) {
let name = diff.document.get("Name") as? String ?? "No Name"
print("removed: \(name)") //remove the request from the dataSource
}
}
//tableView.reloadData()
}
}
The above code will return all of the documents that match the query. Iterate over the items in the snapshot, with each being either .added, .modified or .removed. The first time the function is called, all differences will be .childAdded which allows you to initially populate the dataSource.
Any document changes after that will be just the document that was changed with the difference being by .added, .modified and .removed.
EDIT:
To address question 1)
The reason the array is empty is because of how the extension is structured - it's pretty much an all or none. Here's how it is now
extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let name = dictionary["name"] as? String
let dorm = dictionary["Dorm"] as? String,
let room = dictionary["Room"] as? Int,
let status = dictionary["Status"] as? Bool,
let uid = dictionary["UID"] as? String,
let timestamp = dictionary["Timestamp"] as? String
else { return nil}
self.init(Name: name)
} }
If a field is not found then the entire thing fails and returns nil, and compactMap igores nil so you end up when an empty array. Your structure does not include Timestamp, so it fails.
I would suggest something to protect your code but allow for missing fields. The nil-coalescing operator would work well here
extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
let name = dictionary["name"] as? String ?? "No Name"
let room = dictionary["room") as? String ?? "No Room"
etc
I just began to learn Firebase a week ago, but right now I am facing a problem of not able to load image from Firebase to my TableViewCell. I can retrieve data such as text information and the URL of the image from Firebase Realtime Database but not able to make use of those URL in order to fire up image on the TableViewCell. May you all help me identify the problems? I can retrieve everything such as text information as well as the image URL but how can I make the image pop up on the cell? All your help would be highly appreciate!
This is the ViewController that responsible to display the TableViewCell
import UIKit
import Firebase
import FirebaseStorage
class NewsFeedViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var rubthort:String = ""
var linkRub:String?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrItem.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! NewsFeed
// textlabel
// detailtextlabel
cell.textLabel?.text = arrItem[indexPath.row].name
cell.detailTextLabel?.text = arrItem[indexPath.row].price
//cell.imageView?.image = UIImage(named: "flower")
// Get image
let id = RetrieveData()
if let imageLink = self.linkRub {
let url = URL(string: imageLink)
//let data = NSData(contentsOf: url!)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
// download hit an error so return out
if error != nil {
print(error)
return
}
DispatchQueue.main.async {
cell.imageView?.image = UIImage(data: data!)
}
}.resume()
}
return cell
}
let ref = Database.database().reference()
// Array of PlasticItem
var arrItem = [RetrieveData]()
#IBOutlet weak var tblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
retrieveData()
} // Ends of viewDidLoad
func retrieveData() {
// Getting a node from database
let retRef = ref.child("item/electronic")
// Observing data changes
retRef.observe(DataEventType.value) { (dataSnapshot) in
// Remove array item everytime there is a new reference to the data in Firebase
self.arrItem.removeAll()
// Check if there are any children or second object inside the parent object
if dataSnapshot.childrenCount > 0 {
// Loop over all children's object
for post in dataSnapshot.children.allObjects as! [DataSnapshot] {
let object = post.value as! [String: Any]
let getName = object["name"] as! String
let getPrice = object["price"] as! String
let getImage = object["itemURL"] as! String
print(getName)
print(getPrice)
print(getImage)
self.linkRub = getImage
self.arrItem.append(RetrieveData(cat: "", name: getName, price: getPrice, rub: getImage))
}
self.tblView.reloadData()
}// Ends of if statement
else if dataSnapshot.childrenCount == 0{
print("No Data Found")
}
} // Ends of retRef.observe
} // Ends of retrieveData()
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
This is the model struct
import Foundation
import UIKit
struct RetrieveData{
var cat: String
var name: String
var price: String
var rub: String?
init(){
self.cat = ""
self.name = ""
self.price = ""
self.rub = ""
}
init(cat:String, name:String, price:String, rub: String){
self.cat = cat
self.name = name
self.price = price
self.rub = rub
}
}
I am very new to Swift 4 and I am not able to get an example on parsing JsonArray using JSONDecoder.
Below is the JSON Response which I am trying to parse from past many days.
{
"News": [ // how can i use this news as key in swift and parse it
{
"intId": 354,
"Guid": "4829b85d-56ed-46ed-b489-ddbaf0eaeb05",
"stTitle": "Hyatt place pune CsR Thrive Activity - 2017",
"dtUpdatedDate": "2017-06-01T11:25:00"
},
{
"intId": 115,
"Guid": "bcc1272c-6a47-4878-9091-5af224be494c",
"stTitle": "CIRCULAR W.R.T. BOMBAY HIGH COURT INJUNCTION AGAINST NOVEX",
"dtUpdatedDate": "2014-06-26T17:29:00"
},
{
"intId": 120,
"Guid": "274275db-9aa9-45d3-a00a-0f2eed662e7e",
"stTitle": "Extension of FSSAI deadline.",
"dtUpdatedDate": "2014-08-08T16:07:00"
}
]
}
Below is my Swift code:
import UIKit
/* This is struct i have created to parse jsonArray and JsonObject*/
struct JsonFromWeb:Codable {
let News: [jsonstruct]
}
struct jsonstruct:Codable {
let stTitle:String
let dtNewsDate:String
}
class ViewController:UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet var tableview: UITableView!
// below is the array for storing the response values
var arradata = [jsonstruct]()
override func viewDidLoad() {
super.viewDidLoad()
getdata() // funcction call to load the json api
}
func getdata() { // function getData to load the Api
let url = URL(string : "http://www.hrawi.com/HrawiService.svc")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
if (error == nil) {
print(url!)
let news = try JSONDecoder().decode(JsonFromWeb.self, from: data!)
self.arradata = news.News
}
} catch {
print("Error In Json Data")
}
}.resume()
}
//Tableview to set the JSON Data in UITableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.arradata.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
//lblname to set the title from response
cell.lblname.text = "News Title \(arradata[indexPath.row].stTitle)"
cell.lblcapital.text = "news Date \(arradata[indexPath.row].dtNewsDate)"
return cell
}
}
Use these structs:
struct JSONFromWeb: Codable {
let news: [JsonStruct]
enum CodingKeys: String, CodingKey {
case news = "News"
}
}
struct JsonStruct: Codable {
let intID: Int
let guid, stTitle, dtUpdatedDate: String
enum CodingKeys: String, CodingKey {
case intID = "intId"
case guid = "Guid"
case stTitle, dtUpdatedDate
}
}
Note the use of coding keys to conform to the camel case naming convention in Swift. Also, the first letter in a struct name should be uppercased: JsonStruct. arradata should then be declared as [JsonStruct].
Now you can decode the json like so:
do {
let jsonFromWeb = try JSONDecoder().decode(JSONFromWeb.self, from: data)
//This web call is asynchronous, so you'll have to reload the table view
DispatchQueue.main.async {
self.arradata = jsonFromWeb.news
self.tableview.reloadData()
}
} catch {
print(error)
}
Change this to
struct jsonstruct:Codable {
let stTitle:String
let dtUpdatedDate:String
}
let news = try JSONDecoder().decode(JsonFromWeb.self, from:ata!)
DispatchQueue.main.async {
self.arradata = news.News
self.tableview.reloadData()
}
You better start struct names with capital , and vars with small , i left it as not to confuse you with your current code
When I run my project I am just able to get only tableView but datas are not fetched its blank. The struct codable and view controller codes were as follows. Please help me in viewing the datas in my tableView cells using alamofire and SwiftyJSON,
class Loads: Codable {
let loads: [Load]
init(loads: [Load]) {
self.loads = loads
}
}
class Load: Codable {
let id: String
let ad_title: String
let ad_description:String
let ad_price: String
let ad_post_date: String
let image1: String
init(ad_title: String, ad_description: String, ad_price: String, ad_post_date: String, image1: String) {
self.ad_title = ad_title
self.ad_description = ad_description
self.ad_price = ad_price
self.ad_post_date = ad_post_date
self.image1 = image1
}
}
View Controller codes:
import UIKit
import SwiftyJSON
import Alamofire
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var loads = [Load]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
downloadJson()
self.tableView.reloadData()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return loads.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LoadCell") as? LoadCell else { return UITableViewCell() }
cell.labelA.text = loads[indexPath.row].ad_title
cell.labelB.text = loads[indexPath.row].ad_price
cell.labelC.text = loads[indexPath.row].ad_description
cell.labelD.text = loads[indexPath.row].ad_post_date
if let imageURL = URL(string: loads[indexPath.row].image1) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.loadImage.image = image
}
}
}
}
return cell
}
func downloadJson(){
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
//Printing strings from a JSON Dictionary
print(json[0]["ad_title"].stringValue)
print(json[0]["ad_price"].stringValue)
print(json[0]["ad_description"].stringValue)
print(json[0]["ad_post_date"].stringValue)
print(json[0]["image1"].stringValue)
}
self.tableView.reloadData()
}
self.tableView.reloadData()
}
}
I am using xcode9, swift 4.
datas are not fetched its blank
Your code does not seem to update var loads by downloaded data, that is why you just get blank table view. So, you need to assign fetched data to var loads.
Here is sample:
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON { response in
// you should assign response data into var loads here
if let data = response.data {
do {
self.loads = try JSONDecoder().decode([Load].self, from: data)
} catch {
// exception
}
}
}
And after that, self.tableView.reloadData().
PS: Of course I don't know your response JSON format and your source code overall, so it might not be a directory answer to your question, but I hope it will help!
At first glance, the url looks faulty to me. https is typed twice, please remove once in below line.
Change this :
Alamofire.request("https:https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON
to :
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON
First off, your...
struct codable
... is not a struct at all, it is a class. change that to an actual struct.
Second, you start downloading before your delegate and datasource are set to the tableview.
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
downloadJson()
}
Thirdly, you are using Alamofire for a simple GET request, but you ignore the fact of asyncronous loading of images for a cell, which you do with Data. I'd suggest to use AlamoFireImageDownloader or remote AlamoFire all together. Using URLSession is just as easy:
private func request(url: URL, completionHandler: #escaping ((Data?, URLResponse?, Error?) -> Void)) {
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request, completionHandler: completionHandler)
task.resume()
}
Fourth, I don't believe you need the loads codable.
Just an extra reminder, depending on the amount of Loads you'll have in your table, you will get problems with the loading of your images. The closures are not guaranteed to finish in order, but when they do they will update the cell regardless to whether the cell is already reused for another Load or not. Just FYI.