Swift - No data displayed in TableView after Retrieving it from UserDefaults - swift

I am trying to get the data I have saved and then load it into the TableView. Currently, if I am saving the object for the first time, the data gets encoded, saved, decoded, read and displayed correctly. However, if the key already exists and I am adding to the existing data, nothing gets displayed in the TableView.
This is currently how I am saving it in the first view controller:
let userEntry = UserEntries(date: String(todayDate), questions: [UserEntries.Question(question: q1Text, answer: q1Answer), UserEntries.Question(question: q2Text, answer: q2Answer)])
var allEntries : [UserEntries] = []
if doesKeyExist(key: "allEntries") == true {
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode(UserEntries.self, from: data) {
allEntries = [userEntries]
}
allEntries.insert(userEntry, at: 0)
let jsonEncoder = JSONEncoder()
if let value = try? jsonEncoder.encode(allEntries) {
UserDefaults.standard.set(value, forKey: "allEntries")
UserDefaults.standard.synchronize()
}
} else {
let jsonEncoder = JSONEncoder()
if let value = try? jsonEncoder.encode(userEntry) {
UserDefaults.standard.set(value, forKey: "allEntries")
UserDefaults.standard.synchronize()
}
}
let newViewController = storyboard?.instantiateViewController(withIdentifier: "tabViewController") as! UITabBarController
present(newViewController, animated: true, completion: nil)
}
This is how I'm displaying it in the TableView
var TableData : [UserEntries] = []
override func viewDidLoad() {
super.viewDidLoad()
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode(UserEntries.self, from: data) {
print(userEntries.date)
TableData = [userEntries]
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "entryCell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row].date
cell.detailTextLabel?.text = TableData[indexPath.row].questions[0].answer
return cell
}
I have a feeling that this is a logic error, in getting/displaying the data in the TableView, but am unsure of what exactly it is/how to fix it. Thank you for your help!

I don't know what your doesKeyExists function does but you can improve your data manipulation by this way:
let userEntry = UserEntries(date: String(todayDate), questions: [UserEntries.Question(question: q1Text, answer: q1Answer), UserEntries.Question(question: q2Text, answer: q2Answer)])
var allEntries : [UserEntries] = []
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode([UserEntries].self, from: data) {
allEntries = userEntries
}
allEntries.insert(userEntry, at: 0)
let jsonEncoder = JSONEncoder()
if let value = try? jsonEncoder.encode(allEntries) {
UserDefaults.standard.set(value, forKey: "allEntries")
UserDefaults.standard.synchronize()
}
let newViewController = storyboard?.instantiateViewController(withIdentifier: "tabViewController") as! UITabBarController
present(newViewController, animated: true, completion: nil)
}
And the problem with your controller is that you are saving only one entry to UserDefaults. Try to change your code to
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode([UserEntries].self, from: data) {
print(userEntries)
TableData = userEntries
}

Related

Saving TableView cell using UserDefaults

I'm trying to get cell of tableView using UserDefaults, but after i reload app it is always empty
This is my Model:
struct Note: Codable {
var title: String
var description: String
}
class Notes {
var stock: [Note] = []
}
View contoller
var model = Notes()
This is how i get data
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
tableView.reloadData()
if let fetchedData = UserDefaults.standard.data(forKey: "notes") {
let fetchedBookies = try! PropertyListDecoder().decode([Note].self, from: fetchedData)
print(fetchedBookies)
} else {
model.stock = []
}
}
This is my cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.titleOutlet.text = self.model.stock[indexPath.row].title
cell.descriptionOutlet?.text = self.model.stock[indexPath.row].description
return cell
}
How i save data
#IBAction func check(_ sender: Any) {
let newstock = Note(title: "check", description: "check2")
model.stock.append(newstock)
print(model.stock.count)
let bookiesData = try! PropertyListEncoder().encode(model.stock)
UserDefaults.standard.set(bookiesData, forKey: "notes")
tableView.reloadData()
}
Thank you very much!
I recommend you to use Json Encoder/Deocder.
First set your Notes class to conform to Codable:
class Notes: Codable {
var stock: [Note] = []
}
Here is an example of how to use Json Encoder / Decoder:
func save(notes: Notes) throws {
let encoder = JSONEncoder()
do {
let data = try encoder.encode(notes)
UserDefaults.standard.set(data, forKey: "notes")
} catch let error {
throw error
}
}
func load() -> Notes {
guard let data = UserDefaults.standard.data(forKey: "notes") else {
return Notes() // Default
}
let decoder = JSONDecoder()
do {
let object = try decoder.decode(Notes.self, from: data)
return object
} catch {
return Notes() // Default
}
}
In your code just call load() to get your notes from User Defaults
And save(notes:) to save them into User Defaults.

