Стиль написания кода на Swift

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Xcode: 
6
Swift: 
1.2

Имена

Наша цель - сделать код коротким, читабельным и простым.

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

Предпочтительно

private let maximumWidgetCount = 100

class WidgetContainer {
  var widgetButton: UIButton
  let widgetHeightPercentage = 0.85
}

Не предпочтительно

let MAX_WIDGET_COUNT = 100

class app_widgetContainer {
  var wBut: UIButton
  let wHeightPct = 0.85
}

Для функций и методов инициализации, предпочитают именованные параметры, для всех аргументов, если контекст не очень ясный. Также, хорошо использовать включение имен внешних параметров, если это делает вызов функции более читабельным.

func dateFromString(dateString: String) -> NSDate
func convertPointAt(#column: Int, #row: Int) -> CGPoint
func timedAction(#delay: NSTimeInterval, perform action: SKAction) -> SKAction!

// would be called like this:
dateFromString("2014-03-14")
convertPointAt(column: 42, row: 13)
timedAction(delay: 1.0, perform: someOtherAction)

Касательно метода, хорошо работает стандартное соглашение об использовании первого параметра, в качестве ссылки имени самого метода:

class Guideline {
  func combineWithString(incoming: String, options: Dictionary?) { ... }
  func upvoteBy(amount: Int) { ... }
}

Перечисления

Используйте Горбатую нотацию Верхнего регистра для элементов перечислений

enum Shape {
  case rectangle
  case square
  case triangle
  case circle
}

Проза

Когда вы ссылаетесь на функцию, как внутри текста (статьи, книги, комментарии), включайте необходимые имена параметров, а не вырывайте только название функции. Это придаст вашему коду большей ясности. То есть, к примеру, вы можете написать про метод tableView, но лучше, в развернутом виде tableView(_:cellForRowAtIndexPath:).

Если сомневаетесь, то вы всегда можете посмотреть как Xcode перечисляет свои методы в выпадающей панели. Наш стиль соответствует его стилю.

Префиксы класса

Типы в Swift автоматически обретают пространство имени в том модуле, в котором они содержатся. И как результат, для того, чтобы свести к минимуму конфликты имен, префиксы не требуются. Если два имени из разных модулей сталкиваются, вы можете разделить их, используя префикс имени типа с именем модуля:

import SomeModule

let myClass = MyModule.UsefulClass()

Вам не следует добавлять префиксы к вашим типам Swift.

Если вам нужно работать с типом Swift в языке Objective-C, то вы можете использовать подходящий префикс, как например:

@objc (RWTChicken) class Chicken {
   ...
}

Расстановка пробелов

  • Для обозначения отступа используйте два пробела, а не один tab (нажатие кнопки табуляции), что сэкономит вам место и предотвратит перенос строки. Убедитесь, что вы используете такие настройки Xcode как показано ниже:
  • Открывайте фигурные скобки на той же строке, что и (if/else/switch/while и т.д.), но закрывайте на новой.
  • Совет: вы можете повторно выровнять код (⌘A для его выделения), затем нажмите Control-I(или в меню Editor\Structure\Re-Indent). 

Предпочитительно

if user.isHappy {
  // Do something
} else {
  // Do something else
}

Не предпочтительно

if user.isHappy
{
    // Do something
}
else {
    // Do something else
}
  • Должна быть ровно одна пустая строка между методами для визуальной четкости и организованности. Пробелами внутри методов следует разделять функциональность, но наличие слишком большого числа разделов часто означает, что вы должны реорганизовать их в несколько методов.

Комментарии

Используйте комментарии для объяснения почему конкретный кусок кода делает что-то. Комментарии должны быть актуальными или удаленными.

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

Классы и структуры

Что из них использовать?

Помните, что структуры имеют семантику значений. Используйте их в тех случаях, когда не наблюдается идентичности. Массив, который имеет элементы [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)
  }

  func describe() -> String {
    return "I am a circle at \(centerString()) with an area of \(computeArea())"
  }

  override func computeArea() -> Double {
    return M_PI * radius * radius
  }

  private func centerString() -> String {
    return "(\(x),\(y))"
  }
}

