Weather apps are great programming exercises, as they combine a number of different features, including fetching information from online sources, and geolocation. In this series, we’ll build a weather app from scratch, starting with a simple, no-user-interface, only-developers-could-love, bare-bones weather app, and over time, turn it into something better.

This first article looks at where we’ll get the weather data for our app and signing up for a free subscription to that weather source. We’ll then retrieve weather data from that source, first manually, then programatically. At the end of this article, you’ll have a very basic weather app; it won’t have a user interface, and the way it presents information is more developer-friendly than user-friendly, but it will perform a key task: retrieve the current weather for a specified location.

Step 1: Get an OpenWeatherMap API key for current weather data

There are a number of weather APIs to choose from, and for this tutorial, we’ll use OpenWeatherMap’s API. It’s simple to use, and you can make up to 60 calls a minute on their free API subscription plan, which should be more than enough for testing purposes and personal use.

If you go to OpenWeatherMap’s API page (pictured below), you’ll see that they offer all sorts of weather data. For this app, which answers the question “What’s the weather like right now for a given place?”, we’ll use their current weather data API. Click on the API doc button under Current weather data:

and you’ll be taken to this page. Under the Free column, click the Get API key and Start button:

which will take you to yet another page. Click the Sign up button…

…which takes you to a page where you provide a little info:

Once you’ve submitted your info, you’ll have created an OpenWeatherMap account. You’ll be sent to a page that shows your API key (pictured below). If you ever need to look up this hey again, you can log into the site and click on the Home link near the upper right-hand corner of the page, which will take you to your account information, which includes your API key.

Try constructing your own call, using the format above, along with Tampa for the city and your API key after the part that goes APPID=, and enter it into your browser’s address bar. You should be taken to a very plain page displaying something that looks something like the text below:

base: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.

main: A dictionary containing the quantitative data about the weather. The dictionary contains the following keys:

temp: The current temperature, expressed in Kelvin by default.

pressure: Atmospheric pressure, expressed in hPa (hectopascal, which is equivalent to 1 millibar). This is the pressure at sea level, if there’s no data for the atmospheric pressure at sea level or ground level.

humidity: Humidity, expressed as a percentage.

temp_min: Minimum temperature at the moment — the deviation from the current temperature, which you’ll find in large metropolitan areas, expressed in Kelvin by default.

temp_max: Maximum temperature at the moment — the deviation from the current temperature, which you’ll find in large metropolitan areas, expressed in Kelvin by default.

sea_level: Atmospheric pressure at sea level, expressed in hPa (hectopascal, which is equivalent to 1 millibar).

grnd_level: Atmospheric pressure at ground level, expressed in hPa (hectopascal, which is equivalent to 1 millibar).

wind: A dictionary containing data about the wind, with the following keys:

speed: The wind speed, expressed in meters per second by default.

deg: The direction that the wind is coming from, expressed in degrees.

clouds: A dictionary containing data about cloud cover, with the following keys:

all: The amount of cloud cover, expressed as a percentage.

dt: The time when the weather data was provided, in UTC, formatted as Unix time (number of seconds since January 1, 1970, 00:00:00 UTC).

sys: A dictionary containing system information, as well as a number of useful items. It contains these keys:

type: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.

id: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.

message: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.

country: The two-letter ISO country code for the weather data’s location. Useful when you want to be sure that you’re getting the weather for St. Petersburg in the U.S. or in Russia.

sunrise: The time when the sun will rise at the weather data’s location, in UTC, formatted as Unix time (number of seconds since January 1, 1970, 00:00:00 UTC).

sunset: The time when the sun will set at the weather data’s location, in UTC, formatted as Unix time (number of seconds since January 1, 1970, 00:00:00 UTC).

id: OpenWeatherMap’s internal ID number for the city corresponding to the weather data’s location.

name: OpenWeatherMap’s internal name for the city corresponding to the weather data’s location.

cod: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.

Once you’ve confirmed that you can manually get current weather forecasts using your API key and browser, let’s do it programatically with an app.

Step 3: Create a basic app to get the data from OpenWeatherMap

Start a new project by doing the File → New → Project… dance and then selecting Single View Application.

Our first iteration won’t have any user interface. It’ll simply try to communicate with OpenWeatherMap, and print the results to the console. Once we’ve confirmed that we’ve got our API communications code working, we’ll add a simple user interface, and I’ll leave making it fancier as an exercise for you.

