Reload UiTableView After Data Has Been Retrieved From Database - Swift - ios

How do I reload a UiTableView only after the data has been loaded from the database. I have a datasource class that contains the function to load in all the data, and stores the data after it has been loaded into an array.
When I access the data using the Tableview though it displays nothing because the tableview loads before the database has returned its data. The only way to load the data is for me to have a button that manually reloads the tableview.
How do I cause the tableview to automatically reload once the array in the datasource object has been populated from the database?

Being a new programmer it took me a while to learn this was even possible, but I just realized completion blocks are the way to go. It will carry out some specified code only after your function has finished fetching from the database. In this case, I can update the tableview AFTER I've retrieved the data from the database. Here's an example.
Function definition:
func getDataFromDatabase(someInput: Any, completion: #escaping(String?) -> Void) {
let retrievedData: String = databaseCallFunction()
// 'Get data from database' code goes here
// In this example, the data received will be of type 'String?'
// Once you've got your data from the database, which in this case is a string
// Call your completion block as follows
completion(retrievedData)
}
How to call the function:
getDataFromDatabase(someInput: "Example", completion: { (data) in
// Do something with the 'data' which in this example is a string from the database
// Then the tableview can be updated
let tableView.title = data
tableView.reloadTableView
})
The code in the completion block will happen AFTER the database call has completed.