I want my tableview to reload after it sees a change in firestore database

** I want my tableview to reload after it sees a change in firestore database I thought that using tableview reload would make it reload but no it doesn't it only loads the new data after I restart the app I want the new data to reload right after function load daily motivation has a change in it **
import UIKit
import Firebase
//MARK: MAINVIEW MOTIVATION
class motivationviewcontroller : UIViewController,UITableViewDataSource,UITableViewDelegate{
var motivationThoughts = [MotivatioNDataModel]()
var tableview : UITableView!
override func viewDidLoad() {
print("madicc")
print("the user logged in is \( Auth.auth().currentUser?.email)")
tableview = UITableView(frame: view.bounds, style: .plain)
tableview.backgroundColor = UIColor.white
view.addSubview(tableview)
var layoutGuide : UILayoutGuide!
layoutGuide = view.safeAreaLayoutGuide
let cellNib = UINib(nibName: "dailyMotivationTableViewCell", bundle: nil)
tableview.register(cellNib, forCellReuseIdentifier: "DailyThoughtCELL")
tableview.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).isActive = true
tableview.topAnchor.constraint(equalTo: layoutGuide.topAnchor).isActive = true
tableview.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).isActive = true
tableview.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor).isActive = true
tableview.dataSource = self
tableview.delegate = self
loaddailymotivation()
self.tableview.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
//loaddailymotivation()
self.tableview.reloadData()
}
//======================================================================
//MARK: LOADS THE DATA INTO THE TABLEVIEW
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
motivationThoughts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DailyThoughtCELL", for: indexPath) as? dailyMotivationTableViewCell
cell!.generateCellsforDailymotivation(_MotivationdataMODEL: motivationThoughts[indexPath.row])
return cell!
}
//MARK: FUNCTION THAT HANDLES GETTING THE DATA FROM FIREBASE
func loaddailymotivation() {
FirebaseReferece(.MotivationDAILY).getDocuments { (snapshot, error) in
if let error = error {
print("error getting MOTIVATIONDAILY DATA \(error.localizedDescription)")
}
else {
guard let snapshot = snapshot else { return }
for allDocument in snapshot.documents {
let data = allDocument.data()
print("\(allDocument.documentID) => \(allDocument.data())")
print("we have\(snapshot.documents.count) documents in this array")
let dailymotivationTitle = data["Motivation title"] as! String //calls the data thats heald inside of motivation title in firebase
let dailyMotivationScripture = data["daily motivation scripture"] as! String //calls the data thats heald inside of Motivation script in firebase
let dailyMotivationNumberOfLikes = data["Number of likes in daily motivation post"]as! Int
let newthought = MotivatioNDataModel(RealmotivationTitle: dailymotivationTitle, RealmotivationScrip: dailyMotivationScripture, RealmotivationNumberOfLikes: dailyMotivationNumberOfLikes )
self.motivationThoughts.append(newthought)
}
}
}
}
Problem is you are fetching the data but not reloading your tableView after that, Change your loaddailymotivation() with the below one
func loaddailymotivation() {
FirebaseReferece(.MotivationDAILY)
.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 data = diff.document.data()
let dailymotivationTitle = data["Motivation title"] as! String //calls the data thats heald inside of motivation title in firebase
let dailyMotivationScripture = data["daily motivation scripture"] as! String //calls the data thats heald inside of Motivation script in firebase
let dailyMotivationNumberOfLikes = data["Number of likes in daily motivation post"]as! Int
let newthought = MotivatioNDataModel(RealmotivationTitle: dailymotivationTitle, RealmotivationScrip: dailyMotivationScripture, RealmotivationNumberOfLikes: dailyMotivationNumberOfLikes )
self.motivationThoughts.append(newthought)
}
if (diff.type == .modified) {
print("Modified data: \(diff.document.data())")
// here you will receive if any change happens in your data add it to your array as you want
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
}
}
here i have added listeners to your firestore data so if any new data adds up or any data changes into the database you will receive it in the App & will reflect that changes realtime.
Do one thing follow my comment in the code.
You can try add "addSnapshotListener" to Your "FUNCTION THAT HANDLES GETTING THE DATA FROM FIREBASE".
Let's try add it like this:
func loaddailymotivation() {
FirebaseReferece(.MotivationDAILY).getDocuments.addSnapshotListener { (snapshot, error) in
if let error = error {
print("error getting MOTIVATIONDAILY DATA \(error.localizedDescription)")
}
else {
guard let snapshot = snapshot else { return }
for allDocument in snapshot.documents {
let data = allDocument.data()
print("\(allDocument.documentID) => \(allDocument.data())")
print("we have\(snapshot.documents.count) documents in this array")
let dailymotivationTitle = data["Motivation title"] as! String //calls the data thats heald inside of motivation title in firebase
let dailyMotivationScripture = data["daily motivation scripture"] as! String //calls the data thats heald inside of Motivation script in firebase
let dailyMotivationNumberOfLikes = data["Number of likes in daily motivation post"]as! Int
let newthought = MotivatioNDataModel(RealmotivationTitle: dailymotivationTitle, RealmotivationScrip: dailyMotivationScripture, RealmotivationNumberOfLikes: dailyMotivationNumberOfLikes )
self.motivationThoughts.append(newthought)
}
}
}

Two Search bars are appearing when I run my app but only one shows on storyboard

I am currently building a table view and I noticed that after my table view is populated I am getting two search bar fields. My original intention was to have only one search bar field appear (shown on my interface) I can't seem to figure out what this happens. When I try to delete the search bar from my interface then my table view will not load and populate data.
Here is a screenshot of the interface storyboard:
Here is the screenshot of when I run the app:
Here is some code including my viewDidLoad:
EDIT: Added TableView Code
class AddHarvestPlanViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var AddHarvestPlanPluCodeTable: UITableView!
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
findPluCodeParents(searchTextField: "apple")
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Find your Commodity"
navigationItem.searchController = searchController
definesPresentationContext = true
AddHarvestPlanPluCodeTable.delegate = self
AddHarvestPlanPluCodeTable.dataSource = self
AddHarvestPlanPluCodeTable.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count = harvestCommodities.count
return count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AddPluCodeCustomCell", for: indexPath) as! AddPluCodeCustomCell
do{
let item = harvestCommodities[indexPath.row]
cell.commodity?.text = item.plu_code_commodity
cell.package?.text = "Cases"
return cell
}
}
func findPluCodeParents(searchTextField:String){
let searchText = searchTextField
print("Searching for....",searchTextField)
let aggriEndpoint: String = "https://xxxx.xxxx.com/xxxx/xxxxx"
let url = URL(string:aggriEndpoint)
var urlRequest = URLRequest(url:url!)
urlRequest.httpMethod = "GET"
let session = URLSession.shared
print("running task")
let task = session.dataTask(with: urlRequest, completionHandler: {data,response,error -> Void in
do{
print("All finiished")
print(data)
if let json = try JSONSerialization.jsonObject(with: data!) as? [[String:Any?]]{
// print(json)
for item in json{
print(item as? [String:Any?])
var active = item["active"] as? Bool
let cases_per_week = (item["cases_per_week"] as! NSString).doubleValue
var cases_per_palette = item["cases_per_pallette"] as? Int
var lbs_per_week = (item["lbs_per_week"] as! NSString).doubleValue
var cases_per_week_avg = item["cases_per_week_avg"] as? Int
var pounds_per_case = item["pounds_per_case"] as? Int
var repeat_harvest = item["repeat_harvest"] as? Bool
var harvest_week_count = item["harvest_week_count"] as? Int
var plant_days = item["plant_days"] as? Int
var pounds_per_acre = item["pounds_per_acre"] as? Int
var options = item["options"] as? Int
var plu_code_variety = item["plu_code_variety"] as? String
var plu_code_commodity = item["plu_code_commodity"] as? String
var id = item["id"] as? Int
var plu_code = item["plu_code"] as? Int
var acres = item["acres"] as? Int
self.harvestCommodities.append(PluCode(id: id!, commodity: plu_code_commodity!, cases_per_week_avg: cases_per_week_avg!, repeat_harvest: repeat_harvest!, cases_per_week: cases_per_week, lbs_per_week: lbs_per_week, acres: acres!, plu_code: plu_code!, active: active!, options: options!, plant_days: plant_days!, plu_code_commodity: plu_code_commodity!, plu_code_variety: plu_code_variety!, cases_per_palette: cases_per_palette!, harvest_week_count: harvest_week_count!, pounds_per_acre: pounds_per_acre!))
}
}
DispatchQueue.main.async {
self.AddHarvestPlanPluCodeTable.reloadData()
}
} catch let error{
print("error")
}
})
task.resume()
}
Either remove the code where you create the upper Search Bar, ("let searchController = UISearchController(searchResultsController: nil)") and then connect an IBOutlet to the one you have created on the interface to your swift file, or remove the one on the storyboard and use the one you have created in your code, and connect the results to your table view