Пример выше демонстрирует следующие стилистические установки:

  • Укажите типы для свойств, переменных, констант, определения аргументов и другие выражения с пробелом после двоеточия, но не до (x: Int и Circle: Shape).
  • Определите несколько переменных и структур в одной строке, если они имеют общую цель/контекст.
  • Отступ определения геттера, сеттера и наблюдателей свойств.
  • Не добавляйте модификатора типа internal, так как они уже идут по умолчанию. Аналогично, не повторяйте модификатор доступа, когда переписываете метод.

Использование Self

Для сокращения кода избегайте использования self, так как Swift не требует его для доступа к свойствам объектов или вызовам методов.

Используйте self, когда требуется разграничить между собой имена свойства и инициализаторы аргументов, или когда ссылаетесь на свойства в выражениях замыкания (как требует компилятор):

class BoardLocation {
  let row: Int, column: Int

  init(row: Int, column: Int) {
    self.row = row
    self.column = column

    let closure = {
      println(self.row)
    }
  }
}

Соответствие протоколу

Когда добавляете соответствие протоколу классу, то лучше добавить отдельное расширение класса, для методов протокола. Это оставляет связанные методы, сгруппированные вместе с протоколом, и упрощает действия по его добавлению.

Не забывайте: // ОТМЕТКА - оставляйте комментарии для организации кода

Предпочтительно

class MyViewcontroller: UIViewController {
  // class stuff here
}

// MARK: - UITableViewDataSource
extension MyViewcontroller: UITableViewDataSource {
  // table view data source methods
}

// MARK: - UIScrollViewDelegate
extension MyViewcontroller: UIScrollViewDelegate {
  // scroll view delegate methods
}

Не предпочтительно