Why not just call tableView.reloadData() once you've populated your data? It'll repopulate the entire table (well technically it'll only re-create whatever cells are visible).
The point is that your tableView:cellForRowAt: method should have the ability to access your data so as to fill in each table cell appropriately based on that data. Anytime your data changes, just call tableView's reloadData() again. It's efficient in that only the visible cells will be created, and the rest will be created whenever they are about to become visible, if ever.
Also, tableView:numberOfRowsInSection: should similarly be looking at your data to judge how many rows shall exist. It too will be called again during each call to reloadData().
Your data should always be prepared to be interrogated by the tableView delegate methods, even when it doesn't contain anything yet. When there's no data, your tableView:numberOfRowsInSection: would decide there should be 0 rows.

Related

UITableViewDataSourcePrefetching->prefetchRowsAt always has 0 and 1 in indexPaths array

I have used UITableViewDiffableDataSource as the datasource for my tableView.
On initial load, 8 items are fetched from server and displayed.
When I try to scroll,
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
is called, but indexPaths always has 0 and/or 1. It is never getting incremented to ask for prefetching 10, 11,12, etc rows.
What am I missing here?
From Apple Docs:
Table views do not call this method for cells they require immediately, so your data source object must also be able to fetch the data itself.
This approach is offered by the docs:
The tableView(:prefetchRowsAt:) method is not necessarily called for every cell in the table view. Your implementation of tableView(:cellForRowAt:) must therefore be able to cope with the following potential situations:
Data has been loaded via the prefetch request, and is ready to be displayed.
Data is currently being prefetched, but is not yet available.
Data has not yet been requested.
One approach that handles all of these situations is to use Operation to load the data for each row. You create the Operation object and store it in the prefetch method. The data source method can then either retrieve the operation and the result, or create it if it doesn’t exist.

Saving cell properties for handsontable

In handsontable documentation, it is suggested that we used the hot.getData() method to get the data of the current instance, and then be able to save that data to the database (where hot is an instance of handsontable). the hot.gatData() method returns an array of arrays, of all the data in the handsontable.
the problem is, each cell has other properties/metadata that I like to save (most importantly, the type property). That metadata is not returned as part of the hot.getData() method. So if I want to retrieve my saved data and use the that table correctly in future, I need a way of saving the cell properties.
How can I do that? I could not find that in documentation.
Thank.s

NSFetchedResultsController notifies its Delegate of delete changes when a managed object is modified, and never notifies for Insert or Update

I have a UITableViewController, which is a delegate for an NSFetchedResultsController. My NSFetchedResultsControllerDelegate functions are set up as per "Typical Use" in Apple's documentation, with the table view controller as the fetched result controller's delegate property.
I also have some view controllers which are presented on top of the table view where the managed objects can be modified. These modifications are done on the same managed object context. There is only one managed object context constructed in my application, and it is accessed globally. (I have put some print statements in my object context construction just to be sure I am not accidentally re-constructing it elsewhere.)
When I modify one of the managed objects, the delegate function controller:didChangeObject is called, but the NSFetchedResultsChangeType is always .Delete. When I create a managed object, the delegate function does not fire at all.
However, when I manually call call performFetch() and tableView.reloadData(), the cells are restored to the correct state: the removed row comes back, any not-inserted rows are created.
The result is that deleting an object works as expected (the cell is removed), but updates to an object cause its cell to be removed, and object creations do not trigger cell inserts.
I tried to create a simple demo of this behaviour, but when I re-created the situation from a blank application, I don't see this behaviour. So something within my application is causing this strange behaviour, but I can't figure out what. Any ideas?
Extra Info:
The actual construction of the predicate and sort descriptors are done over several different classes, but printing them via print(resultsController.fetchRequest.predicate) and print(resultsController.fetchRequest.sortDescriptors) gives the following:
Optional(readState == "2" OR readState == "1")
Optional([(readState, ascending, compare:), (title, ascending, compare:)])
I have put a print statement in my controller:didChangeObject: method, and I can see that this only gets called with type.rawValue = 2 (i.e. .Delete), and only when I modify objects, not when I create them.
It's an inconsistency with how NSFetchedResultsController handles its NSPredicate.
If the NSFetchedResultsController is constructed with a fetch request which has a predicate which does a comparison between an integer and a string like follows:
let predicate = NSPredicate(format: "integerAttribute == %#", String(1))
this will lead to the predicate string being:
integerAttribute == "1"
When this is the case, initial fetches work fine: calling the function performFetch() on the fetched results controller returns all objects where the integerAttribute is equal to 1 (where integerAttribute is of type Int32).
However, the notifications to NSFetchedResultsControllerDelegate do not work fine. Modifications of managed objects result in the delegate being notified of a NSFetchedResultsChangeType.Delete change. Creations of managed objects do not invoke the delegate at all.
To make all this weirdness go away, fix the predicate format string as follows:
let predicate = NSPredicate(format: "integerAttribute == %d", 1)

Swift - Updating a UITableView with remote JSON data

i am trying to access data from a remote json file. When something is typed into the searchbar a new NSURLSessionDataTask is made to access the json file. I have successfully been able to extract all the data i need from the json file, but have not been able to update the table view as soon as the data comes in. To show the data i must either scroll so one cell is not visible on the screen. This will make it so that only that one cell has the correct values in it, alternatively, i can also type in a other letter into the searchable which will generate a new json file, but the data from the previous request is only shown now.
I have tried updating the table view in the main thread but that doesn't seem to make any difference. I have also tried multiple other methods, but still no luck.
You can try like this
if let resData = swiftyJsonVar["loginNodes"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.tableView.reloadData()
}
Note :arrRes is the name of array.
complete details are :How get JSON Data in Swift and add it to URL
To get the best answer you'll want to post code, specifics, and maybe even a link to the full project, but based on what you are saying the data is loaded in the code properly and is not being refreshed in the table view.
To fix this you first need a reference to the table view that is not loading, for this example I'll call it tableView. Then you can force it to reload all the visible data by calling
tableView.reloadData()
After your JSON data has finished loading.
When your data is loaded successfully just call
self.table.reloadData()
What it will do is it will populate the cells again with the updated data.

Scrolling tableview while updating its datasource fetched from CoreData crash

Here is my context:
When I launch my app I fetch local data from CoreData and fill a tableview with it. At the same time I send an asynchronous request to my webservice to fetch what will be the new content of my tableview.
As soon as the request sends me a response I delete all the instances of the current NSManagedObjects and create new ones with the new data I got. Then I replace the datasource of my tableview to an array of the new NSManagedObjectContexts instances.
My problem:
I'm getting an error : CoreData could not fulfill a fault for ... if I scroll my tableview when the request finished and is triggering the deletion/creation of my tableview's data source.
I understand that this problem occurs because I'm trying to access an old NSManagedObject instance while doesn't exist anymore as it is explained in the doc : Apple doc. But I have no idea of what are the best practices in my case.
I don't want to block the user until my request finished but I have to prevent any error if he accesses "old" data while the request didn't finish (e.g : what if the user taps on a cell and I pass an instance of an NSManagedObject to another viewcontroller but when the request finishes this object doesn't exist anymore ?)
I would appreciate any help !
I highly recommend you to use NSFetchedResultsController since it's sole purpuse is:
You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.
When using a fetched results controller it is much easier to handle the Core Data events like insert, delete, update.
You say you have three sections in your table view? That's no problem, NSFetchedResultsController can handle all of that.
Take a look at this. Apple provides a very nice set of instructions on how to configure and use NSFetchedResultsController.

Resources