menu

Swift Style Guidelines

Preface

bookmark_border

Swift is a language whose goal is to be easy to write and read, perform well, and let the compiler do a lot of the work for you. These guidelines attempt to assist in achieving that goal.

If you have not already, read The Swift Programming Language (also available as a book!). That introduces concepts and patterns in a way that’s necessary before reading this guide.

If that book doesn’t appeal to you, to can also look at Apple’s Swift Language Guide, which is in a more traditional documentation format, including lexical structure.

Overall, embrace Swift. Swift-y patterns in .swift files are preferred over patterns used in other languages. Following this simple rule helps the compiler create better assembly, avoids bugs, and makes the code more maintainable.

If you disagree with any of these or have something new to add, you are encouraged to create a pull request or issue using the links at the bottom.

Compatibility

These guidelines are relevant and accurate as of Swift 5.1

Deferral

Primarily, if anything is not explicitly defined here, defer to the below list (prefer items higher on the list). If you still can’t find anything in this or those guides, then what you are trying to do is freeform; do it however you prefer.

With very few exceptions, if Xcode’s build-in code completion/formatting does it some way, that is the way we want it done. If the compiler issues a warning about it, address that warning. Don’t fight the IDE.

  1. The Swift.org API Design Guidelines
  2. HIGHLY ENCOURAGED! WWDC 2016 Session 403: Official | Unofficial YouTube mirror
  3. Google’s Swift Style Guide
  4. The style already present in the file you’re editing

Naming

bookmark_border

Names should be in American English.

See also:

If this is difficult for you to understand at the call-site, then you may have chosen the wrong words. If all else fails, simply use Quick Help (by -clicking the symbol or by selecting it and using the Quick Help inspector) and the resulting documentation will describe its declaration for you.

Capitalization

This one is simple. Types (classes, protocols, typealiases, structs, enums, etc.) are always CapitalizedCamelCase. Instances (lets, vars, cases, etc.) and functions (funcs, dynamic vars, non-anonymous closures, etc.) are always uncapitalizedCamelCase.

When an acronym or initialism like HTTP or JSON is a part of a name, treat it like an English word rather than an abbreviation, even if it’s not pronounceable like a word.

Good Bad
fileprivate class Foo {
    let bar = Bar()
    private var baz: Baz? = nil

    let httpSample: HttpSample? = nil

    func qux(myUrl: URL) {
        bar.hoge(myUrl)
    }
}

enum Fruit {
    case apple
    case blackberry
    case cherry(let isPitted: Bool)

    static let defaultIsPitted = true
}

let globalConstant = "c"

typealias Bar = Norf
fileprivate class _Foo {
    let Bar = bar()
    // Underscores before private fields are not a strict "no", but they are discouraged
    private var _baz: Baz? = nil

    let hTTPSample: HTTPSample? = nil

    func Qux(myURL: URL) {
        Bar.Hoge(myURL)
    }
}

enum fruit {
    case Apple
    case Blackberry
    case Cherry(let is_pitted: Bool)

    static let DEFAULT_IS_PITTED = true
}

let kGlobalConstant = "c"

typealias bar = Norf

Do not use Systems Hungarian notation!

Knowing the type, implementation, etc. in something’s name is not important. If, however, you find that such information is necessary, you may append it as full words to the end of a name, like “nutritionString”, “widthInt”, or “base64ContentWideCString

Do not use Apps Hungarian notation!

Knowing the purpose of something in its name is very important. However, you should prepend it as full words (not short letter abbreviations), like “contentLocalCoordinates”, “unsanitizedUserInput”, or “rowMaximumCount

Grammar

Names should be descriptive and self-documenting (however, that does not mean you should forego documentation). The name of a function, for instance, should describe everything that function does. If that name seems too unwieldy, it’s probably because that function does too much. In that case, split it up into multiple functions, each also with descriptive names.

If your instance, function, or type name is only a few characters long, it probably needs a longer name.

Here are some good rules from Swift.org’s design guidelines:

RULE-OF-THUMB Clarity at the point of use is your most important goal. Entities such as methods and properties are declared only once but used repeatedly. Design APIs to make those uses clear and concise. When evaluating a design, reading a declaration is seldom sufficient; always examine a use case to make sure it looks clear in context.

RULE-OF-THUMB Clarity is more important than brevity. Although Swift code can be compact, it is a non-goal to enable the smallest possible code with the fewest characters. Brevity in Swift code, where it occurs, is a side-effect of the strong type system and features that naturally reduce boilerplate.

RULE-OF-THUMB Write a documentation comment for every declaration. Insights gained by writing documentation can have a profound impact on your design, so don’t put it off.

GOLDEN RULE If you are having trouble describing your API’s functionality in simple terms, you may have designed the wrong API.

It’s important to note that this page uses placeholder names like foo, bar, baz, etc. to name things when the name is unimportant to the example. It’s strongly discouraged to use such obscure andor short names in production code!

Functions

  • Getters are named the same as fields; that is to say they are simply the noun or noun phrase which describes the thing they get. If properties need to be passed in, they are named parameters, not part of the actual function name.
    • Boolean (or other very restricted sets of values) getters are named as assertions about something. They might be prefixed with is/does/etc. or be a present-tense verb or verb phrase.
  • Setters take the same syntax to getters, but with the word “set” prefixing them. Note that it’s preferred to use a var rather than a func to get & set a single value. The incoming parameter which holds the new value should not have a label (use _).
  • Non-Setter Mutators are verbs or phrases describing the mutation they perform
  • Copiers (functions which otherwise act like mutators, but instead of mutating return a new object with the changed data) are present- or past-participle verbs or verb phrases (ending in “ing”, “ed”, etc.) describing the mutation they perform
Good Bad
protocol Funcs {

    // MARK: Getters

    func font(style: FontStyle) -> Font

    func data(for provider: Provider) -> Data

    func element(_ callback: (Element) -> Void)

    func isDataValid(_ data: Data) -> Bool

    var resizes: Bool { get }

    func doesMatch(_ other: Funcs) -> Bool

    func listMatches(in other: Funcs) -> [Match]


    // MARK: Setters

    mutating func setFont(_ font: Font, style: FontStyle)

    mutating func setData(_ data: Data, for provider: Provider)

    func setAsynchronously(with setter: () -> Element)


    // MARK: Mutators

    mutating func resetData(for provider: Provider)

    mutating func increaseFontSize()


    // MARK: Copiers

    func resettingData(for provider: Provider) -> Self

    func increasedFontSize() -> Self
}
protocol Funcs {

    // MARK: Getters

    func getFont(style: FontStyle) -> Font // Don't use "get" for getters

    func FetchDataForProvider(_ provider: Provider) -> Data // "fetch" implementation detail is unnecessary in the signature; function is improperly capitalized

    func get(_ callback: (Element) -> Void) // Get what?

    func validate(_ data: Data) -> Bool // Sounds like it might mutate the data to make it valid

    var resize: Bool { get } // Sounds like it resizes something, possibly returning a success

    func matches(_ other: Funcs) -> Bool // Unclear; are you asserting that this matches the other, or asking for the list of matches?

    func matches(_ other: Funcs) -> [Match] // Same problem as above. Also doesn't compile.


    // MARK: Setters

    mutating func newFont(_ font: Font, style: FontStyle) // Sounds like it returns a new font instead of setting one

    mutating func data(_ data: Data, for provider: Provider) // Sounds like it returns data

    func set(with setter: () -> Element) // Doesn't describe critical behavior (it's asynchronous)


