Skip to content

Руководство по оформлению кода на языке Swift

Sergej Jaskiewicz edited this page Jun 16, 2016 · 7 revisions

Руководство по оформлению кода на языке Swift

Версия raywenderlich.com в переводе на русский язык.

Содержание

Имена

Используем говорящие имена с ГорбатымСинтаксисом ("CamelCase") для классов, методов, переменных и т. д. Имена классов, структур и перечислений пишем с заглавной буквы, имена методов и переменных – со строчной.

Следует писать так:

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
}

При описании функций и инициализаторов (init) следует задавать и использовать именованные параметры для всех аргументов, исключение – когда контекст максимально понятен интуитивно. Кроме того, лучше использовать еще и внешние имена параметров, если это сделает вызовы функции более читабельными.

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

// Вызов выглядит так:
dateFromString("2014-03-14")
convertPointAt(column: 42, row: 13)
timedAction(afterDelay: 1.0, perform: someOtherAction)

При описании методов лучше следовать стандартному соглашению Apple — ссылаться на первый параметр в имени метода таким образом, чтоб в сочетании друг с другом имя метода и параметр представляли собой одну фразу (в следующем примере это "combine with other counter"):

class Counter {
    func combine(with otherCounter: Counter, options: Dictionary?) { ... }
    func increment(by amount: Int) { ... }
}

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

Используем горбатыйСинтаксис ("camelCase") для значений перечислений

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

Литература

Описыва функции в литературе (обучающих статьях, книгах, комментариях), включаем внешние (со стороны вызова) имена параметров или символ _ для безымянных параметров.

Вызываем convertPoint(column:row:) из своей собственной реализации init.

Если вызываем dateFromString(_:), нужно убедиться, что передаем строку в формате "yyyy-MM-dd".

Если вызываем timedAction(afterDelay:perform:) из viewDidLoad(), не забываем передавать урегулированное значение задержки и действие, которое нужно совершить.

Не следует непосредственно вызывать data source-метод tableView(_:cellForRowAt:)

Если сомневаемся, смотрим, как Xcode описывает метод в выпрыгивающем меню (jump bar) – наше руководство соответствует этим описаниям.

Префиксы принадлежности к модулю

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

import SomeModule

let myClass = MyModule.UsefulClass()

Пробелы и табуляция

  • Делаем отступы, используя 4 пробела, а не табуляцию. Убедитесь, что эти параметры в настройках Xcode установлены по умолчанию:

  • Скобки метода и другие скобки (if/else/switch/while etc.) всегда открываем на той же строке, что и название метода/оператора, но закрываем всегда на отдельной новой строке.
  • Подсказка: Чтоб автоматически перерасставить отступы, выделите часть кода (или выделите весь код, воспользовавшись сочетанием клавиш ⌘A) и затем воспользуйтесь сочетанием клавиш Ctrl-I (или Editor → Structure → Re-Indent в меню).

Следует писать так:

if user.isHappy {
    // Что-нибудь делаем
} else {
    // Делаем что-нибудь другое
}

А не так:

