Building Breather (Part 1 — Bonus): Populating the UI with sample data, namespacing with enums and formatting the data with Swift Extensions
Breather is an open-source iOS app that shows the weather, air pollution and asthma conditions around you.
To raise awareness about air pollution and practice my iOS skills at the same time, I’m building an open-source app where I apply some of the latest knowledge I gain about iOS, Swift and programming. Since there is a lot of information to digest, I’m splitting it into small manageable parts:
- Intro: APIs, air pollution & global health, and design.
- Part 1: Building a responsive UI for any screen in Storyboard.
- Part 1 (Bonus): Populating the UI with sample data, namespacing with enums and formatting the data with Swift Extensions.
- Part 2: Refactoring with MVVM and RxSwift.
- Part 2 (Bonus): Refreshing the UI with loading state and mocking API requests with delay.
- Part 3: Managing networking with RxMoya and handling errors with RxSwift’s retry and materialize.
- Part 3 (Bonus): Combining network requests with RxSwift’s zip and switching tuples with Swift’s switch.
- Part 4: Nested JSON Parsing with Decodable, CodingKeys and RxMoya’s filter and map.
- Part 4 (Bonus): Smooth API error handling with Moya & RxSwift custom operators.
- Part 5: Coming soon…
Populating the UI with sample data
I created some models that will hold the data that we need to display in this app:
I also made an extension that provides some sample data that I got by manually querying the APIs.
Now, in MainViewController, we can populate the UI with the sample data:
This is a chunky piece of code, but basically we’re updating the UI with sample data on viewDidLoad and updating the air quality data when the segmented control value changes. The rest of the methods deal with appropriately formatting the data for display to the user. I will go through the parts that are the most worth mentioning.
Namespacing with enums
You will notice some functions that are called like:
- Asset.forIconCode(data.weather.iconCode)
- Color.forTemperature(data.weather.temperature)
- Text.forAQI(data.pollution.aqiUS)
- Color.forAQI(data.pollution.aqiUS)
- Color.forAsthmaRisk(data.asthma.risk)
These functions take the data and return the appropriate colour, text or icon.
This is a pattern that I like to use for constants that are used throughout the app.
I will give one example here and the rest you can find in the app.
By declaring Text as an enum instead of a struct or a class, you prevent someone from trying to initialise an object from it. The only way to access it is through the static func that returns a String. So this is quite handy when you need to declare some constants in your project in a neat and safe way.
You can define Text in a separate Constants.swift file, or if you have a lot of constants, in a separate Text.swift file in a Constants folder.
In this example I gave it the common name Text, but if you’re planning for your code to be used alongside other people’s code, it’s better naming practice to prefix with your project’s name or initials, for example BreatherText or BRText or something, to avoid conflicts.
And you can of course define static lets instead of static functions, for example:
In this example you can also see that you get a compiler error if you try to initialise an enum.
And you can even nest enums to create a “Constants directory”:
Neat.
Encoding wind direction through a rotating UIImageView
The AirVisual API returns the wind direction in degrees like: North=0, East=90, South=180, West=270 and all the values in between.
To relay this information to the user, I decided to make a cute little compass icon and rotate it depending on the wind direction.
First, we need to convert the degrees to radians:
let rotationAngle = (CGFloat(data.weather.windDirection) * .pi) / 180.0
And then apply the transform.
windDirectionImageView.transform = CGAffineTransform(rotationAngle: rotationAngle)
Displaying a UILabel with subscripts
The final part is displaying the pollutants with the appropriate format.
AirVisual API returns the pollutants as codes for example “p2” for PM₂.₅.
First, we want to get the appropriate pollutant name and then we want to display it with the numbers in subscript.
To get the pollutant name, we add a handy static func to our Text enum, like before.
These are the primary pollutants that pose a danger to human health. The riskiest pollutant at any given moment will dictate the AQI value, that’s why we have the main pollutant data. For example, if the API returns “p2”, it means the AQI value is based on the concentration of PM₂.₅.
PM₂.₅ is very frequently returned by the API as the main pollutant, because of its high danger to health. PM₂.₅ stands for particulate matter that is less than 2.5 micrometers in diameter. Because of its size, it can penetrate deep into the lungs and cause all sorts of health problems, including asthma, lung cancer, respiratory diseases, cardiovascular disease, birth defects, and premature death.
A scary metric: Particulate matter pollution was estimated to cause 3.22 million deaths globally in 2010!
Unfortunately, until we clean up our act as a species on this planet, there is not much we can do to protect ourselves on an individual level, other than to wear anti-pollution masks when we’re near traffic (my favourite one), or have a home filter if we live in the centre of the city.
And of course, spread the word as much as we can, so change can slowly take place. We can also help by reducing our energy consumption, reducing our waste by reusing and recycling things, avoid buying excess stuff we don’t really need, buying second hand items where we can, switching to a renewable energy provider or installing solar panels, driving electric cars, planting trees, investing in clean projects and going out in the streets and shouting!
You’re probably thinking: “You still haven’t told me how to display a UILabel with subscripts.” and you’re right.
For this, I made an extension on UILabel with a function that sets the attributedText property of UILabel with an attributed string that displays all the characters at the given indices in subscript.
It’s used like this:
For finding the indices of the subscripts, I made another extension on String, that finds the indices of all the numbers in a string.
Keep reading → Part 2: Refactoring with MVVM and RxSwift.