Swift Style Guide
Swift Style Guide
Updated for Swift 5.0
The goals are clarity, consistency and brevity, in that order.
- Clarity at the point of use.
- Codes should always be consistent within one data set. Pay attention to spelling and case; most frequent problems are with abbreviations.
- Clarity is more important than brevity.
- Write a documentation comment.
References
This guide mainly follows:
Formatting
Column Limit
Swift code has a column limit of 100 characters. Except as noted below, any line that would exceed this limit must be line-wrapped as described in Line-Wrapping.
Exceptions:
- Lines where obeying the column limit is not possible without breaking a meaningful unit of text that should not be broken (for example, a long URL in a comment).
import
statements.- Code generated by another tool.
Braces
In general, braces follow Kernighan and Ritchie (K&R) style for non-empty blocks with exceptions for Swift-specific constructs and rules:
- There is no line break before the opening brace (
{
). - There is a line break after the opening brace (
{
). - There is a line break before the closing brace (
}
). - There is a line break after the closing brace (
}
) iff that brace terminated a statement.- Exceptions:
} else {
- Exceptions:
Semicolons
Semicolons (;
) are not used. The only location where a semicolon may
appear is inside a string literal or a comment.
One Statement Per Line
Line-Wrapping
Google gives clear picture here. The key point is to be careful to judge breakable or unbreakable units.
- If the entire declaration, statement, or expression fits on one line, then do that.
- Comma-delimited lists are only laid out in one direction: horizontally or vertically. In other words, all elements must fit on the same line, or each element must be on its own line. A horizontally-oriented list does not contain any line breaks, even before the first element or after the last element. Except in control flow statements, a vertically-oriented list contains a line break before the first element and after each element.
- A continuation line starting with an unbreakable token sequence is indented at the same level as the original line.
- A continuation line that is part of a vertically-oriented comma-delimited list is indented exactly +2 from the original line.
- When an open curly brace (
{
) follows a line-wrapped declaration or expression, it is on the same line as the final continuation line unless that line is indented at +2 from the original line. In that case, the brace is placed on its own line, to avoid the continuation lines from blending visually with the body of the subsequent block.
Some examples:
public func index<Elements: Collection, Element>(
of element: Element,
in collection: Elements
) -> Elements.Index?
where
Elements.Element == Element,
Element: Equatable { // AVOID.
for current in elements {
// ...
}
}
public func index<Elements: Collection, Element>(
of element: Element,
in collection: Elements
) -> Elements.Index?
where
Elements.Element == Element,
Element: Equatable
{ // GOOD.
for current in elements {
// ...
}
}
For declarations that contain a where clause followed by generic constraints, additional rules apply:
If the generic constraint list exceeds the column limit when placed on the same
line as the return type, then a line break is first inserted before the where
keyword and the where
keyword is indented at the same level as the original
line. If the generic constraint list still exceeds the column limit after
inserting the line break above, then the constraint list is oriented vertically
with a line break after the where
keyword and a line break after the final
constraint.
Functions Calls
The followings are both good, choose one and stick to it consistently.
// good
let index = index(
of: veryLongElementVariableName,
in: aCollectionOfElementsThatAlsoHappensToHaveALongName)
// good
let index = index(
of: veryLongElementVariableName,
in: aCollectionOfElementsThatAlsoHappensToHaveALongName
)
Control Flow Statements
// bad
if cond1() &&
muchlongerCond2() &&
shortCond3()
{
doSomething()
}
// good
if cond1() &&
muchlongerCond2() &&
shortCond3() {
doSomething()
}
// bad
guard let value = returnValue()
let value2 = returnValue2() else {
doSomething()
}
// good
guard let value = returnValue()
let value2 = returnValue2()
else {
doSomething()
}
Other Expressions
When there are multiple continuation lines, indentation may be varied in increments of +2 as needed.
// bad
let result = anExpression + thatIsMadeUpOf * aLargeNumber +
ofTerms / andTherefore % mustBeWrapped + (
andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)
// good
let result = anExpression + thatIsMadeUpOf * aLargeNumber +
ofTerms / andTherefore % mustBeWrapped + (
andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)
Horizontal Whitespace
Here is common convention in most languages.
// bad
if(x==0 && y==0) || z==0 {
}
// good
if (x == 0 && y == 0) || z == 0 {
}
// bad
let nonNegativeCubes = numbers.map { $0 * $0 * $0 }.filter { $0 >= 0 }
// good
let nonNegativeCubes = numbers.map { $0 * $0 * $0 } .filter { $0 >= 0 }
// bad
func sum(_ numbers: [Int])->Int {
// ...
}
// good
func sum(_ numbers: [Int]) -> Int {
// ...
}
// bad
for number in 1 ... 5 {
// ...
}
let substring = string[index ..< string.endIndex]
// good
for number in 1...5 {
// ...
}
let substring = string[index..<string.endIndex]
// bad
let numbers = [1,2,3]
let numbers = [1 ,2 ,3]
let numbers = [1 , 2 , 3]
// good
let numbers = [1, 2, 3]
// bad
struct HashTable : Collection {
// ...
}
struct AnyEquatable<Wrapped : Equatable> : Equatable {
// ...
}
// good
struct HashTable: Collection {
// ...
}
struct AnyEquatable<Wrapped: Equatable>: Equatable {
// ...
}
Horizontal Alignment
// bad
struct DataPoint {
var value: Int
var primaryColor: UIColor
}
// good
struct DataPoint {
var value: Int
var primaryColor: UIColor
}
Naming
Clarity
Include all the words needed to avoid ambiguity.
Proper labeling can effectively reduce ambiguity.
// bad extension List { public mutating func remove(position: Index) -> Element } friends.remove(x) // unclear: are we removing x?
// good extension List { public mutating func remove(at position: Index) -> Element } friends.remove(at: x)
Omit needless words.
// bad public mutating func removeElement(_ member: Element) -> Element? allViews.removeElement(cancelButton)
// good public mutating func remove(_ member: Element) -> Element? allViews.remove(cancelButton)
The naming
removeElement
is bad since the wordElement
doesn’t increase the readibility.Occasionally, it is necessary to repeat type information to avoid ambituity. However, in general it is better to use a word that describes a parameter’s role rather than its type.
Name variables, parameters, and associated types according to their roles.
// bad var string = "Hello world" protocol ViewController { associatedtype ViewType: View } class ProductLine { func restock(from widgetFactory: WidetFactory) }
// good var greeting = "Hello world" protocol ViewController { associatedtype ContentView: View } class ProductLine { func restock(from supplier: WidetFactory) }
Compensate for weak type information.
To achieve clarity, precede each weakly typed parameter with a noun describing its role.
// bad func add(_ observer: NSObject, for keyPath: String) grid.add(self, for: graphics) // vague
// good func addObserver(_ observer: NSObject, forKeyPath path: String) grid.addObserver(self, forKeyPath: graphics) // clear
Class prefixes are not needed.
// bad class MAUICollectionViewController: UICollectionViewController { ... }
// good class UICollectionViewController { ... }
Why? Because what you’ve declared is declared in current module, which is your current target. And
UICollectionViewController
fromUIKit
is declared inUIKit
module.How to use it within current module?
// good var customController = UICollectionViewController() // your custom class var uikitController = UIKit.UICollectionViewController() // class from UIKit
When creating custom delegate methods, an unnamed first parameter should be the delegate source.
// bad func didSelectName(namePicker: NamePickerViewController, name: String) func namePickerShouldReload() -> Bool
// good func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String) func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
Use type infered context to write shorter, clean code.
// bad let selector = #selector(ViewController.viewDidLoad) view.backgroundColor = UIColor.yellow let toView = context.view(forKey: UITransitionContextViewKey.to) let view = UIView(frame: CGRect.zero)
// good let selector = #selector(viewDidLoad) view.backgroundColor = .yellow let toView = context.view(forKey: .to) let view = UIView(frame: .zero)
Generic type parameters should be descriptive, UpperCamelCase names.
When a type name doesn’t have a meaningful relationship or role, use a traditional single uppercase letter such as
T
,U
, orV
.// bad struct Stack<T> { ... } func write<target: OutputStream>(to target: inout target) func swap<Thing>(_ a: inout Thing, _ b: inout Thing)
// good struct Stack<Element> { ... } func write<Target: OutputStream>(to target: inout Target) func swap<T>(_ a: inout T, _ b: inout T)
If a computed property is read-only, omit the get clause.
// bad var area: Double { get { return width * height } }
// good var area: Double { return width * height }
Use of
final
can sometimes clarify your intent and is worth the cost. In the below example, Box has a particular purpose and customization in a derived class is not intended. Marking itfinal
makes that clear.final class Box<T> { let value: T init(_ value: T) { self.value = value } }
Fluency
Prefer grammatical English phrases.
// bad x.insert(y, position: z) x.subViews(color: y) x.nounCapitalize()
// good 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
Begin names of factory methods with “make”.
// bad newIterator()
// good makeIterator()
The first argument to initializer and factory methods calls should not form a phrase.
// bad let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128) let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14) let ref = Link(to: destination)
// good let foreground = Color(red: 32, green: 64, blue: 128) let newPart = factory.makeWidget(gears: 42, spindles: 14) let ref = Link(target: destination)
Name Mutating/Nonmutating method pairs consistently.
Prefer to name the nonmutating variant using the verb’s past participle (usually appending “ed”).
// good // mutating mutating func reverse() x.reverse() // nonmutating: V-ed func reversed() -> Self let y = x.reversed()
Name the nonmutating variant using the verb’s present participle, by appending “ing.”
// good // mutating mutating func stripNewlines() s.stripNewlines() // nonmutating: V-ing func strippingNewlines() -> String let oneLine = t.strippingNewlines()
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.
// good // mutating: form + N y.formUnion(z) c.formSuccessor(&i) // nonmutating x = y.union(z) j = c.successor(i)
Uses of Boolean methods and properties should read as assertions about the receiver when the use is nonmutating.
// good x.isEmpty line1.intersects(line2)
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
, oring
(e.g.Equatable
,ProgressReporting
).The names of other types, properties, variables, and constants should read as nouns.
Terminology
Use US English spelling to match Apple’s API.
// bad let colour = "yellow"
// good let color = "yellow"
Avoid obscure terms.
Don’t say “epidermis” if “skin” will serve your purpose.
Avoid abbreviations.
However, don’t say
verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)
sincesin(x)
has been in common use.
Conventions
General Conventinos
Document the complexity of any computed property that is not O(1).
Prefer methods and properties to free functions.
// When there's no obvious self min(x, y, z) // When the function is an unconstrained generic print(x) // When function syntax is part of the established domain notation sin(x)
Follow case convention.
- Names of types and protocols are
UpperCamelCase
. - Everything else if
lowerCamelCase
.
Acronyms and initialisms that commonly appear as all upper case in American English should be uniformly up- or down-cased according to case conventions:
// good var utf8Bytes: [UTF8.CodeUnit] var isRepresentableAsASCII = true var userSMTPServer: SecureSMTPServer
- Names of types and protocols are
Methods can share a base name.
Overloading is good if the methods do essentially the same things.
The following three all do the same things:
// good extension Shape { func contains(_ other: Point) -> Bool { ... } func contains(_ other: Shape) -> Bool { ... } func contains(_ other: LineSegment) -> Bool { ... } }
And since geometric types and collections are separate domains, this is also fine in the same program:
// good extension Collection where Element: Equatable { func contains(_ sought: Element) -> Bool { ... } }
However, these
index
methods have different semantics, and should have been named differently:// bad extension Database { // Rebuilds the database's search index func index() { ... } // Returns the `n`th row in the given table. func index(_ n: Int, inTable: TableID) -> TableRow { ... } }
Avoid “overloading on return type”:
// bad extension Box { func value() -> Int? { ... } func value() -> String? { ... } }
Parameters
Choose parameter names to serve documentation.
// bad // Return an `Array` containing the elements of `self` // that satisfy `includedInResult`. func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element] // Replace the range of elements indicated by `r` with // the contents of `with`. mutating func replaceRange(_ r: Range, with: [E])
// good // Return an `Array` containing the elements of `self` // that satisfy `predicate`. func filter(_ predicate: (Element) -> Bool) -> [Generator.Element] // Replace the given `subRange` of elements with `newElements`. mutating func replaceRange(_ subRange: Range, with newElements: [E])
Take advantage of defaulted parameters.
// bad let order = lastName.compare(royalFamilyName, options: [], range: nil, locale: nil)
// good let order = lastName.compare(royalFamilyName)
Prefer to locate parameters with defaults toward the end.
Argument Labels
Omit all labels when arguments can’t be usefully distinguished.
// good min(number1, number2) zip(sequence1, sequence2)
In initializers that perform value preserving type conversions, omit the first argument label.
// good Int64(someUInt32)
When the first argument forms part of a prepositional phrase, give it an argument label.
// good x.removeBoxes(havingLength: 12)
An exception arises when the first two arguments represent parts of a single abstraction:
// bad a.move(toX: b, y: c) a.fade(fromRed: b, green: c, blue: d)
// good a.moveTo(x: b, y: c) a.fadeFrom(red: b, green: c, blue: d)
Otherwise, if the first argument forms part of a grammatical phrase, omit its label.
// good x.addSubview(y)
// bad view.dismiss(false) // Don't dismiss? Dismiss a Bool? words.split(12) // Split the number 12?
// good view.dismiss(animated: false) let text = words.split(maxSplits: 12) let studentByName = students.sorted(isOrderedBefore: Student.namePrecedes)
Label all other argument
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.
// bad class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate { // all methods }
// good 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 }
Comments
When they are needed, use comments to explain why a particular piece of code does something. Comments must be kept up-to-date or deleted.
Avoid block comments inline with code, as the code should be as self-documenting as possible. Exception: This does not apply to those comments used to generate documentation.
Avoid the use of C-style comments (
/* ... */
). Prefer the use of double- (//
) or triple-slash (///
).
Function Declarations
Keep short function declarations on one line including the opening brace.
// good func reticulateSplines(spline: [Double]) -> Bool { // code goes here }
For functions with lot’s of parameters, put each parameter on a new line and add an extra indent on subsequent lines.
// good func reticulateSplines( spline: [Double], adjustmentFactor: Double, translateConstant: Int, comment: String ) -> Bool { // code goes here }
Don’t use
(Void)
to represent the lack of an input; simply use()
. UseVoid
instead of()
for closure and function outputs.// bad func updateConstraints() -> () { ... } typealias CompletionHandler = (result) -> ()
// good func updateConstraints() -> Void { ... } typealias CompletionHandler = (result) -> Void
Function Calls
Mirror the style of function declarations at call sites.
// good let success = reticulateSplines(splines)
If the call site must be wrapped, put each parameter on a new line, indented one additional level.
// good let success = reticulateSplines( spline: splines, adjustmentFactor: 1.3, translateConstant: 2, comment: "normalize the display")
Closure Expressions
For single-expression closures where the context is clear, use implicit returns.
// good attendeeList.sort { a, b in a > b }
Types
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!
To declare a type property as a constant simply use
static let
.// bad let e = 2.7182818 // pollutes global namespace let root2 = 1.414 let hypotenuse = side * root2 // what is root2?
// good enum Math { static let e = 2.7182818 static let root2 = 1.414 } let hypotenuse = side * Math.root2
Optionals
Declare variables and function return types as optional with
?
where anil
value is acceptable.Use implicitly unwrapped types declared with
!
only for instance variables that you know will be initialized later before use, such as subviews that will be set up inviewDidLoad()
. Prefer optional binding to implicitly unwrapped optionals in most other cases.When accessing an optional value, use optional chaining if the value is only accessed once or if there are many optionals in the chain:
textContainer?.textLabel?.setNeedsDisplay()
Use optional binding when it’s more convenient to unwrap once and perform multiple operations:
if let textContainer = textContainer { ... }
When naming optional variables and properties, avoid naming them like
optionalString
ormaybeView
since their optional-ness is already in the type declaration.For optional binding, shadow the original name whenever possible rather than using names like
unwrappedView
oractualLabel
.// bad var optionalSubview: UIView? var volume: Double? if let unwrappedSubview = optionalSubview { if let realVolume = volume { // do something with unwrappedSubview and realVolume } } // another example UIView.animate(withDuration: 2.0) { [weak self] in guard let strongSelf = self else { return } strongSelf.alpha = 1.0 }
// good var subview: UIView? var volume: Double? // later on... if let subview = subview, let volume = volume { // do something with unwrapped subview and volume } // another example UIView.animate(withDuration: 2.0) { [weak self] in guard let self = self else { return } self.alpha = 1.0 }
Lazy Initialization
Consider using lazy initialization for finer grained control over object lifetime. This is especially true for
UIViewController
that loads views lazily. You can either use a closure that is immediately called{ }()
or call a private factory method. Example:lazy var locationManager = makeLocationManager() private func makeLocationManager() -> CLLocationManager { let manager = CLLocationManager() manager.desiredAccuracy = kCLLocationAccuracyBest manager.delegate = self manager.requestAlwaysAuthorization() return manager }
Notes:
[unowned self]
is not required here. A retain cycle is not created.- Location manager has a side-effect for popping up UI to ask the user for permission so fine grain control makes sense here.
Type Inference
Prefer compact code and let the compiler infer the type for constants or variables of single instances. Type inference is also appropriate for small, non-empty arrays and dictionaries. When required, specify the specific type such as
CGFloat
orInt16
.// bad let message: String = "Click the button" let currentBounds: CGRect = computeViewBounds() var names = [String]()
// good let message = "Click the button" let currentBounds = computeViewBounds() var names = ["Mic", "Sam", "Christine"] let maximumWidth: CGFloat = 106.5
Type Annotation for Empty Arrays and Dictionaries
For empty arrays and dictionaries, use type annotation. (For an array or dictionary assigned to a large, multi-line literal, use type annotation.)
//bad var names = [String]() var lookup = [String: Int]()
// good var names: [String] = [] var lookup: [String: Int] = [:]
NOTE: Following this guideline means picking descriptive names is even more important than before.
Syntactic Sugar
Prefer the shortcut versions of type declarations over the full generics syntax.
// bad var deviceModels: Array<String> var employees: Dictionary<Int, String> var faxNumber: Optional<Int>
// good var deviceModels: [String] var employees: [Int: String] var faxNumber: Int?
Functions vs Methods
Free functions, which aren’t attached to a class or type, should be used sparingly. When possible, prefer to use a method instead of a free function. This aids in readability and discoverability.
Free functions are most appropriate when they aren’t associated with any particular type or instance.
// bad let sorted = mergeSort(items) // hard to discover launch(&rocket)
// good let sorted = items.mergeSorted() // easily discoverable rocket.launch() // acts on the model
Free Function Exceptions
let tuples = zip(a, b) // feels natural as a free function (symmetry) let value = max(x, y, z) // another free function that feels natural
Memory Management
Code (even non-production, tutorial demo code) should not create reference
cycles. Analyze your object graph and prevent strong cycles with weak
and
unowned
references. Alternatively, use value types (struct
, enum
) to
prevent cycles altogether.
Extending object lifetime
Extend object lifetime using the
[weak self]
andguard let self = self else { return }
idiom.[weak self]
is preferred to[unowned self]
where it is not immediately obvious thatself
outlives the closure. Explicitly extending lifetime is preferred to optional chaining.// bad // might crash if self is released before response returns resource.request().onComplete { [unowned self] response in let model = self.updateModel(response) self.updateUI(model) }
// bad // deallocate could happen between updating the model and updating UI resource.request().onComplete { [weak self] response in let model = self?.updateModel(response) self?.updateUI(model) }
// good resource.request().onComplete { [weak self] response in guard let self = self else { return } let model = self.updateModel(response) self.updateUI(model) }
Access Control
Full access control annotation in tutorials can distract from the main topic and is not required. Using
private
andfileprivate
appropriately, however, adds clarity and promotes encapsulation. Preferprivate
tofileprivate
; usefileprivate
only when the compiler insists.Only explicitly use
open
,public
, andinternal
when you require a full access control specification.Use access control as the leading property specifier. The only things that should come before access control are the
static
specifier or attributes such as@IBAction
,@IBOutlet
and@discardableResult
.// bad fileprivate let message = "Great Scott!" class TimeMachine { lazy dynamic private var fluxCapacitor = FluxCapacitor() }
// good private let message = "Great Scott!" class TimeMachine { private dynamic lazy var fluxCapacitor = FluxCapacitor() }
Control Flow
Prefer the
for-in
style offor
loop over thewhile-condition-increment
style.// bad var i = 0 while i < 3 { print("Hello three times") i += 1 }
// bad var i = 0 while i < attendeeList.count { let person = attendeeList[i] print("\(person) is at position #\(i)") i += 1 }
// good for _ in 0..<3 { print("Hello three times") } for (index, person) in attendeeList.enumerated() { print("\(person) is at position #\(index)") } for index in stride(from: 0, to: items.count, by: 2) { print(index) } for index in (0...3).reversed() { print(index) }
Ternary Operator
The Ternary operator,
?:
, should only be used when it increases clarity or code neatness. A single condition is usually all that should be evaluated. Evaluating multiple conditions is usually more understandable as anif
statement or refactored into instance variables. In general, the best use of the ternary operator is during assignment of a variable and deciding which value to use.// bad result = a > b ? x = c > d ? c : d : y
// good let value = 5 result = value != 0 ? x : y let isHorizontal = true result = isHorizontal ? x : y
Multi-line String Literals
When building a long string literal, you’re encouraged to use the multi-line string literal syntax. Open the literal on the same line as the assignment but do not include text on that line. Indent the text block one additional level.
// bad let message = """You cannot charge the flux \ capacitor with a 9V battery. You must use a super-charger \ which costs 10 credits. You currently \ have \(credits) credits available. """
// good let message = """ You cannot charge the flux \ capacitor with a 9V battery. You must use a super-charger \ which costs 10 credits. You currently \ have \(credits) credits available. """
Special Instructions
Label tuple members and name closure parameters
Take extra care with unconstrained polymorphism
// bad struct Array { // Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) // Inserts the contents of `newElements`, in order, at // `self.endIndex`. public mutating func append(_ newElements: S) where S.Generator.Element == Element } var values: [Any] = [1, "a"] values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?
// good struct Array { // Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) // Inserts the contents of `newElements`, in order, at // `self.endIndex`. public mutating func append(contentsOf newElements: S) where S.Generator.Element == Element } var values: [Any] = [1, "a"] values.append([2, 3, 4]) // it's [1, "a", [2, 3, 4]]!