Practical MVVM in iOS

Practical MVVM in iOS

In the last few posts (here, here and here) I explained how MVVM architecture pattern can assist in writing cleaner iOS applications. In this post I am going to create a small application called “Headlines” which will utilize the MVVM pattern in a more practical way.

Headlines app will consume the amazing News API and display a list of news sources. When the user taps on the source, it takes them to a separate screen displaying a list of headlines associated with the selected source. You can watch the demo of the app below:

Displaying List of Sources

The first step is to download the list of sources from the News API and display it in a UITableView. Before we do that we need to setup a SourceListViewModel which will represent the list of sources displayed on the view.

SourceListViewModel consists of a list of SourceViewModel objects which represents an actual source. SourceListViewModel is also dependent on web service object, through the principles of dependency injection. SourceViewModel is pretty straightforward as it represents the news source using id, name and the body of the source.

The web service, represented by “Webservice” class is responsible for making a request to the News API service and fetching all the sources. The sources are fetched as JSON objects and then they are populated in “Source” model objects. The implementation of Source class is shown below:

The web service consists of loadSources function which invokes the News API and downloads JSON.

After receiving the sources in JSON format, we convert them into the source objects and finally to view model objects. Once, the consumed data is converted to view model representation we are ready to display it on the view.

The view controller “SourcesTableViewController” is responsible for instantiating the view model and the web service as shown in the implementation below:

Instead of returning a list of SourceViewModel objects, we exposed additional methods on the SourceListViewModel which allows us to work with the list of SourceViewModel objects.

The primary reason behind adding methods instead of allowing the list directly is control. We don’t want the list of sources to change underneath us without knowing. Obviously, this will require the sourceViewModels list to be updated to private(set) scope as shown below:

In order to display items in the UITableView control we must implement the required delegate methods numberOfRowsInSection and cellForRowAtIndexPath. Since, these delegate methods have to be implemented in every UITableViewController, we decided to make a TableViewDataSource class which is responsible for providing the required details. You can download the complete source code to see the implementation of the TableViewDataSource class.

Inside the updateUI function we also setup our bindToSourceViewModels binding which will trigger each time the sourceViewModels collection is changed.

The result is shown in the screenshot below:

We have also used the iOS 11 self sizing cells feature to make sure that our source description fits nicely within the cell.

Headlines by Source

A user can tap on any source to view the headlines related to the source. The transition is performed using segues from SourcesTableViewController to SourceDetailsViewController as shown in the Storyboard.

Source to Headlines

I thought about removing the segues from the Storyboard and programmatically adding the flow of the application in a separate class but in the end I decided against it. I believe if you are using Storyboards then your Storyboard should tell the whole … story. If I extract out the segues in separate files then I will have to consult two different files to understand the complete … story.

The prepareForSegue function inside the SourcesTableViewController is shown below:

Instead of comparing segue.identifier against hard-coded string values we used the simplicity of Swift constants as shown below:

You can use enums for the instead of constants but I found using constants to be much simpler and cleaner.

Adding News Source

The add new source screen consists of a UITextField, UITextView and a Save UIBarItemButton. The textfield and textview are used to enter title and description of the source and save button is used to persist those changes.

I thought about if this particular scenario will need a live binding between the textfields and the view model. This means as soon as I enter values in the textfields it gets automatically populated in the view model. Although it sounds intriguing and cool but I decided against it. The reason is that there is no need for live binding since, the user still need to press the “Save” button to take any action.

Having said that I do want to demonstrate live binding. So, let’s limit the number of characters that can be typed in a UITextView. Once, the limit has been reached we will not allow the user to type any more characters in the textview.

We will start by creating a custom UITextView control called “BindingTextView”. BindingTextView class will be responsible for sending back the text typed into the UITextView control as well as limiting the number of characters that can be typed.

The view model “AddSourceViewModel” consists of the title and description, as well as the remainingNumberOfAllowedCharacters function which will return either 0 or the number of remaining characters. The number of remaining characters are displayed on the screen.

Now, inside the AddSourceTableViewController we can initialize and use the AddSourceViewModel as shown below:

Live Binding
  • I am still looking for better ways on removing the observers from the sourceViewModels collection which are set in SourceListViewModel class. If you find a better way please share it in the comments below.

The complete code is available on Github.

I am also working on an Udemy course “Mastering MVVM in iOS”. You can sign up here to get notified when the course is published.

Thank you! Happy coding,


Practical MVVM in iOS was originally published in iOS App Development on Medium, where people are continuing the conversation by highlighting and responding to this story.

Powered by WPeMatico

Comments are closed.