Swift Tableview Refresh Error

I get this error:
This is my code:
I am using refresh in the tableView section of the project. What could be causing this error during the refresh?
But in which phase it falls to the fault I could not solve that part
var kategoriId = ""
var refresher = UIRefreshControl()
var arrayKonularData = [konularData]()
let singleton = konularClass.sharedGlobal
override func viewDidLoad() {
super.viewDidLoad()
refresher.attributedTitle = NSAttributedString(string: "Yükleniyor")
refresher.addTarget(self, action: #selector(KonuDetayViewController.refresh), for: UIControlEvents.valueChanged)
self.tableview.addSubview(refresher)
KonulariGetir(sirala: "order by tarih desc")
navigationController?.delegate = self
tableview.layer.cornerRadius = 10
}
func refresh()
{
DispatchQueue.main.async {
if self.segmentControl.selectedSegmentIndex == 0
{
self.arrayKonularData.removeAll()
self.KonulariGetir(sirala: "order by tarih desc")
}
if self.segmentControl.selectedSegmentIndex == 1
{
self.arrayKonularData.removeAll()
self.KonulariGetir(sirala: "order by indirimpuani desc")
}
}
DispatchQueue.main.async {
self.refresher.endRefreshing()
}
}
I am taking data from web service in this section
func KonulariGetir(sirala:String)
{
var request = URLRequest(url: URL(string:"http://212.xxx.xxx.xxx:7001/IndirimiKovala/KonuGetir")!)
request.httpMethod = "POST"
let postString = "filtre="+sirala
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil
{
print("error")
}
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let gelenDizi = jsonResult as? NSArray
{
for i in 0..<gelenDizi.count
{
if let baslik = (gelenDizi[i] as? NSDictionary)?["baslik"] as? String
{
self.singleton.baslik = baslik
}
if let indirimPuani = (gelenDizi[i] as? NSDictionary)?["indirimpuani"] as? Int
{
self.singleton.indirimPuani = String(indirimPuani)
}
if let konuId = (gelenDizi[i] as? NSDictionary)?["id"] as? Int
{
self.singleton.konuId = String(konuId)
}
if let haberVeren = (gelenDizi[i] as? NSDictionary)?["uye"] as? String
{
self.singleton.haberVerenUye = haberVeren
}
if let gelenTarih = (gelenDizi[i] as? NSDictionary)?["tarih"] as? String
{
self.singleton.tarih = gelenTarih
}
if let gelenAktif = (gelenDizi[i] as? NSDictionary)?["aktif"] as? Int
{
self.singleton.aktif = gelenAktif
}
self.arrayKonularData.append(konularData.init(baslik: self.singleton.baslik, indirimPuani: self.singleton.indirimPuani, konuId: self.singleton.konuId,haberVeren:self.singleton.haberVerenUye , tarih:self.singleton.tarih,aktif:self.singleton.aktif))
}
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
catch
{
print("server hatası")
}
}
}
task.resume()
}
I guess the problem comes from the part of code where you try to populate tableview. So the possible solution can be in tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) delegate methode check if arrayKonularData array is not empty like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: orderCell, for: indexPath)
if !arrayKonularData.isEmpty {
.....// Your code
}
return cell
}
Another solution (which I thing will be the right solution in your case) add completion function of
func KonulariGetir(sirala:String)
and reload tableview in the completion method