    // MARK: Mutators

    mutating func reset(for provider: Provider) // Doesn't clarify it only resets data

    mutating func inc() // Increment? Incorporate? Increase (Increase what?)?


    // MARK: Copiers

    func resetData(for provider: Provider) -> Self // Sounds like it mutates

    func inc_font() -> Self // Same problem as above and in inc(), uses an underscore, and is unclear whether it mutates
}

Types

Types should not be prefixed; this was a Cocoa pattern used until Swift as a primitive form of namespacing, but since Swift is modular, no prefix is needed.

  • Classes and structs have varied purposes, so they are named in a way that reflects their purpose, using nouns or noun phrases.
  • Protocols define a contract, so there are two approaches:
    • Protocols which define what something is are named exactly like the classes which might implement them; nouns or noun phrases.
    • Protocols which define what something can do are named using present-participle verbs or verb phrases (end in ing, albe, ible, etc.).
  • Enums are named as the singular noun which describes what their cases represent.
  • Typealiases are named just like the type for which they are an alias.
    • Associated types are named just like typealiases.
Good Bad
class BasicDataAccessor: DataAccessor { }
class MainView: NSView { }
class DynamicEncryptionPolicyHandler { }

protocol DataAccessor { }
protocol Encryptable {
    associatedtype Encrypted
}

struct RegistrationWorksheet { }
struct EncryptableFile: Encryptable { }

enum LogSeverity { }
enum ServerErrorCode { }

typealias Label = String
typealias HttpErrorCode = UInt16
typealias Encrypting = Encryptable
class DataAccessImpl: DataAccessProtocol { }
class BHSMainView: NSView { }
class EncPolicy { }

protocol DataAccessProtocol { }
protocol Encrypt {
    associatedtype T
}

struct BHSReg { }
struct EncFile: Encrypt { }

enum LogSeverities { }
enum ErrorCode { }

typealias label = String
typealias HTTPErr = UInt16
typealias Encryptor = Encrypt

Instances

Anything that holds an instance/value (stored fields, enums’ cases, etc.) are named as nouns. An exception can be made for Booleans, which are named as asserting verbs or verb phrases like their function counterparts.

Note that fancy vars may be named like functions.

All instances have the same naming scheme: uncapitalizedCamelCase.

Do not use ALL_CAPS_NAMES, m prefixes, etc.

_underscorePrefixes should only be used if absolutely necessary.

Good Bad
func performAction(sender: Any) { ... }

struct WellDefinedStructure {

    static let shared = WellDefinedStructure(values: ["A", "2", "III"])

    var values: [String]
    private var isBusy = false


    internal init(values: [String]) {
        self.values = values
    }


    func isEqual(to other: WellDefinedStructure) -> Bool { ... }


    var jsonValue: String {
        isBusy = true
        defer { isBusy = false }

        var jsonValue = "{\"values\":["

        jsonValue += values.map { "\"\($0)\"" } .joined(separator: ",")

        return jsonValue + "]}"
    }
}

let wellDefinedStructure = WellDefinedStructure.shared
func performAction(_ sender: Any) { ... }

struct WellDefinedStructure {

    static let SHARED = WellDefinedStructure(vals: ["A", "2", "III"])

    var vals: [String]
    private var m_busy = false


    internal init(vals: [String]) {
        self.vals = vals
    }


    func isEqual(to obj: WellDefinedStructure) -> Bool { ... }


    var json: String {
        m_busy = true
        defer { m_busy = false }

        var str = "{\"values\":["

        str += vals.map { "\"\($0)\"" } .joined(separator: ",")

        return str + "]}"
    }
}

let wds = WellDefinedStructure.SHARED

Plurality

Only collection types (and instances of those types) should be plural (for instance, OptionSets and Arrays). Everything else should be singular.

Good Bad
struct Pizza {
    let size: Size
    let toppings: Toppings


    static let defaultSize = Size.medium
    static let defaultToppings: Toppings = [.cheese, .pepperoni]



    enum Size {
        case small
        case medium
        case large
    }


    struct Toppings: OptionSet {
        let rawValue: UInt8

        static let cheese = Toppings(1 << 0)
        static let pepperoni = Toppings(1 << 1)
        static let bellPeppers = Toppings(1 << 2)
        static let sausage = Toppings(1 << 3)
        static let spinach = Toppings(1 << 4)
    }
}
struct Pizza {
    let size: Sizes
    let toppings: Topping


    static let defaultSize = Sizes.small
    static let defaultToppings: Topping = [.cheese, .pepperoni]



    enum Sizes {
        case small
        case medium
        case large
    }


    struct Topping: OptionSet {
        let rawValue: UInt8

        static let cheese = Topping(1 << 0)
        static let pepperoni = Topping(1 << 1)
        static let bellPeppers = Topping(1 << 2)
        static let sausage = Topping(1 << 3)
        static let spinach = Topping(1 << 4)
    }
}

Punctuation

bookmark_border

Semicolons

Don’t use semicolons. If you think you need to use semicolons, try to find a different way to do it.

Sole Exception

There is actually one case where semicolons are acceptable in the code: When a scope opens and you are logging both the entrance and exit of that scope. In this case, you may place both these log lines and the defer block on the same line of code.

Good Acceptable
func importantFunction() {
    logFunctionEntry()
    defer {
        logFunctionExit()
    }
}
func importantFunction() {
    logFunctionEntry(); defer { logFunctionExit() }
}

Braces

Along with Apple’s guidelines, curly braces ({ and }) open on the same line and close on a new line. Curly braces that are the first non-whitespace character on a line must be indented to the same level as the code around its block, not inside it.

The main difference between our guidelines and Apple’s is the else statement; we place them on the line following their if’s closing brace, whereas Apple places them on the same line.

Braces are required, by all guidelines and by the compiler, for all if, else, guard, switch, while, and for statements.

Good Bad
class Foo {
    func bar() {
        if baz {
            qux.hoge()
        }
        else {
            qux.norf()
        }
    }
}
class Foo
{ // Opening curly braces belong on the same line
    func bar()
        { // Indent standalone braces along with the code AROUND the block, not inside
        if baz {
            qux.hoge()
            } // Indent standalone braces along with the code AROUND the block
        else {
            qux.norf()} // This obfuscates the end of the block
    }
}
if foo {
    bar()
}
else if let baz = self.baz {
    baz.qux()
}
else {
    hoge()
}
if foo {
    bar()
} else if let baz = self.baz { baz.qux() }
else
{
    hoge()
}

Parentheses

RULE-OF-THUMB When in doubt, more parentheses is better.

if, else, guard, switch, while, and for statements should not use parentheses unless necessary for compiling, or if their logic is so complex that parentheses are required for clear human andor compiler understanding. If this logic is very complex, consider moving it into its own function.

Always strive for clarity; when using multiple operators (mathematical, Boolean, or otherwise), use parentheses to explicitly group each pair. This ensures it does what you think it does, and future readers know what you intended it to do.

Good Bad
if foo {
    bar()
}
else if baz, qux {
    hoge()
}
else if norf {
    foobar = ((2 + (6 / foobar)) * 3)
}

// ...

var norf: Bool {
    return (!foo && baz)
            || (qux && !baz)
}
if (foo) {
    bar()
}
else if (baz && qux) {
    hoge()
}
else if !foo && baz || qux && !baz {
    foobar = 2 + 6 / foobar * 3
}

Whitespace

bookmark_border

Indentation

