The user interface of the day view controller is ready to be populated with weather data. Before we populate the user interface, I'd like to clean up the implementation of the DayViewController class.

As I mentioned earlier in this series, object literals are very often an opportunity for improvement. In this episode, I show you how we can improve the implementation of the DayViewController class.

Open DayViewController.swift. The text color of the date label of the day view controller is a tint of blue. That tint of blue is the base color of the project, which means we plan to use it throughout the application. In the didSet property observer of the dateLabel property, we use number literals to create a UIColor instance. If we plan to use this tint of blue in other places of the project, it's wise to make it easier to reference that color. We don't want to instantiate the same UIColor instance with a collection of number literals every time we want to use that tint of blue.

The same is true for the fonts we use to style the user interface of the day view controller. If the goal is creating an application that looks and feels consistent, we need to reuse the same fonts and font sizes throughout the application. The use of object literals make this goal unnecessarily hard to achieve. And what do we do if we decide to change the fonts or font sizes in the project? In that scenario, we would need to make sure we update every font definition in the application, hoping that we didn't overlook one.

Long story short, it's well worth the effort to implement a solution that makes managing styles easier. The answer doesn't need to be complicated. In this episode, I show three possible solutions that are easy to implement. The syntax is simple and feels intuitive.

Defining Colors

Let's start with the text color of the date label in DayViewController.swift. This tint of blue is the base color of the application. Every user interface element that needs to be highlighted should have this color. Let me show you what I have in mind.

Create a new file in the Configuration group by choosing the Swift File template from the iOS section.

Create a Swift File

Name the file Styles.swift. The name isn't important.

Create a Swift File

Add an import statement for the UIKit framework and create an extension for UIColor. We define a static constant property, base. The value we assign to base is the UIColor instance we created in the DayViewController class.

import UIKit

extension UIColor {

    static let base: UIColor = UIColor(red:0.31, green:0.72, blue:0.83, alpha:1.0)

}

Because we extend UIColor, we need to be careful not to introduce naming collisions. The UIColor class defines a number of class properties, such as black and white. I usually use special names for colors to avoid naming collisions.

Revisit DayViewController.swift and replace the UIColor instance with the static property we defined in Styles.swift. That looks quite nice.

@IBOutlet var dateLabel: UILabel! {
    didSet {
        dateLabel.textColor = UIColor.base
        dateLabel.font = .systemFont(ofSize: 20.0, weight: .heavy)
    }
}

We can take advantage of Swift's type inference and omit UIColor to make the syntax more concise.

@IBOutlet var dateLabel: UILabel! {
    didSet {
        dateLabel.textColor = .base
        dateLabel.font = .systemFont(ofSize: 20.0, weight: .heavy)
    }
}

If you don't want to pollute the UIColor class with static properties, then there's another slightly more verbose option. Revisit Styles.swift and define an enum, Rainstorm, in the UIColor extension. Move the static property to the Rainstorm enum.

import UIKit

extension UIColor {

    enum Rainstorm {

        static let base: UIColor = UIColor(red:0.31, green:0.72, blue:0.83, alpha:1.0)

    }

}

The disadvantage is that we can no longer take advantage of Swift's type inference. The syntax at the call site becomes a bit more verbose.

@IBOutlet var dateLabel: UILabel! {
    didSet {
        dateLabel.textColor = UIColor.Rainstorm.base
        dateLabel.font = .systemFont(ofSize: 20.0, weight: .heavy)
    }
}

There's a third approach we can take. We can define an enum named Styles that encapsulates every color and font used in the project. It looks something like this. Which approach you use is up to you.

import UIKit

enum Styles {

    enum Colors {

        static let base: UIColor = UIColor(red:0.31, green:0.72, blue:0.83, alpha:1.0)

    }

}

Defining Fonts

We use the same approach for defining fonts and font sizes. Create an extension for UIFont and define an enum, Rainstorm. The user interface of the day view controller uses three styles. We define a static property for each style, lightRegular, lightSmall, and heavyLarge. It's up to you to decide how you name the styles. Be sure to be consistent.

import UIKit

...

extension UIFont {

    enum Rainstorm {

        static let lightRegular: UIFont = .systemFont(ofSize: 17.0, weight: .light)
        static let lightSmall: UIFont = .systemFont(ofSize: 15.0, weight: .light)

        static let heavyLarge: UIFont = .systemFont(ofSize: 20.0, weight: .heavy)

    }

}

Revisit DayViewController.swift and update the didSet property observer of the dateLabel property.

@IBOutlet var dateLabel: UILabel! {
    didSet {
        dateLabel.textColor = UIColor.Rainstorm.base
        dateLabel.font = UIFont.Rainstorm.heavyLarge
    }
}

We repeat this step for the regularLabels and smallLabels properties.

@IBOutlet var regularLabels: [UILabel]! {
    didSet {
        for label in regularLabels {
            label.textColor = .black
            label.font = UIFont.Rainstorm.lightRegular
        }
    }
}

@IBOutlet var smallLabels: [UILabel]! {
    didSet {
        for label in regularLabels {
            label.textColor = .black
            label.font = UIFont.Rainstorm.lightSmall
        }
    }
}

What's in a Name?

You can take the solution we implemented as far as you like or is necessary. For example, you could define separate static properties for text colors, button colors, background colors, etc. The advantage is that you can change the color of the text in the user interface without affecting other user interface elements that use the same color.

Let me show you what I mean. Revisit Styles.swift and define a static computed property, baseTextColor. The computed property returns the value stored in the base static property. We can define another static computed property, baseBackgroundColor, that also returns the value stored in the base static property.

import UIKit

extension UIColor {

    enum Rainstorm {

        static let base: UIColor = UIColor(red:0.31, green:0.72, blue:0.83, alpha:1.0)

        static var baseTextColor: UIColor {
            return base
        }

        static var baseBackgroundColor: UIColor {
            return base
        }

    }

}

...

To avoid that the base static property is used to style the user interface, we declare it as private by prepending the private keyword.

import UIKit

extension UIColor {

    enum Rainstorm {

        private static let base: UIColor = UIColor(red:0.31, green:0.72, blue:0.83, alpha:1.0)

        static var baseTextColor: UIColor {
            return base
        }

        static var baseBackgroundColor: UIColor {
            return base
        }

    }

}

...

This solution introduces flexibility. The baseTextColor and baseBackgroundColor properties act as aliases of the base property. We can modify the return value of baseBackgroundColor without affecting the value returned by baseTextColor.

Don't forget to update the didSet property observer of the dateLabel of the DayViewController class.

@IBOutlet var dateLabel: UILabel! {
    didSet {
        dateLabel.textColor = UIColor.Rainstorm.baseTextColor
        dateLabel.font = UIFont.Rainstorm.heavyLarge
    }
}

What's Next?

You can make your solution as complex as you want. What's important is that we accomplished the goal we set for this episode. We extracted the number literals from the DayViewController class by moving them to a central location. This will help us keep the user interface consistent and it will also make styling the rest of the application easier.

In the next episode, we implement the DayViewModel struct and populate the day view controller with weather data.