The Swift Style Guide. This guide should help you to improve your Swift code style, its readability, consistency and simplicity.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
README.md

README.md

The Swift Style Guide

This guide is based on the following sources:

Purpose of the style guide

This guide should help you to improve your Swift code style, its readability, consistency and simplicity. It's not a manifest, it doesn’t tell you what to do (though sometimes it may tell you what not to do). It's a set of hints about subjectively best practices that might help you and your team decrease number of programmers errors.

Table of contents

Spacing

Indent code with tabs, not spaces. End files with an empty line.

Vertical spaces should be used in long methods to separate its name from implementation. You may also want to use vertical spaces to separate logic within a function. Shorter methods (one or two lines) don't need such spacing.

Comments

Use comments to describe why something is written as it is, or working like it does. Remember that code should be self-documenting, so use comments only if necessary.

If you decide to add comments, keep them up-to-date. Unmaintained comments should be removed.

Code organization

Source files should have the following organization.

// 1. imports

import MoneyKit

// 2. classes, structs, enums

final class Wallet {

    // 2.1. public, internal properties

    let cards: [Card]
    private(set) var cash: Cash
    
    // 2.2. private properties
    
    unowned private let owner: Person
    
    // 2.3. initializers
    
    init(cash: Cash, cards: [Card], owner: Person)
    
    // 2.4. public, internal functions

    func affords(transaction: Transaction) -> Bool

    // 2.5. private functions
    
    private func cardWithSuffiecientCash(cash: Cash) -> Card?
    
}

// 3. extensions, protocol implementations

extension Wallet: CustomStringConvertible {

    var description: String {
        return "\(owner.name) has \(cash) cash and \(cards.count) cards"
    }

}

Such organization helps others to reach important content earlier. It also saves time, confusion and improves readability.

Naming

Names should be meaningful and compact, written in camelCase. Try to ask yourself whether the name of a type sufficiently explains its behavior. Meaningful naming is very important to other developers because they define some expectations about their own roles.

It is strongly misadvised to name suffix your types with words like Manager, Helper or Utility because they're meaningless and their role can be easily misinterpreted.

Names of classes, structs, enums, enum cases, typealiases, protocols and generic types should be upper camel case.

Generic types

Generic types' names should be descriptive and meaningful. Single uppercase letter such as T, U, or V can be used when generic type is not related in any way to the implementation.

Functions and arguments

Function names should be as descriptive and meaningful as possible performs. Try to express its intent in the name, by keeping it compact at the same time.

Arguments should also be descriptive and should help potential user to understand function's intent.

// preferred

func convert(point: Point, fromView view: View) -> Point
// not preferred

func convertPoint(point: Point, toView view: View) -> Point

Use default values for arguments where a function expects any value or some specific value most of the time. If a particular argument is not required for a function, it's good to make it optional and nil by default.

Closures

Trailing closure syntax should be used if a function accepts a closure as its last argument. If it is its only argument, parentheses may be omitted. Unused closure arguments should be replaced with _ (or fully omitted if no arguments are used). Argument types should be inferred.

func execute<Result>(request: Request<Result>, completion: (Result, Error?) -> Void)

execute(request: someRequest) { (result, _) in /* ... */ }

Use implicit return in one-line closures with clear context.

let numbers = [1, 2, 3, 4, 5]
let even = numbers.filter { $0 % 2 == 0 }

Also, remember that global functions are closures and sometimes it's convenient to pass a function name as a closure.

func isPositive(number: Int) -> Bool

let numbers = [-1, 2, 3, -4, 5]
let positive = numbers.filter(isPositive)

Types

Try to use native Swift types before you come up with your own. Every type can be extended, so sometimes instead of introducing new type, it's convenient to extend or alias existing ones.

Remember that Objective-C classes that have native Swift equivalents are not automatically bridged, e.g. NSString is not implicitly bridged to String in the following example.

func lowercase(string: String) -> String

let string: NSString = /* ... */

lowercase(string) // compile error
lowercase(string as String) // no error

Types should be inferred whenever possible. Don't duplicate type identifier if it can be resolved in compile time:

// preferred

let name = "John Appleseed"
let planets: [Planet] = [.Mars, .Saturn]
let colors = ["red": 0xff0000, "green": 0x00ff00]
// not preferred

