Students will build an app that allows them to filter countries, states in those countries, and cities in those states and get the current air quality of that city. Network calls will be made using JSONSerialization in Objective C.
If you havent already fork
and clone
this repository and work from the starter
branch.
AirVisual provides weather and polution data for given locations around the globe. The documentation can be found here.
- Create an account and confirm your email address to recieve your API key.
- The documentation has a downloadable Postman link in the upper right hand corner that autofills all available calls in Postman.
- Plug in the base url (https://api.airvisual.com/) and your API key into the Postman requests to look at the data returned.
Create the needed models for the get specified city data
request that we will be using to pull information on specific cities.
-
Create the
CityAirQuality.h/.m
files -
Review the endpoint in the docuementation and see what JSON (information) you will get back.
- Using this information, add the properties to the
CityAirQuality.h
file.Properties
- @property (nonatomic, copy, readonly) NSString * city;
- @property (nonatomic, copy, readonly) NSString * state;
- @property (nonatomic, copy, readonly) NSString * country;
- You'll notice that the end point has two more levels of dicitonaries:
weather
andpollution
. We will create two more models to handle these endpoints. For now, add them as properties to theCityAirQuality.h
file.Properties
- @property (nonatomic, copy, readonly) DVMWeather * weather;
- @property (nonatomic, copy, readonly) DVMPollution * pollution;
- Using this information, add the properties to the
-
Add the designated initializer declaration for this Model object
Designated Initializer
-(instancetype)initWithCity:(NSString *)city state:(NSString *)state country:(NSString *)country weather:(DVMWeather *)weather pollution:(DVMPollution *)pollution; -
Add the convenience initializer in an extension called JSONConvertable
Designated Initializer
-(instancetype)initWithDictionary:(NSDictionary *)dictionary;
-
Create the
Weather.h/.m
files -
Review the endpoint for the
weather
level of the JSON object and add the appropriate properties to the.h
fileProperties
- @property (nonatomic, readonly) NSInteger temperature;
- @property (nonatomic, readonly) NSInteger humidity;
- @property (nonatomic, readonly) NSInteger windSpeed;
-
Add the designated initializer declaration for this Model object
Designated Initializer
-(instancetype)initWithWeatherInfo:(NSInteger)temperature humidity:(NSInteger)humidity windSpeed:(NSInteger)windSpeed; -
Add the convenience initializer in an extension called JSONConvertable
Designated Initializer
-(instancetype)initWithDictionary:(NSDictionary *)dictionary;
-
Create the
Pollution.h/.m
files -
Review the endpoint for the
pollution
level of the JSON object and add the appropriate properties to the.h
file- The only thing we really care about here is the AQI, add
@property (nonatomic, readonly) NSInteger airQualityIndex;
as the only property
- The only thing we really care about here is the AQI, add
-
Add the designated initializer declaration for this Model object
Designated Initializer
-(instancetype)initWithInt:(NSInteger) aqi; -
Add the convenience initializer in an extension called JSONConvertable
Designated Initializer
-(instancetype)initWithDictionary:(NSDictionary *)dictionary;
Implement the initializers for the created models.
- Implement the declared initializers in the
Pollution.m
file.- The designated initializer will live in the implementation body
- The JSONConvertable convenience initializer will live in an extension
- Implement the declared initializers in in the
Weather.m
file.- The designated initializer will live in the implementation body
- The JSONConvertable convenience initializer will live in an extension
- Implement the declared initializers in in the
CityAirQuality.m
file.- The designated initializer will live in the implementation body
- The JSONConvertable convenience initializer will live in an extension
Create the CityAirQualityController.h/.m
files
Create and implement the api calls needed to pull air quality data for a city.
On this file we will declare the various network call methods we will be using.
- Add
fetchSupportedCountries
,fetchSupportedStatesInCountry
,fetchSupportedCitiesInState
, andfetchDataForCity
to the .h filefetchSupportedCountries
will take in no parameters and complete with an array of stringsfetchSupportedStatesInCountry
will take in a string parameter for the country and complete with an array of stringsfetchSupportedCitiesInState
will take in a string parameter for the country, a string parameter for the state, and complete with an array of stringsfetchDataForCity
will take in a string parameter for the country, a string parameter for the state, a string parameter for the city, and complete with a CityAirQuality object
We have four network call methods to implement. To make our lives easier when implementing them, we will use string constants to store the frequently used string values for the API calls.
- Above the class declaration, add string constants for the following:
- baseUrlString with value "https://api.airvisual.com/"
- versionComponent with value "v2"
- countryComponent with value "countries"
- stateComponent with value "states"
- cityComponent with value "cities"
- cityDetailsComponent with value "city"
- apiKey with your key as the value
Implement the function bodies inside the class declaration.
fetchSupportedCountries
method body- Start with defining a url from the baseUrlString
- Append the versionComponent string as a pathComponent
- Append the countryComponent string as a pathComponent
- Add your apiKey as a queryItem with the key of "key"
- With your finalURL constructed, perform a shared URLSession dataTaskWithURL and completion
- handle your error
- print out your response
- unwrap your data
- Create a topLevel dicitonary from serializing the JSON from the unwrapped data
- Step into your data dictioary from the topLevel dictionary
- Loop through the data dictionary and create an array of strings from the names of the countries found
- Complete with the array of strings
fetchSupportedStatesInCountry
method body- Follow the same flow as the
fetchSupportedCountries
method - Change the countryComponent for the stateComponent
- Add a queryItem for the country, use the string value for the country parameter as the value and "country" as the key
- Perform your dataTask with the finalURL and complete with the array of strings found for the states
- Follow the same flow as the
fetchSupportedCitiesInState
method body- Follow the same flow as the
fetchSupportedCitiesInState
method - Change the stateComponent for the cityComponent
- Add a queryItem for the country, use the string value for the country parameter as the value and "country" as the key
- Add a queryItem for the state, use the string value for the state parameter as the value and "state" as the key
- Perform your dataTask with the finalURL and complete with the array of strings found for the cities
- Follow the same flow as the
fetchDataForCity
method body- Follow the same flow as the
fetchSupportedCitiesInState
method - Change the cityComponent for the cityDetailsComponent
- Add a queryItem for the country, use the string value for the country parameter as the value and "country" as the key
- Add a queryItem for the state, use the string value for the state parameter as the value and "state" as the key
- Add a queryItem for the city, user the string value for the city parameter as the value and "city" as the key
- Perform your dataTask with the finalURL and complete with the CityAirQuality object found
- Follow the same flow as the
- Create a new CocoaTouch Class file named
CountriesListViewController
subclassed from a UIViewController, change the language to Swift, and add a `Bridging-Header - In the
Bridging-Header
import all of the.h
files.
Looking at our network call methods, we have three methods that return an array of results that build off of one another. We'll display the results of our network calls in tableViews with basic cells.
- Navigate to
Main.storyboard
. Delete the existing ViewController, drag out a new ViewController, set it as your initial ViewController, and embed it in a NavigationController - Drag a
tableView
onto the ViewController and constrain it to all zeros along the top, bottom, leading, and trailing - Add a prototype cell to the tableView and make it a basic cell, give it a reuseIdentifier "countryCell"
- Subclass the ViewController as CountriesListViewController
- Drag out another ViewController, add a
tableView
with a basic prototype cell and constrain it - From the CountriesListViewController, drag a
show
segue from the tableViewCell to the new ViewController- Give the segue a reuseIdentifier "toStatesVC"
- Create a new CocoaTouch Class file named StatesListViewController subclassed from UIViewController, and subclass the storyboard ViewController.
- Repeat the above process for the CitiesListViewController
- Lastly, add a ViewController with labels to display the selected city's name, state, country, airQualityIndex, temperature, humidity, and windspeed.
We will implement the code needed to display the Supported Countries.
- Start by opening the assistant editor and dragging out an outlet for the tableView called
tableView
- Extend the class and conform to
UITableViewDelegate
andUITableViewDataSource
and plug in the stubs.
We will need an array of countries to finish the the tableView data source methods.
- Create a local variable called
countries
that is an empty array ofString
- Using that array, finish writing the dataSource methods for the tableView
- In
viewDidLoad
assign thetableView.delegate
andtableView.dataSource
to self - Call the
fetchSupportedCountries
method inviewDidLoad
, assign theself.countries
property to the array of strings returned in the completion block
Since the network call is handled asychronosly, we will need to refresh the tableView when the call is complete. We will do this with a property observer.
- At the bottom of the class, write a method called
updateTableView
- In the body of the function, call
self.tableView.reloadData()
on the main thread
- In the body of the function, call
- On the
countries
property, implement a didSet and call theupdateTableView
method
We will need to pass the selected country string along to the next ViewController to perform the fetchSupportedStatesInCountry
network call
- In
prepare(for segue:)
, check your segue identifier, unwrap the indexPath and destination, and define a property for the selected index `let selectedCountry = countries[indexPath.row] - On the
StatesListViewController
, declare an option property for the country to recieve the data passed through the segue - Back on
CountriesListViewController
, assign the destinationVC'scountry
property to theselectedCountry
property
Repeat the process you just finished above for this ViewController. The only changes will be the name of the dataSource array, and you'll call the fetchSupportedCitiesInState
method to populate your tableView. Additionally, you will need to pass the values for both the country
property and the selected state through prepare(for segue:)
Now, you'll repeate the proccess again for the supported cities. You will need to pass values for the country
, state
, and selected city to the CityDetailViewController
to perform the fetchDataForCity
method.
In viewDidLoad
, call the fetchDataForCity
method and assign the lables with the string values in the completion block. You should have lables to show the city name, state, country, airQualityIndex, temperature, humidity, and windspeed.
- Implement a UISearchController to search through the countries, states, and cities on their viewControllers
- Think about how you might be able to cut down on reused code the most using an extension and generics.
- Instead of using
UITableViewDelegate
andUITableViewDataSource
, useUITableViewDiffableDataSource
- Switch from a tableView to a collectionView to display the results of the network calls.