Tag2075 / Code-style-guide

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Правила оформления Swift кода

Оглавление

1. Форматирование кода

  • 1.1 Не забывайте про вложенность кода. Используйте 4 пробела для табуляции.
// ХОРОШО
struct SomeStruct {
    var firsrNumber: Int
    var secondNumber: Int
    if firsrNumber == firsrNumber {
        print("Equal!")
    }
}

// ПЛОХО
struct SomeStruct {
var firsrNumber: Int
var secondNumber: Int
if firsrNumber == firsrNumber {
print("Equal!")
}
}
  • 1.2 Не допускайте использования строк длинной более 160 символов (Xcode->Preferences->Text Editing->Page guide at column: 160 включение этого пункта поможет наглядно видеть если строка будет больше)
  • 1.3 Убедитесь, что в конце каждого файла есть новая строка.
  • 1.4 Убедитесь, что нигде нет конечных пробелов (Xcode->Preferences->Text Editing->Automatically trim trailing whitespace + Including whitespace-only lines).
  • 1.5 Не ставьте открывающиеся скобки на новую строку 1TBS style.
// ХОРОШО
class SomeClass {
    func someMethod() {
        if x == y {
            /* ... */
        } else if x == z {
            /* ... */
        } else {
            /* ... */
        }
    }

    /* ... */
}

// ПЛОХО
class SomeClass 
{
    func someMethod() 
    {
        if x == y 
        {
            /* ... */
        } else if x == z 
        {
            /* ... */
        } else 
        {
            /* ... */
        }
    }

    /* ... */
}
  • 1.6 При написании типов констант, переменных, ключей для словарей, аргументов функций, подписывания под протоколы или суперклассы не ставьте пробелы перед двоеточием, при этом после двоеточия пробел должен быть.
// ХОРОШО
let pirateViewController: PirateViewController

// dictionary syntax (note that we left-align as opposed to aligning colons)
let ninjaDictionary: [String: AnyObject] = [
    "fightLikeDairyFarmer": false,
    "disgusting": true
]

// объявление функции
func myFunction<T, U: SomeProtocol>(firstArgument: U, secondArgument: T) where T.RelatedType == U {
    /* ... */
}

// вызов функции
someFunction(someArgument: "Kitten")

// суперклассы
class PirateViewController: UIViewController {
    /* ... */
}

// протоколы
extension PirateViewController: UITableViewDataSource {
    /* ... */
}

// ПЛОХО
let pirateViewController:PirateViewController
let pirateViewController : PirateViewController
let pirateViewController :PirateViewController
  • 1.7 Как правило, после запятой должен стоять пробел.
// ХОРОШО
let myArray = [1, 2, 3, 4, 5]

// ПЛОХО
let myArray = [1,2,3,4,5]
  • 1.8 До и после бинарного оператора должны стоять пробелы +, ==, or ->. При этом не должно быть пробелов после ( и перед ).
// ХОРОШО
let myValue = 20 + (30 / 2) * 3
if 1 + 1 == 3 {
    fatalError("The universe is broken.")
}
func pancake(with syrup: Syrup) -> Pancake {
    /* ... */
}

// ПЛОХО
let myValue=20+(30/2)*3
  • 1.9 Мы должны следовать рекомендуемому стилю Xcode, то есть ваш код не должен менять при нажатии Ctrl+I. При объявлении функции, охватывающей несколько строк, предпочтительней использовать синтаксис Xcode по умолчанию.
// Отступ Xcode при объявлении функции, которая занимает несколько строк. 
func myFunctionWithManyParameters(parameterOne: String,
                                  parameterTwo: String,
                                  parameterThree: String) {
    // Отступы для такой инструкции
    print("\(parameterOne) \(parameterTwo) \(parameterThree)")
}

// Отступы для многострочного `if`
if myFirstValue > (mySecondValue + myThirdValue)
    && myFourthValue == .someEnumValue {

    // Отступы для такой инструкции
    print("Hello, World!")
}
  • 1.10 При вызове функции сл множеством аргументов, помещайте каждый в отдельную строку с одним дополнительным отступом.
someFunctionWithManyArguments(
    firstArgument: "Hello, I am a string",
    secondArgument: resultFromSomeFunction(),
    thirdArgument: someOtherLocalProperty)
  • 1.11 При работе с неявным массивом или словарем, достаточно большим, чтобы можно было разбить его на несколько строк, обрабатывайте [ и ] как если бы они были фигурными скобками в методе, операторе if и т.д. Замыкания в методе должны обрабатываться сходным образом.
