├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Ⓒ LinkedIn Corporation 2016. Swift Style Guide is made available under a CC BY 4.0. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/legalcode. 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Style Guide 2 | 3 | Make sure to read [Apple's API Design Guidelines](https://swift.org/documentation/api-design-guidelines/). 4 | 5 | Specifics from these guidelines + additional remarks are mentioned below. 6 | 7 | This guide was last updated for Swift 4.0 on February 14, 2018. 8 | 9 | ## Table Of Contents 10 | 11 | - [Swift Style Guide](#swift-style-guide) 12 | - [1. Code Formatting](#1-code-formatting) 13 | - [2. Naming](#2-naming) 14 | - [3. Coding Style](#3-coding-style) 15 | - [3.1 General](#31-general) 16 | - [3.2 Access Modifiers](#32-access-modifiers) 17 | - [3.3 Custom Operators](#33-custom-operators) 18 | - [3.4 Switch Statements and `enum`s](#34-switch-statements-and-enums) 19 | - [3.5 Optionals](#35-optionals) 20 | - [3.6 Protocols](#36-protocols) 21 | - [3.7 Properties](#37-properties) 22 | - [3.8 Closures](#38-closures) 23 | - [3.9 Arrays](#39-arrays) 24 | - [3.10 Error Handling](#310-error-handling) 25 | - [3.11 Using `guard` Statements](#311-using-guard-statements) 26 | - [4. Documentation/Comments](#4-documentationcomments) 27 | - [4.1 Documentation](#41-documentation) 28 | - [4.2 Other Commenting Guidelines](#42-other-commenting-guidelines) 29 | 30 | ## 1. Code Formatting 31 | 32 | * **1.1** Use 4 spaces for tabs. 33 | * **1.2** Avoid uncomfortably long lines with a hard maximum of 160 characters per line (Xcode->Preferences->Text Editing->Page guide at column: 160 is helpful for this) 34 | * **1.3** Ensure that there is a newline at the end of every file. 35 | * **1.4** Ensure that there is no trailing whitespace anywhere (Xcode->Preferences->Text Editing->Automatically trim trailing whitespace + Including whitespace-only lines). 36 | * **1.5** For single-line statements, keep the opening braces on the same line. For multi-line statements, place the closing parentheses and opening braces on separate lines. This adheres to the default style defined by [swift-format](https://github.com/swiftlang/swift-format). 37 | 38 | ```swift 39 | class SomeClass { 40 | // For single-line statements, keep the opening braces on the same line. 41 | func someMethod(firstArgument: String, secondArgument: String) -> String { 42 | if x == y { 43 | /* ... */ 44 | } else if x == z { 45 | /* ... */ 46 | } else { 47 | /* ... */ 48 | } 49 | guard let firstInt = firstArgument.intValue else { 50 | return "..." 51 | } 52 | return "..." 53 | } 54 | 55 | // For multi-line statements, place the closing parentheses and opening braces on separate lines. 56 | func anotherMethod( 57 | firstArgument: String, 58 | ... 59 | lastArgument: String 60 | ) -> String { 61 | if let firstInt = firstArgument.intValue, 62 | ..., 63 | let lastInt = lastArgument.doubleValue 64 | { 65 | /* ... */ 66 | } else { 67 | /* ... */ 68 | } 69 | guard let firstInt = firstArgument.intValue, 70 | ..., 71 | let lastInt = lastArgument.doubleValue 72 | else { 73 | return "..." 74 | } 75 | return "..." 76 | } 77 | } 78 | ``` 79 | 80 | * **1.6** When writing a type for a property, constant, variable, a key for a dictionary, a function argument, a protocol conformance, or a superclass, don't add a space before the colon. 81 | 82 | ```swift 83 | // specifying type 84 | let pirateViewController: PirateViewController 85 | 86 | // dictionary syntax (note that we left-align as opposed to aligning colons) 87 | let ninjaDictionary: [String: AnyObject] = [ 88 | "fightLikeDairyFarmer": false, 89 | "disgusting": true 90 | ] 91 | 92 | // declaring a function 93 | func myFunction(firstArgument: U, secondArgument: T) where T.RelatedType == U { 94 | /* ... */ 95 | } 96 | 97 | // calling a function 98 | someFunction(someArgument: "Kitten") 99 | 100 | // superclasses 101 | class PirateViewController: UIViewController { 102 | /* ... */ 103 | } 104 | 105 | // protocols 106 | extension PirateViewController: UITableViewDataSource { 107 | /* ... */ 108 | } 109 | ``` 110 | 111 | * **1.7** In general, there should be a space following a comma. 112 | 113 | ```swift 114 | let myArray = [1, 2, 3, 4, 5] 115 | ``` 116 | 117 | * **1.8** There should be a space before and after a binary operator such as `+`, `==`, or `->`. There should also not be a space after a `(` and before a `)`. 118 | 119 | ```swift 120 | let myValue = 20 + (30 / 2) * 3 121 | if 1 + 1 == 3 { 122 | fatalError("The universe is broken.") 123 | } 124 | func pancake(with syrup: Syrup) -> Pancake { 125 | /* ... */ 126 | } 127 | ``` 128 | 129 | * **1.9** We follow Xcode's recommended indentation style (i.e. your code should not change if CTRL-I is pressed). For multi-line function declarations, use the syntax that [swift-format](https://github.com/swiftlang/swift-format) defaults to in Xcode 16.0 or later. 130 | 131 | ```swift 132 | // Swift-format indentation for a function declaration that spans multiple lines 133 | func myFunctionWithManyParameters( 134 | parameterOne: String, 135 | parameterTwo: String, 136 | parameterThree: String 137 | ) { 138 | // Xcode indents to here for this kind of statement 139 | print("\(parameterOne) \(parameterTwo) \(parameterThree)") 140 | } 141 | 142 | // Swift-format indentation for a multi-line `if` statement 143 | if myFirstValue > (mySecondValue + myThirdValue) 144 | && myFourthValue == .someEnumValue 145 | { 146 | // Xcode indents to here for this kind of statement 147 | print("Hello, World!") 148 | } 149 | ``` 150 | 151 | * **1.10** When calling a function that has many parameters, put each argument on a separate line with a single extra indentation. 152 | 153 | ```swift 154 | someFunctionWithManyArguments( 155 | firstArgument: "Hello, I am a string", 156 | secondArgument: resultFromSomeFunction(), 157 | thirdArgument: someOtherLocalProperty) 158 | ``` 159 | 160 | * **1.11** When dealing with an implicit array or dictionary large enough to warrant splitting it into multiple lines, treat the `[` and `]` as if they were braces in a method, `if` statement, etc. Closures in a method should be treated similarly. 161 | 162 | ```swift 163 | someFunctionWithABunchOfArguments( 164 | someStringArgument: "hello I am a string", 165 | someArrayArgument: [ 166 | "dadada daaaa daaaa dadada daaaa daaaa dadada daaaa daaaa", 167 | "string one is crazy - what is it thinking?", 168 | ], 169 | someDictionaryArgument: [ 170 | "dictionary key 1": "some value 1, but also some more text here", 171 | "dictionary key 2": "some value 2", 172 | ], 173 | someClosure: { parameter1 in 174 | print(parameter1) 175 | }) 176 | ``` 177 | 178 | * **1.12** Prefer using local constants or other mitigation techniques to avoid multi-line predicates where possible. 179 | 180 | ```swift 181 | // PREFERRED 182 | let firstCondition = x == firstReallyReallyLongPredicateFunction() 183 | let secondCondition = y == secondReallyReallyLongPredicateFunction() 184 | let thirdCondition = z == thirdReallyReallyLongPredicateFunction() 185 | if firstCondition && secondCondition && thirdCondition { 186 | // do something 187 | } 188 | 189 | // NOT PREFERRED 190 | if x == firstReallyReallyLongPredicateFunction() 191 | && y == secondReallyReallyLongPredicateFunction() 192 | && z == thirdReallyReallyLongPredicateFunction() 193 | { 194 | // do something 195 | } 196 | ``` 197 | 198 | ## 2. Naming 199 | 200 | * **2.1** There is no need for Objective-C style prefixing in Swift (e.g. use just `GuybrushThreepwood` instead of `LIGuybrushThreepwood`). 201 | 202 | * **2.2** Use `PascalCase` for type names (e.g. `struct`, `enum`, `class`, `typedef`, `associatedtype`, etc.). 203 | 204 | * **2.3** Use `camelCase` (initial lowercase letter) for function, method, property, constant, variable, argument names, enum cases, etc. 205 | 206 | * **2.4** When dealing with an acronym or other name that is usually written in all caps, actually use all caps in any names that use this in code. The exception is if this word is at the start of a name that needs to start with lowercase - in this case, use all lowercase for the acronym. 207 | 208 | ```swift 209 | // "HTML" is at the start of a constant name, so we use lowercase "html" 210 | let htmlBodyContent: String = "

Hello, World!

" 211 | // Prefer using ID to Id 212 | let profileID: Int = 1 213 | // Prefer URLFinder to UrlFinder 214 | class URLFinder { 215 | /* ... */ 216 | } 217 | ``` 218 | 219 | * **2.5** All constants that are instance-independent should be `static`. All such `static` constants should be placed in a marked section of their `class`, `struct`, or `enum`. For classes with many constants, you should group constants that have similar or the same prefixes, suffixes and/or use cases. 220 | 221 | ```swift 222 | // PREFERRED 223 | class MyClassName { 224 | // MARK: - Constants 225 | static let buttonPadding: CGFloat = 20.0 226 | static let indianaPi = 3 227 | static let shared = MyClassName() 228 | } 229 | 230 | // NOT PREFERRED 231 | class MyClassName { 232 | // Don't use `k`-prefix 233 | static let kButtonPadding: CGFloat = 20.0 234 | 235 | // Don't namespace constants 236 | enum Constant { 237 | static let indianaPi = 3 238 | } 239 | } 240 | ``` 241 | 242 | * **2.6** For generics and associated types, use a `PascalCase` word that describes the generic. If this word clashes with a protocol that it conforms to or a superclass that it subclasses, you can append a `Type` suffix to the associated type or generic name. 243 | 244 | ```swift 245 | class SomeClass { /* ... */ } 246 | protocol Modelable { 247 | associatedtype Model 248 | } 249 | protocol Sequence { 250 | associatedtype IteratorType: Iterator 251 | } 252 | ``` 253 | 254 | * **2.7** Names should be descriptive and unambiguous. 255 | 256 | ```swift 257 | // PREFERRED 258 | class RoundAnimatingButton: UIButton { /* ... */ } 259 | 260 | // NOT PREFERRED 261 | class CustomButton: UIButton { /* ... */ } 262 | ``` 263 | 264 | * **2.8** Do not abbreviate, use shortened names, or single letter names. 265 | 266 | ```swift 267 | // PREFERRED 268 | class RoundAnimatingButton: UIButton { 269 | let animationDuration: NSTimeInterval 270 | 271 | func startAnimating() { 272 | let firstSubview = subviews.first 273 | } 274 | 275 | } 276 | 277 | // NOT PREFERRED 278 | class RoundAnimating: UIButton { 279 | let aniDur: NSTimeInterval 280 | 281 | func srtAnmating() { 282 | let v = subviews.first 283 | } 284 | } 285 | ``` 286 | 287 | * **2.9** Include type information in constant or variable names when it is not obvious otherwise. 288 | 289 | ```swift 290 | // PREFERRED 291 | class ConnectionTableViewCell: UITableViewCell { 292 | let personImageView: UIImageView 293 | 294 | let animationDuration: TimeInterval 295 | 296 | // it is ok not to include string in the ivar name here because it's obvious 297 | // that it's a string from the property name 298 | let firstName: String 299 | 300 | // though not preferred, it is OK to use `Controller` instead of `ViewController` 301 | let popupController: UIViewController 302 | let popupViewController: UIViewController 303 | 304 | // when working with a subclass of `UIViewController` such as a table view 305 | // controller, collection view controller, split view controller, etc., 306 | // fully indicate the type in the name. 307 | let popupTableViewController: UITableViewController 308 | 309 | // when working with outlets, make sure to specify the outlet type in the 310 | // property name. 311 | @IBOutlet weak var submitButton: UIButton! 312 | @IBOutlet weak var emailTextField: UITextField! 313 | @IBOutlet weak var nameLabel: UILabel! 314 | 315 | } 316 | 317 | // NOT PREFERRED 318 | class ConnectionTableViewCell: UITableViewCell { 319 | // this isn't a `UIImage`, so shouldn't be called image 320 | // use personImageView instead 321 | let personImage: UIImageView 322 | 323 | // this isn't a `String`, so it should be `textLabel` 324 | let text: UILabel 325 | 326 | // `animation` is not clearly a time interval 327 | // use `animationDuration` or `animationTimeInterval` instead 328 | let animation: TimeInterval 329 | 330 | // this is not obviously a `String` 331 | // use `transitionText` or `transitionString` instead 332 | let transition: String 333 | 334 | // this is a view controller - not a view 335 | let popupView: UIViewController 336 | 337 | // as mentioned previously, we don't want to use abbreviations, so don't use 338 | // `VC` instead of `ViewController` 339 | let popupVC: UIViewController 340 | 341 | // even though this is still technically a `UIViewController`, this property 342 | // should indicate that we are working with a *Table* View Controller 343 | let popupViewController: UITableViewController 344 | 345 | // for the sake of consistency, we should put the type name at the end of the 346 | // property name and not at the start 347 | @IBOutlet weak var btnSubmit: UIButton! 348 | @IBOutlet weak var buttonSubmit: UIButton! 349 | 350 | // we should always have a type in the property name when dealing with outlets 351 | // for example, here, we should have `firstNameLabel` instead 352 | @IBOutlet weak var firstName: UILabel! 353 | } 354 | ``` 355 | 356 | * **2.10** When naming function arguments, make sure that the function can be read easily to understand the purpose of each argument. 357 | 358 | * **2.11** As per [Apple's API Design Guidelines](https://swift.org/documentation/api-design-guidelines/), a `protocol` should be named as nouns if they describe what something is doing (e.g. `Collection`) and using the suffixes `able`, `ible`, or `ing` if it describes a capability (e.g. `Equatable`, `ProgressReporting`). If neither of those options makes sense for your use case, you can add a `Protocol` suffix to the protocol's name as well. Some example `protocol`s are below. 359 | 360 | ```swift 361 | // here, the name is a noun that describes what the protocol does 362 | protocol TableViewSectionProvider { 363 | func rowHeight(at row: Int) -> CGFloat 364 | var numberOfRows: Int { get } 365 | /* ... */ 366 | } 367 | 368 | // here, the protocol is a capability, and we name it appropriately 369 | protocol Loggable { 370 | func logCurrentState() 371 | /* ... */ 372 | } 373 | 374 | // suppose we have an `InputTextView` class, but we also want a protocol 375 | // to generalize some of the functionality - it might be appropriate to 376 | // use the `Protocol` suffix here 377 | protocol InputTextViewProtocol { 378 | func sendTrackingEvent() 379 | func inputText() -> String 380 | /* ... */ 381 | } 382 | ``` 383 | 384 | ## 3. Coding Style 385 | 386 | ### 3.1 General 387 | 388 | * **3.1.1** Prefer `let` to `var` whenever possible. 389 | 390 | * **3.1.2** Prefer the composition of `map`, `filter`, `reduce`, etc. over iterating when transforming from one collection to another. Make sure to avoid using closures that have side effects when using these methods. 391 | 392 | ```swift 393 | // PREFERRED 394 | let stringOfInts = [1, 2, 3].flatMap { String($0) } 395 | // ["1", "2", "3"] 396 | 397 | // NOT PREFERRED 398 | var stringOfInts: [String] = [] 399 | for integer in [1, 2, 3] { 400 | stringOfInts.append(String(integer)) 401 | } 402 | 403 | // PREFERRED 404 | let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 } 405 | // [4, 8, 16, 42] 406 | 407 | // NOT PREFERRED 408 | var evenNumbers: [Int] = [] 409 | for integer in [4, 8, 15, 16, 23, 42] { 410 | if integer % 2 == 0 { 411 | evenNumbers.append(integer) 412 | } 413 | } 414 | ``` 415 | 416 | * **3.1.3** Prefer not declaring types for constants or variables if they can be inferred anyway. 417 | 418 | * **3.1.4** If a function returns multiple values, prefer returning a tuple to using `inout` arguments (it’s best to use labeled tuples for clarity on what you’re returning if it is not otherwise obvious). If you use a certain tuple more than once, consider using a `typealias`. If you’re returning 3 or more items in a tuple, consider using a `struct` or `class` instead. 419 | 420 | ```swift 421 | func pirateName() -> (firstName: String, lastName: String) { 422 | return ("Guybrush", "Threepwood") 423 | } 424 | 425 | let name = pirateName() 426 | let firstName = name.firstName 427 | let lastName = name.lastName 428 | ``` 429 | 430 | * **3.1.5** Be wary of retain cycles when creating delegates/protocols for your classes; typically, these properties should be declared `weak`. 431 | 432 | * **3.1.6** Be careful when calling `self` directly from an escaping closure as this can cause a retain cycle - use a [capture list](https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-XID_163) when this might be the case: 433 | 434 | ```swift 435 | myFunctionWithEscapingClosure() { [weak self] (error) -> Void in 436 | // you can do this 437 | 438 | self?.doSomething() 439 | 440 | // or you can do this 441 | 442 | guard let strongSelf = self else { 443 | return 444 | } 445 | 446 | strongSelf.doSomething() 447 | } 448 | ``` 449 | 450 | * **3.1.7** Don't use labeled breaks. 451 | 452 | * **3.1.8** Don't place parentheses around control flow predicates. 453 | 454 | ```swift 455 | // PREFERRED 456 | if x == y { 457 | /* ... */ 458 | } 459 | 460 | // NOT PREFERRED 461 | if (x == y) { 462 | /* ... */ 463 | } 464 | ``` 465 | 466 | * **3.1.9** Avoid writing out an `enum` type where possible - use shorthand. 467 | 468 | ```swift 469 | // PREFERRED 470 | imageView.setImageWithURL(url, type: .person) 471 | 472 | // NOT PREFERRED 473 | imageView.setImageWithURL(url, type: AsyncImageView.Type.person) 474 | ``` 475 | 476 | * **3.1.10** Don’t use shorthand for class methods since it is generally more difficult to infer the context from class methods as opposed to `enum`s. 477 | 478 | ```swift 479 | // PREFERRED 480 | imageView.backgroundColor = UIColor.white 481 | 482 | // NOT PREFERRED 483 | imageView.backgroundColor = .white 484 | ``` 485 | 486 | * **3.1.11** Prefer not writing `self.` unless it is required. 487 | 488 | * **3.1.12** When writing methods, keep in mind whether the method is intended to be overridden or not. If not, mark it as `final`, though keep in mind that this will prevent the method from being overwritten for testing purposes. In general, `final` methods result in improved compilation times, so it is good to use this when applicable. Be particularly careful, however, when applying the `final` keyword in a library since it is non-trivial to change something to be non-`final` in a library as opposed to have changing something to be non-`final` in your local project. 489 | 490 | * **3.1.13** When using a statement such as `else`, `catch`, etc. that follows a block, put this keyword on the same line as the block. Again, we are following the [1TBS style](https://en.m.wikipedia.org/wiki/Indentation_style#1TBS) here. Example `if`/`else` and `do`/`catch` code is below. 491 | 492 | ```swift 493 | if someBoolean { 494 | // do something 495 | } else { 496 | // do something else 497 | } 498 | 499 | do { 500 | let fileContents = try readFile("filename.txt") 501 | } catch { 502 | print(error) 503 | } 504 | ``` 505 | 506 | * **3.1.14** Prefer `static` to `class` when declaring a function or property that is associated with a class as opposed to an instance of that class. Only use `class` if you specifically need the functionality of overriding that function or property in a subclass, though consider using a `protocol` to achieve this instead. 507 | 508 | * **3.1.15** If you have a function that takes no arguments, has no side effects, and returns some object or value, prefer using a computed property instead. 509 | 510 | ### 3.2 Access Modifiers 511 | 512 | * **3.2.1** Write the access modifier keyword first if it is needed. 513 | 514 | ```swift 515 | // PREFERRED 516 | private static let myPrivateNumber: Int 517 | 518 | // NOT PREFERRED 519 | static private let myPrivateNumber: Int 520 | ``` 521 | 522 | * **3.2.2** The access modifier keyword should not be on a line by itself - keep it inline with what it is describing. 523 | 524 | ```swift 525 | // PREFERRED 526 | open class Pirate { 527 | /* ... */ 528 | } 529 | 530 | // NOT PREFERRED 531 | open 532 | class Pirate { 533 | /* ... */ 534 | } 535 | ``` 536 | 537 | * **3.2.3** In general, do not write the `internal` access modifier keyword since it is the default. 538 | 539 | * **3.2.4** If a property needs to be accessed by unit tests, you will have to make it `internal` to use `@testable import ModuleName`. If a property *should* be private, but you declare it to be `internal` for the purposes of unit testing, make sure you add an appropriate bit of documentation commenting that explains this. You can make use of the `- warning:` markup syntax for clarity as shown below. 540 | 541 | ```swift 542 | /** 543 | This property defines the pirate's name. 544 | - warning: Not `private` for `@testable`. 545 | */ 546 | let pirateName = "LeChuck" 547 | ``` 548 | 549 | * **3.2.5** Prefer `private` to `fileprivate` where possible. 550 | 551 | * **3.2.6** When choosing between `public` and `open`, prefer `open` if you intend for something to be subclassable outside of a given module and `public` otherwise. Note that anything `internal` and above can be subclassed in tests by using `@testable import`, so this shouldn't be a reason to use `open`. In general, lean towards being a bit more liberal with using `open` when it comes to libraries, but a bit more conservative when it comes to modules in a codebase such as an app where it is easy to change things in multiple modules simultaneously. 552 | 553 | ### 3.3 Custom Operators 554 | 555 | Prefer creating named functions to custom operators. 556 | 557 | If you want to introduce a custom operator, make sure that you have a *very* good reason why you want to introduce a new operator into global scope as opposed to using some other construct. 558 | 559 | You can override existing operators to support new types (especially `==`). However, your new definitions must preserve the semantics of the operator. For example, `==` must always test equality and return a boolean. 560 | 561 | ### 3.4 Switch Statements and `enum`s 562 | 563 | * **3.4.1** When using a switch statement that has a finite set of possibilities (`enum`), do *NOT* include a `default` case. Instead, place unused cases at the bottom and use the `break` keyword to prevent execution. 564 | 565 | * **3.4.2** Since `switch` cases in Swift break by default, do not include the `break` keyword if it is not needed. 566 | 567 | * **3.4.3** The `case` statements should line up with the `switch` statement itself as per default Swift standards. 568 | 569 | * **3.4.4** When defining a case that has an associated value, make sure that this value is appropriately labeled as opposed to just types (e.g. `case hunger(hungerLevel: Int)` instead of `case hunger(Int)`). 570 | 571 | ```swift 572 | enum Problem { 573 | case attitude 574 | case hair 575 | case hunger(hungerLevel: Int) 576 | } 577 | 578 | func handleProblem(problem: Problem) { 579 | switch problem { 580 | case .attitude: 581 | print("At least I don't have a hair problem.") 582 | case .hair: 583 | print("Your barber didn't know when to stop.") 584 | case .hunger(let hungerLevel): 585 | print("The hunger level is \(hungerLevel).") 586 | } 587 | } 588 | ``` 589 | 590 | * **3.4.5** Prefer lists of possibilities (e.g. `case 1, 2, 3:`) to using the `fallthrough` keyword where possible). 591 | 592 | * **3.4.6** If you have a default case that shouldn't be reached, preferably throw an error (or handle it some other similar way such as asserting). 593 | 594 | ```swift 595 | func handleDigit(_ digit: Int) throws { 596 | switch digit { 597 | case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9: 598 | print("Yes, \(digit) is a digit!") 599 | default: 600 | throw Error(message: "The given number was not a digit.") 601 | } 602 | } 603 | ``` 604 | 605 | ### 3.5 Optionals 606 | 607 | * **3.5.1** The only time you should be using implicitly unwrapped optionals is with `@IBOutlet`s. In every other case, it is better to use a non-optional or regular optional property. Yes, there are cases in which you can probably "guarantee" that the property will never be `nil` when used, but it is better to be safe and consistent. Similarly, don't use force unwraps. 608 | 609 | * **3.5.2** Don't use `as!` or `try!`. 610 | 611 | * **3.5.3** If you don't plan on actually using the value stored in an optional, but need to determine whether or not this value is `nil`, explicitly check this value against `nil` as opposed to using `if let` syntax. 612 | 613 | ```swift 614 | // PREFERRED 615 | if someOptional != nil { 616 | // do something 617 | } 618 | 619 | // NOT PREFERRED 620 | if let _ = someOptional { 621 | // do something 622 | } 623 | ``` 624 | 625 | * **3.5.4** Don't use `unowned`. You can think of `unowned` as somewhat of an equivalent of a `weak` property that is implicitly unwrapped (though `unowned` has slight performance improvements on account of completely ignoring reference counting). Since we don't ever want to have implicit unwraps, we similarly don't want `unowned` properties. 626 | 627 | ```swift 628 | // PREFERRED 629 | weak var parentViewController: UIViewController? 630 | 631 | // NOT PREFERRED 632 | weak var parentViewController: UIViewController! 633 | unowned var parentViewController: UIViewController 634 | ``` 635 | 636 | * **3.5.5** When unwrapping optionals, use the same name for the unwrapped constant or variable where appropriate. 637 | 638 | ```swift 639 | guard let myValue = myValue else { 640 | return 641 | } 642 | ``` 643 | 644 | * **3.5.6** Use `XCTUnwrap` instead of forced unwrapping in tests. 645 | ```swift 646 | func isEvenNumber(_ number: Int) -> Bool { 647 | return number % 2 == 0 648 | } 649 | 650 | // PREFERRED 651 | func testWithXCTUnwrap() throws { 652 | let number: Int? = functionThatReturnsOptionalNumber() 653 | XCTAssertTrue(isEvenNumber(try XCTUnwrap(number))) 654 | } 655 | 656 | // NOT PREFERRED 657 | func testWithForcedUnwrap() { 658 | let number: Int? = functionThatReturnsOptionalNumber() 659 | XCTAssertTrue(isEvenNumber(number!)) // may crash the simulator 660 | } 661 | ``` 662 | 663 | ### 3.6 Protocols 664 | 665 | When implementing protocols, there are two ways of organizing your code: 666 | 667 | 1. Using `// MARK:` comments to separate your protocol implementation from the rest of your code 668 | 2. Using an extension outside your `class`/`struct` implementation code, but in the same source file 669 | 670 | Keep in mind that when using an extension, however, the methods in the extension can't be overridden by a subclass, which can make testing difficult. If this is a common use case, it might be better to stick with method #1 for consistency. Otherwise, method #2 allows for cleaner separation of concerns. 671 | 672 | Even when using method #2, add `// MARK:` statements anyway for easier readability in Xcode's method/property/class/etc. list UI. 673 | 674 | ### 3.7 Properties 675 | 676 | * **3.7.1** If making a read-only, computed property, provide the getter without the `get {}` around it. 677 | 678 | ```swift 679 | var computedProperty: String { 680 | if someBool { 681 | return "I'm a mighty pirate!" 682 | } 683 | return "I'm selling these fine leather jackets." 684 | } 685 | ``` 686 | 687 | * **3.7.2** When using `get {}`, `set {}`, `willSet`, and `didSet`, indent these blocks. 688 | * **3.7.3** Though you can create a custom name for the new or old value for `willSet`/`didSet` and `set`, use the standard `newValue`/`oldValue` identifiers that are provided by default. 689 | 690 | ```swift 691 | var storedProperty: String = "I'm selling these fine leather jackets." { 692 | willSet { 693 | print("will set to \(newValue)") 694 | } 695 | didSet { 696 | print("did set from \(oldValue) to \(storedProperty)") 697 | } 698 | } 699 | 700 | var computedProperty: String { 701 | get { 702 | if someBool { 703 | return "I'm a mighty pirate!" 704 | } 705 | return storedProperty 706 | } 707 | set { 708 | storedProperty = newValue 709 | } 710 | } 711 | ``` 712 | 713 | * **3.7.4** You can declare a singleton property as follows: 714 | 715 | ```swift 716 | class PirateManager { 717 | static let shared = PirateManager() 718 | 719 | /* ... */ 720 | } 721 | ``` 722 | 723 | ### 3.8 Closures 724 | 725 | * **3.8.1** If the types of the parameters are obvious, it is OK to omit the type name, but being explicit is also OK. Sometimes readability is enhanced by adding clarifying detail and sometimes by taking repetitive parts away - use your best judgment and be consistent. 726 | 727 | ```swift 728 | // omitting the type 729 | doSomethingWithClosure() { response in 730 | print(response) 731 | } 732 | 733 | // explicit type 734 | doSomethingWithClosure() { response: NSURLResponse in 735 | print(response) 736 | } 737 | 738 | // using shorthand in a map statement 739 | [1, 2, 3].flatMap { String($0) } 740 | ``` 741 | 742 | * **3.8.2** If specifying a closure as a type, you don’t need to wrap it in parentheses unless it is required (e.g. if the type is optional or the closure is within another closure). Always wrap the arguments in the closure in a set of parentheses - use `()` to indicate no arguments and use `Void` to indicate that nothing is returned. 743 | 744 | ```swift 745 | let completionBlock: (Bool) -> Void = { (success) in 746 | print("Success? \(success)") 747 | } 748 | 749 | let completionBlock: () -> Void = { 750 | print("Completed!") 751 | } 752 | 753 | let completionBlock: (() -> Void)? = nil 754 | ``` 755 | 756 | * **3.8.3** Keep parameter names on same line as the opening brace for closures when possible without too much horizontal overflow (i.e. ensure lines are less than 160 characters). 757 | 758 | * **3.8.4** Use trailing closure syntax unless the meaning of the closure is not obvious without the parameter name (an example of this could be if a method has parameters for success and failure closures). 759 | 760 | ```swift 761 | // trailing closure 762 | doSomething(1.0) { (parameter1) in 763 | print("Parameter 1 is \(parameter1)") 764 | } 765 | 766 | // no trailing closure 767 | doSomething(1.0, success: { (parameter1) in 768 | print("Success with \(parameter1)") 769 | }, failure: { (parameter1) in 770 | print("Failure with \(parameter1)") 771 | }) 772 | ``` 773 | 774 | ### 3.9 Arrays 775 | 776 | * **3.9.1** In general, avoid accessing an array directly with subscripts. When possible, use accessors such as `.first` or `.last`, which are optional and won’t crash. Prefer using a `for item in items` syntax when possible as opposed to something like `for i in 0 ..< items.count`. If you need to access an array subscript directly, make sure to do proper bounds checking. You can use `for (index, value) in items.enumerated()` to get both the index and the value. 777 | 778 | * **3.9.2** Never use the `+=` or `+` operator to append/concatenate to arrays. Instead, use `.append()` or `.append(contentsOf:)` as these are far more performant (at least with respect to compilation) in Swift's current state. If you are declaring an array that is based on other arrays and want to keep it immutable, instead of `let myNewArray = arr1 + arr2`, use `let myNewArray = [arr1, arr2].joined()`. 779 | 780 | ### 3.10 Error Handling 781 | 782 | Suppose a function `myFunction` is supposed to return a `String`, however, at some point it can run into an error. A common approach is to have this function return an optional `String?` where we return `nil` if something went wrong. 783 | 784 | Example: 785 | 786 | ```swift 787 | func readFile(named filename: String) -> String? { 788 | guard let file = openFile(named: filename) else { 789 | return nil 790 | } 791 | 792 | let fileContents = file.read() 793 | file.close() 794 | return fileContents 795 | } 796 | 797 | func printSomeFile() { 798 | let filename = "somefile.txt" 799 | guard let fileContents = readFile(named: filename) else { 800 | print("Unable to open file \(filename).") 801 | return 802 | } 803 | print(fileContents) 804 | } 805 | ``` 806 | 807 | Instead, we should be using Swift's `try`/`catch` behavior when it is appropriate to know the reason for the failure. 808 | 809 | You can use a `struct` such as the following: 810 | 811 | ```swift 812 | struct Error: Swift.Error { 813 | public let file: StaticString 814 | public let function: StaticString 815 | public let line: UInt 816 | public let message: String 817 | 818 | public init(message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) { 819 | self.file = file 820 | self.function = function 821 | self.line = line 822 | self.message = message 823 | } 824 | } 825 | ``` 826 | 827 | Example usage: 828 | 829 | ```swift 830 | func readFile(named filename: String) throws -> String { 831 | guard let file = openFile(named: filename) else { 832 | throw Error(message: "Unable to open file named \(filename).") 833 | } 834 | 835 | let fileContents = file.read() 836 | file.close() 837 | return fileContents 838 | } 839 | 840 | func printSomeFile() { 841 | do { 842 | let fileContents = try readFile(named: filename) 843 | print(fileContents) 844 | } catch { 845 | print(error) 846 | } 847 | } 848 | ``` 849 | 850 | There are some exceptions in which it does make sense to use an optional as opposed to error handling. When the result should *semantically* potentially be `nil` as opposed to something going wrong while retrieving the result, it makes sense to return an optional instead of using error handling. 851 | 852 | In general, if a method can "fail", and the reason for the failure is not immediately obvious if using an optional return type, it probably makes sense for the method to throw an error. 853 | 854 | ### 3.11 Using `guard` Statements 855 | 856 | * **3.11.1** In general, we prefer to use an "early return" strategy where applicable as opposed to nesting code in `if` statements. Using `guard` statements for this use-case is often helpful and can improve the readability of the code. 857 | 858 | ```swift 859 | // PREFERRED 860 | func eatDoughnut(at index: Int) { 861 | guard index >= 0 && index < doughnuts.count else { 862 | // return early because the index is out of bounds 863 | return 864 | } 865 | 866 | let doughnut = doughnuts[index] 867 | eat(doughnut) 868 | } 869 | 870 | // NOT PREFERRED 871 | func eatDoughnut(at index: Int) { 872 | if index >= 0 && index < doughnuts.count { 873 | let doughnut = doughnuts[index] 874 | eat(doughnut) 875 | } 876 | } 877 | ``` 878 | 879 | * **3.11.2** When unwrapping optionals, prefer `guard` statements as opposed to `if` statements to decrease the amount of nested indentation in your code. 880 | 881 | ```swift 882 | // PREFERRED 883 | guard let monkeyIsland = monkeyIsland else { 884 | return 885 | } 886 | bookVacation(on: monkeyIsland) 887 | bragAboutVacation(at: monkeyIsland) 888 | 889 | // NOT PREFERRED 890 | if let monkeyIsland = monkeyIsland { 891 | bookVacation(on: monkeyIsland) 892 | bragAboutVacation(at: monkeyIsland) 893 | } 894 | 895 | // EVEN LESS PREFERRED 896 | if monkeyIsland == nil { 897 | return 898 | } 899 | bookVacation(on: monkeyIsland!) 900 | bragAboutVacation(at: monkeyIsland!) 901 | ``` 902 | 903 | * **3.11.3** When deciding between using an `if` statement or a `guard` statement when unwrapping optionals is *not* involved, the most important thing to keep in mind is the readability of the code. There are many possible cases here, such as depending on two different booleans, a complicated logical statement involving multiple comparisons, etc., so in general, use your best judgement to write code that is readable and consistent. If you are unsure whether `guard` or `if` is more readable or they seem equally readable, prefer using `guard`. 904 | 905 | ```swift 906 | // an `if` statement is readable here 907 | if operationFailed { 908 | return 909 | } 910 | 911 | // a `guard` statement is readable here 912 | guard isSuccessful else { 913 | return 914 | } 915 | 916 | // double negative logic like this can get hard to read - i.e. don't do this 917 | guard !operationFailed else { 918 | return 919 | } 920 | ``` 921 | 922 | * **3.11.4** If choosing between two different states, it makes more sense to use an `if` statement as opposed to a `guard` statement. 923 | 924 | ```swift 925 | // PREFERRED 926 | if isFriendly { 927 | print("Hello, nice to meet you!") 928 | } else { 929 | print("You have the manners of a beggar.") 930 | } 931 | 932 | // NOT PREFERRED 933 | guard isFriendly else { 934 | print("You have the manners of a beggar.") 935 | return 936 | } 937 | 938 | print("Hello, nice to meet you!") 939 | ``` 940 | 941 | * **3.11.5** You should also use `guard` only if a failure should result in exiting the current context. Below is an example in which it makes more sense to use two `if` statements instead of using two `guard`s - we have two unrelated conditions that should not block one another. 942 | 943 | ```swift 944 | if let monkeyIsland = monkeyIsland { 945 | bookVacation(onIsland: monkeyIsland) 946 | } 947 | 948 | if let woodchuck = woodchuck, canChuckWood(woodchuck) { 949 | woodchuck.chuckWood() 950 | } 951 | ``` 952 | 953 | * **3.11.6** Often, we can run into a situation in which we need to unwrap multiple optionals using `guard` statements. In general, combine unwraps into a single `guard` statement if handling the failure of each unwrap is identical (e.g. just a `return`, `break`, `continue`, `throw`, or some other `@noescape`). 954 | 955 | ```swift 956 | // combined because we just return 957 | guard let thingOne = thingOne, 958 | let thingTwo = thingTwo, 959 | let thingThree = thingThree 960 | else { 961 | return 962 | } 963 | 964 | // separate statements because we handle a specific error in each case 965 | guard let thingOne = thingOne else { 966 | throw Error(message: "Unwrapping thingOne failed.") 967 | } 968 | 969 | guard let thingTwo = thingTwo else { 970 | throw Error(message: "Unwrapping thingTwo failed.") 971 | } 972 | 973 | guard let thingThree = thingThree else { 974 | throw Error(message: "Unwrapping thingThree failed.") 975 | } 976 | ``` 977 | 978 | * **3.11.7** Don’t use one-liners for `guard` statements. 979 | 980 | 981 | ```swift 982 | // PREFERRED 983 | guard let thingOne = thingOne else { 984 | return 985 | } 986 | 987 | // NOT PREFERRED 988 | guard let thingOne = thingOne else { return } 989 | ``` 990 | 991 | ## 4. Documentation/Comments 992 | 993 | ### 4.1 Documentation 994 | 995 | If a function is more complicated than a simple O(1) operation, you should generally consider adding a doc comment for the function since there could be some information that the method signature does not make immediately obvious. If there are any quirks to the way that something was implemented, whether technically interesting, tricky, not obvious, etc., this should be documented. Documentation should be added for complex classes/structs/enums/protocols and properties. All `public` functions/classes/properties/constants/structs/enums/protocols/etc. should be documented as well (provided, again, that their signature/name does not make their meaning/functionality immediately obvious). 996 | 997 | After writing a doc comment, you should option click the function/property/class/etc. to make sure that everything is formatted correctly. 998 | 999 | Be sure to check out the full set of features available in Swift's comment markup [described in Apple's Documentation](https://developer.apple.com/library/tvos/documentation/Xcode/Reference/xcode_markup_formatting_ref/Attention.html#//apple_ref/doc/uid/TP40016497-CH29-SW1). 1000 | 1001 | Guidelines: 1002 | 1003 | * **4.1.1** 160 character column limit (like the rest of the code). 1004 | 1005 | * **4.1.2** Even if the doc comment takes up one line, use block (`/** */`). 1006 | 1007 | * **4.1.3** Do not prefix each additional line with a `*`. 1008 | 1009 | * **4.1.4** Use the new `- parameter` syntax as opposed to the old `:param:` syntax (make sure to use lower case `parameter` and not `Parameter`). Option-click on a method you wrote to make sure the quick help looks correct. 1010 | 1011 | ```swift 1012 | class Human { 1013 | /** 1014 | This method feeds a certain food to a person. 1015 | 1016 | - parameter food: The food you want to be eaten. 1017 | - parameter person: The person who should eat the food. 1018 | - returns: True if the food was eaten by the person; false otherwise. 1019 | */ 1020 | func feed(_ food: Food, to person: Human) -> Bool { 1021 | // ... 1022 | } 1023 | } 1024 | ``` 1025 | 1026 | * **4.1.5** If you’re going to be documenting the parameters/returns/throws of a method, document all of them, even if some of the documentation ends up being somewhat repetitive (this is preferable to having the documentation look incomplete). Sometimes, if only a single parameter warrants documentation, it might be better to just mention it in the description instead. 1027 | 1028 | * **4.1.6** For complicated classes, describe the usage of the class with some potential examples as seems appropriate. Remember that markdown syntax is valid in Swift's comment docs. Newlines, lists, etc. are therefore appropriate. 1029 | 1030 | ```swift 1031 | /** 1032 | ## Feature Support 1033 | 1034 | This class does some awesome things. It supports: 1035 | 1036 | - Feature 1 1037 | - Feature 2 1038 | - Feature 3 1039 | 1040 | ## Examples 1041 | 1042 | Here is an example use case indented by four spaces because that indicates a 1043 | code block: 1044 | 1045 | let myAwesomeThing = MyAwesomeClass() 1046 | myAwesomeThing.makeMoney() 1047 | 1048 | ## Warnings 1049 | 1050 | There are some things you should be careful of: 1051 | 1052 | 1. Thing one 1053 | 2. Thing two 1054 | 3. Thing three 1055 | */ 1056 | class MyAwesomeClass { 1057 | /* ... */ 1058 | } 1059 | ``` 1060 | 1061 | * **4.1.7** When mentioning code, use code ticks - \` 1062 | 1063 | ```swift 1064 | /** 1065 | This does something with a `UIViewController`, perchance. 1066 | - warning: Make sure that `someValue` is `true` before running this function. 1067 | */ 1068 | func myFunction() { 1069 | /* ... */ 1070 | } 1071 | ``` 1072 | 1073 | * **4.1.8** When writing doc comments, prefer brevity where possible. 1074 | 1075 | ### 4.2 Other Commenting Guidelines 1076 | 1077 | * **4.2.1** Always leave a space after `//`. 1078 | * **4.2.2** Always leave comments on their own line. 1079 | * **4.2.3** When using `// MARK: - whatever`, leave a newline after the comment. 1080 | 1081 | ```swift 1082 | class Pirate { 1083 | 1084 | // MARK: - instance properties 1085 | 1086 | private let pirateName: String 1087 | 1088 | // MARK: - initialization 1089 | 1090 | init() { 1091 | /* ... */ 1092 | } 1093 | 1094 | } 1095 | ``` 1096 | --------------------------------------------------------------------------------