ViewModel for UITableViewCell: Database operations "allowed"? - ios

I am trying to understand how to correctly use the MVVM pattern in iOS. Lets assume we have a music player app with a playlist like in the following sketch:
I have a PlaylistViewController and a PlaylistViewModel. Furthermore the cells (SongTableViewCell) have a view model SongCellViewModel which holds the data (song name,..). If the user presses the X button on the TableViewCell I have to delete the song from the database. The cell is calling the following function in the SongCellViewModel:
func deleteSongFromPlaylist() {
DatabaseService.shared.deleteSong(self.song)
}
My question: Is it good to do it that way? It doesn't feel right to initiate database operations in the view model of the TableViewCell.

I suggest moving such operations to PlaylistViewModel and do all operations from there. It also simplifies implementation of later improvements like showing activity indicator if needed or reloading certain cells or views from there.
I think that SongCellViewModel should have a actionBlock which will take enum with associated values - for example .select(song) .delete(song) - and this way you can easily communicate between PlaylistViewModel and SongCellViewModel.
One more suggestion is to use Dependency Injections instead of using shared for all type of services.

Related

UITableView w/ optimistic UI updates in iOS

I have a UITableView that contains a number of 'likeable' items.
This status can be toggled via a UIButton in the cell.
I'd like to update the UI to reflect changes when a user un/likes an item in an optimistic fashion. Should the operation fail, I will then roll back these values.
I am trying to understand how to implement this change without complication the design of my application.
Currently I have 3 layers;
API
Presentation
UI
Each layer communicates via adapters that map between delegates and interfaces to avoid coupling the implementations.
The Presentation layer maps a RemoteFeedItem into a FeedItemViewModel which a FeedItemCellController will then use to prepare a UITableViewCell.
The FeedItemViewModel contains only the values for the UI in the correct state/type.
For example the 'likes' count is now a String not an Int so this can be rendered by a UILabel
My UITableView has an array of FeedItemCellController's which are used in cellForRowAt.
I am trying to understand where the best place for this optimistic UI change to take place is.
Updating the value in the cell on tap works, however should the user scroll before the async operation is completed the change reverts when the cell is reused as the original model is unchanged.
If I make a change in the model, this will persist across cell reuse, however I need to keep a reference to the updated model somewhere - the issue here is I now have 2 sources of truth for my cell -
The model produced by the Presentation layer
My local copy, updated with the new status
Is this OK - under the assumption both will be eventually consistent?
Or should the Presentation layer be notified of the action and produce a new model based on this?
That way of the action fails, it can produce a new model again with the reverted values?
I appreciate this may be an opinion point, but I am keen to find a way to implement this in a clean and testable fashion.

How to load different data classes from Parse into uitableview with UISegementedControl -> Swift?

Fairly new to Parse backend and coding all together...
I am trying to implement a UISegmentedControl within a viewcontroller to load different Parse classes (User Products, User Services, User Favorites) into a uitableview. uitableview covers only half of the viewcontroller
I am wondering what is the best approach to properly implement this function?
Please forgive me if my question seems too vague.
Thank you!
Your solution should have three components:
Detect changes in your UISegmentedControl
Load the appropriate data with a query
Update the UITableView
1. Detect Changes in your UISegmentedControl
You can find an excellent tutorial here on implementing and detecting changes: http://www.mobisoftinfotech.com/blog/iphone/iphone-segmented-controluisegmentedcontrol-tutorial/
2. Load the appropriate data with a query
https://www.parse.com/docs/ios/guide#queries is where you can learn about how to format your queries for different Parse classes. Use a switch statement to decide which class to load.
3. Update the TableView
This one is the easiest. Use a global variable for your Swift class that stores the returned objects from the PFQuery. If you need to use different cell layouts for the classes, use a switch statement with your UISegmentedControl's index to determine how to load the data appropriately in your UITableView's cellForIndexPath: method.
Sorry I can't be more specific, but hopefully this helps!

What is a better way to deal with data after an async call using NSURLConnection?