//ХОРОШО
someFunctionWithABunchOfArguments(
    someStringArgument: "hello I am a string",
    someArrayArgument: [
        "dadada daaaa daaaa dadada daaaa daaaa dadada daaaa daaaa",
        "string one is crazy - what is it thinking?"
    ],
    someDictionaryArgument: [
        "dictionary key 1": "some value 1, but also some more text here",
        "dictionary key 2": "some value 2"
    ],
    someClosure: { parameter1 in
        print(parameter1)
    })

// ПЛОХО
someArrayArgument: 
[
    "dadada daaaa daaaa dadada daaaa daaaa dadada daaaa daaaa",
    "string one is crazy - what is it thinking?"
]

someClosure: 
{ parameter1 in
    print(parameter1)
}

2. Именование

  • 2.1 Используйте PascalCase для названий типов (например struct, enum, class, typedef, associatedtype, и т.д.).

  • 2.2 Используйте camelCase (начальная буква строчная) для функций, методов, свойств, констант, переменных, имен аргументов, значений enum и т.д.

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

// "HTML" присутствует в начале константы, значит мы будем писать строчные буквы "html"
let htmlBodyContent: String = "<p>Hello, World!</p>"
// В данном примере лучше использовать ID вместо Id
let profileID: Int = 1
// Лучше писать URLFinder чем UrlFinder
class URLFinder {
    /* ... */
}
  • 2.5 Все константы, не зависящие от экземпляра должны быть static. Все такие static константы должны быть помещены в определенный раздел их class, struct или enum. Для классов с большим количеством констант вы должны группировать константы с похожими или одинаковыми префиксами, суффиксами и/или вариантами использования.
// ПРЕДПОЧТИТЕЛЬНО    
class MyClassName {
    // MARK: - Constants
    static let buttonPadding: CGFloat = 20.0
    static let indianaPi = 3
    static let shared = MyClassName()
}

// НЕ ЖЕЛАТЕЛЬНО
class MyClassName {
    // Не используй `k`-prefix
    static let kButtonPadding: CGFloat = 20.0

    // Не используй имя констант
    enum Constant {
        static let indianaPi = 3
    }
}
  • 2.6 Имена должны говорящими и недвусмысленными.
// ПРЕДПОЧТИТЕЛЬНО
class RoundAnimatingButton: UIButton { /* ... */ }

// НЕ ЖЕЛАТЕЛЬНО
class CustomButton: UIButton { /* ... */ }
  • 2.8 Не используйте аббревиатуры, сокращенные и однобуквенные имена.
// ПРЕДПОЧТИТЕЛЬНО
class RoundAnimatingButton: UIButton {
    let animationDuration: NSTimeInterval

    func startAnimating() {
        let firstSubview = subviews.first
    }

}

// НЕ ЖЕЛАТЕЛЬНО
class RoundAnimating: UIButton {
    let aniDur: NSTimeInterval

    func srtAnmating() {
        let v = subviews.first
    }
}
  • 2.9 Включайте в имена информацию о типе данных, если он не очевиден.
// ПРЕДПОЧТИТЕЛЬНО
class ConnectionTableViewCell: UITableViewCell {
    let personImageView: UIImageView

    let animationDuration: TimeInterval

    // возможно использовать `Controller` вместо` ViewController` 
    let popupController: UIViewController
    let popupViewController: UIViewController

    // при работе с подклассом `UIViewController` таким как table view
    // controller, collection view controller, split view controller, и т.д.,
    // полностью укажите тип в имени.
    let popupTableViewController: UITableViewController

    // при работе с аутлетами будьте уверены, что их типы указаны в названии свойств. 
    @IBOutlet weak var submitButton: UIButton!
    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var nameLabel: UILabel!

}

// НЕ ЖЕЛАТЕЛЬНО
class ConnectionTableViewCell: UITableViewCell {
    // это не `UIImage`, поэтому нужно
    // использовать personImageView вместо этого
    let personImage: UIImageView

    // это не `String`, поэтому нужно использовать`textLabel`
    let text: UILabel

    // `animation` не является временным интервалом
    // используйте `animationDuration` или `animationTimeInterval` вместо этого
    let animation: TimeInterval

    // не очевидно, что это `String`
    // используйте `transitionText` или `transitionString` вместо этого
    let transition: String

    // это view controller, а не view
    let popupView: UIViewController

    // как упоминалось ранее мы не должны использовать сокращения
    // поэтому не нужно использовать`VC` вместо `ViewController`
    let popupVC: UIViewController

    // хотя технически это все еще `UIViewController`, это свойство
    // это свойство должно указывать, что мы работаем с *Table* View Controller
    let popupViewController: UITableViewController

    // для единообразия мы должны помещать 
    // имя типа в конец, но не в начало
    @IBOutlet weak var btnSubmit: UIButton!
    @IBOutlet weak var buttonSubmit: UIButton!