Before lines, use 4 spaces, not tabs (set this in your preferences). Allow the IDE to guide your indentation practices; if it automatically indents something, don’t change that, but adopt it.

Always indent inside curly-brace-blocks, with the sole exception of switch cases, which appear at the same indentation level as the switch statement, itself.

When there are multiple parts of an if, else, guard, or while statement, chop them down and align as the IDE prefers. This makes it clear that you have one clear intent for each line.

Good Bad
if (!foo && baz)
    || (qux && baz) {
    // Content...
}

guard
    let hoge = self.hoge,
    let norf = hoge.norf,
    norf.isInitialized
    else {
        assertionFailure("hoge and norf are required")
        return
}
if (!foo && baz) || (qux && baz) {
  // Content...
}

guard let hoge = self.hoge, let norf = hoge.norf, norf.isInitialized else {
	assertionFailure("hoge and norf are required")
	return
}
switch parseError {
case nil:
    return .success

case .undefinedKey:
    continue

case .unexpectedToken:
    return .failure
}
switch parseError {
    case nil:
        return .success

    case .undefinedKey:
        continue

    case .unexpectedToken:
        return .failure
}

Chopping-Down Functions

When a function has a long declaration with many parameters, it may be chopped-down at both the declaration and the call site. That is to say, hard-wrapped so that they align nicely. If you prefer to keep it all on one line, that is also OK.

Don’t chop-down Objective-C style (so that colons align).

Good Bad
func foobar(_ baz: Baz,
            for qux: Qux,
            hoge: Hoge,
            norfProvider: (() -> Norf)?) -> Foobar {
    // Content...
}
func foobar(_ baz: Baz, for qux: Qux, hoge: Hoge, norfProvider: (() -> Norf)?) -> Foobar {
    // Content...
}
let x = foobar(baz,
               for: Qux(),
               hoge: baz.hoge,
               norfProvider: nil)
let x = foobar(baz, for: Qux(), hoge: baz.hoge, norfProvider: nil)
func foobar(_ baz: Baz, for qux: Qux,
    hoge: Hoge, norfProvider: (() -> Norf)?) -> Foobar {
    // Content...
}
func foobar(_ baz: Baz,
          for qux: Qux,
             hoge: Hoge,
     norfProvider: (() -> Norf)?
) -> Foobar {
    // Content...
}
let x = foobar(baz, for: Qux(),
    hoge: baz.hoge, norfProvider: nil)
let x = foobar(baz,
           for: Qux(),
          hoge: baz.hoge,
  norfProvider: nil)

Inline Padding

The quick summary of the below table is:

  • Colons are X: Y or X : Y
  • Put a space before opening curly braces
  • Put a space around the assignment “=
  • Don’t put spaces around parentheses and angle brackets unless some other rule dictates that you should
  • Keep parentheses as close to the tokens they group as possible
Place Number of spaces Good Bad

Around typing colon

  • 0 or 1 before
  • 1 after

Depending on preference

class Foo: Bar {
    // Content...
}

struct Baz : Qux {
    let hoge: Hoge
    var norf : Norf
}
class Foo:Bar {
    // Content...
}

struct Baz :Qux {
    let hoge:Hoge
    var norf :Norf
}

Generics’ angle brackets

  • 0

Keep them flush with the type they modify and the types within them.

Rules dictating more spacing override this (do place a space after closing angle bracket and opening curly brace).

class Foo<Baz> {
    // Content...
}

class Bar: Qux<Hoge, Norf> {
    // Content...
}
class Foo < Baz > {
    // Content...
}

class Bar: Qux <Hoge, Norf> {
    // Content...
}

After type declaration

Between last declaration character and opening curly brace

  • 1
struct Foo {
    // Content...
}

protocol Bar: Baz {
    // Content...
}

class Qux: Hoge<Norf> {
    // Content...
}
struct Foo  {
    // Content...
}

protocol Bar: Baz{
    // Content...
}

class Qux: Hoge<Norf>{
    // Content...
}

Around assignment “=

Around operators

  • 1

Consecutive assignments are allowed to have more spacing for pretty alignment.

Note that Swift will not allow things like x= y or x =y.

let foo    = 5 + 7
var bazqux = false
let hoge   = "norf"
let foo = 5 + 7
var bazqux = false
let hoge = "norf"
let foo=5+7
var foobar  =  false
let hoge =     "norf"
let foo =    5 + 7
var bazqux = false
let hoge =   "norf"

Functions

  • 0 between name and open parenthesis
  • 0 between open parenthesis and first parameter label (or close parenthesis if no parameters)
  • 0 before commas between parameters
  • 1 after commas between parameters
  • 0 between last parameter and close parenthesis
  • 1 between close parenthesis and open brace
func foo(bar baz: Qux, hoge: Norf) {
    // Content...
}

func foo2() -> Bool {
    return true
}
func foo ( bar baz: Qux , hoge: Norf ){
    // Content...
}

func foo2( )->Bool{
    return true
}

if, else, guard, switch, while, and for statements

  • 1 around (but not within) necessary parentheses
  • 1 before opening brace Spaces emerge from other rules;

First write or imagine it without parentheses. Then, place parentheses as necessary to group elements.

guard foo else {
    assertionFailure("need foo")
    return
}

if (bar && baz) || qux {
    // Content...
}
guard ( foo ) else{
    assertionFailure("need foo")
    return
}

if(bar && baz) || qux{
    // Content...
}

Blank Lines

When in doubt, more blank lines are okay. It’s better for readability when things are spaced out rather than crammed together, and storage/bandwidth/compile time is not a concern.

The quick summary of the below table is:

  • 0 or 1 blank line between fields
  • 2 blank lines between functions and other such blocks
  • 3 blank lines between classes and other types
Place Number of spaces Good Bad

Before opening comment

(copyright, file documentation, etc.)

  • 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//
//  MyFile.swift
//  Project Name
//
//  Created by Ben Leggiero on 2019-07-16
//  Copyright © 2019 Ben Leggiero. All rights reserved.
//

import Cocoa
import FancySDK


let defaultFooSize = 5
fileprivate var instanceCount = 0



class Foo {

    private var usageCount = 0
    let size: Int


    var intValue: Int {
        return usageCount + size
    }


    // MARK: Initialization

    init(size: Int) {
        instanceCount += 1
        self.size = size
    }


    convenience init() {
        self.init(size: defaultFooSize)
    }


    // MARK: Class-specific functions

    func bar() {
        print("Bar!")
        usageCount += 1
    }


    func handleWithBaz(text: String) {

        if let baz = FancySDK.baz {

            let hogeText = text.hoge()
            baz.handle(hogeText)
        }

        print("Did handle baz")

        usageCount += 1
    }
}



// MARK: - Extensions