This is the current logic that I'm using to populate a table view with NSURLConnection. It doesn't seem elegant to me.
Table View Controller's viewDidLoad method calls "sendConnection" method in my api wrapper class with the URL string as a parameter. This method makes the NSURLConnection. In connectionDidFinishLoading (which is in my wrapper class), another method is called (also in the wrapper class) with the connection as a parameter. This method extracts the URL from the connection object and examines it. It then uses a switch statement to deal with the data depending on the URL. The data is stored in variables in the wrapper class itself. By the time cellForRowAtIndexPath is called, the async call has finished and the data has been processed.
Is there a better way of doing this?
My reason for asking this is as follows:
I want to refresh a cell with a new height and a new text label when it is clicked. The data for this text label will be retrieved from the server upon the cell being tapped. Each cell will have slightly different data in the label (each cell represents a 'user' and the label will display how many mutual friends you have with the user). I want to store the data in the cell itself when the data is retrieved and then place it into the text label. This doesn't seem possible with my current way of making URL calls.
Any help with how to achieve this would be appreciated.
Here is some pseudo code for a pattern I like to use in these situations. Maybe it will help you as well.
- (void)viewDidLoad {
//1. put up some type of progressHud or spinner
//2. call your NSURL wrapper
//3. in the completion block of your wrapper, set your datasource variables
//example: #property (nonatomic,strong) NSArray *listOfData;
//4. create a custom setter for your datasource that calls tableview reload
//5. enable a refresh function; like "pull to refresh" or a bar button
//6. when pull to refresh is tapped or called, just repeat these steps
}
- (void)setListOfData:(NSArray*)listOfData {
_listOfData = listOfData;
if (_listOfData) {
[self.tableView reloadData];
}
}
As I read your question again, here are a couple more thoughts:
the pattern above will work for your initial load, to create the list of people or friends, etc.
If you plan on making another round trip after the cell is tapped, then you have to consider a number of issues. This is similar to a common problem with lazy loading images into tableview cells. There are issues like scrolling to consider - what if the cell is scrolled off the view before the data returns, for example, what if the cell has been reused, now the data is not tied to that cell any longer.
There are many async image libraries available on Github that would be good to look at to see how they solved those issues. Generally they are keeping track of the item in the cell and then checking if the cell is still in view and if so, they set the image.
You have a similar issue to solve. Tap the cell, get the new data, then update the cell. Resizing the cell will require you to reload it.
Look into [tableview reloadRowsAtIndexPaths:(NSArray*) with RowAnimation:(UITableViewRowAnimation)];
hope that helps
best wishes;
You should have a "Data Model" which represents the content (that is the cells) of your Table View.
Since you have "rows" in your table view, it makes sense this data model is a kind of array (possibly a NSArray) whose elements keep the data and state of the cell.
The data for each cell should not only have all the "data" properties rendered in your cell (e.g. the label) but also its state:
When a user tabs on a cell it will start an asynchronous task. This task may take a while to finish since it fetches data from a remote server. Think of several seconds, or even longer. You need to keep track of pending update tasks, since your implementation should prevent the user to update a cell again before the corresponding pending update task has been finished.
There are several techniques to accomplish this. One way is to have a property in your "Cell Data" class which reflects this state, for example:
#interface CellModel : NSObject
#property (atomic) BOOL hasPendingUpdate;
...
When the cell will be rendered, you retrieve the value of the property and render the cell appropriately.
When the update task finishes, it updates its cell model data.
This model update will eventually update your Table View. There are several techniques to accomplish this. You should take care about thread-safety here and the "synchronization" of your Data Model and the table view cells. For example ensure the value of the hasPendingUpdate only changes on the main thread - since otherwise your rendered cell may become out of sync with the data model (not to mention race conditions in case you modify and access the property on different threads without synchronization primitives).
While the cell waits for an update, it should visually indicate this state (using a spinner for example) and disable the action to start an update task.
Very much recommended is a "Cancel" button, which either cancels a certain cell update task or all pending update tasks.
When the user moves away from this view, you may consider to cancel all pending tasks.

Best way to simplify/refactor tableView code setup in objective-c