    // при работе с аутлетами у нас всегда должен быть тип в имени
    // для примера, тут мы должны использовать`firstNameLabel` вместо этого
    @IBOutlet weak var firstName: UILabel!
}
  • 2.10 При именовании аргументов функции убедитесь, что функция легко читается, что было понятно назначение каждого аргумента.

  • 2.11 Согласно Apple's API Design Guidelines, protocol должны именоваться существительными (например Collection) и используя суффиксы able, ible, или ing если он описывает возможность (например Equatable, ProgressReporting). Если не один из этих вариантов не подоходит, то мжете использовать суффикс Protocol к имени проткола. Несколько примеров ниже.

// здесь существительное, описываюшее, что делает протокол
protocol TableViewSectionProvider {
    func rowHeight(at row: Int) -> CGFloat
    var numberOfRows: Int { get }
    /* ... */
}

// здесь протокол это возможность и описываем соответствующим образом
protocol Loggable {
    func logCurrentState()
    /* ... */
}

// предположим, что у нас есть класс `InputTextView`, но также мы хотим, 
// чтобы протокол обобщал некоторые функции - поэтому тут уместно 
// использовать суффикс `Protocol`
protocol InputTextViewProtocol {
    func sendTrackingEvent()
    func inputText() -> String
    /* ... */
}

3. Стиль кода

  • 3.1.1 Используйте let вместо var везде где эт возможно.

  • 3.1.2 При трансформации одной коллекции в другую предпочтительней использовать map, filter, reduce, и т.д.

// ПРЕДПОЧТИТЕЛЬНО
let stringOfInts = [1, 2, 3].flatMap { String($0) }
// ["1", "2", "3"]

// НЕ ЖЕЛАТЕЛЬНО
var stringOfInts: [String] = []
for integer in [1, 2, 3] {
    stringOfInts.append(String(integer))
}

// ПРЕДПОЧТИТЕЛЬНО
let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 }
// [4, 8, 16, 42]

// НЕ ЖЕЛАТЕЛЬНО
var evenNumbers: [Int] = []
for integer in [4, 8, 15, 16, 23, 42] {
    if integer % 2 == 0 {
        evenNumbers.append(integer)
    }
}
  • 3.1.3 Предпочтительней не объявлять типы для константант и переменных.
// ПРЕДПОЧТИТЕЛЬНО
let number = 3

// НЕ ЖЕЛАТЕЛЬНО
let number: Int = 3
  • 3.1.4 Если функция возвращает несколько значений, предпочтительнее возвращать кортеж вместо использования аргументов inout. Если вы используете определенный кортеж более одного раза, подумайте об использовании typealias. Если вы возвращаете 3 или более элементов в кортеже, рассмотрите возможность использования вместо них struct или class.
func pirateName() -> (firstName: String, lastName: String) {
    return ("Guybrush", "Threepwood")
}

let name = pirateName()
let firstName = name.firstName
let lastName = name.lastName
  • 3.1.5 Будьте осторожны с циклами сильных ссылок при создании delegates/protocols для ваших классов classes; как правило их нужно объявлять weak.

  • 3.1.6 Будьте осторожны при вызове self напрямую из escaping closure так как это может вызвать цикл сильных ссылок - используйте capture list:

myFunctionWithEscapingClosure() { [weak self] (error) -> Void in
    // вы можете сделать это

    self?.doSomething()

    // или вы можете сделать это

    guard let strongSelf = self else {
        return
    }

    strongSelf.doSomething()
}
  • 3.1.7 Не заключайте в скобки сравнения.
// ПРЕДПОЧТИТЕЛЬНО
if x == y {
    /* ... */
}

// НЕ ЖЕЛАТЕЛЬНО
if (x == y) {
    /* ... */
}
  • 3.1.8 По возможности используйте значение enum вместе указания полной строки.
// ПРЕДПОЧТИТЕЛЬНО
imageView.setImageWithURL(url, type: .person)

// НЕ ЖЕЛАТЕЛЬНО
imageView.setImageWithURL(url, type: AsyncImageView.Type.person)
  • 3.1.9 Не используйте сокращения для методов классов.
// ПРЕДПОЧТИТЕЛЬНО
imageView.backgroundColor = UIColor.white

// НЕ ЖЕЛАТЕЛЬНО
imageView.backgroundColor = .white
  • 3.1.10 Предпочтительно не использовать self., кроме случаев где это необходимо.

  • 3.1.11 При написании методов подумайте, предназначен ли метод для переопределения или нет. Если нет, пометьте его как final, но имейте в виду, что это предотвратит перезапись метода в целях тестирования. В общем, методы final приводят к сокращению времени компиляции, поэтому рекомендуется использовать их, когда это применимо.

  • 3.1.12 Если у вас есть функция, которая не принимает аргументов, не имеет побочных эффектов и возвращает какой-либо объект или значение, лучше вместо этого использовать вычисляемое свойство.

About