extension String {
    func hoge() -> String {
        return self + " - Hoge"
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
//  MyFile.swift
//  Project Name
//
//  Created by Ben Leggiero on 2019-07-16
//  Copyright © 2019 Ben Leggiero. All rights reserved.
//
import Cocoa
import FancySDK

let defaultFooSize = 5
fileprivate var instanceCount = 0

class Foo {
    private var usageCount = 0
    let size: Int
    var intValue: Int {
        return usageCount + size
    }


    // MARK: Initialization

    init(size: Int) {
        instanceCount += 1
        self.size = size
    }
    convenience init() {
        self.init(size: defaultFooSize)
    }

    // MARK: Class-specific functions
    func bar() {
        print("Bar!")
        usageCount += 1
    }

    func handleWithBaz(text: String) {
        if let baz = FancySDK.baz {
            let hogeText = text.hoge()
            baz.handle(hogeText)
        }
        print("Did handle baz")
        usageCount += 1
    }

}
// MARK: - Extensions
extension String {
    func hoge() -> String {
        return self + " - Hoge"

    }

}

Between opening comment and group of imports

  • 1

After group of imports

  • 2 or 3

Depending on following number of variables/constants outside scope

Around group of variables/constants outside scope

  • 2 or 3

Depending on number of variables/constants outside scope

Around types (classes, enums, etc.)

  • 3

At beginning of type

After opening brace and before actual code

0 or 1

Depending on preference

Around functions and other scoped blocks

  • 2

At beginning of functions and other scoped blocks

After opening brace and before actual code

  • 0 or 1

Depending on preference

At the end of functions and other scoped blocks

  • 0

Around variables

  • 0 or 1

Depending on complexity of declaration

Around local blocks (if, closures, etc.)

  • 1

Before // MARK:

  • 2

Before // MARK: -

  • 3

After // MARK: and // MARK: -

  • 1

Line breaks

Place Number of spaces Good Bad
  • After type declaration attribute
  • After function attribute
  • After member attribute
  • 1
@objc
class Foo: NSObject {
    @objc
    @IBAction
    func bar(_ sender: Any?) {
        // Content...
    }


    @objc(baz)
    var qux: Hoge? = nil


    func norf(callback: @escaping () -> Void) {
        // Content...
    }
}
@objc class Foo: NSObject {
    @objc @IBAction
    func bar(_ sender: Any?) {
        // Content...
    }


    @objc(baz)


    var qux: Hoge? = nil


    func norf(callback: @escaping
                        () -> Void) {
        // Content...
    }
}

Scoping

bookmark_border

Also called “visibility” or “access level”, it’s important to use the proper scope to ensure your code is used properly, both by others and by Future You.

Prefer more-private scopes first, and only make something more public if necessary.

In case it’s unclear, here are the visibility scopes as of Swift 5:

  • private: Only the current context can see this
    • This is our preferred default scope
    • Top-level in file: Only visible in this file; synonymous with fileprivate
    • In class, struct, enum, extension, etc.: Only visible in this type, and only in this file. When used in a nested type, it’s only visible in the nested one, not its parent.
  • fileprivate: Only (and all) things in this file can see this
    • Top-level in file: Avoid this; synonymous with private
    • In class, struct, enum, extension, etc.: This is okay; use this for utilities only needed in this file
  • internal: Only (and all) things in this Swift module can see this
    • You may use this liberally or judicously, to your preference
  • public: Everything can see this
    • This is okay, but only if necessary.
  • open: By itself, this is public, but subclassable/overridable.
    • If subclassing/overriding is the goal, great. Otherwise, avoid this, or at least pair it with another scope to shield it from the public.
  • unspecified
    • Assume surrounding context’s visibility. This is OK.
    • If top-level (no surrounding context), synonymous with internal. Avoid this.

Objective-C Interop

bookmark_border

Sometimes it is necessary to write a Swift class or function that is accessible by Objective-C code. For this, we have a pattern that allows us to keep everything important in Swift Land, only using a thin wrapper to expose our code to Objective-C Land. This has the added benefit of keeping all code that ties us to Objective-C contained, so as we move away from it, the only changes that must be made to Swift files are deleting these thin wrappers.

Good Bad
struct MySwiftStruct {
    let foo: [String]

    var bar: Int? {
        didSet {
            baz()
        }
    }
}



@objc(MySwiftStruct)
@available(swift, deprecated: 0.0.1, message: "Available in Objective-C Only.")
class __objc__MySwiftStruct : NSObject {
    private var basis: MySwiftStruct


    var foo: NSArray { return basis.foo as NSArray }


    var bar: NSNumber? {
        get {
            return basis.bar.map(NSNumber.init(value:))
        }
        set {
            basis.bar = newValue.map { $0.intValue }
        }
    }
}
@objcMembers
class MySwiftStruct : NSObject { // Sacrifices the powerful & safe value semantics
    let foo: [String] // But what if ObjC tries to put a NSURL in here?


    var bar: Int? { // Unclear how this is represented in ObjC
        didSet {
            baz()
        }
    }
}

EXCEPTION Assuming their counterparts are well-documented and easy to find, it is OK to not document these thin __objc__ wrappers.

Cocoa-, Xcode-, and Swift-Specific Features

bookmark_border

Selecting a kind of type

Prefer using Structs, Protocols, and Extensions more than classes and subclasses; Swift will behave more predictably and optimize better this way. It also allows for some patterns which are impossible with subclassing.

Documentation Comments

GOLDEN RULE If you are having trouble describing your API’s functionality in simple terms, you may have designed the wrong API.

Documentation comments are highly encouraged. We do not have any preferences of its style, so long as the doc comment is provided. You can view them in the Quick Help sidebar on the right of Xcode when the typing cursor is on the name, or by -clicking the name.

For reference, here are a couple examples of documentation comments:

Sparse Doc CommentAcceptable GoodDetailed Doc Comment
/// English comment. **Markdown** is _supported!_
func foo(x: Bar) -> Baz

/// English comment. **Markdown** is _supported!_
var qux: Qux
/// Fulltext English comment. **Markdown** is _supported!_
///
/// - Parameter x: Description of parameter
/// - Return: Description of return
func foo(x: Bar) -> Baz

/// Fulltext English comment. **Markdown** is _supported!_
///
/// - Note: Some subtle functionality/usage note
/// - Attention: Some important functionality/usage note
var qux: Qux

Note that documentation comments are not to explain what something does in a way its name cannot, but instead to elaborate on what its name already says, including intended uses, parameter limits, possible return values, related code, external documentation, etc. Do not use documentation comments as an excuse to poorly name anything.

Also, be sure you know what something actually does before documenting it.

RULE-OF-THUMB Less documentation is better than incorrect documentation.

Good Bad
/// Called when the encrypt dialog is about to close
func encryptDialogWillClose(notification: Notification)
/// This function is called when the encrypt dialog is closed
func myWindowWillClose(notification: Notification)

ACTUAL EXAMPLE FROM CODEBASE 😱

See Also

Here’s a couple starting guides on how to write awesome documentation:

Numbers

By default, use Int for integers and CGFloat for fractions (or Length if you have access to SwiftUI), as they automatically scale to the current processor architecture. If a specific task requires a specific number type like UInt8 or NSDecimalNumber, then that’s OK, but for common operations, use those aforementioned types.

Optionals

In Swift, “optional” means “able to be nil”. The great thing is that, in Swift, anything can be optional, even primitives like Ints! This is a very powerful feature, and such power comes with a lot of responsibility.

Do Not Use Exclamation Points*

!” means “I know this might be nil, but I’m smart enough to know it isn’t nil right now. It’s okay to crash if I’m wrong.” The problem here is that there is still a chance for crashing*, whereas there’s almost always another approach which does not provide a chance of crashing.

The following are preferred ways to unwrap an optional:

  1. The ?., ?(, and ?[ operators. These somewhat mirror Objective-C’s behavior, meaning “If it’s nil, stop evaluating and return nil if anything expects a value. Either way, continue as normal.”
  2. If the question-mark syntax does not accomplish the goal, or would be overly verbose for this context, if let (or if var) is the next best option. This creates a new sub-context in which the existence of the value is guaranteed, but not before nor after.
  3. If one or more values are required for a context (usually a function), or if the if let nesting is getting too deep, guard let (or guard var) is preferred. This is much like an inverted if let, in that it creates a sub-context in which the nonexistence of the value is guaranteed, the difference being that after that context, its existence is guaranteed. * These should go at the top of a context which requires the value, rather than farther down.
  4. If you have a backup value handy, use ?? to use it as an alternative in case the ideal value doesn’t exist.
Good Bad
func foo() -> Foo? {
    guard let bar = self.bar else {
        assertionFailure("foo() requires bar")
        return nil
    }

    let success = bar.baz()

    if success,
        let hoge = bar.qux?.hoge {
        hoge.norf()
    }

    return bar.foo ?? self
}
func foo() -> Foo? {
    if bar == nil {
        assertionFailure("foo() requires bar")
        return nil
    }

    let success = bar!.baz() // bar may have become nil by now!

    if success,
        bar!.qux != nil {
        bar!.qux!.hoge.norf() // bar andor qux may have become nil by now!
    }

    if let foo = bar!.foo { // bar may have become nil by now!
        return foo
    }
    else {
        return self
    }
}

* There is one exception where exclamation points are acceptable: On nib-initialized fields which are guaranteed to be available after load. If you are using SwiftUI, this exception does not apply.

Obviously, using them to mean “Boolean invert” / “not” is okay (!condition).

Throwing functions

Another way to write a function that may or may not return a value is to declare it with the throws keyword. This is perfectly acceptable, and in fact encouraged when it would lead to a more clear API. However, never return an optional value from a throwing function. Instead, where you would return nil, throw a new, custom, descriptive error that tells why you couldn’t return a value. This is because, once the error has been handled, the API caller shouldn’t have to then deal with a nil value, as from their perspective all errors that would cause that have been handled. Additionally, that approach wouldn’t play well with the try? keyword.

Good Bad
struct NoBarWhenRequired: Error {}

func foo() throws -> Foo {
    guard let bar = self.bar else {
        assertionFailure("foo() requires bar")

        throw NoBarWhenRequired()
    }

    return try bar.baz() ?? self
}


func usage() {
    try {
        let result = try foo()
        result.sideEffect()
    }
    catch let error as NoBarWhenRequired {
        print("ERROR: Function called prematurely; `bar` was not initialized")
    }
    catch let error {
        print("ERROR: Unknown error occurred:", error)
    }
}
func foo() throws -> Foo? {
    guard let bar = self.bar else {
        assertionFailure("foo() requires bar")
        return nil
    }

    return try bar.baz() // Might result in a `Foo??`
}


func usage() {
    try {
        if let result = try foo() {
            result.sideEffect()
        }
        else {
            print("INFO: Could not get `foo`, but no error thrown. Check logs for why this happened")
        }
    }
    catch let error {
        print("ERROR: Unknown error occurred:", error)
    }
}

if and guard

if should never be used in place of guard, and vice-versa. As its name implies, guard should be used to guard against a bad state; if something is required, it acts as a guard that everything is OK before proceeding.

When entering a guard’s else block constitutes a bad state, use assertionFailure (or at least log a message) when a guard’s else block was entered.

Do not use guard or if in place of each other! guard is meant to be used as a guard against a bad state, not as an unless-style statement.

extension

Extensions are the bread and butter of Swift and Cocoa. Always prefer creating an extension function over a utility function. Always prefer creating an extension over subclassing. It’s OK to make private extensions if something is only needed in the current context and nowhere else.

GOLDEN RULE Always prefer extensions over subclassing.

Good Bad
extension String {
    func reversed() -> String {
        //...
    }
}
// Anywhere:
func reversedString(_ string: String) -> String {
    // ...
}
extension Dictionary where Key: Bar, Value: Baz {
    convenience init(foo: Foo) {
        // ...
    }
}
func createDictionary(from foo: Foo) -> [Bar : Baz] {
    // ...
}
extension Foo {
    convenience init(bar: Bar) {
        self.init(baz: bar.baz)
    }

    func qux() {
        // ...
    }
}
// in some class Bar:
    var fooValue: Foo {
        return Foo(baz: self.baz)
    }



class QuxFoo: Foo {
    func qux() {
        // ...
    }
}
// Use examples:
let racecar = "racecar".reversed()
var foo = Foo(bar: Bar())
foo.qux()
let barBazDict = [Bar : Baz](foo: foo)
// Use examples:
let racecar = reversedString("racecar")
var foo = Bar().fooValue
foo.qux() // ERROR! foo is Foo, not QuxFoo
(foo as! QuxFoo).qux() // DANGER! foo might not be QuxFoo, so this might crash
let barBazDict = createDictionary(from: foo)
extension URL {
    static var desktopOutput: URL {
        return URL(fileURLWithPath: "\(NSHomeDirectory())/Desktop/Output/")
    }
}

// Use example:
let outputUrl = URL.desktopOuput
writeFile(to: .desktopOutput)

This is more natural. “I’m looking for a common URL; I will look in the URL class.”

class AppPaths {
    class var desktopOutput: URL {
        return URL(fileURLWithPath: "\(NSHomeDirectory())/Desktop/Output/")
    }
}

// Use example:
let outputUrl = AppPaths.desktopOuput
writeFile(to: AppPaths.desktopOutput)

This is less natural. “I’m looking for a common URL; I will look in one of possibly several utility classes.”

Equally bad is placing the constant in global scope, since it’s still not obvious how to find it, and it overpopulates autocomplete.

// MARK:

Separate logical sections of code with // MARK: and // MARK: - messages. Use // MARK: - more sparingly than // MARK:.

When implementing a protocol (or subclassing) outside an extension, make sure you place a // MARK: message before the variables and functions you’ve implemented, making sure the mark’s message is the name of the protocol.

See also: Blank Lines

Good Acceptable
struct Square {
    var sideLength: CGFloat = 0.0
}



// MARK: ExpressibleByIntegerLiteral

extension Square: ExpressibleByIntegerLiteral {
    init(integerLiteral: Int) {
        self.init(sideLength: integerLiteral)
    }
}



// MARK: CustomStringConvertible

extension Square: CustomStringConvertible {
    var description: String {
        return "A \(sideLength)×\(sideLength) square"
    }
}
struct Square: ExpressibleByIntegerLiteral, CustomStringConvertible {

    var sideLength: CGFloat = 0.0


    // MARK: ExpressibleByIntegerLiteral

    init(integerLiteral: Int) {
        self.init(sideLength: integerLiteral)
    }


    // MARK: CustomStringConvertible

    var description: String {
        return "A \(sideLength)×\(sideLength) square"
    }
}

Fancy vars: get, set, willSet, and didSet

In case you don’t know or need a refresher, here are the basics:

Swift vars are very powerful, much moreso than allowing a value to be re-set. There are three ways to use a var:

  1. As a normal (unobserved*) instance, whose value can be set and gotten. The scope of these operations can be set individually, like public private(set).
  2. As an observed* instance whose changes are monitored just before andor just after the value is changed
  3. As a dynamic value, whose setter and getter are specified by the developer. In this mode, it can also be read-only.

These are mutually exclusive; an unobserved instance cannot* report when it changes, an observed instance cannot have a dynamic getter and setter, and a dynamic value does not have a backing stored value.

It’s also important to know that a var in a struct inherits the mutability and observation status of the instance in which it’s held. That is to say, let parentStruct’s fields won’t be changeable, regardless of whether they’re lets or vars. However, var parentStruct’s fields will be changeable if (and only if) they are vars, but not if they’re lets. Even better, if parentStruct has a willSet or a didSet, it will be notified if any of its fields changes.

Do note that this behavior does not exist in class instances, even if those instances are inside a struct (the best immutability those will get is not being able to change the reference; its own fields’ mutability won’t change). This is one reason why you should avoid using classes as much as possible.

With the basics over, here’s the guidelines:

  • Normal, unobserved* vars should be used only for values that need to change rapidly, but whose changes aren’t important.
  • Observed* vars should be used for important values, which should propagate other changes or send notifications when they change.
  • Dynamic vars should be used in place of functions which only get or set a single value
Good
struct Square {
    var sideLength: CGFloat = 0.0

    var name: String = "Square" {
        willSet {
            print("Renaming \(self) to \(newValue)")
        }
    }


    var perimeter: CGFloat {
        get {
            return sideLength * 4.0
        }
        set {
            sideLength = newValue / 4.0
        }
    }
}

* Here, “unobserved” refers to lacking the built-in willSet andor didSet blocks; these can still be observed using key-value approaches. Conversely, “observed” refers to the presence of these blocks and does not imply KVO.

Here, “dynamic” refers to returning a generated, abstracted, translated, or conditional value (like a function). It does not refer to using the dynamic keyword for KVO.

Function Overloading and Default Parameters

In Objective-C, it was necessary to create multiple methods when you wanted one parameter to be optional. With Swift’s default parameters, this is no longer necessary.

Of course, overloading is okay if it is necessary for the current task.

Good Bad
func foo(_ bar: Bar, for baz: Baz = Baz.shared) {
    // ...
}
func foo(_ bar: Bar, for baz: Baz) {
    // ...
}


func foo(_ bar: Bar) {
    foo(bar, for: Baz.shared)
}
func stretch(_ square: Square) -> Rectangle {
    // ...
}

func stretch(_ muscle: Muscle) -> Exercise.Result {
    // ...
}
func stretch(_ squareOrMuscle: Any) -> Any {
    if let square = squareOrMuscle as? Square {
        // ...
    }
    else if let muscle = squareOrMuscle as? Muscle {
        // ...
    }
    else {
        // ...
    }
}

Function parameter labels

Sometimes it’s a good idea to label function parameters, and sometimes it’s not.

  1. Parameter labels may be omitted when the parameter reflects the main purpose of the function. For instance, the string message of a log function, the new value of a setter, or the two items in a two-item comparison function. This overrides the following guidelines.
  2. Boolean parameters must always have labels
  3. Unlike Objective-C, the first parameter can have a label separate from the method name. When possible, prefer this.
  4. When the value being passed in has a specific purpose other than just providing required data, try to give it a separate, more-English label than its variable name (not simple ones like for or with)
  5. Do not provide a label for parameters whose name is in the function name
  6. When calling an external API which does not label its parameters, but leaves their purpose confusing, add “label” block-comments

RULE-OF-THUMB When in doubt, use a label.

Good Bad
func setFoo(_ foo: Foo, // 1 & 5
            with barProvider: (Foo) -> Bar, // 4
            bazzing: Bool) { // 2
    // ...
}
// Call-site example:
setFoo(Foo(), with: BarProvider.default, bazzing: true)



func qux(for hoge: Hoge) -> Qux { // 3
    // ...
}
// Call-site example:
_ = qux(for: Hoge())


let x = cApiCall(descriptiveValueA,
                 descriptiveValueB,
                 /* silently: */ true) // 2 & 6
func setFoo(foo: Foo, // 1 & 5
            barProvider: (Foo) -> Bar, // 4
            _ bazzing: Bool) { // 2
    // ...
}
// Call-site example:
setFoo(foo: Foo(), barProvider: .default, true)



func quxForHoge(_ hoge: Hoge) -> Qux { // 3
    // ...
}
// Call-site example:
_ = quxForHoge(Hoge())


let x = cApiCall(descriptiveValueA,
                 descriptiveValueB,
                 true) // 2 & 6

Mutability

Swift was designed around less-mutating, more-functional approaches first. In general, use approaches that create changed immutable copies, rather than ones that mutate an existing value. This may sound inefficient, but remember that the Swift compiler and runtime expect this to be the default approach, and will optimize for it.

If you run tests and find that using a non-mutating approach is too slow, then it’s okay to delicately choose which parts of your approach to convert to mutating. Use a scalpel, not a mallet when deciding what needs to be mutated. If you’re using Swift’s functional collection functions like map and filter, first try preceding these with .lazy to turn it into a LazySequence, whose performance is improved by waiting until the last moment to perform the operations. This can be even faster than using mutation.

This is much more expressive, safer, and often even faster than mutating.

Good Bad
extension Sequence where Iterator.Element == String {

    func sum() -> CGFloat.NativeType {
        return self
            .lazy
            .compactMap(CGFloat.NativeType.init)
            .reduce(into: 0, +=)
    }
}


// Usage:
["1", "2.0", "3.5", "4e1", "-4.5"].sum() // 42.0

In my tests, this takes an average of ~82% as long as the mutating approach.

extension Sequence where Iterator.Element == String {

    func sum() -> CGFloat.NativeType {

        var sum: CGFloat.NativeType = 0

        self.forEach { numberString in
            if let floatNumber = CGFloat.NativeType(numberString) {
                sum += floatNumber
            }
        }

        return sum
    }
}


// Usage:
["1", "2.0", "3.5", "4e1", "-4.5"].sum() // 42.0

In my tests, this takes an average of ~122% as long as the functional approach.

Control Flow

One of the beautiful things Swift does is decrease the amount of control flow that’s needed by turning common patterns into production-ready functions in the standard library, called higher-order functions. When at all possible, prefer these functions and paradigms over traditional control flow.

Good Bad
extension Array where Element == CGFloat {
    func positives() -> [CGFloat] {
        return self.filter { $0 >= 0.0 }
    }
}
extension Array where Element == CGFloat {
    func positives() -> [CGFloat] {
        var positives = [CGFloat]()
        for number in self {
            if number >= 0 {
                positives += number
            }
        }
        return positives
    }
}
extension Array where Element == CGFloat {
    func sum() -> CGFloat {
        return self.reduce(into: 0.0, +=)
    }
}
extension Array where Element == CGFloat {
    func sum() -> CGFloat {
        var sum = 0.0
        for number in self {
            sum += number
        }
        return sum
    }
}
extension Array where Element == CGRect {
    func union() -> CGRect? {
        return self.first.map { firstRectangle in
            self.reduce(into: firstRectangle) { grandUnion, rect in
                grandUnion = grandUnion.union(rect)
            }
        }
    }
}
extension Array where Element == CGRect {
    func union() -> CGRect? {
        guard var grandUnion = self.first else {
            return nil
        }

        for rect in self {
            grandUnion = grandUnion.union(rect)
        }

        return grandUnion
    }
}
extension Sequence where Iterator.Element == CGRect {
    func nonEmptyIntersections(_ otherRectangles: [CGRect]) -> [CGRect] {
        return zip(self, otherRectangles)
            .map { $0.0.intersection($0.1) }
            .filter { !$0.isEmpty }
    }
}
extension Sequence where Iterator.Element == CGRect {
    func nonEmptyIntersections(_ otherRectangles: [CGRect]) -> [CGRect] {
        var nonEmptyIntersections = [CGRect]()
        for i in 0..<min(self.count, otherRectangles.count) {
            let lhs = self[i]
            let rhs = otherRectangles[i]
            let intersection = lhs.intersection(rhs)
            if !intersection.isEmpty {
                nonEmptyIntersections += intersection
            }
        }
        return nonEmptyIntersections
    }
}
extension Array where Element == Int {
    func asFloats() -> [CGFloat] {
        return map(CGFloat.init)
    }
extension Array where Element == Int {
    func asFloats() -> [CGFloat] {
        var floats = [CGFloat]()
        for foo in foos {
            floats += CGFloat(foo)
        }
        return floats
    }
}
struct OptionalIntCollection {
    var optionalInts: [Int?]

    func nonOptionalInts() -> [Int] {
        return optionalInts.compactMap { $0 }
    }


    func nonOptionalStrings() -> [String] {
        return optionalInts.compactMap { optionalInt in
            return optionalInt.flatMap { $0.description }
        }
    }
}
struct OptionalIntCollection {
    var optionalInts: [Int?]

    func nonOptionalInts() -> [Int] {
        var nonOptionalInts = [Int]()
        for optionalInt in optionalInts {
            if let actualInt = optionalInt {
                nonOptionalInts += actualInt
            }
        }
        return nonOptionalInts
    }


    func nonOptionalStrings() -> [String] {
        var nonOptionalStrings = [String]()
        for optionalInt in optionalInts {
            if let actualInt = optionalInt {
                nonOptionalStrings += actualInt.description
            }
        }
        return nonOptionalStrings
    }
}

Enum Case Associated Values

See Also:

Associated values are a very powerful Swift paradigm, but there are many ways to use them. In our codebase, we prefer this approach:

  1. Always label the associated values
  2. When reading the values in a switch case, always give the reader variable the same name as the label
  3. When pattern-matching in a switch case, always use the label
  4. When pattern-matching in a switch case and ignoring the associated values, always write the label followed by an underscore, rather than just an underscore or omitting the associated values entirely
Good Bad
enum Result {
    case asExpected
    case needTweaks(firstItemIndex: Int, secondItemIndex: Int, reverse: Bool) // 1
    case didIncrement(incrementedIndex: Int) // 1
    case errorOccurred(fullError: Error) // 1
}
switch result {
    case asExpected:
        return


    case needTweaks(firstItemIndex: 123, secondItemIndex: _, reverse: true): // 3 & 4
        foo()

    case needTweaks(firstItemIndex: _, secondItemIndex: 456, reverse: false): // 3 & 4
        bar()

    case needTweaks(firstItemIndex: _, secondItemIndex: _, reverse: _): // 3 & 4
        baz()


    case didIncrement(incrementedIndex: _): // 4
        return


    case errorOccurred(let fullError) // 3
        throw fullError
}
enum Result {
    case asExpected
    case needTweaks(Int, Int, reverse: Bool) // 1
    case didIncrement(Int) // 1
    case errorOccurred(Error) // 1
}
switch result {
    case asExpected:
        return


    case needTweaks(123, _, true): // 3 & 4
        foo()

    case needTweaks(_, 456, false): // 3 & 4
        bar()

    case needTweaks(_, _, _): // 3 & 4
        baz()

    case didIncrement: // 4
        return


    case errorOccurred(let cause) // 3
        throw cause
}

NSCache

NSCache is very useful when applied correctly. It cannot be used for all caching needs. This is because NSCache can and will evict things even if everything is under the limits.

In case you’re unfamiliar with NSCache, it is a Cocoa class which allows you to give limits on both total number of items in that cache, as well as total cost (with each item optionally given a cost when it is cached). As previously mentioned, NSCache only loosely respects these guidelines. For performance reasons, it might evict items even if no limits have been reached. Similarly, it might not yet have evicted items well after its limits have been passed.

Never use NSCache to cache values which cannot be recreated (for instance, items with UUIDs)!

If your requirements are okay with this looseness, then NSCache might be the right tool for the job. Otherwise, you should probably write your own cache which is specialized to fit those requirements.

General Patterns

bookmark_border

Singleton

Singletons are often unnecessary, but if you find one necessary, name the single instance shared (if there can only ever be one) or default (if a custom instance can be created), and place it in the class’ static scope.

Good Bad
class Foo {
    static let shared = Foo()

    private init() {
    }
}

// Call-site example:
let foo = Foo.shared
let bar = bar(foo: .shared)
class Bar {
    static let `default` = Foo()

    init() {
    }
}

// Call-site example:
let bar = Bar.default
let baz = baz(bar: .default)
class Foo {
    static let sharedInstance = Foo()

    private init() {
    }
}

// Call-site example:
let foo = Foo.sharedInstance
let bar = bar(foo: .sharedInstance)
class Foo {
    static let foo = Foo()

    private init() {
    }
}

// Call-site example:
let foo = Foo.foo
let bar = bar(foo: .foo)
class Bar {
    init() {
    }
}

let defaultBar = Bar()

// Call-site example:
let bar = Bar
let baz = baz(bar: defaultBar)

Lazy initialization

Thankfully, Swift makes this easy with its lazy keyword. The only matter of style is how to handle resetting the lazy value.

Good Bad
lazy var foo = Foo()
lazy private(set) var bar = Bar()
private var _foo: Foo? = nil
var foo: Foo {
    get {
        if let foo = _foo {
            return foo
        }
        _foo = Foo()
        return _foo
    }

    set {
        _foo = newValue
    }
}

private var _bar: Bar? = nil
var bar: Bar {
    if let bar = _bar {
        return bar
    }
    _bar = Bar()
    return _bar
}
lazy private(set) var baz: Baz = {
    let baz = Baz()
    baz.qux = "qux?"
    return baz
}()
private var lazyBaz: Baz? = nil
var baz: Baz {
    get {
        if let baz = lazyBaz {
            return baz
        }
        let baz = Baz()
        baz.qux = "qux?"
        lazyBaz = baz
        return baz
    }
    private set {
        lazyBaz = newValue
    }
}

Resettable Lazy Fields

For resettable lazy fields, declare a private backing field as optional and a more-exposed field getter that will set it if it’s nil. For this, you can use Swift Lazy PatternsResettableLazy:

Good Bad
private var _foo = ResettableLazy<Foo> { Foo() }


internal var foo: Foo! {
    get {
        return _foo.value
    }
    set {
        if let newValue = newValue {
            _foo.value = newValue
        }
        else {
            _foo.reset()
        }
    }
}


print(foo)
foo = nil
internal lazy var foo: Foo!

print(foo)
foo = nil // This does not reset the lazy value! https://bugs.swift.org/browse/SR-5172

Assertions

Assertions can be a powerful way to help yourself debug; if a very bad state occurs, instead of it being quietly logged, the app halts immediately and won’t continue. Remember assertions are ignored in production builds so they won’t lead to end-user crashes.

When no logic is necessary and an assertion should always be thrown, use assertionFailure("message") instead of assert(false, "message").

Good Bad
guard let bar = self.bar else {
    assertionFailure("foo requires bar")
    return
}
guard let bar = self.bar else {
    print("foo requires bar")
    return
}
guard let bar = self.bar else {
    return
}

Factory Initializers

A common Objective-C approach to fancy initializers is to create a static function named like arrayWithObject: or stringByAppendingString:. Swift no longer needs these bodges. Instead, create another initializer (perhaps in an extension), whose first parameter is named like the old factory methods. These are often also annotated as convenience initializers.

Good Bad
class Foo {
    let bar: Bar


    required init(bar: Bar) {
        self.bar = bar
    }


    convenience init?(with baz: Baz) {
        guard let bar = baz.bar else {
            return nil
        }
        self.init(bar: bar)
    }
}

let example = Foo(with: Baz())
class Foo {
    let bar: Bar


    required init(bar: Bar) {
        self.bar = bar
    }


    static func foo(withBaz baz: Baz) -> Foo? {
        guard let bar = baz.bar else {
            return nil
        }
        return Foo(bar: bar)
    }
}

let example = Foo.foo(withBaz: Baz())

Global Constants

See also:

There will always be a need for global constants. However, it’s better to keep them out of global scope. Prefer placing these constants in structs, extensions, etc.

Good Bad
public extension Int {
    public static let answer = 42
}

public extension MyTimelyOperation {
    public static let maxDuration: TimeInterval = 0.5
}

public extension Notification.Name {
    public static let hotFolderUpdate = Notification.Name("org.bh.hotFolderUpdate")
}


// Usage:

reverseEngineerQuestion(from: .answer)

extension MyTimelyOperation {
    static var shortOperation: MyTimelyOperation {
        return MyTimelyOperation(duration: self.maxDuration / 3.0)
    }
}
let shortOperation = MyTimelyOperation.shortOperation

let notification = Notification(name: .hotFolderUpdate)
public let answer = 42

public let maxTimeSensitiveOperationDuration: TimeInterval = 0.5

public let notificationNameHotFolderUpdate = Notification.Name("org.bh.hotFolderUpdate")


// Usage:

reverseEngineerQuestion(from: answer)

extension MyTimelyOperation {
    static var shortOperation: MyTimelyOperation {
        return MyTimelyOperation(duration: maxTimeSensitiveOperationDuration / 3.0)
    }
}
let shortOperation = MyTimelyOperation.shortOperation

let notification = Notification(name: notificationNameHotFolderUpdate)

Compile-Time Warnings and Errors

Treat warnings as errors. Very seldom is it okay to allow code with a warning to be committed. It’s up to you to make sure code that you commit is warning- and error-free.

The same goes for tests; if a test fails, treat that like a compiler error. It’s up to you to ensure every build passes its tests.

Runtime Warnings and Errors

It’s often necessary to indicate that a function could not complete its duties. The preferred Objective-C way was to use an NSError**. In Swift, that is to throw an Error. Embrace this, and use it to bring more information and compile-time peace-of-mind.

Good Bad
struct Foo {

    enum JsonParseError: Error {
        case unknownSymbol(symbol: String)
        case unexpectedEnd
    }


    func parse(json: String) throws -> ParsedJson {
        ...
        guard let nextCharacter = reader.nextCharacter() else {
            throw JsonParseError.unexpectedEnd
        }
        ...
        switch symbol {
        ...
        default: throw JsonParseError.unknownSymbol(symbol: symbol)
        }
        ...
        return parsedJson
    }
}

Example usage:

func didReceive(json: String) {
    do {
        self.parsedJson = try self.parse(json: json)
    }
    catch let error: JsonParseError {
        let message: String

        switch error {
        case .unknownSymbol(let symbol):
            message = "What is this?: \"\(symbol)\""

        case .unexpectedEnd:
            message = "That EOF came out of nowhere!"
        }

        assertionFailure(message)
    }
}
let empty = try! Foo().parse(json: "{}")
if let unimportantString = self.unimportantString,
    let unimportant = try? Foo().parse(json: unimportantString) {
    ...
}
struct Foo {
    var parsedJson: ParsedJson?

    enum JsonParseError: Int {
        case noError = 0 // Non-error defined in "Error" enum
        case unknownSymbol = 1
        case unexpectedEnd = 2
    }


    // An `inout` `parsedJson` would also be inappropriate here, even if it does reduce spooky action and mutability
    mutating func parse(json: String) -> JsonParseError {
        self.parsedJson = nil
        ...
        guard let nextCharacter = reader.nextCharacter() else {
            return .unexpectedEnd
        }
        ...
        switch symbol {
        ...
        default: return .unknownSymbol
        }
        ...
        self.parsedJson = parsedJson
        return .noError
    }
}

Example usage:

mutating func didReceive(json: String) {
    let error = self.parse(json: json) // spooky action: Where is the result?

    switch error {
    case .unknownSymbol
        assertionFailure("Some symbol was wrong!") // Less information

    case .unexpectedEnd:
        assertionFailure("That EOF came out of nowhere!")

    case .noError:
        break
    }
}
var emptyWrapper = Foo()
let error = emptyWrapper.parse(json: "{}")
guard error == .noError else {
    assertionFailure("Didn't expect \(error) when parsing {}")
    exit(error.rawValue) // Not the same effect
}
let empty = emptyWrapper.parsedJson
if let unimportantString = self.unimportantString {
    var unimportantWrapper = Foo()
    _ = unimportantWrapper.parse(json: unimportantString) // What is ignored? It's unclear
    if let unimportant = unimportantWrapper.parsedJson {
        ...
    }
}

Runtime Errors in Callbacks

This is all well and good, but when receiving a callback from an asynchronous function, you might also want to know if any error occurred in there. Swift’s standard library provides the Result type for this:

Good Bad
func someAsync(then callback: @escaping (Result<String, Error>) -> Void) {
    onAnotherThread {
        do {
            callback(.success(try someDangerousStringGenerator()))
        }
        catch let error {
            callback(.failure(cause: error))
        }
    }
}

Example usage:

someAsync { result in
    switch result {
    case .failure(let cause): print("ERROR: Could not get string. Cause:", cause)
    case .success(let result): print(result)
    }
}
func someAsync(then callback: @escaping (String?, Error?) -> Void) {
    onAnotherThread {
        do {
            callback(try someDangerousStringGenerator(), nil)
        }
        catch let error {
            callback(nil, error)
        }
    }
}

Example usage:

someAsync { result, error in
    if let error = error {
        print("ERROR: Could not get string. Cause:", cause)
    }
    else if let result = result {
        print(result)
    }
    else {
        preconditionFailure("Undefined state! No result, and no error!")
    }
}

Getting Results from Functions

There are two ways you should get results from a function: by them it directly, or by using a callback. Do not use “return parameters” (via inout). Note that inout, itself, has valid uses in our style, for instance when the point of the function is to mutate some other object, but it should not be used to “return” a value.

If more than 1 value must be returned, use a tuple. If a great many values must be returned (like, 4 or more), create a specialized struct.

To indicate an error, instead of using an inout Error, use the throws keyword.

Good Bad
func asynchronous(callback: (Result) -> Void) { ... }

func manyReturnValues() -> (ResultA, ResultB, ResultC) { ... }

func wayTooManyReturnValues() -> SpecializedStruct { ... }

func danger() throws -> Result { ... }

func nicelySynchronous() -> Result { ... }

Remember, inout still has its place:

func prepareForDelivery(payload: inout Payload, options: PayloadDeliveryOptions) { ... }



// Usage:

prepareForDelivery(payload: &payload, options: [])

… but always consider using a mutating func in an extension instead:

extension Payload {
    mutating func prepareForDelivery(options: PayloadDeliveryOptions) { ... }
}



// Usage:


payload.prepareForDelivery(options: [])
func asynchronous(return: inout Result?) { ... }

func manyReturnValues(returnA: inout ResultA?, returnB: inout ResultB?, returnC: inout ResultC?) { ... }

func wayTooManyReturnValues() -> (ResultA, ResultB, ResultC, ResultD, ResultE, ResultF, ResultG) { ... }

func danger(error: inout Error) -> Result? { ... }

// More discouraged than bad; sometimes signatures require this
func nicelySynchronous(callback: (Result) -> Void) { ... }