let name: String = "Amanda Smith"
let planets: [Planet] = [Planet.Venus, Planet.Earth]
let colors: [String: UInt32] = ["blue": 0x0000ff, "white": 0xffffff]

Also, associate colon with type identifier.

// preferred

class VideoArticle: Article
let events: [Timestamp: Event]
// not preferred

class VideoArticle : Article
let events : [Timestamp : Event]

Typealiases should be short and meaningful.

// preferred

typealias MoneyAmount = Double
// not preferred

typealias Money = Double

Mutability – let over var

It's safer to assume that a constant is immutable, thus it's highly recommended to declare values as constants, using let. Immutable constants ensure their values will never change, which results in less error-prone code.

Mutable var variables should only be used when necessary, e.g. when you're absolutely sure you will be changing their values in the future.

Optionals

Force unwrapping should be avoided as much as possible. Implicitly unwrapped optionals lead to less safe code and can cause unwanted crashes. Use optional chaining or if-let bindings to unwrap optional values.

let user: User? = findUser(id: 123)

if let user = user {
    print("found user \(user.name) with id \(user.id)")
}

Unwrapping several optionals in nested if-let statements is forbidden, as it leads to "pyramid of doom". Swift allows you to unwrap multiple optionals in one statement.

let name: String?
let age: Int?

if let name = name, let age = age, age >= 13 {
    /* ... */
}

However, implicitly unwrapped optionals can sometimes be useful. They may be used in unit tests, where system under test should never be nil. There's no point executing rest of the tests if one of them fails.

var sut: SystemUnderTest!

beforeEach {
    sut = /* ... */
}

afterEach {
    sut = nil
}

it("should behave as expected") {
    sut.run()
    expect(sut.running).to(beTrue())
}

Static vs. dynamic code

Static code is a code where logic and control can be resolved at compile-time. The Swift compiler is able to optimize predictable code to work better and faster. Try to make use of this feature and write as much static code as possible.

On the other hand, dynamic code's control flow is resolved at run-time, which means it's not predictable and, as a result, can't be optimized by the compiler. Avoid using dynamic and @objc attributes unless you really need them.

Implicit getters

Read-only computed properties don't need an explicit getter, thus it can be omitted. This also applies to read-only subscripts.

struct Person {

    let height: Float
    let weight: Float

    var bmi: Float {
        return weight / (height * height)
    }
    
}

Access control

Access control modifiers should be specified on a top-level scope.

public extension String {
    var uppercaseString: String
}

Use access control modifiers only if necessary. Pay attention to variables. Use fileprivate, private or private(set) appropriately. Default access control modifiers can be added for clarity, especially at the top level declaration.

Explicit references to self

Explicit references to self should only take place in closures and when the language requires it.

final class Person {

    let firstName: String
    let lastName: String
    
    var fullName: String {
        return "\(firstName) \(lastName)"
    }

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }

}

Value types over reference types

Value types, such as structs, enums and tuples are usually simpler than reference types (classes) and they're always passed by copying. This means they're independent thread-safe instances, which makes code simpler and safer. In addition, let and var work as expected.

Value types are great for representing data in the app.

struct Country {
    let name: String
    let capital: City
}

On the other hand, reference types, such as classes, are passed by referencing the same mutable instance in memory. There are several cases when classes should be preferred. The first case emerges when subclassing current Objective-C classes. The second one is when you need to use reference types with mutable state, or if you need to perform some actions on deinitialization.

Reference types are great to represent behavior in the app.

final class FileStream {
    let file: File
    var currentPosition: StreamPosition
}

Keep in mind that inheritance is not a sufficient argument to use classes. You should try to compose your types using protocols. Therefore try to declare class as final by default.

// preferred

protocol Polygon {
    var numberOfSides: Int { get }
}

struct Triangle: Polygon {
    let numberOfSides = 3
}
// not preferred

final class Polygon {
    let numberOfSides: Int
    init(numberOfSides: Int)
}

final class Triangle: Polygon {
    init() {
        super.init(numberOfSides: 3)
    }
}

Forbidden

Types should never have prefixes because their names are already implicitly mangled and prefixed by their module name.

Semicolons are obfuscative and should never be used. Statements can be distributed in different lines.

Rewriting standard library functionalities should never take place. Your code will most probably be less optimized and more confusing to other developers.