iOS: Design pattern for populating asynchronously fetched data
Asked Answered
C

4

7

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 -

  1. 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
  2. Use an NSFetchedResultsController (this is a bit tricky because I am not using it with a table view controller)
  3. 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!

Colene answered 25/2, 2014 at 6:27 Comment(2)
That's not a good problem description for asking a design pattern. You may apply the known variants of MVC and "lazy loading table view", as well as common practices for asynchronous programming. Your stated requirement is also unclear: "Now, the 6th review is fetched and I want to display it as the 4th review to the user. How should my model class inform the view controller?" Huh? Who is "I"? Literally, if you want the 6th review placed at positon 4, then just insert it into the array at this position, then "update" the table view. Though, I don't think you had that in mind, isn't it?Sowens
I is myself, the developer. I have clearly mentioned that instead of displaying a table view I am displaying a view where users can swipe right or left to see the next item (similar to the photos app).Colene
B
7

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.
Beshrew answered 4/3, 2014 at 4:47 Comment(2)
I do this for my needs. However, I've been familiar with Core data since 2007. @Akshay, if you're willing this is the best route, but you may have some growing pains to get through on your first release and after the following release when you find your model needs something different.Trieste
Thanks! I ended up going with option #3 in my question. The view controller is dumb and always asks for the next review to display. The model manages all the ordering. This way, only the model has the "tricky" code and the controller is blissfully unaware.Colene
B
1

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.

Bobker answered 27/2, 2014 at 14:12 Comment(0)
R
1

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

Rattling answered 1/3, 2014 at 5:55 Comment(4)
Strangely he doesn't want to display loading, he only wants to show whatever loads first. Which is why I made a different recommendation. However, if he didn't have that requirement, I think your suggestion would be more appropriate.Bobker
Thanks for helping out. The reason why true order is different from display order is - a) I don't want the user to wait until all reviews are fetched b) In this scenario, a new review holds a lot more importance and should be displayed as soon as it is fetchedColene
A design pattern is a solution to a frequently occurring problem. The way you are describing your problem is pretty unique, but maybe others will have ideas.Rattling
Which is why I asked - "Are there any established design patterns that are employed in such a scenario?" The answer can be "No". :-)Colene
R
1

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]; }

Riancho answered 5/3, 2014 at 9:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.