└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # Ultimate Swift Code Style Guidelines
2 |
3 | The main purpose of these guidelines is to determine specific rules, following which may make an engineers's code have a unified style – which improves readability and maintainability, hence less fallibility. Most of the other existing similar guidelines don't provide exhaustive requirements on any code style aspect, or aren't sufficiently accurate, or have their own purposes which don't correlate with the general software development. This document is a try to create one that does.
4 |
5 | An engineer is free to use these guidelines, changing any rules according to their wish. However, if any rule is broken, it must be "broken" the same way across the whole project. I.e., any broken rule shall be replaced by another.
6 |
7 | One handy way to base own guidelines on these ones is to fork the repository and change any rule. However, if an engineer feels that something is completely wrong and should be fixed, they are free to fork and create a pull-request for initiating a discussion.
8 |
9 |
Table of Сontents
10 |
11 | ## 1. [Guidelines](#guidelines)
12 |
13 | ### 1.1. [Project](#project)
14 |
15 | #### 1.1.1. [Files and Directories](#files-and-directories)
16 |
17 | #### 1.1.2. [CVS](#cvs)
18 |
19 | ### 1.2. [File Structure](#file-structure)
20 |
21 | #### 1.2.1. [Imports](#imports)
22 |
23 | #### 1.2.2. [Members' Order](#members-order)
24 |
25 | #### 1.2.3. [Protocol Conformances](#protocol-conformances)
26 |
27 | ### 1.3. [Explicit Documentation](#explicit-documentation)
28 |
29 | #### 1.3.1. [Comments](#comments)
30 |
31 | ### 1.4. [Naming](#naming)
32 |
33 | #### 1.4.1. [Types](#types)
34 |
35 | #### 1.4.2. [Methods](#methods)
36 |
37 | ### 1.5. [API](#api)
38 |
39 | #### 1.5.1. [Encapsulation](#encapsulation)
40 |
41 | ### 1.6. [Implementation](#implementation)
42 |
43 | #### 1.6.1. [Methods](#implementation-methods)
44 |
45 | #### 1.6.2. [Optionality](#optionality)
46 |
47 | #### 1.6.3. [State and Side-Effects](#state-and-side-effects)
48 |
49 | #### 1.6.4. [Testing](#testing)
50 |
51 | ### 1.7. [Formatting](#formatting)
52 |
53 | #### 1.7.1. [Methods](#formatting-methods)
54 |
55 | #### 1.7.2. [Closures](#closures)
56 |
57 | #### 1.7.3. [Control Flow](#control-flow)
58 |
59 | #### 1.7.4. [Literals](#literals)
60 |
61 | #### 1.7.5. [Constants](#constants)
62 |
63 | #### 1.7.6. [Colons](#colons)
64 |
65 | #### 1.7.7. [Pre-processor Directives](#macros)
66 |
67 | ### 1.8. [Miscellaneous](#miscellaneous)
68 |
69 | ## 2. [Links](#links)
70 |
71 | Guidelines
72 |
73 | Project
74 |
75 | Projects don't tolerate any warnings.
76 |
77 | Type, function, and names, documentation, comments – everything, shall be written in American English.
78 |
79 | [Return to Table of Contents](#table-of-contents)
80 |
81 | Files and Directories
82 |
83 | All sub-folders and files in a folder or a project section are sorted alphabetically; first directories, then files.
84 |
85 | Files sorting doesn't consider files' extensions. However, an engineer is strongly encouraged to name files to have files which are close to each other semantically, allowing them to be close to each other in the list.
86 |
87 | Files are called after their containing types, or the primary one in the case of multiple type declarations.
88 |
89 | If the primary content of the file is a type extension, the file shall be named after this type, optionally concatenating a `+` sign and conformance protocol name (e.g. `String+ConvertibleToNumber.swift`). It's acceptable to name a file without a protocol name if the file contains multiple protocol conformances or helper and convenience methods (e.g. `String.swift`).
90 |
91 | [Return to Table of Contents](#table-of-contents)
92 |
93 | CVS
94 |
95 | _Not going deeply into the topic, since each CVS has its own guidelines._
96 |
97 | Every commit is atomic and presents a meaningful and working state of the project, including documentation and tests. Checking out at any commit results in a working software. Considering these two rules, an engineer shall think thoroughly about implementation phases, or refactoring steps.
98 |
99 | Commit names are formulated in the imperative case ("Do the thing"). Comprehensive details are optional and separated by a blank line if present.
100 |
101 | Any temporary code, debug console output, and such shall never be committed.
102 |
103 | [Return to Table of Contents](#table-of-contents)
104 |
105 | File Structure
106 |
107 | File is divided into sections by `// MARK:` comments. Root declarations (e.g. two classes on the same level) are separated by `// MARK: -`. Declarations within the same group of a type are gathered together in sections, like `// MARK: - Methods`. Multiple declarations within a group – like `// MARK: Private methods`. This type of comments (as well as any other) is kept close to grammatically correct language.
108 |
109 | **Don't:**
110 | ```swift
111 | //MARK:
112 |
113 | //MARK :
114 |
115 | // MARK: -Methods
116 | ```
117 |
118 | Indentation shall be made with whitespaces, because tab symbols can be represented differently in different environments. One level of indentation is four whitespaces. Two whitespaces is an acceptable option.
119 |
120 | Braces should be weighted: If there's a whitespace after the opening brace, there always must be one before the closing brace. Type declarations shall always have such whitespaces coming with braces, methods shall not.
121 |
122 | Methods with implementation shall be always separated by an empty line. Method declarations and any properties shall not.
123 |
124 | Files shall end with a single blank line.
125 |
126 | *Examples of file structure:*
127 |
128 | **Do**:
129 |
130 | ```swift
131 | import GatewayFoundation
132 | import Foundation
133 |
134 | protocol GatewayServiceDelegate {
135 |
136 | // MARK: - Methods
137 |
138 | func dataUpdated()
139 |
140 | }
141 |
142 | // MARK: -
143 |
144 | protocol GatewayService {
145 |
146 | // MARK: - Properties
147 |
148 | var delegate: GatewayServiceDelegate? { get set }
149 | var active: Bool { get }
150 | var data: [GatewayDataObject] { get }
151 |
152 | // MARK: - Methods
153 |
154 | func connect()
155 | func disconnect()
156 |
157 | }
158 |
159 | // MARK: -
160 |
161 | final class GatewayStandartService: GatewayService {
162 |
163 | enum GatewayType {
164 |
165 | case demo
166 | case stage
167 |
168 | }
169 |
170 | // MARK: - Properties
171 |
172 | // MARK: GatewayService protocol properties
173 |
174 | var active: Bool { server.active }
175 | var data: [GatewayDataObject] { server.data }
176 | weak var delegate: GatewayServiceDelegate?
177 |
178 | // MARK: Private properties
179 |
180 | private static let gatewayStandardAddress = URL(string: "https://standard.gateway.edu/")!
181 | private let serviceType: GatewayType
182 | private lazy var server: DataServer {
183 | let server = DataServer(serviceType: serviceType)
184 | server.delegate = self
185 |
186 | return server
187 | }
188 |
189 | // MARK: - Initialization
190 |
191 | init(serviceType: GatewayType = .stage) {
192 | self.serviceType = serviceType
193 | }
194 |
195 | // MARK: - Methods
196 |
197 | // MARK: GatewayService protocol methods
198 |
199 | func connect() {
200 | try? server.establishConnection()
201 | }
202 |
203 | func disconnect() {
204 | server.breakConnection()
205 | }
206 |
207 | }
208 |
209 | // MARK: - DataServerDelegate
210 |
211 | extension GatewayStandartService: DataServerDelegate {
212 |
213 | // MARK: - Methods
214 |
215 | // MARK: GatewayStandartService protocol methods
216 |
217 | func connectionEstablished() {
218 | notifyDelegate()
219 | }
220 |
221 | func dataUpdated() {
222 | notifyDelegate()
223 | }
224 |
225 | // MARK: Private methods
226 |
227 | private func notifyDelegate() {
228 | delegete?.dataUpdated()
229 | }
230 |
231 | }
232 | ```
233 |
234 | **Don't**:
235 |
236 | ```swift
237 | import Foundation // FIXME: Wrong imports order
238 | import GatewayFoundation
239 | import UIKit // FIXME: Unused import
240 | import Darwin // FIXME: Excessive import – Foundation includes Darwin.
241 |
242 |
243 | // FIXME: Extra empty line
244 | protocol GatewayServiceDelegate { // FIXME: Absent section dividers
245 |
246 | func dataUpdated() // FIXME: Broken indentation
247 |
248 | }
249 | protocol GatewayService { // FIXME: Absent file structural divider and an empty line
250 |
251 | // MARK: - Properties
252 |
253 | var active: Bool { get }
254 | var data: [GatewayDataObject] { get }
255 | var delegate: GatewayServiceDelegate? { get set } // FIXME: Less strict accessable properties should go first.
256 |
257 | // MARK: - Methods
258 |
259 | func connect()
260 | func disconnect()
261 | } // FIXME: Absent empty line before type declaration closing brace
262 |
263 | // MARK: -
264 |
265 | final class GatewayStandartService: GatewayService, DataServerDelegate { // FIXME: Additional conformances should be declared in extensions.
266 |
267 | // MARK: - Properties
268 |
269 | // MARK: GatewayService protocol properties
270 |
271 | private static let gatewayStandardAddress: URL = URL(string: "https://standard.gateway.edu/")! // FIXME: Private and "public" members are mixed.
272 | private let serviceType: GatewayType
273 | weak var delegate: GatewayServiceDelegate?
274 | var data: [GatewayDataObject] {
275 | return server.data // FIXME: Single statements shall not use the "return" keyword.
276 | }
277 | var active: Bool { // FIXME: Properties of the same access level should be sorted alphabetically.
278 | server.active // FIXME: Single statement scopes are preferred to be placed on a single line with both braces.
279 | }
280 | private lazy var server: DataServer {
281 | let server = DataServer(serviceType: serviceType)
282 | server.delegate = self
283 |
284 | return server
285 | }
286 |
287 | // MARK: - Initialization
288 |
289 | init(serviceType: GatewayType = .stage) {
290 | self.serviceType = serviceType
291 | }
292 |
293 | // MARK: - Methods
294 |
295 | // MARK: GatewayService protocol methods
296 |
297 | func connect() {
298 | try? server.establishConnection()
299 | }
300 |
301 | func disconnect() {
302 |
303 | server.breakConnection()
304 |
305 | } // FIXME: Empty lines in the beginning and the end of the method.
306 |
307 | // MARK: GatewayStandartService protocol methods
308 |
309 | func connectionEstablished() {
310 | notifyDelegate()
311 | }
312 |
313 | func dataUpdated() {
314 | notifyDelegate()
315 | }
316 |
317 | // MARK: Private methods
318 |
319 | private func notifyDelegate() {
320 | delegete?.dataUpdated()
321 | }
322 |
323 | enum GatewayType { // FIXME: Nested types accessible from the outside should go first.
324 | case demo
325 | case stage
326 | } // FIXME: No whitespaces after the opening and before the closing brace.
327 |
328 | }
329 | ```
330 |
331 | Here's another short example of good file structure, a SwiftUI View.
332 |
333 | **Do**:
334 |
335 | ```swift
336 | import SwiftUI
337 |
338 | struct ContentView: View {
339 |
340 | // MARK: - Properties
341 |
342 | // MARK: View protocol properties
343 |
344 | var body: some View {
345 | VStack {
346 | Text("Hello, \(name)!")
347 | Text("And have a nice day.")
348 | }
349 | .frame(maxWidth: 200.0)
350 | }
351 |
352 | // MARK: Private properties
353 |
354 | @State
355 | private var name = "Steve"
356 |
357 | }
358 | ```
359 |
360 | File length doesn't have a specific line number limitation, since basically, code shall respect contemporary software development principles (mainly, S.O.L.I.D., but also D.R.Y., K.I.S.S., Y.A.G.N.I., and so on.) Following the principles won't let an engineer create an unacceptably long file. However, if an engineer isn't experienced and doesn't have an experienced reviewer, the number of 200 lines (including all formatting and comments) could be used for guidance. Though, this doesn't mean that well organized files of the length of 201 or even 300 lines are necessarily evil – an engineer shall be guided by their common sense.
361 |
362 | [Return to Table of Contents](#table-of-contents)
363 |
364 | Imports
365 |
366 | All imports go in the very beginning of the file and sorted lexicographically. Empty lines between imports are not used.
367 |
368 | More specific imports (like `Darwin`) are preferable over less specific (like `Foundation`) to keep namespace cleaner and probably, the resulting build thinner.
369 |
370 | More specific imports (like `Foundation`) are not used explicitly if less specific ones (like `UIKit`) are also used.
371 |
372 | [Return to Table of Contents](#table-of-contents)
373 |
374 | Members' Order
375 |
376 | Properties within a subgroup, as well as enum cases, shall be ordered, firstly, by its access modifier, then by the class/instance membership type, then lexicographically.
377 |
378 | Logical sorting is acceptable, but not encouraged and may be used only if all neighboring declarations can be sorted logically. Partially logical and partially lexicographical ordering is not used.
379 |
380 | Root declarations start from the beginning of the line. Each level of nesting adds a step of indentation.
381 |
382 | Public and internal methods shall be sorted in a logical order. It's always possible to decide on one for methods. For example, by remembering the type's lifecycle and methods' calling order.
383 |
384 | **Do**:
385 |
386 | ```swift
387 | func loadView() {
388 | // ...
389 | }
390 |
391 | func viewDidLoad() {
392 | // ...
393 | }
394 |
395 | func viewWillLayoutSubviews() {
396 | // ...
397 | }
398 |
399 | func viewDidLayoutSubviews() {
400 | // ...
401 | }
402 |
403 | func viewWillAppear() {
404 | // ...
405 | }
406 |
407 | func viewDidAppear() {
408 | // ...
409 | }
410 |
411 | func viewWillDisappear() {
412 | // ...
413 | }
414 |
415 | func viewDidDisappear() {
416 | // ...
417 | }
418 |
419 | ```
420 |
421 | Private methods are sorted by their first mention (mentioned earlier go first).
422 |
423 | Mixing public and private APIs shall be strictly avoided. However, placing methods in order of their usage and closer to the first calling (i.e., "as an engineer would read" the source file in order to understand the flow) is acceptable.
424 |
425 | [Return to Table of Contents](#table-of-contents)
426 |
427 |
428 |
429 | Protocol conformances are added within separate extensions unless it's the main role of the type.
430 |
431 | **Do**:
432 |
433 | ```swift
434 | protocol Presenter {
435 | // ...
436 | }
437 |
438 | struct DefaultPresenter: Presenter {
439 | // ...
440 | }
441 | ```
442 |
443 | ```swift
444 | protocol Presentable {
445 | // ...
446 | }
447 |
448 | final class ViewController: UIViewController {
449 | // ...
450 | }
451 |
452 | extension ViewController: Presentable {
453 | // ...
454 | }
455 | ```
456 |
457 | **Don't**:
458 |
459 | ```swift
460 | protocol Presenter {
461 | // ...
462 | }
463 |
464 | struct DefaultPresenter {
465 | // ...
466 | }
467 |
468 | extension DefaultPresenter: Presenter {
469 | // ...
470 | }
471 | ```
472 |
473 | Protocol extensions go after the main declaration and are separated by a ```// MARK: -``` comment with the protocol name. It results in a nice "table of contents" in Xcode's file navigator:
474 |
475 | **Do**:
476 |
477 | ```swift
478 | struct SomeType {
479 |
480 | // ...
481 |
482 | }
483 |
484 | // MARK: - DifferentProtocol
485 |
486 | extension SomeType: DifferentProtocol {
487 |
488 | // MARK: - Methods
489 |
490 | // MARK: DifferentProtocol protocol methods
491 |
492 | // ...
493 |
494 | }
495 | ```
496 |
497 | [Return to Table of Contents](#table-of-contents)
498 |
499 | Explicit Documentation
500 |
501 | Each non-private API should have a [HeaderDoc](https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/HeaderDoc/intro/intro.html) comment, even if the API is well self-documented. The main assignment of having exhaustive documentation of public APIs is to be visible from different code base places through IDE's help features, such as quick help popovers.
502 |
503 | Private APIs should be self-documented and have no explicit documentation in order not to distract from reading implementation details.
504 |
505 | Indicating a method's time complexity in documentation generally is a good idea, unless irrelevant.
506 |
507 | The basic structure of multi-line descriptions:
508 |
509 | **Do:**
510 |
511 | ```swift
512 | /// Summary.
513 | ///
514 | /// Detailed description.
515 | ///
516 | /// - Parameters:
517 | /// - parameter parameter1: Parameter description.
518 | /// - parameter parameter2: Parameter description.
519 | /// - Returns: Return value description.
520 | /// - Throws: Exception description.
521 | ```
522 |
523 | Multi-line documentation put between `/**` and `*/` is not used, because the former way is generated by Xcode by default and it's shorter.
524 |
525 | **Don't:**
526 |
527 | ```swift
528 | /**
529 | Summary.
530 |
531 | Detailed description.
532 |
533 | - Parameter parameter1: Parameter description.
534 | - Parameter parameter2: Parameter description.
535 | - returns: Return value description.
536 | - Throws: Exception description.
537 | */
538 | ```
539 |
540 | Everything but summary is optional. The order shall be:
541 | - Summary
542 | - Discussion
543 | - `Parameter`/`Parameters`
544 | - `Returns`
545 | - `Throws`
546 |
547 | Though, if a method has any parameters, they are necessary to be documented. A single parameter must be documented as `- Parameter: Description.` Multiple parameters shall be documented by the help of `- Parameters:` syntax. If the method has functions as parameters, their arguments must have labels and be documented, as well.
548 |
549 | If a method's return value type is not `Void`, it should be documented.
550 |
551 | If a method throws an exception, the exception is documented with `Throws`.
552 |
553 | Two HeaderDoc parts are divided by a single blank line. However, if there's no description, blank lines are omitted:
554 |
555 | **Do:**
556 |
557 | ```swift
558 | // Summary.
559 | // - Parameter parameter: Parameter description.
560 | // - Returns: Return value description.
561 | // - Throws: Exception description.
562 | ```
563 |
564 | **Don't:**
565 |
566 | ```swift
567 | // Summary.
568 | //
569 | // - Parameter parameter: Parameter description.
570 | //
571 | // - Returns: Return value description.
572 | //
573 | // - Throws: Exception description.
574 | ```
575 |
576 | Links, code snippets, and such shall make use of the [Apple Markup](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/) syntax.
577 |
578 | Links to related methods are encouraged and implemented by means of tags. That is, the referenced method is marked with `- Tag: SomeTag` and the referencing method uses it as this: `[click here](x-source-tag://SomeTag)`.
579 |
580 | There's no blank lines between a documentation comment and the documented member.
581 |
582 | [Return to Table of Contents](#table-of-contents)
583 |
584 |
585 |
586 | Self-documented code is preferred over comments.
587 |
588 | If a commented code is changed, the comments shall be either updated or deleted.
589 |
590 | One-line comments start with `\\` (although `\\\` makes comments look "fancier" in IDE, it's reserved for HeaderDoc.)
591 |
592 | Multi-line comments start with `\*` and end with `*\`. Comment delimiters are placed on separate lines. There's no blank lines after symbols denoting the beginning of comment and before ones at the end.
593 |
594 | Comments, both single- and multi-line, are placed on the line above the commented code and separated from a preceding code by a blank line. The latter is not relevant if a comment is the first line of the scope. Also, it's acceptable to place one-line comments at the end of the commented line of code, separating by a space, if and only if the comment doesn't make this line too long.
595 |
596 | [Return to Table of Contents](#table-of-contents)
597 |
598 | Naming
599 |
600 | All declarations should be self-explanatory and consist of American English words and form correct human-readable phrases.
601 |
602 | Everything but types is `lowerCamelCase`. The types are `UpperCamelCase` (a.k.a. `PascalCase`.)
603 |
604 | Abbreviations don't have different cases within them and are cased up or down according to the rule above (e.g., `let asciiTable: [String : String]`, `class SMTPServer`). (A common mistake is to use `Id` instead of `ID` – a convention, common in Java.)
605 |
606 | No underscores, even at the beginning of property names, shall be used. No Hungarian notation for constants (e.g., leading "k") either.
607 |
608 | In most cases, variables have noun phrase names: `let number: Int`. The most significant exception is booleans which are expected to have assertive names (e.g., `let necessary: Bool`). Boolean names are preferred without a verb at the beginning, unless it introduces ambiguity:
609 |
610 | **Do:**
611 |
612 | ```swift
613 | let necessary: Bool
614 | ```
615 |
616 | **Don't:**
617 |
618 | ```swift
619 | let isNecessary: Bool
620 | ```
621 |
622 | [Return to Table of Contents](#table-of-contents)
623 |
624 | Types
625 |
626 | Classes and structures are named using noun phrases, as well as protocols describing what an object is. Protocols which add abilities are named descriptively (e.g., `Sortable`). Protocols should not have the word `Protocol` at the end of the name. Instead, conforming types should have a specifying word in the name, because protocols are general, but classes and structures are specific.
627 |
628 | **Do**:
629 |
630 | ```swift
631 | protocol Engine { }
632 | struct DefaultEngine: Engine { }
633 | ```
634 |
635 | **Don't**:
636 |
637 | ```swift
638 | protocol EngineProtocol { }
639 | struct Engine: EngineProtocol { }
640 | ```
641 |
642 | Types implementing design patterns are usually named with the pattern name at the end (e.g., `ViewBuilder`, `DisplayingStrategy`).
643 |
644 | No prefixes shall be used (e.g., just `PriceCalculator` instead of `XYZPriceCalculator`), since there's no necessity for that in Swift (other than, maybe, to maintain consistency with Objective-C libraries and components).
645 |
646 | [Return to Table of Contents](#table-of-contents)
647 |
648 | Methods
649 |
650 | Methods without side-effects have names in the form of what: `let size = view.originalSize()`.
651 |
652 | Methods with side effects are called in the form of an imperative verb: `list.sort()`.
653 |
654 | Similar non-mutating methods that return new values must be called using past participle: `let newList = oldList.sorted()`. Or present participle: `let newList = oldList.sortingLexicographically()`.
655 |
656 | Parameter names follow rules for variable names. An engineer shall make use of Swift syntax possibilities to make the full declaration a human-readable phrase. For example, `func insert(_ element: Element, at index: Int)` is nicely read as `insert(someElement, at: someIndex)` at the call site.
657 |
658 | Factory method names start with the word "make". Both factory methods and initializers have their parameters as a list of items, they are not required to form phrases.
659 |
660 | **Do**:
661 |
662 | ```swift
663 | init(name: String, id: Int)
664 | func makeView(position: CGPoint, size: CGSize) -> UIView
665 | ```
666 |
667 | **Don't**:
668 |
669 | ```swift
670 | init(withName name: String, andID id: Int)
671 | ```
672 |
673 | It's very common to force engineers to put an object of delegation as the first argument of delegation methods. This is not strictly necessary and is used only in cases when it makes sense.
674 |
675 | **Do**:
676 |
677 | ```swift
678 | func buttonTapped(_ button: UIButton)
679 | ```
680 |
681 | **Don't** (unless the object of delegation is really needed at the call site:)
682 |
683 | ```swift
684 | func screen(_ screen: UIViewController, hasButtonTapped button: UIButton)
685 | ```
686 |
687 | UIKit's `UIControl` actions are called with the control's name in the beginning and the "action" word in the end:
688 |
689 | **Do**:
690 |
691 | ```swift
692 | @objc
693 | private func nextButtonAction(_ sender: UIButton) { // ...
694 | ```
695 |
696 | (The sender argument is optional since it's often unused.)
697 |
698 | **Don't**:
699 |
700 | ```swift
701 | @objc
702 | private func onNextButtonTapped(_ sender: UIButton) { // ...
703 | ```
704 |
705 | The latter naming convention is confusing because makes you think of delegation. However, actions may be thought of as a kind of delegation, and the naming style might make sense for some code bases.
706 |
707 | [Return to Table of Contents](#table-of-contents)
708 |
709 | API
710 |
711 | Basically, types representing pure values which could be fairly interchanged by values themselves should be `struct`s, otherwise (e.g., objects containing state or having a lifecycle) – `class`s. Other considerations for choosing over `struct`s and `class`es – expected semantics, memory management, etc., are not the topic of this document.
712 |
713 | Properties are expected to have a constant time complexity. If one doesn't, it's changed to a method.
714 |
715 | Optional booleans and collections are avoided, because they bring degraded states: What's the difference between an empty array and a `nil` array within the same context?
716 |
717 | Free functions are usually a design flaw. An engineer shall double-check, whether free functions really shouldn't correspond to some type.
718 |
719 | Methods that perform an action don't return the object of the action (e.g., a presenting view controller method shall not return the presented view controller).
720 |
721 | **Do**:
722 |
723 | ```swift
724 | func presentErrorAlert()
725 | ```
726 |
727 | **Don't**:
728 |
729 | ```swift
730 | func presentErrorAlert() -> UIAlertViewController
731 | ```
732 |
733 | However, if the return value might be useful in some specific situations, it doesn't force one to use it (the `@discardableResult` annotation serves this purpose). Though, such return values are not encouraged. An engineer shall follow the principle of [the command-query separation](https://en.wikipedia.org/wiki/Command–query_separation).
734 |
735 | Abbreviations and acronyms (except for the common ones) are avoided. (Possible exceptions are local variables within implementation blocks of code.)
736 |
737 | **Do**: `let currentValue = 1`
738 |
739 | **Don't**: `let curValue = 1` (Though, it's acceptable in a very limited scope, like a method implementation).
740 |
741 | [Return to Table of Contents](#table-of-contents)
742 |
743 | Encapsulation
744 |
745 | Any class declaration gains from adding the `final` modifier, an engineer adds it by default and remove in case of a necessity. (An engineer shall prefer composition over inheritance.)
746 |
747 | `fileprivate` access modifier is usually a code smell. An engineer shall re-consider an alternative design approach (e.g., nested types).
748 |
749 | The default access modifier, namely `internal`, is not specified explicitly (as well as any other default, in general).
750 |
751 | `class` type members are rarely useful because of discouraged use of inheritance, especially for static members. An engineer must be aware of the use-cases of `class` and `static` modifiers (confusing them is a common mistake.)
752 |
753 | Generally, encapsulation is honored in any way. E.g., `@IBOutlet` properties and `@IBAction` methods are always `private`. Implementation details are hidden behind meaningful API.
754 |
755 | [Return to Table of Contents](#table-of-contents)
756 |
757 | Implementation
758 |
759 | An engineer makes use of type inference and the current namespace. The compiler notifies them when a type should be stated explicitly. However, there might be occasions when specifying a type explicitly makes code more effective.
760 |
761 | Similar rules are applied to nested types and enum cases. The shorthand notation starting with a dot is preferred whenever is possible.
762 |
763 | An engineer makes use of lazy initialization of the properties that aren't immediately needed. They also prefer complex lazily initialized properties for subviews of UIKit's views and view controllers, especially over configuring them within view controller's lifecycle methods (a common mistake is to write big and awkward `viewDidLoad()` implementations consisting of the full view controller's initial configuration.)
764 |
765 | Conditional code checks "the golden path" (the most probable option) first. Inverted checks (like `!incorrect`) are discouraged because of poor readability. When both rules appear to be mutually exclusive, alternatives shall be considered.
766 |
767 | **Initialization**
768 |
769 | If the initial or constant value of a property doesn't depend on the initializer's parameters, a default value is preferred over setting it within the initialization code.
770 |
771 | `.init()` is not used for initialization:
772 |
773 | **Do**:
774 |
775 | ```swift
776 | let color = UIColor(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
777 | ```
778 |
779 | **Don't**:
780 |
781 | ```swift
782 | let color: UIColor = .init(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
783 | ```
784 |
785 | [Return to Table of Contents](#table-of-contents)
786 |
787 | Methods
788 |
789 | Any scope contains only elements of the same level of abstraction (i.e., either variables and related direct calculations, or method calls of the same layer of abstraction).
790 |
791 | **Do**:
792 | ```
793 | func viewDidLoad() {
794 | super.viewDidLoad()
795 | presenter.viewLoaded()
796 | }
797 | ```
798 |
799 | **Don't**:
800 | ```
801 | func viewDidLoad() {
802 | super.viewDidLoad()
803 |
804 | let button = UIButton()
805 | button.addTarget(self, action: #selector(buttonAction), event: .touchUpInside)
806 | layoutButton()
807 |
808 | view.backgroundColor = .black
809 | }
810 | ```
811 |
812 | Methods, as well as any code part, follow the Single Responsibility Principle. Beside maintainability, this doesn't let methods grow long and difficult to read. However, as for length, there might be exceptions. For example, complex drawing methods that can't be broken up into several small ones (or breaking up would make the code less readable.)
813 |
814 | [Return to Table of Contents](#table-of-contents)
815 |
816 | Optionality
817 |
818 | An engineer avoids force-unwraps. Basically, if an object is optional, that's because it really might be `nil` in certain conditions. If an engineer is 100% sure (which is usually impossible) that the optional value can't be `nil`, it's better to state their point of view explicitly. For instance, by adding some kind of assertion inside a `guard` statement.
819 |
820 | A possibly justified reason to have a force-unwrapped variable is late initialization. However, the latter may also be error-prone and should be avoided.
821 |
822 | Another acceptable reason to have a force-unwrap is an optional API which returns `nil` only if there's a programming error (e.g., creating an URL from `String` in Foundation : `let url = URL(string: "https://www.apple.com")!`). However, an engineer should not rely on third-party APIs' implementation details since they might change any moment and without notice.
823 |
824 | In testing code, force-unwraps are more welcome because if they fail, the test fails too, and this is desired.
825 |
826 | [Return to Table of Contents](#table-of-contents)
827 |
828 | State and Side Effects
829 |
830 | Clean functions are preferred over state-changing ones. Unidirectional flow is preferred over side effects. It makes code less error-prone, improves readability and testability.
831 |
832 | **Don't**:
833 | ```
834 | private var button: UIButton!
835 |
836 | // ...
837 |
838 | func viewDidLoad() {
839 | super.viewDidLoad()
840 | button = UIButton()
841 | configureButton()
842 | }
843 |
844 | // ...
845 |
846 | private func configureButton() {
847 | // ...
848 | }
849 | ```
850 |
851 | In the example above it's hard to track the actions' sequence.
852 |
853 | **Do**:
854 |
855 | ```
856 | private var button: UIButton! {
857 | didSet { configure(button: button) }
858 | }
859 |
860 | // ...
861 |
862 | func viewDidLoad() {
863 | super.viewDidLoad()
864 | button = UIButton()
865 | }
866 |
867 | // ...
868 |
869 | private func configure(button: UIButton) {
870 | // ...
871 | }
872 | ```
873 |
874 | In the latter example, `button` is initialized once, within the `viewDidLoad()` method. Once it's set, `configure(button:)` is called – a pure function that doesn't rely on any kind of state and has no side effects. That is, it always results in the same way for the same arguments.
875 |
876 | _I'm aware that it's not the canonical, functional, definition of the term "pure function". Here I use it in the OOP context, for a basic understanding of the idea._
877 |
878 | [Return to Table of Contents](#table-of-contents)
879 |
880 | Testing
881 |
882 | An engineer tests interfaces, not implementation – by calling public APIs and asserting the expected output against an input.
883 |
884 | Testing purposes don't intervene in the principles of encapsulation. If anything is wanted to be overridden or substantiated in testing code, protocols and their mock or stub implementations are always to the rescue.
885 |
886 | [Return to Table of Contents](#table-of-contents)
887 |
888 |
889 |
890 | Multiple variables declarations on a single line using the single `var`/`let` keywords (like `var x: Int, y: Int`) are restricted. Readability shall not be sacrificed for brevity.
891 |
892 | Custom horizontal alignment isn't used because it welcomes further maintenance problems:
893 |
894 | **Don't**:
895 |
896 | ```swift
897 | let top: CGPoint
898 | let middle: CGPoint
899 | let bottom: CGPoint
900 | ```
901 |
902 | **Type aliases**
903 |
904 | `typealias` declaration is used only for the sake of brevity when it doesn't prevent clarity.
905 |
906 | **Do**:
907 |
908 | ```swift
909 | typealias Task = (_ token: Sting) -> (_ result: Result)
910 | func enqueue(task: Task)
911 | ```
912 |
913 | **Don't**:
914 |
915 | ```swift
916 | typealias T = (Int, Int) -> (String)
917 | func process(task: T) -> String
918 | ```
919 |
920 | [Return to Table of Contents](#table-of-contents)
921 |
922 |
923 |
924 | A method declaration is placed on a single line if it can fit most display screen widths without a carry-over. Otherwise, each parameter is placed on its own line and matches the beginning of the previous one. Return type carries on to the last parameter's line.
925 |
926 | **Do**:
927 |
928 | ```swift
929 | func fetchResults(
930 | from endpoint: URL = .remoteServerPath,
931 | transferringTo device: Device = .current,
932 | compressed: Bool = true,
933 | completionHandler: ((_ success: Bool) -> ())? = nil
934 | ) –> [Data]
935 | ```
936 |
937 | **Don't**:
938 |
939 | ```swift
940 | func fetchResults(from endpoint: URL,
941 | transferringTo device: Device,
942 | compressed: Bool,
943 | completionHandler: (() -> Void)?) –> [Data]
944 |
945 | func fetchResults(from endpoint: URL, transferringTo device: Device,
946 | compressed: Bool, completionHandler: (() -> Void)?) –> [Data]
947 | ```
948 |
949 | In the former example, the formatting will be broken if the function is renamed. In the latter example, parameters, which go second on the lines are hard to notice.
950 |
951 | A bottomline: here's the prioritized order, in which the arguments are formatted, depending on the available space:
952 |
953 | **Do**:
954 |
955 | ```swift
956 | func doSomething1(argument1: Int, argument2: Int)
957 |
958 | func doSomething3(
959 | argument1: Int, argument2: Int, argument3: Int, argument4: Int
960 | )
961 |
962 | func doSomething4(
963 | argument1: Int,
964 | argument2: Int,
965 | argument3: Int,
966 | argument4: Int,
967 | argument5: Int
968 | )
969 | ```
970 |
971 | **Custom operators**
972 |
973 | Operator functions have a whitespace between the implemented operator's name and the left parens.
974 |
975 | **Do**:
976 |
977 | ```swift
978 | static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool {
979 | ```
980 |
981 | **Line length**
982 |
983 | Java Code Conventions can be used for deducing a maximum symbols-per-line value. Or common sense, as an option – from 80 to 120 symbols per line.
984 |
985 | **Annotations**
986 |
987 | Attributes starting with a `@` symbol shall be on separate lines before the corresponding main declarations.
988 |
989 | **Do**:
990 |
991 | ```swift
992 | @testable
993 | import ServiceFoundation
994 | ```
995 |
996 | ```swift
997 | @objc
998 | private func acceptanceButtonAction(_ sender: UIButton) {
999 | ```
1000 |
1001 | **Line breaks**
1002 |
1003 | Long expressions are broken into several parts on different lines so that the symbol that connects two parts of expression starts the new line. Each new line is indented with one additional indentation level. Having a special character starting a new line avoids creating the illusion that a new line is a new statement.
1004 |
1005 | **Do**:
1006 | ```swift
1007 | let a = (a + b)
1008 | + (a + c)
1009 | ```
1010 |
1011 | **Don't**:
1012 | ```swift
1013 | let a = (a + b) +
1014 | (a + c)
1015 | ```
1016 |
1017 | **Return**
1018 |
1019 | Code within the scope (e.g., method's implementation) is separated into logical blocks by empty lines. If all blocks are one line, no separation is used. `return` statement is usually separated, the only exception is if there's only two lines overall.
1020 |
1021 | **Do**:
1022 |
1023 | ```swift
1024 | func makeOrderViewController(orderNumber: Int) -> UIViewController {
1025 | guard let order = Order(orderNumber: Int) else {
1026 | fatalError("Unexisting order.")
1027 | }
1028 |
1029 | let model = OrderModel(orderNumber: orderNumber)
1030 | let vc = OrderViewController(model: model)
1031 |
1032 | return vc
1033 | }
1034 | ```
1035 |
1036 | ```swift
1037 | func makeOrderViewController(orderNumber: Int) -> UIViewController {
1038 | let model = OrderModel(orderNumber: orderNumber)
1039 | let vc = OrderViewController(model: model)
1040 |
1041 | return vc
1042 | }
1043 | ```
1044 |
1045 | ```swift
1046 | func makeOrderViewController(model: OrderModel) -> UIViewController {
1047 | let vc = OrderViewController(model: model)
1048 | return vc
1049 | }
1050 | ```
1051 |
1052 | **Don't**:
1053 |
1054 | ```swift
1055 | func makeOrderViewController(orderNumber: Int) -> UIViewController {
1056 | let model = OrderModel(orderNumber: orderNumber)
1057 | let vc = OrderViewController(model: model)
1058 | return vc // FIXME: The return statement is not separated.
1059 | }
1060 | ```
1061 |
1062 | ```swift
1063 | func makeOrderViewController(model: OrderModel) -> UIViewController {
1064 | let vc = OrderViewController(model: model)
1065 |
1066 | return vc // FIXME: Senseless blank line between the two lines.
1067 | }
1068 | ```
1069 |
1070 | If the implementation consists only of a return statement, the `return` keyword is omitted:
1071 |
1072 | **Do**:
1073 |
1074 | ```swift
1075 | func getViewController() -> UIViewController {
1076 | vc
1077 | }
1078 | ```
1079 |
1080 | [Return to Table of Contents](#table-of-contents)
1081 |
1082 | Closures
1083 |
1084 | Trailing closure syntax is preferred anywhere possible. Therefore, methods with closures as arguments aren't overloaded differing only by the name of the trailing closures – that leads to ambiguity on the call site.
1085 |
1086 | Empty parentheses are not used on the call site for the methods with the only closure as an argument.
1087 |
1088 | **Do**:
1089 |
1090 | ```swift
1091 | requestMoreMoney { spend($0) }
1092 | ```
1093 |
1094 | **Don't**:
1095 |
1096 | ```swift
1097 | requestMoreMoney() { spend($0) }
1098 | ```
1099 |
1100 | ```swift
1101 | requestMoreMoney(completionHandler: { spend($0) })
1102 | ```
1103 |
1104 | The functional style is preferable whenever possible: explicit argument names are omitted (placeholders like `$0` are used), one line closure content scope is placed on the same line with braces (with respect to the line width limits, though).
1105 |
1106 | **Don't**:
1107 |
1108 | ```swift
1109 | requestMoreMoney { money in
1110 | spend(money)
1111 | }
1112 | ```
1113 |
1114 | Chained functional-style calls, if don't fit a single line have line breaks before a dot. Even the first call of the chain is not left on the first line. Each new line of this call has exactly one extra level of indentation.
1115 |
1116 | **Do**:
1117 |
1118 | ```swift
1119 | array
1120 | .filter { $0 > 0 }
1121 | .sort { $0 > $1 }
1122 | .map { Int($0) }
1123 | ```
1124 |
1125 | **Don't**:
1126 |
1127 | ```swift
1128 | array.filter { $0 > 0 }
1129 | .sort { $0 > $1 }.map { Int($0) }
1130 | ```
1131 |
1132 | The latter snippet gives a false impression of only two elements in the call chain.
1133 |
1134 | One indentation level is also used after parentheses.
1135 |
1136 | **Do**:
1137 | ```swift
1138 | Button(action: proceed) {
1139 | Text("Proceed")
1140 | }
1141 | .frame(maxWidth: 200.0)
1142 | ```
1143 |
1144 | **Don't**:
1145 | ```swift
1146 | Button(action: proceed) {
1147 | Text("Proceed")
1148 | }
1149 | .frame(maxWidth: 200.0)
1150 | ```
1151 |
1152 | [Return to Table of Contents](#table-of-contents)
1153 |
1154 | Control Flow
1155 |
1156 | Braces are open on the same line with declaration and closed on a separate line. No empty lines are left after opening brace and before the closing one. (The only exception is functional-style calls or, say, property observers which syntactically might be considered as functional statements. See the Methods and Closures sections.)
1157 |
1158 | **Do**:
1159 |
1160 | ```swift
1161 | if name.isEmpty {
1162 | router.showSettings()
1163 | } else {
1164 | router.showProfile()
1165 | }
1166 | ```
1167 |
1168 | **Don't**:
1169 |
1170 | ```swift
1171 | if name.isEmpty
1172 | {
1173 | router.showSettings()
1174 | }
1175 | else
1176 | {
1177 | router.showProfile()
1178 | }
1179 | ```
1180 |
1181 | ```swift
1182 | if name.isEmpty {
1183 | router.showSettings()
1184 | }
1185 | else {
1186 | router.showProfile()
1187 | }
1188 | ```
1189 |
1190 | ```swift
1191 | if name.isEmpty {
1192 |
1193 | router.showSettings()
1194 |
1195 | } else {
1196 |
1197 | router.showProfile()
1198 |
1199 | }
1200 | ```
1201 |
1202 | Multiple conditions of a single `if`-statement are placed on separate lines. Using commas instead of logic "and" (`&&`) is preferred.
1203 |
1204 | **Do**:
1205 |
1206 | ```swift
1207 | if password.isCorrect,
1208 | user.exist {
1209 | // ...
1210 | ```
1211 |
1212 | **Don't**:
1213 |
1214 | ```swift
1215 | if password.isCorrect, user.exist {
1216 | // ...
1217 | ```
1218 |
1219 | ```swift
1220 | if password.isCorrect
1221 | && user.exist {
1222 | // ...
1223 | ```
1224 |
1225 | If multiple paths exist, the most likely one goes first. Early returns are preferred over multi-level conditional paths.
1226 |
1227 | **Do**:
1228 |
1229 | ```swift
1230 | if currency.supported {
1231 | router.proceedToWithdrawal()
1232 | return
1233 | }
1234 | if country.isAlly {
1235 | router.proceedToAgreement()
1236 | return
1237 | }
1238 | createAgreement()
1239 | ```
1240 |
1241 | **Don't**:
1242 |
1243 | ```swift
1244 | if currency.supported {
1245 | return withdrawnAmount
1246 | } else if country.isAlly {
1247 | return withdrawnAmount / 2
1248 | } else {
1249 | return 0
1250 | }
1251 | ```
1252 |
1253 | In any `guard`-statement, the `else` (and its left brace) goes on the same line after the last condition.
1254 |
1255 | **Do**:
1256 |
1257 | ```swift
1258 | guard !array.isEmpty else {
1259 | // ...
1260 | ```
1261 |
1262 | **Don't**:
1263 |
1264 | ```swift
1265 | guard !array.isEmpty
1266 | else {
1267 | // ...
1268 | ```
1269 |
1270 | Multiple and complex logical conditions are distinguished with braces. Distinguished conditions are placed on their own lines, with corresponding indentation:
1271 |
1272 | `let goodToGo = ((firstNumber > 0)
1273 | || (firstNumber < -10)) // Two levels of indentation, because it's a sub-condition of the first part of the main condition expression.
1274 | && isFirstTry`. // One level of indentation, because it's the second part of the main condition expression.
1275 |
1276 | Unnecessary parentheses within `if`-statement conditions are avoided:
1277 |
1278 | **Do**:
1279 |
1280 | ```swift
1281 | if happens {
1282 | // ...
1283 | ```
1284 |
1285 | **Don't**:
1286 |
1287 | ```swift
1288 | if (number != 0) {
1289 | // ...
1290 | ```
1291 |
1292 | If the control flow's main token is followed by left parentheses, exactly one standard whitespace is inserted between them.
1293 |
1294 | The ternary operator `?`-`:` shall not be used where the single `if`-check is sufficient, because although it can save lines, it makes the intention unclear and spawns extra entities (empty tuples or functions).
1295 |
1296 | **Do**:
1297 |
1298 | ```swift
1299 | if error == nil {
1300 | completion()
1301 | }
1302 | ```
1303 |
1304 | **Don't**:
1305 |
1306 | ```swift
1307 | error == nil
1308 | ? completion()
1309 | : ()
1310 | ```
1311 |
1312 | Ternary operator expressions are placed on three lines, with line breaks before the first and the second symbols of the operator.
1313 |
1314 | The `for`-`where` clause is preferred over the `if`-statements nested inside loops.
1315 |
1316 | Clearly named, easy-to-understand local constants are preferred over inlined conditions:
1317 |
1318 | **Do**:
1319 |
1320 | ```swift
1321 | let withinSolarSystem = ((2 * 2) == 4)
1322 | if withinSolarSystem {
1323 | // ...
1324 | ```
1325 |
1326 | **Don't**:
1327 |
1328 | ```swift
1329 | if (2 * 2) == 4 {
1330 | // ...
1331 | ```
1332 |
1333 | **Switch Statements**
1334 |
1335 | One level of indentation is used inside a `switch`'s parentheses and for `case` implementations.
1336 |
1337 | All statements inside the cases of a `switch` statement start on separate lines.
1338 |
1339 | **Do**:
1340 |
1341 | ```swift
1342 | switch direction {
1343 | case .left:
1344 | turnLeft()
1345 | case .right:
1346 | turnRight():
1347 | case .straight:
1348 | break
1349 | }
1350 | ```
1351 |
1352 | **Don't**:
1353 |
1354 | ```swift
1355 | switch direction {
1356 | case .left: turnLeft()
1357 | case .right: turnRight()
1358 | case .straight: break
1359 | }
1360 | ```
1361 |
1362 | `default` cases are not used (unless it's an imported, non `NS_CLOSED_ENUM`, Objective-C enumeration) because they may lead to erroneous states if new cases are added.
1363 |
1364 | [Return to Table of Contents](#table-of-contents)
1365 |
1366 | Literals
1367 |
1368 | **Arrays**
1369 |
1370 | Array literals shall not contain spaces after the left square bracket and before the right one. The included items shall be listed one below another, aligned at the same level of indentation. The first element shall be on the line right next after the declaration. The closing bracket shall go on the next line after the last item. However, if items are short and their sequence can be read easily (e.g., integer literals) it's acceptable to have them all on the one line.
1371 |
1372 | **Do**:
1373 |
1374 | ```swift
1375 | var numbers = [1, 2, 3]
1376 |
1377 | let airVehicles = [
1378 | helicopter,
1379 | airLiner,
1380 | carrierRocket,
1381 | wings
1382 | ]
1383 | ```
1384 |
1385 | **Don't**:
1386 | ```swift
1387 | var numbers = [1,
1388 | 2,
1389 | 3]
1390 |
1391 | let airVehicles = [helicopter, airLiner, carrierRocket, wings]
1392 | ```
1393 |
1394 | The option with braces on separate lines is used when elements don't fit the line width:
1395 |
1396 | **Do**:
1397 |
1398 | ```swift
1399 | let airVehicles = [
1400 | assumeThisNameDoesNotFitTheLineWidth,
1401 | airLiner
1402 | ]
1403 | ```
1404 |
1405 | The trailing comma after the last element shall not be used.
1406 |
1407 | **Strings**
1408 |
1409 | Multi-line strings shall be preferred over concatenation.
1410 |
1411 | **Do**:
1412 |
1413 | ```swift
1414 | let fullName = """
1415 | Nikita
1416 | Lazarev-Zubov
1417 | """
1418 | ```
1419 |
1420 | **Don't**:
1421 |
1422 | ```swift
1423 | let fullName = "Nikita\n"
1424 | + "Lazarev-Zubov"
1425 | ```
1426 |
1427 | An exception is `NSLocalizedString`, because the genstrings tool cannot handle Swift's multi-line strings.
1428 |
1429 | **Numbers**
1430 |
1431 | Long numbers shall have underscores separating groups of digits: `1_000_000_000`
1432 |
1433 | Floating-point numbers all shall carry an explicit decimal fraction, even when it's zero. Breaking this rule leads to ambiguity when decimal number variables are assigned with new values: `let interval: TimeInterval = 2.0`
1434 |
1435 | **Don't**:
1436 |
1437 | ```swift
1438 | let coordinate: CGFloat = 2.1
1439 | // ...
1440 | coordinate = 2 // Without looking at the declaration it's impossible to know that it's a floating point number.
1441 | ```
1442 |
1443 | [Return to Table of Contents](#table-of-contents)
1444 |
1445 | Constants
1446 |
1447 | Global constants shall be avoided.
1448 |
1449 | The constants within a type declaration shall be grouped logically into private case-less `enum`s as their static properties. Using case-less `enum`s instead of structures or classes prevents unwanted initialization of the containing entity, without adding an explicit private empty initializer.
1450 |
1451 | [Return to Table of Contents](#table-of-contents)
1452 |
1453 | Colons
1454 |
1455 | Colons always have no space on the left and one space on the right. Exceptions are ternary operators (`?`-`:`) and dictionaries.
1456 |
1457 | **Do**: `let scoreTable: [String : Int] = [:]`
1458 |
1459 | **Don't**s (three of them in a row – spot them all): `let scoreTable : [String: Int] = [ : ]`
1460 |
1461 | The ampersand in composition statements (`Codable & Encodable`) is surrounded by spaces too.
1462 |
1463 | **Operators**
1464 |
1465 | All operators (including `=`, but excluding the already discussed `:`) have exactly one standard whitespace before and after.
1466 |
1467 | **Do**: `let a = b + c`
1468 |
1469 | **Don't**: `if (b+c) == 1 {`
1470 |
1471 | [Return to Table of Contents](#table-of-contents)
1472 |
1473 | Pre-processor Directives
1474 |
1475 | Any macros shall not be indented, the surrounded code shall be formatted as if the macro doesn't exist.
1476 |
1477 | **Do**:
1478 |
1479 | ```swift
1480 | func handleLogin() {
1481 | #if DEBUG
1482 | printDebugInfo()
1483 | #endif
1484 | openHomeScreen()
1485 | }
1486 | ```
1487 |
1488 | **Don't**:
1489 |
1490 | ```swift
1491 | func handleLogin() {
1492 | #if DEBUG
1493 | printDebugInfo()
1494 | #endif
1495 | openHomeScreen()
1496 | }
1497 | ```
1498 |
1499 | [Return to Table of Contents](#table-of-contents)
1500 |
1501 | Miscellaneous
1502 |
1503 | Semicolons are not used. The only case in Swift when they are necessary is multiple statements on a single line, which are strongly discouraged.
1504 |
1505 | Only the Latin alphabet and numbers are used in the source code (no Cyrillic alphabet, hieroglyphs, emojis, etc.). Although the Swift programming language supports any UTF symbols, they are hard to type using some of the various localized keyboards and encourage error-prone copying-and-pasting.
1506 |
1507 | Unused code (including imports and resources) shall be always removed, as well as empty or inherited method implementations.
1508 |
1509 | Although empty blocks of code indicate design flaws, if they are unavoidable, they are filled with a comment explaining why it's empty:
1510 |
1511 | **Do**:
1512 |
1513 | ```swift
1514 | func buttonAction() {
1515 | // The button is inactive in this state and don't need an action.
1516 | }
1517 | ```
1518 |
1519 | **Void**
1520 |
1521 | `()`, `Void` and `(Void)` are syntactically interchangeable. However, the first one means an empty tuple that can be used, for example, as empty list of argumants of a closure. The second one is used as an empty return from a closure. The latter is just a pointless usage of a possibility to put any statement between parentheses and is not used.
1522 |
1523 | **Do**: `let completionHandler: () -> Void`
1524 |
1525 | **Don't**: `let completionHandler: () -> ()`
1526 |
1527 | **Access modifiers**
1528 |
1529 | Access modifiers of types, methods etc. go first.
1530 |
1531 | **Do**: `public static func goPublic()`
1532 |
1533 | **Don't**: `static public func goPublic()`
1534 |
1535 | [Return to Table of Contents](#table-of-contents)
1536 |
1537 | Links
1538 |
1539 | [**Finding the Ultimate Swift Code-Style Guidelines**](https://betterprogramming.pub/finding-the-ultimate-swift-code-style-guidelines-59025a7c163c)
1540 |
1541 | A brief introduction to these guidelines.
1542 |
1543 | [**Swift.org API Design Guidelines**](https://swift.org/documentation/api-design-guidelines/)
1544 |
1545 | Non-neglectable official guidelines, focused mainly on API and naming.
1546 |
1547 | [**Java Code Conventions**](https://oracle.com/technetwork/java/codeconvtoc-136057.html)
1548 |
1549 | A inspiring example of an exhaustive code style for a programming language. Though, it has many atavisms and old-fashioned rules. It's really surprising to come across such an ugly formatting every here and there:
1550 |
1551 | **Don't**:
1552 |
1553 | ```java
1554 | void layoutScreen() {
1555 | layoutHeader()
1556 |
1557 | layoutFooter()
1558 | }
1559 | ```
1560 |
1561 | [**Steve McConnell – Code Complete: A Practical Handbook of Software Construction**](https://en.wikipedia.org/wiki/Code_Complete)
1562 |
1563 | An impressive work, guiding software development in general, paying a lot of reader's attention on the code style, exhaustive explanations included.
1564 |
--------------------------------------------------------------------------------