Пару слов о нейминге переменных и методов в Swift

Hadevs
5 min readFeb 8, 2019

--

За свой опыт разработки на iOS я видел много чужого кода. Как новичков, так и более опытных разработчиков. И часто приходилось сталкиваться с плохим неймингом. В этой статье я хочу показать свое представление какими должны выглядеть названия переменных и функций.

Переменная — отображение абстракции

Начнем с переменных. Так, как iOS разработчики большинство своей работы взаимодействуют с UIKit, стоит обсудить его элементы.

Допустим, у нас есть элемент UITextView, который отображает описание. Правильное название переменной — descriptionTextView. Многие любят называть подобный элемент следующим образом: descriptionText.

Если бы я не видел тип этой переменной, то я бы подумал, что это String, это же текст, ну. Когда вы используете элементы UIKit — возьмите название вашего класса и уберите префикс UI — получившееся должно соответствовать названию вашей переменной.

Я даже написал функцию, которая это может сделать за вас. Вы можете понять как это работает, запуская ее в Playground (конечно, она вам не пригодится, показываю просто для примера):

func variableName(of type: AnyClass, for purpose: String) -> String {
let className = String(describing: type)
let postFix = className.replacingOccurrences(of: "UI", with: "")
let result = purpose + postFix
return result
}
let textFieldClass = UITextField.self
let textFieldPurpose = "description"
variableName(of: textFieldClass, for: textFieldPurpose) // descriptionTextField
let labelClass = UILabel.self
let labelPurpose = "title"
variableName(of: labelClass, for: labelPurpose) // titleLabel

Приведу примеры неправильных названий:

@IBOutlet weak var descriptionTextView: UITextView! // Правильно!
@IBOutlet weak var descriptionText: UITextView! // Неправильно.

@IBOutlet weak var avatarImageView: UIImageView! // Правильно!
@IBOutlet weak var image: UIImageView! // Неправильно.
@IBOutlet weak var avatarIcon: UIImageView! // Неправильно.

@IBOutlet weak var titleLabel: UILabel! // Правильно!
@IBOutlet weak var labelTitle: UILabel! // Неправильно.

@IBOutlet weak var contentScrollView: UIScrollView! // Правильно!
@IBOutlet weak var contentView: UIScrollView! // Неправильно.

Если говорить о случае когда imageView называют image, то когда мы хотим получить image из него, то получается

image.image = ...

Выглядит — просто ужасно, поэтому так делать никак нельзя.

Это первая беда переменных. Вторая беда — регистр букв. Думаю, когда человек начинает учить Swift — в первых уроках ему расскажут, что переменные не должны начинаться с большой буквы. Думаю, на объяснение этого не стоит тратить время, но бывают и такие примеры: titleTextfield, backgroundcontentView. Многие забывают про Camel-case с первой буквой нижнего регистра. С этим думаю все понятно, правильные названия этих переменных: titleTextField, backgroundContentView.

С UIKit закончили, идем дальше. Допустим, у вас есть такой кусок кода:

import UIKitvar ages = [18, 45, 60, 2] // Массив возрастов
// Нужно найти сумму всех возрастов (будем использовать for вместо reduce)
var sum = 0
for i in 0..<ages.count {
sum += ages[i]
}
print(sum) // 125

Что в нем не так? Переменная i. Мы со школьной скамьи привыкли к такому виду счетчика еще с C++. Но это довольно неправильно. У нас ведь могут быть вложенные циклы (а там пойдут j, k, t). Стоит избавляться от этого и назвать переменную indexInAges или хотя-бы index (хоть что-то).

Привыкайте, чтобы ваши количество символов в ваших переменных было больше или равно 3. Вам в помощь SwiftLint , который поможет адаптироваться под эти правила. Можете его настроить под себя, если боитесь 1000+ errors и в 5 раз больше warnings в вашем проекте.

Теперь поговорим про кастомные классы. Конечно, их может быть бесконечность, но я постараюсь рассмотреть самые частые примеры.

Очень часто я встречал в функциях роутера или в подобных, когда нужно передать свойства в UIViewController, допустим или запрезентить его, используется следующий код:

// Неправильно!
let vc = NewsViewController()
vc.isNeedToLoadAllNews = true
UIViewController().present(vc, animated: true, completion: nil)

Первым делом, я уже говорил о том, что нужно использовать больше трех символом в названии и вы помните о постфиксе в ваших переменных UIKit. vc — не подходит. Более того, здесь передаются проперти через точку. Более правильным было бы сделать передачу их через конструктор, так вы обезопасите целостность данных, самого UIViewController. Если же они у вас необязательны для передачи, то default аргументы в конструкторе никто не отменял. Правильной реализацией считалось бы следующее:

class NewsViewController: UIViewController {
private var isNeedToLoadAllNews: Bool = false

convenience init(isNeedToLoadAllNews: Bool = false) {
self.init()
self.isNeedToLoadAllNews = isNeedToLoadAllNews
}
}
let newsViewController = NewsViewController(isNeedToLoadAllNews: true)
UIViewController().present(newsViewController, animated: true, completion: nil)

Еще один плюс данного подхода в том, что мы инкапсулировали значение isNeedToLoadAllNews и оно стало безопаснее.

Если говорить о моделях данных, то здесь все намного проще.

// Неправильно
let msg = Message()
let animal1 = Animal()
// Правильно:
let firstAnimal = Animal() // Альтернатива animal1
let cat = Animal() // Модель называется животное, но кошка может им являться
let lastMessage = Message() // Здесь мы можем конкретизировать какое именно это сообщение
let message = Message() // Но можно и так

Поговорим еще немного о примитивных типах, например, Bool.
До Swift 3.2, в стандартных библиотеках Apple вы могли встретить такие названия:

  • view.hidden
  • element.expanded
  • viewController.presented

После, Apple начала добавлять префикс is в булевых типах, и это очень хорошее решение, переменные начали выглядеть так:

  • view.isHidden
  • element.isExpanded
  • viewController.isPresented

Читайте функции как предложения

Хотелось бы пару слов сказать о методах. Самое первое, что я хотел бы отметить — это слова перед аргументами, которые используются в функциях.

Говорю я про них:

fetchNews(with count: Int, and completion: () -> ())

Они невероятно важны и делают ваш код красивее и читабельнее. Но многие не используют их, а если даже и используют, до делают это следующим образом:

fetchNews(with: 5, and: {
print("News fetched.")
})

Это делает ваш код не читабельным. Непонятно, что такое 5 и к чему оно относится, но если мы вынесем их в константы, все станет намного приятнее:

let newsCompletion = {
print("News fetched.")
}
let count = 5fetchNews(with: count, and: newsCompletion)

Не бойтесь выносить все в константы, Swift это очень любит. Теперь, когда мы попробуем прочитать функцию по-человечески, то получим предложение: “fetch news with count and news completion”, все максимально понятно. Такой подход к нам пришел еще из языка Smalltalk.

Но не со всеми функциями нужно поступать так. Иногда нам подобные слова вообще не нужны. Привожу пример:

func select(index: Int)

В данном случае у нас нет потребности во вспомогательных аргументах, то есть наша функция читается как “select index” — читабельно. Значит все ок.

На этом основные мысли насчет нейминга у меня заканчиваются, надеюсь статья была полезна для вас.

Ссылки на меня:

--

--