How to build an iOS weather app in Swift, part 3: Giving the app a user interface

by Joey deVilla on May 8, 2016

weather

The story so far

In the first article in this series, we introduced the OpenWeatherMap service and showed you how to send it a request for the current weather in a specified city and get a JSON response back, first manually, then programmatically. We expanded on this in the second article, where we gave the app the ability to convert that JSON into a dictionary from which Swift can easily extract data.

In both articles, the weather information wasn’t being presented in the user interface. All output was printed out to the debug console. It’s now time to make our app display information to the user. By the end of this article, we want our app to look like this:

tampa forecast

Let’s get started!

The WeatherGetter class

So far, we’ve spent most of our time working on the WeatherGetter class, which connects to OpenWeatherMap.org, provides it with a city name, and retrieves the current weather for that city.

WeatherGetter makes use of:

  • the Shared Session instance of the NSURLSession class (which provides an API for sending and receiving data to and from a given URL), and
  • an instance of NSURLSessionDataTask, which downloads data from a specified URL into memory.

Here’s how these classes relate to each other:

The user interface lives in the view controller, while the weather networking apparatus lives in WeatherGetter. Here’s the setup we’re aiming for:

view controller - weathergetter relationship 1

We create this setup using delegation, a design pattern that you’ll often see in iOS programming when one object coordinates its activities with another object:

view controller - weathergetter relationship 2

We’ll add a protocol definition to WeatherGetter:

This definition specifies the interface for two methods:

  • didGetWeather, which we’ll call if we were able to retrieve the weather data from OpenWeatherMap and parse its contents. It will provide the weather data in the form of a Weather struct, which we’ll talk about in a moment.
  • didNotGetWeather, which we’ll call if we were not able to retrieve the weather data from OpenWeatherMap or if we weren’t able parse its contents. It will provide an error object that will explain what kind of error occurred.

We’ll have the view controller register itself with WeatherGetter, and both didGetWeather and didNotGetWeather will be implemented in the view controller.

Here’s the complete WeatherGetterDelegate.swift file:

The Weather struct

In order to more easily send the weather data from WeatherGetter to the view controller, I created a struct called Weather. It lives in a file called Weather.swift, and it’s shown in its entirety below:

When initializing the Weather struct, you pass it the [String: AnyObject] dictionary created by parsing the JSON from OpenWeatherMap. Weather then takes that data from that dictionary and uses it to initialize its properties.

The Weather struct does a number of useful things:

  • It turns the dictionary-of-dictionaries structure of the data from OpenWeatherMap into a nice, flat set of weather properties,
  • it provides the date/time values provided by OpenWeatherMap in iOS’ native NSDate format rather than in Unix time (an integer specifying time in seconds after January 1, 1970), and
  • it uses computed properties to report temperatures in degrees Celsius and Fahrenheit rather than Kelvin.

You may notice that the windDirection and rainfallInLast3Hours properties are optionals. That’s because OpenWeatherMap doesn’t always provide those values. It reports a wind direction if and only if the wind speed is greater than zero, and it reports rain information if and only if it’s raining.

The view controller

Here are the controls I put on the view, along with the names of their outlets:

view controller layout

I also created a Touch Up Inside action for the Get weather for the city above button called getWeatherForCityButtonTapped.

Here’s the code for ViewController.swift:

If you run the app, you should get results similar to this:

tampa forecast

st. johns forecast

If the temperatures seem a little chilly to you, it’s because you’re probably expecting them in Fahrenheit and I’m currently displaying them in Celsius. You can change this by changing the line in the didGetWeather method from

self.temperatureLabel.text = "\(Int(round(weather.tempCelsius)))°"

to

self.temperatureLabel.text = "\(Int(round(weather.tempFahrenheit)))°"

Take a good look at the code for the view controller — I’ve included a couple of tricks that I think improve the UI. I’ll explain them in greater detail in posts to follow.

web horizontal rule

In the next installment in this series, we’ll add geolocation capability to the app, so that the user can get the weather at his/her current location without having to type it in.

Download the project files

xcode download

You can download the project files for this article (51KB zipped) here.

{ 1 comment… read it below or add one }

1 Simon September 8, 2016 at 9:52 am

Hi,
I was wondering if you could help I’m getting the following error when running the app at this point:
2016-09-08 13:57:04.652 WeatherApp[4894:98417] *** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key cityText.’

Thank you in advance!

Leave a Comment

{ 3 trackbacks }

Previous post:

Next post: