- 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.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.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 Если у вас есть функция, которая не принимает аргументов, не имеет побочных эффектов и возвращает какой-либо объект или значение, лучше вместо этого использовать вычисляемое свойство.