if user.isHappy
{
  // Что-нибудь делаем
}
else {
  // Делаем что-нибудь другое }
  • Чтобы обеспечить визуальную ясность и организованность, между методами следует оставлять ровно одну пустую строку. Пропуски внутри методов должны разделять части кода по функциональности, притом, наличие слишком большого количества пропусков (при следовании этому предписанию) обычно означает, что метод следует разделить на несколько методов.

Комментарии

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

Не следует злоупотреблять комментариями. Код должен быть максимально понятным и сам по себе ("самодокументирующимся"). Исключение: Это не касается тех комментариев, которые используются непосредственно для генерации документации

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

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

Помним, что структуры (structs) имеют семантику значений (value semantics). Используем структуры для вещей, которые не являются индивидуальными объектами. Массив, содержащий только [a, b, c] абсолютно таков же, как и любой друой, содержащий только [a, b, c], и они абсолютно равнозначны и заменяемы. Совсем не важно, используете ли вы первый массив или второй, потому что они представляют из себя одну и ту же вещь. Вот почему массивы считаются структурами.

Классы имеют семантику ссылок (reference semantics). Используем классы для вещей, которые являются индивидуальными объектами или имеют специфический жизненный цикл. Например, людей следует представлять (моделировать) как класс. Два объекта, являющиеся экземплярами класса людей – две разные сути. То, что два человека имеют одно и то же имя и одну и ту же дату рождения, совсем не означает, что они – один и тот же человек. При этом, дата рождения человека может быть структурой, ведь дата, обозначающая третье марта 1950 года всегда совпадает с любым объектом-датой, обозначающим 3 марта 1950. Дата сама по себе не являются индивидуальным объектом.

Иногда некоторые вещи, при том, что они должны быть структурами, должны наследоваться от NSObject, или они уже исторически смоделированы как классы (NSDate, NSSet). Тем не менее, нужно пытаться следовать этим предписаниям настолько близко, насколько это возможно.

Пример описания

Пример хорошо оформленного описания класса:

class Circle: Shape {

    var x: Int, y: Int
    
    final var radius: Double
    
    final 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.
  • Определяем несколько переменных (структур) на одной строке, если они имеют общую цель/контекст.
  • При описании getter-ов и setter-ов не забываем о правилах отступов и др.
  • Не добавляем модификаторы доступа, такие как internal, если они уже установлены по умолчанию. Таким же образом, не повторяем модификатор доступа при переопределении метода.
  • Используем final для свойств, которые не должны быть перегружены при наследовании. Если нет необходимости в наследовании от данного класса вообще, помечаем весь класс как final. Это помогает компилятору в оптимизации кода.

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

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

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

class BoardLocation {
    let row: Int, column: Int

    init(row: Int, column: Int) {
        self.row = row
        self.column = column
    
        let closure = {
            println(self.row)
        }
    }
}

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

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

Также, не забываем про // MARK: — комментарий для поддержания организованности!

Следует писать так:

class MyViewcontroller: UIViewController {
    // всё, что относится к классу
}

// MARK: - UITableViewDataSource
extension MyViewcontroller: UITableViewDataSource {
    // data source-методы для Table View
}

// MARK: - UIScrollViewDelegate
extension MyViewcontroller: UIScrollViewDelegate {
    // delegate-методы для Scroll View
}

А не так:

class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
    // вообще все методы
}

Вычисляемые свойства

Из соображений краткости, если вычисляемое свойство (computed property) позволяет только извлекать своё значение, но не задавать его, опускаем конструкцию get. Конструкция get требуется только в сопровождении с конструкцией set.

Следует писать так:

var diameter: Double {
    return radius * 2
}

А не так:

var diameter: Double {
    get {
        return radius * 2
    }
}

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

Короткие объявления функций оставляем на одной линии:

func reticulateSplines(spline: [Double]) -> Bool {
    // тело функции
}

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

func reticulateSplines(spline: [Double],
                       adjustmentFactor: Double,
                       translateConstant: Int,
                       comment: String) -> Bool {
                       
    // тело функции
}

Замыкания (closures)

В случаях, когда последним аргументом передается замыкание, размещаем его не внутри круглых скобок, а уже за ними (см. пример кода ниже). Параметрам этих функций даем максимально говорящие имена.

Следует писать так:

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()
}

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

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

Типы

Всегда, когда возможно, используем собственные типы языка Swift. Swift поддерживает т. н. bridging на Objective-C, таким образом можно пользоваться и полным арсеналом методов языка 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

Константы

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

Подсказка: рекомендуемая тактика — объявляем всё, используя ключевое слово let, и меняем на var только тогда, когда начинает возмущаться компилятор.

Опциональные типы

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

Распаковываем (unwrap) optionals с помощью !, только если уверены, что к этому моменту переменная будет инициализирована непустым значением. Используем неявно распакованные optionals (! вместо ? при объявлении) только для тех переменных, для которых мы уверены, что они будут инициализированы до использования.