Every single time I need to create a simply tableview that is populated by a simple data set retrieved from my web server which has its code executed like this: SELECT * FROM table I find myself spending two blady whole hours trying to get the new view controller up and running as I try to update some variable names, copy and paste the required code from my previous view controllers. etc its ridiculous.
This is the end result for all my view controller pages where each will contain different data sets depending on the web service urlĀ being called:
Here is a link:
Link to downloading staple code .h .m and .xib files
This view controller contains a few simple elements seen throughout all data viewing pages:
UITableView
Titled header views
table indices.
refresh table control feature
data connection retrieval code
data connection succeeded
data connection failed
setting up all my bloody delegate and data source methods.
I find myself having to copy and paste all the staple code, functions, variables, properties, and IBOutlets; and to be frank, its getting ridiculously paintaking to have to repeat the same procedure over and over again but changing variable names between the different view controllers.
This is why I believe people create simple component like structures that make it easy for users to get tables setup and up and running.
How can I reduce this big chunk of code:
to something that will allow me at most do this:
Create a new view controller
Setup xib file
create appropriate IBOutlets, and hook them up to the xib.
Here's where it needs to change
I need to now simply able to write something like this the next time I am goin to create another data viewing View Controller:
[self setupTableForDataSetType:]; //This will make sure the tableView knows which data set its dealing with and so therefor know which DataModel classes to use
[self retrieveDataWithWebServerURL:]; //of course so that the connection code can make the right server connection with the URL given for the data set required.
Thats it. So that it is super easy for me to create the tableView pages desired and show the results quickly! Atm I have the same code everywhere in different view controllers.
Whats the best way to go about doing this?
Create a viewcontroller with all your customizable values as properties and reuse changing its values.
Well, subclassing is probably the best (maybe only) way. I've done something like this for tables with an index, since they're a bit of a pain to set up. I created a IndexedTableViewController that handles almost all the load. I make my app table view controller a subclass of that controller, and then I only need to feed a simple array of custom objects to the method, convertArray:usingSectionKey:secondarySortKey:(implemented in the IndexedTableViewController) which creates the sections and the index. The only other method I have to implement in my app table view controller is cellForRowAtIndexPath:(though I would have to implement more, especially didSelectRowAtIndexPath:, if I were doing more things with this table).
Your needs sound a bit more ambitious than this, so it would take quite a bit of work to make a superclass that would be general enough to work with most of your apps. A method like setupTableForDataSetType: could be quite complicated if it needs to handle many different data types.

iOS: Handling of data

Explanation
My app basically uses a mapview with an overlay of polygons that represent buildings, coupled with an annotation. So for this it imports a custom class called Annotation that handles the popup details when the annotation is tapped, meaning it store the building's name and address. At this time the callout (the blue disclosure button) loads an empty DetailViewController object (as there's not really any data to pass through).
I soon added a new feature in the form of a searchable table that loads custom objects of the Building class (with similar properties to Annotation, plus images and more details) that then loads in the aforementioned DetailViewController class with the building's details.
So to summarise, the MapVC contains multiple annotations, which when the relevant disclosure button is tapped open the DetailVC. SearchVC is accessed by a button on the MapVC and has a table of Building objects, which loads a DetailVC with the relevant data, like so:
Next Step
So now I want to implement functionality into the blue disclosure button on the callout, so when the user taps it it'll load the building details. At the moment all it has is the annotation's details. I could add the extra properties to the annotation to make it complete but I think it's much better to just work with one custom class called Building, which has a MKAnnotation nature. Then this Building class is loaded for the annotations and searchVC's table.
Question
So finally, what's the best way to go around this? I want all the data to be stored independently of any of the VCs in the diagram. I followed a tutorial from Apple (the BirdSighting one) which uses a separate Datacontroller class, which I'd then load into other classes. Is this the best approach?
You're talking about MVC, model-view-controller. This is a very smart way to handle things, and is a good practice to get into. The model stores the data, in this case, building names, etc. The view displays data. The controller is what connects the view to the model, updates the view, gets notifications from users and in turn updates the model.
I use a singleton pattern for model data. Only one instance of a singleton is ever present in an application. That way, the data is not bound to any one particular view controller. Data only gets updated in once place, the model. It's a lot easier to trouble shoot issues with a singular point of convergence for application data objects.
I have a macro that I define in my PCH file.
#import "DataController.h"
#define DATA() [DataController sharedInstance]
In my code I can easily get to my model class by calling
DataController *data = DATA();
All of my views and viewcontrollers access objects stored in DataController, so there are never two view controllers handling separate pieces of information independently. All references point to the same place.
Some people use the AppDelegate object to store data, but it can quickly turn into a 1000 line beast. I prefer to keep the AppDelegate clean :-)

Resources