Can any one help me to solve this error using Swift

would you please help me to solve this error .I'am trying to download an Image From Firebase Database, this is my code and I put a snapshot for the error . ThanksThis is a snapshot for the error in Xcode
import UIKit
import FirebaseDatabase
class ViewController: UIViewController , UITableViewDataSource , UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var ref:FIRDatabaseReference?
var Handle:FIRDatabaseHandle?
var myClass = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
ref=FIRDatabase.database().reference()
Handle = ref?.child("Posts").observe(.childAdded, with: { (snapshot) in
let post = snapshot.valueInExportFormat()
for url in post! as! [Post] { // Error Here
self.myClass.append(url)
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return myClass.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)as? TableViewCell{
cell.MyImage.alpha = 0
cell.textLabel?.text = PostData[indexPath.row]
DispatchQueue.main.async(execute: {
let imgurl = URL(string : self.myClass [(indexPath as NSIndexPath).row].url)
let imgdata = NSData(contentsOf: imgurl!)
cell.MyImage.image = UIImage(data: imgdata as! Data)
UIView.animate(withDuration: 0.5, animations: {
cell.MyImage.alpha = 1
})
})
return cell
} else {
let cell = TableViewCell()
DispatchQueue.main.async(execute: {
let imgurl = URL(string : self.myClass [(indexPath as NSIndexPath).row].url)
let imgdata = NSData(contentsOf: imgurl!)
cell.MyImage.image = UIImage(data: imgdata as! Data)
})
return cell
}
}
}
})
}
Sometimes simple is the way to go.
assume you have a Firebase structure
Planets
planet_4
some_text = "My post about Mars"
image_url = "images/mars.jpg"
planet_2
some_text = "My post about Venus"
image_url = "images/venus.jpg"
and suppose we want to load each text and image and display in a tableview. We can do it one of two ways, one at a time with .childAdded or all at once with .value. In this example, we'll walk through them one at a time.
let planetsRef = myRootRef.child("Planets")
planetsRef.observe(.childAdded, with: { snapshot in
let dict = snapshot.value as! [String: AnyObject]
let text = dict["text"]
let imageUrl = dict["image_url"]
// Create a reference to the file you want to download
let planetRef = storageRef.child(imageUrl) //storageRef is defined elsewhere
// Download in memory with a maximum allowed size
// of 1MB (1 * 1024 * 1024 bytes)
planetRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
// Got an error so handle it
} else {
// Data for "images/some planet.jpg" is returned
// let planetImage: UIImage! = UIImage(data: data!)
// then add the text and the image to your dataSource array
// and reload your tableview.
}
})
})
This is not tested but will provide the general idea
Maybe you want:
for url in post! {
var wrappedPost = Post()
wrappedPost.url = url
... use wrappedPost for whatever you need a Post object for
}