class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate { // all methods }

Объявление функций

Не растягивайте объявление функций более чем на одну строку, включая фигурную скобку:

func reticulateSplines(spline: [Double]) -> Bool { // reticulate code goes here }

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

func reticulateSplines(spline: [Double], adjustmentFactor: Double,
    translateConstant: Int, comment: String) -> Bool {
  // reticulate code goes here
}

Замыкающие выражения

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

Предпочтительно

UIView.animateWithDuration(1.0) {
  self.myView.alpha = 0
}

UIView.animateWithDuration(1.0,
  animations: {
    self.myView.alpha = 0
  },
  completion: { finished in
    self.myView.removeFromSuperview()
  }
)

Не предпочтительно

UIView.animateWithDuration(1.0, animations: {
  self.myView.alpha = 0
})

UIView.animateWithDuration(1.0,
  animations: {
    self.myView.alpha = 0
  }) { f in
    self.myView.removeFromSuperview()
}

Для однострочных замыканий, где контекст понятен, используйте неявный возврат значения:

attendeeList.sort { a, b in
  a > b
}

Типы

По возможности используйте родные типы Swift. Swift предлагает мост в Objective-C, так что вы можете использовать все методы оттуда, если вам нужно.

Предпочитительно

let width = 120.0                                    // Double
let widthString = (width as NSNumber).stringValue    // String

Не предпочтительно

let width: NSNumber = 120.0                          // NSNumber
let widthString: NSString = width.stringValue        // NSString

В коде Sprite Kit используйте CGFloat, если это сделает ваш код сжатым и позволит избежать большого количества конвертирования.

Константы

Константы объявляются с использованием ключевого слова let, переменные - var. Каждое значение, которое не меняется, должно быть объявлено как константа, с использованием let. В результате, вы вероятнее всего заметите, насколько чаще вы используете let, чем var.

Совет: попробуйте все объявлять как константа, и менять на переменную, когда компилятор ругается!

Опционалы

Объявляйте переменные и функциональные возвращаемые типы как опционалы с ?, где значение nil - допустимо как величина.

Используйте неявно извлеченные типы, объявленные с !, только для переменных экземпляров, которые вы знаете и что будут инициализированы позже. Но это должно быть сделано до использования, например, как некоторые элементы в viewDidLoad.

При обращении к опциональному значению, используйте опциональную последовательность или цепочку, если вы обращаетесь к значению только один раз, или у вас несколько опционалов:

self.textContainer?.textLabel?.setNeedsDisplay()

Используйте привязку опционала, когда более удобно извлечь значение и провести несколько операции сразу:

if let textContainer = self.textContainer {
  // do many things with textContainer
}

Когда вы даете имена опциональным переменным и свойствам, избегайте таких имен как optionalString или maybeView, так как их "опциональность" указана уже в типе.

Для связки опционала лучше следовать первоначальному имени, чем использовать такое как unrappedView или actualLabel

Предпочитительно

var subview: UIView?
var volume: Double?

// later on...
if let subview = subview, volume = volume {
  // do something with unwrapped subview and volume
}

Не предпочтительно

var optionalSubview: UIView?
var volume: Double?

if let unwrappedSubview = optionalSubview {
  if let realVolume = volume {
    // do something with unwrappedSubview and realVolume
  }
}

Инициализаторы структуры

Лучше используйте родные инициализаторы Swift, а не наследие конструкторов CGGeometry:

Предпочитительно

let bounds = CGRect(x: 40, y: 20, width: 120, height: 80)
let centerPoint = CGPoint(x: 96, y: 42)

Не предпочтительно

let bounds = CGRectMake(40, 20, 120, 80)
let centerPoint = CGPointMake(96, 42)

Предпочитайте константы уровня структуры CGRect.infiniteRect, CGRect.nullRect и другие, глобальным константам CGRectInfinite, CGRectNull. Для существующих переменных вы можете использовать сокращенный вариант .zeroRect.

Вывод типа

Компилятор Swift может вывести тип переменной или константы. Вы можете явно указать тип через через алиас типа, но в большинстве случаев в этом нет необходимости.

Предпочтите в данном случае компактный вариант, пусть компилятор сам выведет тип для переменной или константы.

Предпочитительно

let message = "Click the button"
let currentBounds = computeViewBounds()
var names = [String]()
let maximumWidth: CGFloat = 106.5

Не предпочтительно

let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
var names: [String] = []

Синтаксический сахар

Предпочитайте сокращенные версии объявления типов:

Предпочитительно

var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?

Не предпочтительно

var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>

Управление потоком

Лучше использовать цикл for-in, но не классический for:

Предпочитительно

for _ in 0..<3 {
  println("Hello three times")
}

for (index, person) in enumerate(attendeeList) {
  println("\(person) is at position #\(index)")
}

Не предпочтительно

for var i = 0; i < 3; i++ {
  println("Hello three times")
}

for var i = 0; i < attendeeList.count; i++ {
  let person = attendeeList[i]
  println("\(person) is at position #\(i)")
}

Точка с запятой

Точка с запятой после каждого выражения больше не обязательна. Они нужны только для тех случаев, когда вы хотите записать несколько выражений в одну строку.

Не пишите несколько выражений в одну строку, разделяя их точкой с запятой!

Единственное исключение из этого правила - классический цикл for, который требует такого синтаксиса. Однако, используйте for-in вместо for, где только это возможно:

Предпочитительно

let swift = "not a scripting language"

Не предпочтительно

let swift = "not a scripting language";

Заметка: Swift совершенно другой язык, не похожий на JavaScript, где отсутствие запятых считается небезопасным.

Язык

Используйте правописание анлийского языка США, так как оно соответствует Apple's API.

Предпочитительно

let color = "red"

Не предпочтительно

let colour = "red"

Улыбающееся лицо

Улыбающиеся лица являются очень характерной чертой программирования на Swift! Это очень важно иметь правильную улыбку, означающую, огромное количество счастья и волнения в этой среде кодирования. Закрывающая квадратная скобка «]» используется потому, что она представляет собой большую улыбку и способна быть захваченной с помощью ASCII Art. В то время как закрывающая скобка «)» создает половинчатую улыбку, и, таким образом, не является предпочтительной.

Предпочитительно

:]

Не предпочтительно

:)

Что дальше?

Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!

Урок подготовил: Иван Акулов

Источник урока: https://github.com/raywenderlich/swift-style-guide