I don't like long or complex viewDidLoad()
methods. The viewDidLoad()
method of a view controller should be short and easy to understand. The tip I am about to share with you helps declutter the viewDidLoad()
method of the view controllers in your projects.
Decluttering View Did Load
Developers tend to configure the outlets of a view controller in the viewDidLoad()
method. This isn't wrong, but it can easily lead to a long or complex viewDidLoad()
method. A few years ago, I picked up a tip that leverages property observers to declutter the viewDidLoad()
method. It has another important benefit I explain in a moment.
Take a look at the following code snippet. The NotesViewController
class defines two outlets, tableView
of type UITableView
and messageLabel
of type UILabel
.
import UIKit
internal final class NotesViewController: UIViewController {
// MARK: - Properties
@IBOutlet private var tableView: UITableView!
// MARK: -
@IBOutlet private var messageLabel: UILabel!
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
// Configure Table View
tableView.delegate = self
tableView.dataSource = self
tableView.showsVerticalScrollIndicator = false
// Register Note Table View Cell
tableView.register(NoteTableViewCell.self, forCellReuseIdentifier: NoteTableViewCell.reuseIdentifier)
// Configure Message Label
messageLabel.numberOfLines = 0
messageLabel.font = .systemFont(ofSize: 15.0)
messageLabel.text = NSLocalizedString("notes_message_no_notes", comment: "")
}
}
The table view and the label are configured in the viewDidLoad()
method of the notes view controller. We have several options to optimize the implementation of the NotesViewController
class. We could configure the outlets in the storyboard or XIB file. While that is an option, I avoid that as much as possible. Configuring user interface elements in code makes it easier to understand how the user interface is set up and configured. It also adds flexibility. Updating a user interface element is as simple as updating its configuration in the view controller.
Another option is to create a helper method in which the user interface elements are configured. By invoking the helper method in viewDidLoad()
, we keep the implementation of viewDidLoad()
short and readable.
Using Property Observers to Configure Outlets
The option I have been using for several years is even better and far more elegant. We define a didSet
property observer for each outlet and use the didSet
property observer to configure the user interface element. Take a look at the updated implementation.
import UIKit
internal final class NotesViewController: UIViewController {
// MARK: - Properties
@IBOutlet private var tableView: UITableView! {
didSet {
// Configure Table View
tableView.delegate = self
tableView.dataSource = self
tableView.showsVerticalScrollIndicator = false
// Register Note Table View Cell
tableView.register(NoteTableViewCell.self, forCellReuseIdentifier: NoteTableViewCell.reuseIdentifier)
}
}
// MARK: -
@IBOutlet private var messageLabel: UILabel! {
didSet {
// Configure Message Label
messageLabel.numberOfLines = 0
messageLabel.font = .systemFont(ofSize: 15.0)
messageLabel.text = NSLocalizedString("notes_message_no_notes", comment: "")
}
}
}
Because we move the configuration of each outlet to its didSet
property observer, we can remove the viewDidLoad()
method. We don't need a helper method to configure the outlets and, more important, the declaration and the configuration of each outlet are grouped together. This makes it easy to read and understand the implementation of the NotesViewController
class.
Each outlet is set only once, which means that its didSet
property observer is also invoked only once. I really enjoy using this pattern to clean up the view controllers of a project. Give it a try to see if you like it.