In the remainder of this book, we’re going to refactor an application that’s built with MVC and make it adopt MVVM instead. This will teach you two important lessons:
The application we’re going to refactor is Cloudy. Cloudy is a lightweight weather application that shows the user the weather of their current location or a saved location. It shows the current weather conditions and a forecast for the next few days. The weather data is retrieved from the Dark Sky API, an easy-to-use weather service.
The user can add locations and switch between locations by bringing up the locations view controller.
Cloudy has a settings view to change the time notation, the application’s units system, and the user can switch between degrees Fahrenheit and degrees Celcius.
In this chapter, I walk you through the source code of Cloudy. You can follow along by opening the project of this chapter.
The main storyboard is the best place to start. You can see that we have a container view controller with two child view controllers. The top child view controller shows the current weather conditions, the bottom child view controller displays the forecast for the next few days in a table view.
If the user taps the location button in the top child view controller (top left), the locations view controller is shown. The user can switch between locations and add new locations using the add location view controller.
If the user taps the settings button in the top child view controller (top right), the settings view controller is shown. This is another table view listing the options we discussed earlier.
If we open the View Controllers group in the Project Navigator, we can see the view controller classes that correspond with what I just showed you in the storyboard.
The RootViewController
class is the container view controller. The DayViewController
is the top cild view controller and the WeekViewController
is the bottom child view controller. The WeatherViewController
class is the superclass of the DayViewController
and the WeekViewController
.
The root view controller is responsible for several tasks:
The root view controller delegates the fetching of the weather data to the DataManager
class. This class sends the request to the Dark Sky API and converts the JSON response to model objects. I use a simple, lightweight JSON parser for this task. The implementation of the JSON parser and the DataManager
class are unimportant for this discussion.
In the completion handler of the weatherDataForLocation(latitude:longitude:completion:)
method of the RootViewController
class, the weather data is sent to the day view controller and the week view controller.
RootViewController.swift
The model objects we’ll be working with are Location
, WeatherData
and WeatherDayData
. You can find them in the Models group.
The Location
structure makes working with locations a bit easier. There’s no magic involved. The WeatherData
and WeatherDayData
structures contain the weather data that’s fetched from the Dark Sky API. Notice that a WeatherData
object contains an array of WeatherDayData
instances.
WeatherData.swift
The current weather conditions are stored in the WeatherData
object and the forecast for the next few days is stored in an array of WeatherDayData
objects.
The root view controller only hands the week view controller the array of WeatherDayData
objects, which it displays in a table view.
WeekViewController.swift
The day view controller receives the WeatherData
object from the root view controller.
DayViewController.swift
The now
property of the DayViewController
class stores the WeatherData
object. Every time this property is set, the user interface is updated with new weather data by invoking updateView()
.
DayViewController.swift
In updateView()
, we hide the activity indicator view and update the weather data container, this is nothing more than a view that contains the views displaying the weather data.
DayViewController.swift
The implementation of updateWeatherDataContainer(withWeatherData:)
is a classic example of the Model-View-Controller pattern. The model object is torn apart and the raw values are transformed and formatted for display to the user.
DayViewController.swift
The week view controller looks similar in several ways. The week
property stores the weather data and every time the property is set, the view controller’s view is updated with the new weather data by invoking updateView()
.
WeekViewController.swift
In updateView()
, we stop the activity indicator view, stop refreshing the refresh control, and invoke updateWeatherDataContainer(withWeatherData:)
if there’s weather data we need to show the user.
WeekViewController.swift
In updateWeatherDataContainer(withWeatherData:)
, we show the weather data container, which contains the table view, and reload the table view.
WeekViewController.swift
The most interesting aspect of the week view controller is the configuration of table view cells in tableView(_:cellForRowAt:)
. In this method, we dequeue a table view cell, fetch the weather data for the day that corresponds with the index path, and populate the table view cell.
WeekViewController.swift
As in the day view controller, we take the raw values of the model objects and format them before displaying the weather data to the user. Notice that we use several if
statements to make sure the weather data is formatted based on the user’s preferences in the settings view controller.
The locations view controller manages a list of locations and it displays the coordinates of the device’s current location. If the user selects a location from the list, Cloudy asks the Dark Sky API for that location’s weather data and displays it in the weather view controllers.
The user can add a new location by tapping the plus button in the top left. This summons the add location view controller. The user is asked to enter the name of a city. Under the hood, the add location view controller uses the Core Location framework to perform a forward geocoding request. Cloudy is only interested in the coordinates of any matches the Core Location framework returns.
Despite the simplicity of the settings view, the SettingsViewController
class is almost 200 lines long. Later in this book, we attempt to use the Model-View-ViewModel pattern to make its implementation shorter and more transparent.
The SettingsViewController
class has a delegate, which it notifies whenever a setting changes.
SettingsViewController.swift
The root view controller is the delegate of the settings view controller and it tells its child view controllers to reload their user interface whenever a setting changes.
RootViewController.swift
That’s all you need to know about Cloudy for now. In the next chapter, we focus on several aspects in more detail and discuss which bits we plan to refactor with the help of the Model-View-ViewModel pattern.
If you want to run Cloudy, you need to add your Dark Sky API key to Configuration.swift. Signing up for a developer account is free and it only takes a minute.
Configuration.swift