Краткое руководство по грамотному оформлению интерфейсов.
Предисловие
Это руководство я публикую для своих учеников, которые только начинают свой пусть в изучении языка Swift. В этой небольшой заметке я хочу расписать основные аспекты правильного структурирования классов, что бы навигация по ним была интуитивно понятной, как для создателя, так и для стороннего наблюдателя.
Введение
Читаемость кода является одним из самых важным аспектом в программировании и поэтому существуют определенные стандарты, как в написании кода, так и в оформлении интерфейсов. Особенно это касается вью контроллеров, которые сами по себе являются довольно массивными объектами и поэтому навигация по ним должна быть предсказуемой, в не зависимости от того работаете ли вы со своим проектом или же со сторонним.
Структура класса
Класс всегда начинается со свойств, к которым относятся аутлеты, вычисляемые свойства, а также публичные и приватные переменные и константы:
final class ViewController: UIViewController {
// MARK: - IB Outlets
@IBOutlet private var mainLabel: UILabel!
@IBOutlet private var secondaryLabel: UILabel!
@IBOutlet private var someLabel: UILabel!
@IBOutlet private var mainTextField: UITextField!
@IBOutlet private var secondaryTextField: UITextField!
// MARK: - Public Properties
var dataModel: DataModel!
var someString = ""
// MARK: - Private Properties
private let dataManager = DataManager.shared
private let networkManager = NetworkManager.shared
private let imageManager = ImageManager.shared
}
Обратите внимание в какой последовательности объявлены свойства: сначала объявляются аутлеты, затем публичные переменные и константы, после которых уже идут приватные свойства. Так же обратите внимание на то, что аутлеты для лейблов разделяет пустая строка от аутлетов для текстовых полей. Такие разделители в виде пустых строк визуально отделяют логические блоки кода друг от друга, что повышает его читаемость. Не пренебрегайте ими, но и не злоупотребляйте. Достаточно одной пустой строки, что бы отделить одно от другого.
При помощи комментария // MARK:
класс можно поделить на разделы. Эти комментарии играют роль заголовков, по которым можно быстро перемещаться по разделам. У каждого файла в проекте есть содержание, открыть которое можно, кликнув на название класса в верхней строке, в которой прописан путь до класса:
В этом списке отображаются все свойства и методы класса, а заголовки позволяют легче ориентироваться по всему содержимому и перемещаться в нужное место в один клик.
После свойств класса объявляются инициализаторы, если в них есть необходимость:
// MARK: - Initializers
init(name: String) {
self.name = name
}
После инициализаторов объявляются переопределенные методы родительского класса. Это те методы, которые помечены ключевым словом override
:
// MARK: - View Life Cycles
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
После переопределенных методов объявляются IB Actions, т.е. методы связанные с элементами пользовательского интерфейса:
// MARK: - IB Actions
@IBAction private func buttonDidTapped() {
}
@IBAction private func sliderValueOnChange(_ sender: UISlider) {
}
После IB Actions определяются методы экземпляра:
// MARK: - Public Methods
func fetchData(from url: URL) {
}
func doSomething() {
}
И в самом конце класса объявляются приватные методы. Это те методы, которые создаются для реализации внутренней логики самого класса:
// MARK: - Private Methods
private func setupUI() {
}
private func setupNavigationBar() {
}
private func showAler(witnTitle title: String, andMessage message: String) {
}
Так же классы могу иметь расширения или extensions
. Расширения позволяют группировать методы со связанным функционалом в одном месте, что делает код более организованным и читаемым. Так если класс необходимо подписать под протокол, то для этого лучше определить отдельное расширение с соответствующим заголовком:
// MARK: - UITabvleViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
func tableView(_ tableView: UITabvleView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
}
}
Кроме того расширения полезно использовать для распределения обязанностей между командой. В этом случае для каждого расширения создается отдельный файл, что позволяет работать над одним классом сразу нескольким участникам проекта.
В итоге структура класса должна выглядеть следующим образом:
// MARK: - IB Outlets
// MARK: - Public Properties
// MARK: - Private Properties
// MARK: - Initializers
// MARK: - Overrides Methods
// MARK: - IB Actions
// MARK: - Public Methods
// MARK: - Private Methods
Общие советы
Не храните в классе методы, которые ни как не используются. Т.е. если у вас есть метод
viewDidLoad()
, но при этом он пустой, то смело его удаляйте. Ни чего лишнего в коде быть не должно.Удаляйте все шаблонные комментарии, которые достаются вам “из коробки” при добавлении новых классов.
Используйте в качестве разделителей логических блоков кода пустые строки, но не более одной.
Названия классов должны быть емкими и отражать их суть. В проекте не должно быть классов с именем
ViewController
. Не сокращайте имена классов. В проекте не должно быть классов с именемMainVC
.
varton86
Я бы еще предложил сделать переменные IB Outlets и функции IB Actions приватными, зачем им наружу торчать)