I am developing an app that fetches data from the web and displays it to the user. Assume that the data is reviews of a restaurant and one review is displayed on one view. The user can swipe left or right to go to the prev/next review. The data is fetched asynchronously (one thread for each review).
Here is the problem statement - Assume that 5 reviews have been fetched and the user is looking at the 3rd one currently. Now, the 6th review is fetched and I want to display it as the 4th review to the user (because the publish date of the 6th review is more recent than the 5th review). How should my model class inform the view controller?
I have considered some options -
Provide an array to the view controller and then send NSNotifications about new items to be inserted in-between the array at a specific index
Use an NSFetchedResultsController (this is a bit tricky because I am not using it with a table view controller)
View controller always asks for the next review to be displayed (from the model) and does not have a array of reviews with it
Are there any established design patterns that are employed in such a scenario? Other suggestions apart from the 3 above are welcome!
Just use an NSFetchedResultsController. When using NSIndexPaths just ignore the section. It's basically a glorified NSArray with free notifications.
Here's how I think I'd do it:
Make sure that the NSFetchRequest for your NSFetchedResultsController is sorted by publish date.
Handle NSFetchedResultsControllerDelegate methods.
When the NSFetchedResultsController updates, save the current object, reload the collection view, and then scroll to the saved object without any animation. This will appear to the user as if nothing happened to the current page.
While there is no perfect design pattern for every programming problem, the closest I can think of that relates to your problem is a combination of the Command and Observer patterns.
https://en.wikipedia.org/wiki/Command_pattern
The observer pattern is used in the NSNotification center.
While it's unclear as to why you'd want to skip a review, you could have two arrays to store them when fetched. The first holds all reviews that you have fetched. The second holds all reviews that are displayed.
Then you can get the last review in the fetched array, as if it were a stack. This way you always have the last one loaded displayed to the user.
I am confused why the order of display is different than the true order, ie why the 6th review comes before the 5th, but you asked about patterns to help.
Apart from MVC and observer, which are in the other answers and comments, I'd suggest using lazy loading with a virtual proxy. When reviews have been fetched, you can just display their proxy (eg with a "loading..." Message until they're fully in memory).
See more here: http://en.wikipedia.org/wiki/Proxy_pattern
I would recommend using the observing pattern to inform your controller than new data as been fetched. When receiving the signal, your view controller could update its array of "restaurant review" (either by adding the old one and reordering it according to some sort descriptors of your flavor or by querying the DAO directly).
Let's say you are fetching your data from internet and populating a CoreData entity with the results. Once you got your downloaded data you can populate your core data "Review" entity.
In order to "listen" at the change happening in core data, your controller should, in the viewDidLoad body, register itself as an observer for the NSManagedObjectContextDidSaveNotification.
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(updateInfo:) name:NSManagedObjectContextDidSaveNotification object:nil];
Then in your updateInfo, you can get the changes
- (void) updateInfo:(NSNotification *)notification
{
self.reviews = [self.managedObjectContext performRequest:myFetchRequest error:nil];
}
Related
I'd like to pass to my model the newest fetched data of my core data entities, in order to have them synched.
Is this possible?
The reason is that I have many variables that have to be calculated from the data saved in core data. These values are used in my views, so they should update at the same time.
(Until now I just found a way to pass them around every time with functions, but I find this very chaotic...)
Until now:
func doSomethingWithFetchedData(fetchedData: FetchedResults<Entity>) {
//return what I need
}
Thanks!
NSFetchedResultsController Subscribing to updates for many objects matching a fetch request has been easier than subscribing to updates from a single managed object, thanks to NSFetchedResultsController. It comes with a delegate that informs us about changes to the underlying data in a structured way, because it was designed to integrate with tables and collection views
Here is a good link to start with
I have an app that had a TableView with customer information.
If a click on a cell I load the detail of this customer.
I'm using Magical Record to manage all my entities.
What I'm doing:
1) Load all the CUSTOMER entity on the viewDidLoad of my TableView.
2) User click to see the detail of the customer
3) Pass to the ViewController of the detail the object that represent the entity CUSTOMER (only one).
Everything works well.
The problem is, if the user select to reload the TableView I do this:
1) Go to the API to get all the customers
2) [Customer truncateAll]
3) Create all the entities back on CoreData
5) [[NSManagedObjectContext defaultContext] saveToPersistentStoreAndWait]
6) Reload tableview
BUT, if the user click to see the detail of the Customer during this process, the following ViewController show the customer information correctly for a while. Then after
[Customer truncateAll] occurs all the information dies.
How can I prevent this?
Many thanks!
You can make use of NSFetchedResultController to listen for your Customer entity changes. When your API call finishes and you create proper entities somewhere else in your app, you'll be notified in the controller about those changes and then you can reload the data. If you're not familiar with this concept look here.
First I'll give a bit of background on what I'm working on. I'm creating a CRUD iOS application that gets information from an API we've created. Once I have pulled down the data I store it with CoreData. I then use an NSFetchedResultsController to display this data in a table view. Problem is that when I show a modal view on top of the table view and then dismiss the modal view, the table is cleared and everything short of re-downloading the data and inserting it again doesn't work.
The problem arises though when I'm pulling fresh data from the API. Using trial and error I have narrowed down the problem to where I delete all the old data in the persistent store (I have two stores, one for user created data and one for data from other users). This code snippet is from the API call where I handle fetching data:
[self deleteAllEntitiesNamed:#"Object"];
[self parseResponseObjectIntoManagedObjects:[response results] inStoreWithURL:storeURL];
success(response);
The [self deleteAllEntitiesNamed:#"Object"]; call goes into the store for other users and deletes all Object entities with the following code:
NSArray *result = [self performFetchRequestForEntityNamed:entity inStoreWithURL:url];
for(id r in result)
{
[self.managedObjectContext deleteObject:r];
}
I have traced the problem down to this function call. If I comment out the delete call in the API handler, there is no problem maintaining the table between views, though it has the bad side effect of duplicating a lot of the data already in the store. I feel I may be missing something about deleting items in the store, though it makes no sense to me as I re-insert the data immediately after deleting the old store.
I have tried deleting and re-initializing the entire store, but then no data loads at all.
Thanks ahead of time for any insight available.
EDIT: I should also add that when I have tried to pull out objects from the store after moving back to the table view, I get an empty result using the same fetch request that succeeds at getting Objects out of the store immediately after I finish the API call in my success callback.
My quest to collect the scattered, magical pieces of code needed to create an animated and dynamically updating UITableView - artifacts hidden in the dark and ghastly depths of Apple Inc's feared dungeon, The Documentation - has with the help of the ever-friendly townsfolk and everyday heroes of Stack Overflow finally been completed.
But worry not, the end of this quest is but the beginning of a new one.
I have one UITableView. That tableView is hooked up to a NSFetchedResultsController. The FRC delegate methods are all up and running as pr. Apple's example code.
I have two NSManagedObjectContexts:
The Truth. This MOC is only inserted to / deleted from when the user adds or deletes an object.
The scratch pad. This is the MOC that the tableView's FRC is hooked up to. Any change here is reflected in the tableView with nice animations.
The scratch pad is seeded with objects from The Truth, but it is never saved. This means that I can insert and delete (show and hide) objects here to my heart's content, all while the tableView politely updates.
(To anyone reading this in an attempt to implement something like this I would say: get acquainted with [managedObjectContext objectWithID:id])
My question comes from the need to sort the tableView. As made clear to me by reading about NSSortDescriptor in conjunction with NSFetchRequest, using a sortDescriptor simply won't fly when one's using a SQLite store. The Docs say, "instead you should sort the returned array in memory". All-right then! But how do I go about doing that?
Where, in my logic as described above, do I inject this sorted array? Wherever I turn, there seems to be problems.
I have an NSFetchedResultsController to display a tableView.
And users are allowed to change the cells' order.
How can I record the order so that when users go to the view again,
the table displays by the order modified by users last time?
Thanks a lot!
Hey guys I've done this.
In order to re-order managedObjects fetched by a fetchedResultsController, the most official way I think is to give the entity another attribute of int, such as "order", and give this attribute to the fetch request of a fetchedController, and in table view delegate method "move row from .. to " something like that, deal with this attribute with your hands, and if you use a fetchedController delegate, set a flag in that delegate methods to indicate that you will modity the entity yourself, and notify the delegate to do nothing but return.
Sample codes are Apple Sample code Recipes, and hints on the documentary of fetchedController!