Русифицированный и немного измененный гайдлайн от raywenderlich.com
- Начнем с перфекционизма
- Ширина страницы
- Именование
- Организация кода
- Отступы
- Комментарии
- Классы и структуры
- Объявление и вызов функций
- Выражения замыканий
- Типы
- Функции против Методов
- Управление памятью
- Контроль доступа
- Поток управления
- Золотой путь
- Использование guard
- Точки с запятой
- Круглые скобки
- Формат организации и Bundle Identifier
- Ссылки
Как бы это ни звучало банально – всегда старайтесь компилировать свой код без предупреждений (warnings).
Установите ширину страницы в 120 символов (Xcode / Preferences / Text Editing / Page guide at column). Это значение рекомендуется включить в соответствующем правиле в SwiftLint. Таким образом, весь код будет умещаться в заданную ширину страницы и будет более читабельным.
Правильное именование упрощает чтение и понимание кода. Используйте соглашения именования в Swift, описанные в API Design Guidelines. Некоторые ключевые особенности включают:
- Стремитесь к ясности при вызовах
- Выбирайте приоритет ясности перед крайностью
- Используйте camelCase, а не snake_case
- Используйте верхний регистр для типов и протоколов, нижний регистр для всего остального
- Включайте только необходимые слова, при этом избегая лишних
- Используйте именование на основе роли, а не типа, но при этом по наименованию объекта должно быть понятен его тип (contactsView, nameLabel, descriptionViewHeightConstraint)
- Иногда давайте расширенную информацию для типов со слабой ссылкой
- Стремитесь к свободному использованию
- Начинайте фабричные методы через “make” (Например,
makeProduct) - Методы именования:
- методы в форме глагола следуют правилу -ed, -ing для неизменяемого объекта (Например,
sorted) - методы в форме глагола следуют правилу первой формы для изменяемого объекта (Например,
sort) - булевы типы должны читаться, как утверждения (Например,
isEnabled) - протоколы должны описывать существительные (Например, Sphere ->
Spherable) - протоколы, описывающие возможность, должны заканчиваться в -able или -ible (Например,
Fuckable)
- методы в форме глагола следуют правилу -ed, -ing для неизменяемого объекта (Например,
- Используйте термины, которые не удивляют экспертов или путают начинающих разработчиков
- Избегайте сокращений
- Именуйте веские причины для выбора имени
- Отдавайте приоритет методам и свойствам, вместо свободных функций
- Используйте аббревиатуры только в верхнем или нижнем регистре (Например,
userID,isRepresentableAsASCII,utf8Bytes) - Давайте одинаковое базовое имя для методов, которые имеют одинаковое значение (Например, не использовать где-то
configurate, а где-тоsetup) - Избегайте перегрузок в возвращаемом типе
- Выбирайте само-документируемые имена для параметров
- Маркируйте параметры, если они являются замыканием или кортежем
- Используйте параметры по умолчанию
- Из названия объекта должен быть намек на его тип, это улучшает читаемость кода:
- Массивы с суффиксом множественного числа (Например,
names,userIDs,mainIndexes) - Словари с суффиксом
Map(Например,userRatingMap) - Множества с суффиксом
Set(Например,imageSet)
- Массивы с суффиксом множественного числа (Например,
Очень важно, когда речь идет о наименовании методов. Чтобы обратиться к имени метода, желательно использовать как можно более простейшую форму:
Пишите имя метода без параметров (Например, addTarget)
Пишите имя метода с метками аргументов (Например, addTarget(:action:))
Пишите полное имя метода с метками и типами аргументов (Например, addTarget(_: Any?, action: Selector?))
В приведенном выше примере с использованием UIGestureRecognizer вариант 1 более однозначен и предпочтителен.
Совет: Можно использовать панель перехода Xcode для поиска методов с метками аргументов.
Для булевых переменных принято использовать вспомогательный глагол в начале.
Рекомендуется:
var isFavoriteCategoryFilled: Bool
var hasRoaming: Bool
var isBlocked: BoolНе рекомендуется:
var favoriteCategoryFilled: Bool
var roaming: Bool
var blocked: BoolТипы Swift автоматически называются модулем, в котором они содержатся, и вы не должны добавлять префикс класса, например DP. Если сталкиваются два имени из разных модулей, вы можете сделать различие, предварительно указав имя типа с именем модуля. Вводите имя модуля только в том случае, если существует вероятность путаницы, которая должна быть крайне редкой.
import SomeModule
let myClass = MyModule.UsefulClass()При создании пользовательских методов делегата первый параметр должен быть параметром делегата (UIKit содержит многочисленные примеры подобного).
Рекомендуется:
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> BoolНе рекомендуется:
func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> BoolИспользуйте возможности компилятора для вывода более короткого и понятного кода.
Рекомендуется:
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)Не рекомендуется:
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)Имена обобщений должны быть понятными и в формате UpperCamelCase. Если имя типа не имеет значимой роли, используйте традиционную одиночную прописную букву, такую как T, U или V.
Рекомендуется:
struct Stack<Element> { ... }
func write<Target: OutputStream>(to target: inout Target)
func swap<T>(_ a: inout T, _ b: inout T)Не рекомендуется:
struct Stack<T> { ... }
func write<target: OutputStream>(to target: inout target)
func swap<Thing>(_ a: inout Thing, _ b: inout Thing)Возьмите за основу американско-английское правописание в соответствии с API Apple.
Рекомендуется:
let color = "red"Не рекомендуется:
let colour = "red"Используйте расширения, чтобы разложить свой код на логические функциональные блоки. Каждое расширение должно быть подписано с помощью // MARK: - Комментарий, чтобы все было хорошо организовано.
Лучше соблюдать следующую последовательность:
// MARK: - Types
// MARK: - Constants
// MARK: - IBOutlets
// MARK: - Public Properties
// MARK: - Private Properties
// MARK: - UIViewController(*)
// MARK: - Public Methods
// MARK: - IBActions
// MARK: - Private Methods(*)Вместо
UIViewControllerвы должны написать другой суперкласс, от которого вы наследуетесь.
В частности, при добавлении соответствия протокола желательно добавить отдельное расширение для реализации его методов. Это удерживает связанные методы вместе с протоколом и может упростить инструкции для добавления протокола к классу с помощью связанных с ним методов.
Рекомендуется:
class MyViewController: UIViewController {
// код класса
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// методы датасорса
}
// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
// методы делегата
}Не рекомендуется:
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// все методы
}Это правило не работает в двух случаях:
- Когда вам нужен метод переопределения, который реализован в другом расширении
- Когда вы используете класс с обобщениями и Objective-C протоколами с дополнительными методами (@objc не поддерживается в расширениях Generic-классов или классов, которые наследуются от Generic-классов)
Поскольку компилятор не позволяет повторно объявить соответствие протокола в дочернем классе, не всегда требуется повторять группы расширений базового класса. Это совершенно справедливо, если дочерний класс является финальным и переопределяется лишь небольшое число методов. Сохранение группы расширений оставляют на усмотрение автора.
Для наследников UIViewController можно рассмотреть разложение в отдельные расширения: жизненный цикл, пользовательские аксессоры и IBActions.
Неиспользованный (мертвый) код, включая код шаблона Xcode и комментарии к заполнению, должен быть удален. За исключением того, что вы пишете учебник, где даете указание читателю использовать прокомментированный код.
Методы, непосредственно не связанные с учебником, реализация которых просто вызывает суперкласс, также должны быть удалены. Сюда входят любые пустые/неиспользуемые методы UIApplicationDelegate.
Рекомендуется:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Database.contacts.count
}Не рекомендуется:
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return Database.contacts.count
}Соблюдайте минимальное количество импортов. Например, опустите импорт Foundation, если импортируете UIKit.
-
Отступ использует 4 пробела, а не табуляцию, чтобы сохранить пространство и помочь предотвратить обертку строк. Для этого надо убедиться, что выбрана соответствующая опция в Xcode (Preferences / Text Editing / Indentation / Prefer Indent using: Spaces, и Tab width: 4)
-
Обычные и фигурные скобки (в
if/else/switch/whileи т.д.) всегда открываются в той же строки, что и оператор, но закрываются на новой строке -
Совет: Можно автоматически применить корректный отступ, выделив нужный код, а затем нажав Control-I (или Editor/Structure/Re-Indent)
Рекомендуется:
if user.isHappy {
// что-то сделаем
} else {
// сделаем что-то другое
}Не рекомендуется:
if user.isHappy
{
// что-то сделаем
}
else {
// сделаем что-то другое
}-
Должна быть ровно одна пустая строка между методами, помогающими визуальной ясности и организации. Пропуски внутри методов должны разделять функциональность, но наличие слишком большого их количества в методе часто означает, что вы должны отрефакторить этот метод
-
Двоеточия всегда не имеют пробела слева, но имеют один справа. Исключением является тройной оператор
? :, пустой словарь[:]и синтаксис#selectorдля неназванных параметров(_:).
Рекомендуется:
class TestDatabase: Database {
var dataMap: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}Не рекомендуется:
class TestDatabase : Database {
var dataMap :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}-
В идеале, длинные строки должны быть в пределах 70 символов. Тем не менее, жесткого ограничения нет
-
Пробелов на концах строк быть не должно
-
В конце каждого файла должна быть одна пустая строка
При необходимости, используйте комментарии, чтобы объяснить, почему какой-то фрагмент кода что-то делает. Комментарии должны быть обновлены до текущего состояния кода или удалены совсем.
Избегайте комментариев внутри блока кода, так как код должен быть как можно более самодокументированным. Исключение: это не относится к тем комментариям, которые используются для создания документации.
Структуры имеют семантику значений. Используйте структуры для вещей, которые не имеют личности. Массив, содержащий [a, b, c], действительно тот же, что и другой массив, содержащий [a, b, c], и они полностью взаимозаменяемы. Неважно, используете ли вы первый массив или второй, потому что они представляют собой то же самое. Вот почему массивы – это структуры.
Классы имеют ссылочную семантику. Используйте классы для вещей, которые имеют личность или определенный жизненный цикл. Можно моделировать человека как класс, потому что объекты двух людей – две разные вещи. Все потому, что если у двух людей одно и то же имя и дата рождения, то это не значит, что они одни и те же. Но дата рождения человека была бы структурой, потому что дата 3 марта 1950 года такая же, как и любой другой объект даты 3 марта 1950 года. Сама дата не имеет личности.
Иногда вещи должны быть структурами, но при этом должны соответствовать AnyObject или исторически моделироваться уже как классы (NSDate, NSSet). Старайтесь как можно ближе следовать этим рекомендациям.
Вот пример хорошо продуманного определения класса:
class Circle: Shape {
var x: Int, y: Int
var radius: Double
var diameter: Double {
get {
return radius * 2
}
set {
radius = newValue / 2
}
}
init(x: Int, y: Int, radius: Double) {
self.x = x
self.y = y
self.radius = radius
}
convenience init(x: Int, y: Int, diameter: Double) {
self.init(x: x, y: y, radius: diameter / 2)
}
override func area() -> Double {
return Double.pi * radius * radius
}
}
extension Circle: CustomStringConvertible {
var description: String {
return "center = \(centerString) area = \(area())"
}
private var centerString: String {
return "(\(x),\(y))"
}
}Пример выше демонстрирует следующие стили:
- Указывайте типы для свойств, переменных, констант, объявлений аргументов и других операторов с пробелом после двоеточия, например.
x: IntиCircle: Shape. - Определяйте несколько переменных и структур в одной строке, если они имеют общую цель/контекст
- Определение геттера и сеттера, а также наблюдателя свойств
- Не добавляйте модификаторы доступа, такие как
internal, если они уже установлены по умолчанию. Аналогичным образом не следует повторять модификатор доступа при переопределении метода - Организовывайте дополнительную функциональность (например, печать) в расширениях
- Скрывайте данные реализации, не связанные с общим доступом, такие как
centerStringвнутри расширения с помощью частного контроля доступа
Для краткости кода избегайте использования self, так как Swift не требует доступа к свойствам объекта или вызова его методов.
Используйте self только тогда, когда это требуется компилятором (в сбегающих @escaping замыканиях или в инициализаторах для устранения неоднозначности свойств из аргументов). Другими словами, если код компилируется без self, опустите его.
Если вычисляемое свойство доступно только для чтения, опустите get. Предложение get требуется только тогда, когда предоставляется предложение set.
Рекомендуется:
var diameter: Double {
return radius * 2
}Не рекомендуется:
var diameter: Double {
get {
return radius * 2
}
}Всегда используйте вычисляемое свойство вместо метода без аргументов и побочных эффектов.
Рекомендуется:
var homeFolderPath: Path {
//...
return path
}Не рекомендуется:
func homeFolderPath() -> Path {
//...
return path
}Маркировка классов как final в примерах может отвлекать от основной темы и не всегда требуется. Тем не менее, использование final может иногда уточнять ваши намерения и стоит затрат. В приведенном ниже примере Box имеет особую цель, и расширение в виде производного класса не предназначено. Пометка final делает это более понятным.
final class Box<T> {
let value: T
init(_ value: T) {
self.value = value
}
}Также это улучшает чтение кода и ускоряет компиляцию.
Сохраняйте короткие объявления функций или типов на одной строке, включая открывающую фигурную скобку:
func method(parameter: [Double]) -> Bool {
// ...
}Для функций надо добавить переносы строк для каждого аргумента, включая первый, если они превышают руководство по ширине страницы. Для того, чтобы аргументы визуально не сливались с телом метода (ведь они с одним отступом), переносите завершающую скобку также на новую строку:
func method(
parameter1: [Double],
parameter2: Double,
parameter3: Int
) -> Bool {
// ...
}Это же правило применяется и для вызовов функций.
let result = method(
parameter1: parameter1,
parameter2: parameter2,
parameter3: parameter3
)Оставьте одну пустую строку между функциями и одну до и после MARK
func colorView() {
//...
}
func reticulate() {
//...
}
// MARK: - Private Methods
private func retry() {
//...
}Используйте синтаксис закрывающего замыкания только в том случае, если в конце списка аргументов имеется одно замыкание. При этом, укажи понятное имя для замыкания. Тип и скобки замыкания должны быть опущены.
Рекомендуется:
UIView.animate(withDuration: 1.0) {
// ...
}
UIView.animate(
withDuration: 1.0,
animations: {
// ...
},
completion: { finished in
// ...
})Не рекомендуется:
UIView.animate(withDuration: 1.0, animations: {
// ...
})
UIView.animate(withDuration: 1.0, animations: {
// ...
}) { (f: Bool) in
// ...
}Для замыканий с одним выражением, где контекст понятен, используйте неявный возврат:
participants.sort { a, b in
a > b
}Связанные методы с использованием закрывающих замыканий должны быть понятными и легко читаемыми. Примеры:
let value = numbers
.map { $0 * 2 }
.filter { $0 > 50 }
.map { $0 + 10 }Всегда используйте родные типы Swift, когда они доступны. Swift предлагает ассоциацию к типам Objective-C, поэтому вы можете использовать полный набор методов по мере необходимости.
Рекомендуется:
let width: Double = 120 // Double
let widthString = (width as NSNumber).stringValue // StringНе рекомендуется:
let width: NSNumber = 120.0 // NSNumber
let widthString: NSString = width.stringValue // NSStringВ коде SpriteKit используйте CGFloat, т.к. он сделает код более кратким, избегая слишком большого количества преобразований.
Константы определяются с помощью ключевого слова let, а переменные – var. Всегда используйте let вместо var, если значение переменной не изменится.
Совет: Есть неплохая техника, когда определяете объекты, используя let, и изменяете его на var, если компилятор начнет ругаться.
Вы можете определить константы для типа, а не для экземпляра этого типа, используя свойства типа. Чтобы объявить свойство типа как константу, просто используйте static let. Свойства типа, объявленные таким образом, намного лучше, чем глобальные константами, потому что их легче отличить от свойств экземпляра. Пример:
Рекомендуется:
enum Math {
static let e = 2.718281828459045235360287
static let root2 = 1.41421356237309504880168872
}
let hypotenuse = side * Math.root2Не рекомендуется:
let e = 2.718281828459045235360287 // портит глобальное пространство имен
let root2 = 1.41421356237309504880168872
let hypotenuse = side * root2 // что за root2?Если уж приспичило использовать глобальное пространство имен, то советуем именовать в следующем формате:
Рекомендуется:
let TopMargin: CGFloat = 10Не рекомендуется:
let kTopMargin: CGFloat = 10
let bottomMargin: CGFloat = 100 Статические методы и свойства типа работают аналогично глобальным функциям и глобальным переменным, также должны использоваться экономно. Они полезны, когда функциональность привязана к определенному типу или когда требуется совместимость с Objective-C.
Можно объявлять переменные и возвращаемые типы функций как необязательные ?, где допустимо значение nil.
Используйте неявно развернутые типы, объявленные с помощью ! только тогда, когда заранее известно, что они будут инициализированы раньше, чем будут использованы. Например, subviews, которые будут установлены во viewDidLoad.
При доступе к необязательному значению используйте необязательную цепочку:
self.textContainer?.textLabel?.setNeedsDisplay()Используйте также дополнительную связку if let, когда удобнее разворачивать значение и выполнять несколько операций:
if let textContainer = self.textContainer {
// ...
}При наименовании необязательных переменных и свойств избегайте указывать их необязательность, например, optionalString или maybeView, поскольку их необязательность уже указана в объявлении типа.
Для объектов развернутых типов не используй такие имена, как unwrappedView или actualLabel. Также лучше добавить новую строку после if, если в разделе разворачивания есть несколько переменных.
Рекомендуется:
var subview: UIView?
var volume: Double?
// позднее...
if
let subview = subview,
let volume = volume {
// что-то сделать с развернутыми subview и volume
}Не рекомендуется:
var optionalSubview: UIView?
var volume: Double?
if
let unwrappedSubview = optionalSubview {
if let realVolume = volume {
// что-то сделать с развернутыми unwrappedSubview и realVolume
}
}Рассмотрите возможность использования ленивой инициализации для более тонкого контроля за временем жизни объекта. Это особенно актуально для UIViewController, который лениво загружает вьюшки. Можно использовать замыкание, которое немедленно вызовится { }() или вызвать фабричный метод. Пример:
lazy var locationManager: CLLocationManager = self.makeLocationManager()
private func makeLocationManager() -> CLLocationManager {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}Примечания:
[unowned self]здесь не требуется. Retain-цикл не создается- У менеджера местоположений есть побочный эффект для появления пользовательского интерфейса, чтобы спросить у пользователя разрешение, поэтому подобный тонкий контроль здесь имеет смысл
Всегда предпочитайте компактный код и пусть компилятор сам выводит тип для констант или переменных отдельных экземпляров. Вывод типа также подходит для небольших массивов и словарей. При необходимости можно указать конкретный тип, например CGFloat и Int16.
Рекомендуется:
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5Не рекомендуется:
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
let names = [String]()Для пустых массивов и словарей не надо использовать аннотацию типа.
Рекомендуется:
var names: [String] = []
var lookupMap: [String: Int] = [:]Не рекомендуется:
var names = [String]()
var lookupMap = [String: Int]()Используйте краткие версии объявлений типа, используя синтаксис обобщений.
Рекомендуется:
var deviceModels: [String]
var employeeMap: [Int: String]
var faxNumber: Int?Не рекомендуется:
var deviceModels: Array<String>
var employeeMap: Dictionary<Int, String>
var faxNumber: Optional<Int>Используйте следующий перенос, когда массив или словарь не умещаются по ширине страницы:
Рекомендуется:
let deviceModelMap = ["BMW": 100, "Mercedes": 120]
let wheels = [
"Continental",
"Vitoria",
...
]Не рекомендуется:
let wheels = ["Continental", "Vitoria", // параметры, превышающие ширину страницы]Свободные функции, которые не привязаны к классу или типу, должны использоваться крайне осторожно. Когда это возможно, используйте метод вместо свободной функции. Это помогает в удобочитаемости и открытости.
Свободные функции наиболее подходят, если они не связаны с каким-либо конкретным типом или экземпляром.
Рекомендуется
let sortedItems = items.mergeSorted() // легко найти
rocket.launch() // воздействует только на модельНе рекомендуется
let sortedItems = mergeSort(items) // сложно найти
launch(&rocket)Исключения для свободных функций
let tuples = zip(a, b)
let value = max(x, y, z)Код не должен создавать retain-циклы. Нужно проанализировать график объектов (Xcode / Debug navigator / View Memory Graph Hierarchy) и предотвратить strong-циклы с помощью weak или unowned ссылками. Кроме того, можно использовать типы значений (struct, enum) для предотвращения циклов.
Продлите срок жизни объекта, используя [weak self] и guard let `self` = self else { return } (если это необходимо). Лучше использовать [weak self], нежели [unowned self], т.к. не всегда понятно, что self к этому моменту будет жить. Продление срока жизни объекта – это рекомендуемый вариант для необязательного разворачивания.
Рекомендуется
resource.request().onComplete { [weak self] response in
guard let self = self else { return }
let model = self.updateModel(response)
self.updateUI(model)
}Не рекомендуется
// будет сбой, если self будет освобожден (выгружен из памяти) в момент выполнения замыкания
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}Не рекомендуется
// освобождение self может произойти между обновлением модели и обновлением пользовательского интерфейса, если обновление модели происходит не в главном потоке
resource.request().onComplete { [weak self] response in
let model = self?.updateModel(response)
self?.updateUI(model)
}Аннотации полного контроля доступа в учебниках могут отвлекать от основной темы и в общем-то не требуются. Однако использование private и fileprivate, таким образом, добавляет ясности и способствует инкапсуляции. Лучше использовать private, нежели fileprivate, если это возможно. Использование расширений может потребовать использования fileprivate.
Можно явно использовать open, public и internal, когда вам требуется полная спецификация контроля доступа.
Используйте контроль доступа в качестве первичного спецификатора свойств. Исключения составляют лишь static или атрибуты, такие как @IBAction, @IBOutlet, @discardableResult и @IBInspectable.
Рекомендуется:
private let message = "Great Scott!"
class TimeMachine {
fileprivate dynamic lazy var fluxCapacitor = FluxCapacitor()
}Не рекомендуется:
fileprivate let message = "Great Scott!"
class TimeMachine {
lazy dynamic fileprivate var fluxCapacitor = FluxCapacitor()
}Лучше использовать стиль for-in для цикла for, нежели стиль while-condition-increment.
Рекомендуется:
for _ in 0 ..< 3 {
print("Hello three times")
}
for (index, person) in participants.enumerated() {
print("\(person) is at position #\(index)")
}
for index in stride(from: 0, to: items.count, by: 2) {
print(index)
}
for index in (0 ... 3).reversed() {
print(index)
}Не рекомендуется:
var i = 0
while i < 3 {
print("Hello three times")
i += 1
}
var i = 0
while i < participants.count {
let person = participants[i]
print("\(person) is at position #\(i)")
i += 1
}При написании кода с условными обозначениями левый край кода должен быть, как говорят, «золотым». То есть, не надо добавлять лишние операторы if, заменив их на операторы множественного возврата. Для этого был создан оператор guard.
Рекомендуется:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
guard let context = context else { throw FFTError.noContext }
guard let inputData = inputData else { throw FFTError.noInputData }
// используем context и inputData для подсчета frequencies
return frequencies
}Не рекомендуется:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
if let context = context {
if let inputData = inputData {
// используем context и inputData для подсчета frequencies
return frequencies
} else {
throw FFTError.noInputData
}
} else {
throw FFTError.noContext
}
}Когда в оператор входят несколько условных выражений, надо добавить перенос строки перед каждым операндом. Сложные условия лучше перенести в отдельные функции.
Рекомендуется:
if
isTrue(),
isFalse(),
tax > MinTax {
// ...
}Не рекомендуется:
if a > b && b > c && tax > MinTax && currentValue > MaxValue {
// ...
}Когда несколько опций раскрываются через guard или if let, то вложенность заметно сокращается. Пример:
Рекомендуется:
guard
let number1 = number1,
let number2 = number2,
let number3 = number3
else {
clean()
fatalError("impossible")
}
// что-то делаемили можно использовать вариант else в одну строку, если внутри всего один оператор
guard
let number1 = number1,
let number2 = number2
else { return }
// что-то делаемили однострочный вариант, если условие всего одно
or one-liner with one let operation
guard let number1 = number1 else { return }Не рекомендуется:
if let number1 = number1 {
if let number2 = number2 {
if let number3 = number3 {
// что-то делаем
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}Операторы guard обязаны каким-то образом обозначить выход. Как правило, это должен быть простой однострочный оператор, такой как return, throw, break, continue, или fatalError(). Следует избегать больших блоков кода. Если для нескольких точек выхода требуется код очистки, стоит использовать defer, чтобы избежать дублирования кода очистки.
Swift не требует точки с запятой после каждого оператора в вашем коде. Они необходимы только в том случае, если вы хотите объединить несколько операторов в одной строке. Но это делать крайне нежелательно.
Рекомендуется:
let swift = "not a scripting language"Не рекомендуется:
let swift = "not a scripting language";Примечание: Swift is very different from JavaScript, where omitting semicolons is считается опасным занятием
Скобки вокруг выражений не требуются и их следует опустить.
Рекомендуется:
if name == "Hello" {
print("World")
}Не рекомендуется:
if (name == "Hello") {
print("World")
}В больших выражениях необязательные круглые скобки иногда могут сделать код более понятным.
Рекомендуется:
let isPlayerMarked = (player == current ? "X" : "O") && player.activeВ случае использования проекта Xcode организация должна быть настроена на вашу компанию/имя, также Bundle Identifier должен быть в формате domain.companyName.appName,
где
domain – доменная зона проекта, обычно соотствует доменной зоне сайта компании, например, com,
companyname – наименование компании (обычно в нижнем регистре),
appName – название вашего проекта (обычно в формате UpperCamelCase)