Используем optional binding, если нужно однократно распаковать optional, сразу выполнив несколько операций:

if let textContainer = self.textContainer {
    // производим действия с textContainer
}

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

При объявлении optionals избегаем названий для них типа optionalString или maybeView, так как информация о том, что они optionals уже содержится в объявлении их типа.

Следует писать так:

var subview: UIView?
var volume: Double?

// в дальнейшем...
if let subview = subview, volume = volume {
    // производим действия с распакованными subview и volume
}

А не так:

var optionalSubview: UIView?
var volume: Double?

if let unwrappedSubview = optionalSubview {
    if let realVolume = volume {
        // производим действия с unWrappedSubview и 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)

Автоматическое определение типа

Используем более компактные варианты оформления объявлений переменных и констант, оставив задачу определения типа компилятору. Исключением являются только те случаи, когда необходимо объявить переменную/константу специфического типа, например, CGFloat или Int16.

Следует писать так:

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>

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

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

Не надо так делать.

Следует писать так:

let swift = "not a scripting language"

А не так:

let swift = "not a scripting language";

Замечание: В этом вопрсе Swift сильно отличается от JavaScript, в котором опускание точек с запятой, как правило, небезопасно.

Язык

В спорных ситуациях используем американский английский, чтобы обеспечить соответствие всем API компании Apple.

Следует писать так:

let color = "red"

А не так:

let colour = "red"

Разработка

  1. Стили кодирования
  • [Java](Код стайл для языка Java)
  • [C/C++](Стиль написания кода на С )
  • [C#](Стиль написания кода на С# )
  • [Swift](Руководство по оформлению кода на языке Swift )
  • Написание комментариев к коммитам
  1. Android
  • Android DevGuide
  • [QR codes](Работа c QR на Android)
  • [Полезные вещи](Полезные вещи для Android разработки)
  • [Архитектура приложения](Архитектура приложения)
  • [Используемые компоненты](Используемые компоненты)
  • [Инструкция по сборке проекта](Инструкция по сборке проекта)
  1. iOS
  1. C и C++
  • [Использование CMake для проектов на C++ и C ](Использование CMake для проектов на C и CPP)

Описание

  1. Форматы файлов
  • [.bmp](Cтруктура хранения bmp файлов)
  • [.jpg](Cтруктура хранения jpg файлов)
  • [.png](Cтруктура хранения png файлов)
  1. Алгоритмы шифрования
  1. Примеры использования
  • [Библиотека матричной алгебры](Пример использования библиотеки матричной алгебры)
  1. Описание процесса кодирования файла
  2. Способ обезопасить использование приложения
  3. Java фасад библиотеки алгоритмов
  4. Алгоритм шифрования bmp на java заглушке

Тест-кейсы

  1. Матричная арифметика
  • [A+B](Сложение матриц)
  • [A*p](Умножение матрицы на скаляр)
  • [A*B](Умножение матриц)
  • [Обратные матрицы](Нахождение обратной матрицы)
  1. Взятие по модулю
  • [A mod p](Взятие матрицы по модулю простого числа)
  • [A mod P](Взятие матрицы по модулю - матрицы из простых чисел)
  1. Суперпозиция (модуль - простое число)
  • [A+B mod p](Сложение матриц по модулю простого числа)
  • [A*c mod p](Умножение матрицы на скаляр по модулю простого числа)
  • [A*B mod p](Умножение матриц по модулю простого числа)
  1. Суперпозиция (модуль - матрицы из простых чисел)
  • [A+B mod P](Сложение матриц по модулю - матрице простых чисел)
  • [A*c mod P](Умножение матрицы на скаляр по модулю - матрице простых чисел)
  • [A*B mod P](Умножение матриц по модулю - матрице простых чисел)

##Прочее

  1. [Утечки памяти](Memory Leaks)
  2. [Базовые цвета](Базовые цвета)
  3. [Clean Architecture](Clean Architecture)
Clone this wiki locally