4 What Is Wrong With Cloudy

Now that you have an idea of the ins and outs of Cloudy, I’d like to take a few minutes to highlight some of Cloudy’s issues. Keep in mind that Cloudy is a small project. The problems we’re going to fix with the Model-View-ViewModel pattern are less apparent, which is why I’d like to highlight them before we fix them.

Day View Controller

We start with the day view controller. The first thing to point out is that the view controller keeps a reference to the model. This is a classic example of the Model-View-Controller pattern. Even though there isn’t anything inherently wrong with this, when we adopt the Model-View-ViewModel pattern, this will change.

DayViewController.swift

1 var now: WeatherData? {
2     didSet {
3         updateView()
4     }
5 }

The second and most important problem is the implementation of the updateWeatherDataContainer(withWeatherData:) method. This is another pattern that’s typical for the Model-View-Controller pattern. The raw values of the model data are transformed and formatted before they’re displayed to the user.

DayViewController.swift

 1 private func updateWeatherDataContainer(withWeatherData weatherData:\
 2  WeatherData) {
 3     weatherDataContainer.isHidden = false
 4 
 5     var windSpeed = weatherData.windSpeed
 6     var temperature = weatherData.temperature
 7 
 8     let dateFormatter = DateFormatter()
 9     dateFormatter.dateFormat = "EEE, MMMM d"
10     dateLabel.text = dateFormatter.string(from: weatherData.time)
11 
12     let timeFormatter = DateFormatter()
13 
14     if UserDefaults.timeNotation() == .twelveHour {
15         timeFormatter.dateFormat = "hh:mm a"
16     } else {
17         timeFormatter.dateFormat = "HH:mm"
18     }
19 
20     timeLabel.text = timeFormatter.string(from: weatherData.time)
21 
22     descriptionLabel.text = weatherData.summary
23 
24     if UserDefaults.temperatureNotation() != .fahrenheit {
25         temperature = temperature.toCelcius()
26         temperatureLabel.text = String(format: "%.1f °C", temperatur\
27 e)
28     } else {
29         temperatureLabel.text = String(format: "%.1f °F", temperatur\
30 e)
31     }
32 
33     if UserDefaults.unitsNotation() != .imperial {
34         windSpeed = windSpeed.toKPH()
35         windSpeedLabel.text = String(format: "%.f KPH", windSpeed)
36     } else {
37         windSpeedLabel.text = String(format: "%.f MPH", windSpeed)
38     }
39 
40     iconImageView.image = imageForIcon(withName: weatherData.icon)
41 }

Should the view controller be in charge of this task? Maybe. Maybe not. But is there a more elegant solution? Absolutely.

If we adopt the Model-View-ViewModel pattern, the view controller will no longer be responsible for data manipulation. Moreover, the view controller won’t know about and have direct access to the model. It will receive a view model from the root view controller and use the view model to populate its view. That’s the task it was designed for, controlling a view.

Week View Controller

The week view controller suffers from the same problems. It keeps a strong reference to the array of WeatherDayData objects and uses them to populate the table view.

WeekViewController.swift

1 var week: [WeatherDayData]? {
2     didSet {
3         updateView()
4     }
5 }

In the tableView(cellForRowAt:) method, a WeatherDayData instance is fetched from the array and it’s used to populate a table view cell. The raw values of the model data are transformed and formatted before they’re displayed to the user.

WeekViewController.swift

 1 func tableView(_ tableView: UITableView, cellForRowAt indexPath: Ind\
 2 exPath) -> UITableViewCell {
 3     guard let cell = tableView.dequeueReusableCell(withIdentifier: W\
 4 eatherDayTableViewCell.reuseIdentifier, for: indexPath) as? WeatherD\
 5 ayTableViewCell else { fatalError("Unexpected Table View Cell") }
 6 
 7     if let week = week {
 8         // Fetch Weather Data
 9         let weatherData = week[indexPath.row]
10 
11         var windSpeed = weatherData.windSpeed
12         var temperatureMin = weatherData.temperatureMin
13         var temperatureMax = weatherData.temperatureMax
14 
15         if UserDefaults.temperatureNotation() != .fahrenheit {
16             temperatureMin = temperatureMin.toCelcius()
17             temperatureMax = temperatureMax.toCelcius()
18         }
19 
20         // Configure Cell
21         cell.dayLabel.text = dayFormatter.string(from: weatherData.t\
22 ime)
23         cell.dateLabel.text = dateFormatter.string(from: weatherData\
24 .time)
25 
26         let min = String(format: "%.0f°", temperatureMin)
27         let max = String(format: "%.0f°", temperatureMax)
28 
29         cell.temperatureLabel.text = "\(min) - \(max)"
30 
31         if UserDefaults.unitsNotation() != .imperial {
32             windSpeed = windSpeed.toKPH()
33             cell.windSpeedLabel.text = String(format: "%.f KPH", win\
34 dSpeed)
35         } else {
36             cell.windSpeedLabel.text = String(format: "%.f MPH", win\
37 dSpeed)
38         }
39 
40         cell.iconImageView.image = imageForIcon(withName: weatherDat\
41 a.icon)
42     }
43 
44     return cell
45 }

We also see several if statements to make sure the raw values are formatted correctly, based on the user’s preferences.

The Model-View-Controller pattern has a few other consequences. The week view controller has a couple of properties of type DateFormatter to format the model data that’s displayed in the table view. If we use the Model-View-ViewModel pattern, we can clean this up too. Whenever I see a DateFormatter property in a view controller, I know it’s time for some refactoring.

Locations View Controller

Later in this book, we focus on the locations view controller. It will show you how user interaction is handled by the Model-View-ViewModel pattern. This is a bit more complicated. However, once you understand the ins and outs of the Model-View-ViewModel pattern, this won’t be difficult to understand. I promise you that the result is pure elegance.

Settings View Controller

There doesn’t seem to be anything wrong with the settings view controller. It’s true that it doesn’t look too bad, but I assure you that it’ll look a lot better after we’ve given the settings view controller a facelift using protocols and MVVM.

What’s Next

In the next chapters, you create your very first view model. We start with the view model for the day view controller.