Swift Coding Conventions

Collection of some Swift coding conventions, which will make Swift code more maintainable, more readable.

The following is conventions I like or I will likely misuse. For a complete version, go to:

  1. The Official raywenderlich.com Swift Style Guide.
  2. Swift API Design Guidelines

Naming

Descriptive and consistent naming makes software easier to read and understand. Use the Swift naming conventions described in the API Design Guidelines. Some key principles include:

  1. prioritizing clarity over brevity
  2. striving for fluent usage
  3. using uppercase for types (and protocols), lowercase for everything else
  4. boolean types should read like assertions
  5. choosing good parameter names that serve as documentation
  6. generally avoiding abbreviations
  7. taking advantage of default parameters
  8. labeling closure and tuple parameters
  9. verb methods follow the -ed, -ing rule for the non-mutating version
  10. noun methods follow the formX rule for the mutating version
  11. protocols that describe what something is should read as nouns
  12. protocols that describe a capability should end in -able or -ible
  13. striving for clarity at the call site

Try to Form Grammatical English Phrases

Preferred:

1
2
3
x.insert(y, at: z)          // “x, insert y at z”
x.subViews(havingColor: y)  // “x's subviews having color y”
x.capitalizingNouns()       // “x, capitalizing nouns”

Not Preferred:

1
2
3
x.insert(y, position: z)
x.subViews(color: y)
x.nounCapitalize()

Mutating/Nonmutating Methods Naming

When the operation is naturally described by a verb, use the verb’s imperative for the mutating method and apply the “ed” or “ing” suffix to name its nonmutating counterpart.

Mutating Nonmutating
x.sort() z = x.sorted()
x.append(y) z = x.appending(y)


When the operation is naturally described by a noun, use the noun for the nonmutating method and apply the “form” prefix to name its mutating counterpart.

Nonmutating Mutating
x = y.union(z) y.formUnion(z)
j = c.successor(i) c.formSuccessor(&i)

Boolean Methods Naming

Uses of Boolean methods and properties should read as assertions about the receiver when the use is nonmutating, e.g. x.isEmpty, line1.intersects(line2).

Protocol Naming

Protocols that describe what something is should read as nouns (e.g. Collection).

Protocols that describe a capability should be named using the suffixes -able, -ible, or -ing (e.g. Equatable, ProgressReporting).

Avoid Abbreviations

The intended meaning for any abbreviation you use should be easily found by a web search.

Delegates

When creating custom delegate methods, an unnamed first parameter should be the delegate source. (UIKit contains numerous examples of this.)

Preferred:

1
2
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool

Not Preferred:

1
2
func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool

Code Organization

Use extensions to organize your code into logical blocks of functionality. Each extension should be set off with a // MARK: - comment to keep things well-organized.

Protocol Conformance

In particular, when adding protocol conformance to a model, prefer adding a separate extension for the protocol methods. This keeps the related methods grouped together with the protocol and can simplify instructions to add a protocol to a class with its associated methods.

Preferred:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyViewController: UIViewController {
  // class stuff here
}

// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
  // table view data source methods
}

// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
  // scroll view delegate methods
}

Not Preferred:

1
2
3
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
  // all methods
}

For UIKit view controllers, consider grouping lifecycle, custom accessors, and IBAction in separate class extensions.

Classes and Structures

Use of Self

For conciseness, avoid using self since Swift does not require it to access an object’s properties or invoke its methods.

Use self only when required by the compiler (in @escaping closures, or in initializers to disambiguate properties from arguments). In other words, if it compiles without self then omit it.

Constants

Constants are defined using the let keyword, and variables with the var keyword. Always use let instead of var if the value of the variable will not change.

Tip: A good technique is to define everything using let and only change it to var if the compiler complains!

You can define constants on a type rather than on an instance of that type using type properties. To declare a type property as a constant simply use static let. Type properties declared in this way are generally preferred over global constants because they are easier to distinguish from instance properties.

Preferred:

1
2
3
4
5
6
enum Math {
  static let e = 2.718281828459045235360287
  static let root2 = 1.41421356237309504880168872
}

let hypotenuse = side * Math.root2

Not Preferred:

1
2
3
4
let e = 2.718281828459045235360287  // pollutes global namespace
let root2 = 1.41421356237309504880168872

let hypotenuse = side * root2 // what is root2?

Control Flow

Golden Path

When coding with conditionals, the left-hand margin of the code should be the “golden” or “happy” path. That is, don’t nest if statements. Multiple return statements are OK. The guard statement is built for this.

Preferred:

1
2
3
4
5
6
7
8
9
10
11
12
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {

  guard let context = context else {
    throw FFTError.noContext
  }
  guard let inputData = inputData else {
    throw FFTError.noInputData
  }

  // use context and input to compute the frequencies
  return frequencies
}

Not Preferred:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {

  if let context = context {
    if let inputData = inputData {
      // use context and input to compute the frequencies

      return frequencies
    } else {
      throw FFTError.noInputData
    }
  } else {
    throw FFTError.noContext
  }
}

When multiple optionals are unwrapped either with guard or if let, minimize nesting by using the compound version when possible. Example:

Preferred:

1
2
3
4
5
6
guard let number1 = number1,
      let number2 = number2,
      let number3 = number3 else {
  fatalError("impossible")
}
// do something with numbers

Not Preferred:

1
2
3
4
5
6
7
8
9
10
11
12
13
if let number1 = number1 {
  if let number2 = number2 {
    if let number3 = number3 {
      // do something with numbers
    } else {
      fatalError("impossible")
    }
  } else {
    fatalError("impossible")
  }
} else {
  fatalError("impossible")
}

Failing Guards

guard statements are required to exit in some way. Generally, this should be simple one line statement such as return, throw, break, continue, and fatalError(). Large code blocks should be avoided. If cleanup code is required for multiple exit points, consider using a defer block to avoid cleanup code duplication.

Argument Labels

  1. Good practice
1
2
func move(from start: Point, to end: Point)
x.move(from: x, to: y)
  1. Omit all labels when arguments can’t be usefully distinguished, e.g. min(number1, number2), zip(sequence1, sequence2).

  2. When the first argument forms part of a prepositional phrase, give it an argument label. The argument label should normally begin at the preposition, e.g. x.removeBoxes(havingLength: 12).

    • An exception for the principle above arises when the first two arguments represent parts of a single abstraction. In such cases, begin the argument label after the preposition, to keep the abstraction clear.

Preferred:

1
2
a.moveTo(x: b, y: c)
a.fadeFrom(red: b, green: c, blue: d)

Not Preferred:

1
2
a.move(toX: b, y: c)
a.fade(fromRed: b, green: c, blue: d)