Many tutorials are happy to stick all the code in a view controller in order to keep the tutorial simple. While it may make writing the tutorial simpler, I think it results in cluttered view controllers and teaches bad programming habits. I’d much rather keep the code in the view controller limited to handling user interactions and put what some people call the “business logic” of our app — in this case, the code that communicates with OpenWeatherMap and extracts weather data from what it sends back — into a separate model class.

With that in mind, we’ll create a new class by selecting File → New → File… from the menu bar, then selecting Swift File:

Give the new file the name WeatherGetter.swift. It will be the home of a class we’ll call WeatherGetter, which will house the code to communicate with OpenWeatherMap and provide the weather data that the view controller will eventually use.

Change the contents of WeatherGetter.swift so that it contains the following code:

// We got some kind of error while trying to get data from the server.

print("Error:\n\(error)")

}

else{

// Case 2: Success

// We got a response from the server!

print("Data:\n\(data!)")

}

}

// The data task is set up...launch it!

dataTask.resume()

}

}

You’ve probably figured out that you should replace the string YOUR API KEY HERE with the API key that you got in step 1 and used in step 2. You’re probably eager to see this code in action, so we’ll defer the explanation of how it works until we’ve got the app up and running.

We now need to create an instance of the WeatherGetter class and call its getWeather() method. We’ll do it in the viewDidLoad() method of the view controller:

ViewController.swift (excerpt)

Swift

1

2

3

4

5

6

overridefuncviewDidLoad(){

super.viewDidLoad()

letweather=WeatherGetter()

weather.getWeather("Tampa")

}

Now that we’ve got the WeatherGetter class and a way to instantiate and use it, let’s run the app. Remember that right now, it doesn’t have a user interface, and outputs everything using print statements, which will appear in the output pane of XCode’s debug area:

Step 4: Run the app (and tweak it)

Run the app. You’ll see some disappointing text in the debug area’s output pane that will be similar to this:

2016-04-02 09:03:42.316 SimpleWeather[51150:6547853] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
Error:
Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSUnderlyingError=0x7f89ab201e20 {Error Domain=kCFErrorDomainCFNetwork Code=-1022 "(null)"}, NSErrorFailingURLStringKey=http://api.openweathermap.org/data/2.5/weather?q=Tampa&APPID=***YOUR_APP_ID_HERE***, NSErrorFailingURLKey=http://api.openweathermap.org/data/2.5/weather?q=Tampa&APPID=***YOUR_APP_ID_HERE***, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}

We’ve just run into a security feature of iOS networking that by default requires HTTP connections to be of the secure and encrypted HTTPS variety. Someday, all HTTP communication will be done as HTTPS, but that day hasn’t come yet.

The first thing we should do is see if we can communicate with OpenWeatherMap using HTTPS. Let’s try changing the URL in WeatherGetter.swift so that it starts with https: instead of http:…

So now we know that OpenWeatherMap doesn’t accept HTTPS connection requests. Luckily for us, we can change the default connection security policy for our app so that iOS allows it to communicate using plain HTTP. We can do this by adding a couple of extra rows to Info.plist:

Click the screen shot to see it at full size.

Info.plist is an XML file that contains configuration information, and it’s included in every iOS project. In order to make it more readable, Xcode presents it in a nice list interface. If you prefer, you can edit it in its plain XML form by right-clicking on Info.plist in the Project Navigator and selecting Open As → Source Code in the menu that appears.

To enable our app to use plain HTTP communication, we want to do two things in Info.plist:

Add a new dictionary item with the key App Transport Security Settings

Add an item to the App Transport Security Settings dictionary with the key Allow Arbitrary Loads and the boolean value YES

Rather than describe how you do this, I made a video of the process that should be pretty easy to follow:

In the end, you should have this as part of your Info.plist:

Once that’s done, don’t forget to change the URL in WeatherGetter.swift so that it starts with http: and not https:…

I can’t seem to get pass the WeatherGetter.swift. Xcode is displaying an error on NSURLSession and NSURLResponse. It wants me to change both to URLSession and URLResponse. Can you shed a little light on what that might mean?

As for the fatal error, check your string URL. it shouldn’t contain any spaces or non-ASCII characters. (you will need another workaround for that). For such inputs the method will return nil and thus fail at unwrapping it.

To all getting “unexpectedly found nil while unwrapping an Optional value” error: in my case it was me passing a string with not allowed characters to the function building an API request. E.g. it threw an error when I entered “Wrocław” when calling weather.getWeather(city: ) in the View Controller, but it worked after changing it to “Wroclaw”.