Swift Style Guide
This style guide is based on Apple’s excellent Swift standard library style andalso incorporates feedback from usage across multiple Swift projects withinGoogle. It is a living document and the basis upon which the formatter isimplemented.
Table of Contents
- Source File Basics
- Source File Structure
- General Formatting
- Formatting Specific Constructs
- Naming
- Programming Practices
- Compiler Warnings
- Initializers
- Properties
- Types with Shorthand Names
- Optional Types
- Error Types
- Force Unwrapping and Force Casts
- Implicitly Unwrapped Optionals
- Access Levels
- Nesting and Namespacing
guard
s for Early Exitsfor
-where
Loopsfallthrough
inswitch
Statements- Pattern Matching
- Tuple Patterns
- Numeric and String Literals
- Playground Literals
- Trapping vs. Overflowing Arithmetic
- Defining New Operators
- Overloading Existing Operators
- Documentation Comments
Source File Basics
File Names
All Swift source files end with the extension.swift
.
In general, the name of a source file best describes the primary entity that itcontains. A file that primarily contains a single type has the name of thattype. A file that extends an existing type with protocol conformance is namedwith a combination of the type name and the protocol name, joined with a plus(+
) sign. For more complex situations, exercise your best judgment.
For example,
- A file containing a single type
MyType
is namedMyType.swift
. - A file containing a type
MyType
and some top-level helper functions is alsonamedMyType.swift
. (The top-level helpers are not the primary entity.) - A file containing a single extension to a type
MyType
that adds conformanceto a protocolMyProtocol
is namedMyType+MyProtocol.swift
. - A file containing multiple extensions to a type
MyType
that addconformances, nested types, or other functionality to a type can be named moregenerally, as long as it is prefixed withMyType+
; for example,MyType+Additions.swift
. - A file containing related declarations that are not otherwise scoped under acommon type or namespace (such as a collection of global mathematicalfunctions) can be named descriptively; for example,
Math.swift
.
File Encoding
Source files are encoded in UTF-8.
Whitespace Characters
Aside from the line terminator, the Unicode horizontal space character(U+0020
) is the only whitespace character that appears anywhere in a sourcefile. The implications are:
- All other whitespace characters in string and character literals arerepresented by their corresponding escape sequence.
- Tab characters are not used for indentation.
Special Escape Sequences
For any character that has a special escape sequence (\t
,\n
,\r
,\"
,\'
,\\
, and\0
), that sequence is used rather than the equivalent Unicode(e.g.,\u{000a}
) escape sequence.
Invisible Characters and Modifiers
Invisible characters, such as the zero width space and other control charactersthat do not affect the graphical representation of a string, are always writtenas Unicode escape sequences.
Control characters, combining characters, and variation selectors thatdoaffect the graphical representation of a string are not escaped when they areattached to a character or characters that they modify. If such a Unicode scalaris present in isolation or is otherwise not modifying another character in thesame string, it is written as a Unicode escape sequence.
The strings below are well-formed because the umlauts and variation selectorsassociate with neighboring characters in the string. The second example is infact composed offive Unicode scalars, but they are unescaped because thespecific combination is rendered as a single character.
letsize="Übergröße"letshrug="🤷🏿️"
In the example below, the umlaut and variation selector are in strings bythemselves, so they are escaped.
letdiaeresis="\u{0308}"letskinToneType6="\u{1F3FF}"
If the umlaut were included in the string literally, it would combine withthe preceding quotation mark, impairing readability. Likewise, while mostsystems may render a standalone skin tone modifier as a block graphic, theexample below is still forbidden because it is a modifier that is not modifyinga character in the same string.
letdiaeresis="̈"letskinToneType6="🏿"
String Literals
Unicode escape sequences (\u{????}
) and literal code points (for example,Ü
)outside the 7-bit ASCII range are never mixed in the same string.
More specifically, string literals are either:
- composed of a combination of Unicode code points written literally and/orsingle character escape sequences (such as
\t
, butnot\u{????}
), or - composed of 7-bit ASCII with any number of Unicode escape sequences and/orother escape sequences.
The following example is correct because\n
is allowed to be present amongother Unicode code points.
letsize="Übergröße\n"
The following example is allowed because it follows the rules above, but it isnot preferred because the text is harder to read and understand compared tothe string above.
letsize="\u{00DC}bergr\u{00F6}\u{00DF}e\n"
The example below is forbidden because it mixes code points outside the 7-bitASCII range in both literal form and in escaped form.
letsize="Übergr\u{00F6}\u{00DF}e\n"
Aside: Never make your code less readable simply out of fearthat some programs might not handle non-ASCII characters properly. If thatshould happen, those programs are broken and must be fixed.
Source File Structure
File Comments
Comments describing the contents of a source file are optional. They arediscouraged for files that contain only a single abstraction (such as a classdeclaration)—in those cases, the documentation comment on the abstractionitself is sufficient and a file comment is only present if it providesadditional useful information. File comments are allowed for files that containmultiple abstractions in order to document that grouping as a whole.
Import Statements
A source file imports exactly the top-level modules that it needs; nothing moreand nothing less. If a source file uses definitions from bothUIKit
andFoundation
, it imports both explicitly; it does not rely on the fact that someApple frameworks transitively import others as an implementation detail.
Imports of whole modules are preferred to imports of individual declarations orsubmodules.
There are a number of reasons to avoid importing individual members:
- There is no automated tooling to resolve/organize imports.
- Existing automated tooling (such as Xcode’s migrator) are less likely towork well on code that imports individual members because they areconsidered corner cases.
- The prevailing style in Swift (based on official examples and communitycode) is to import entire modules.
Imports of individual declarations are permitted when importing the whole modulewould otherwise pollute the global namespace with top-level definitions (such asC interfaces). Use your best judgment in these situations.
Imports of submodules are permitted if the submodule exports functionality thatis not available when importing the top-level module. For example,UIKit.UIGestureRecognizerSubclass
must be imported explicitly to expose themethods that allow client code to subclassUIGestureRecognizer
—those arenot visible by importingUIKit
alone.
Import statements are not line-wrapped.
Import statements are the first non-comment tokens in a source file. They aregrouped in the following fashion, with the imports in each group orderedlexicographically and with exactly one blank line between each group:
- Module/submodule imports not under test
- Individual declaration imports (
class
,enum
,func
,struct
,var
) - Modules imported with
@testable
(only present in test sources)
importCoreLocationimportMyThirdPartyModuleimportSpriteKitimportUIKitimportfuncDarwin.C.isatty@testableimportMyModuleUnderTest
Type, Variable, and Function Declarations
In general, most source files contain only one top-level type, especially whenthe type declaration is large. Exceptions are allowed when it makes sense toinclude multiple related types in a single file. For example,
- A class and its delegate protocol may be defined in the same file.
- A type and its small related helper types may be defined in the same file.This can be useful when using
fileprivate
to restrict certain functionalityof the type and/or its helpers to only that file and not the rest of themodule.
The order of types, variables, and functions in a source file, and the order ofthe members of those types, can have a great effect on readability. However,there is no single correct recipe for how to do it; different files anddifferent types may order their contents in different ways.
What is important is that each file and type usessome logical order,which its maintainer could explain if asked. For example, new methods are notjust habitually added to the end of the type, as that would yield “chronologicalby date added” ordering, which is not a logical ordering.
When deciding on the logical order of members, it can be helpful for readers andfuture writers (including yourself) to use// MARK:
comments to providedescriptions for that grouping. These comments are also interpreted by Xcode andprovide bookmarks in the source window’s navigation bar. (Likewise,// MARK: -
, written with a hyphen before the description, causes Xcode toinsert a divider before the menu item.) For example,
classMovieRatingViewController:UITableViewController{// MARK: - View controller lifecycle methodsoverridefuncviewDidLoad(){// ...}overridefuncviewWillAppear(_animated:Bool){// ...}// MARK: - Movie rating manipulation methods@objcprivatefuncratingStarWasTapped(_sender:UIButton?){// ...}@objcprivatefunccriticReviewWasTapped(_sender:UIButton?){// ...}}
Overloaded Declarations
When a type has multiple initializers or subscripts, or a file/type has multiplefunctions with the same base name (though perhaps with different argumentlabels),and when these overloads appear in the same type or extension scope,they appear sequentially with no other code in between.
Extensions
Extensions can be used to organize functionality of a type across multiple“units.” As with member order, the organizational structure/grouping you choosecan have a great effect on readability; you must usesome logicalorganizational structure that you could explain to a reviewer if asked.
General Formatting
Column Limit
Swift code has a column limit of 100 characters. Except as noted below, any linethat would exceed this limit must be line-wrapped as described inLine-Wrapping.
Exceptions:
- Lines where obeying the column limit is not possible without breaking ameaningful unit of text that should not be broken (for example, a long URL ina comment).
import
statements.- Code generated by another tool.
Braces
In general, braces follow Kernighan and Ritchie (K&R) style for non-emptyblocks with exceptions for Swift-specific constructs and rules:
- Thereis no line break before the opening brace (
{
),unless requiredby application of the rules inLine-Wrapping. - Thereis a line break after the opening brace (
{
), except- in closures, where the signature of the closure is placed on the same lineas the curly brace, if it fits, and a line break follows the
in
keyword. - where it may be omitted as described inOne Statement Per Line.
- empty blocks may be written as
{}
.
- in closures, where the signature of the closure is placed on the same lineas the curly brace, if it fits, and a line break follows the
- Thereis a line break before the closing brace (
}
), except where it maybe omitted as described inOne Statement Per Line,or it completes an empty block. - Thereis a line break after the closing brace (
}
),if and only ifthat brace terminates a statement or the body of a declaration. For example,anelse
block is written} else {
with both braces on the same line.
Semicolons
Semicolons (;
) arenot used, either to terminate or separate statements.
In other words, the only location where a semicolon may appear is inside astring literal or a comment.
funcprintSum(_a:Int,_b:Int){letsum=a+bprint(sum)}
funcprintSum(_a:Int,_b:Int){letsum=a+b;print(sum);}
One Statement Per Line
There isat most one statement per line, and each statement is followed by aline break, except when the line ends with a block that also contains zeroor one statements.
guardletvalue=valueelse{return0}defer{file.close()}switchsomeEnum{case.first:return5case.second:return10case.third:return20}letsquares=numbers.map{$0*$0}varsomeProperty:Int{get{returnotherObject.property}set{otherObject.property=newValue}}varsomeProperty:Int{returnotherObject.somethingElse()}requiredinit?(coderaDecoder:NSCoder){fatalError("no coder")}
Wrapping the body of a single-statement block onto its own line is alwaysallowed. Exercise best judgment when deciding whether to place a conditionalstatement and its body on the same line. For example, single line conditionalswork well for early-return and basic cleanup tasks, but less so when the bodycontains a function call with significant logic. When in doubt, write it as amulti-line statement.
Line-Wrapping
Terminology note:Line-wrapping is the activity of dividing code intomultiple lines that might otherwise legally occupy a single line.
For the purposes of Google Swift style, many declarations (such as typedeclarations and function declarations) and other expressions (like functioncalls) can be partitioned intobreakable units that are separated byunbreakable delimiting token sequences.
As an example, consider the following complex function declaration, which needsto be line-wrapped:
publicfuncindex<Elements:Collection,Element>(ofelement:Element,incollection:Elements)->Elements.Index?whereElements.Element==Element,Element:Equatable{// ...}
This declaration is split as follows (scroll horizontally if necessary to seethe full example). Unbreakable token sequences are indicated in orange;breakable sequences are indicated in blue.
public func index<Elements: Collection, Element>(of element: Element, in collection: Elements) ->Elements.Index?whereElements.Element == Element, Element: Equatable{ // ...}
- Theunbreakable token sequence up through the open angle bracket (
<
)that begins the generic argument list. - Thebreakable list of generic arguments.
- Theunbreakable token sequence (
>(
) that separates the genericarguments from the formal arguments. - Thebreakable comma-delimited list of formal arguments.
- Theunbreakable token-sequence from the closing parenthesis (
)
) upthrough the arrow (->
) that precedes the return type. - Thebreakable return type.
- Theunbreakable
where
keyword that begins the generic constraints list. - Thebreakable comma-delimited list of generic constraints.
Using these concepts, the cardinal rules of Google Swift style for line-wrappingare:
- If the entire declaration, statement, or expression fits on one line, then dothat.
- Comma-delimited lists are only laid out in one direction: horizontally orvertically. In other words, all elements must fit on the same line, or eachelement must be on its own line. A horizontally-oriented list does notcontain any line breaks, even before the first element or after the lastelement. Except in control flow statements, a vertically-oriented listcontains a line break before the first element and after each element.
- A continuation line starting with an unbreakable token sequence is indentedat the same level as the original line.
- A continuation line that is part of a vertically-oriented comma-delimitedlist is indented exactly +2 from the original line.
When an open curly brace (
{
) follows a line-wrapped declaration orexpression, it is on the same line as the final continuation line unless thatline is indented at +2 from the original line. In that case, the brace isplaced on its own line, to avoid the continuation lines from blendingvisually with the body of the subsequent block.publicfuncindex<Elements:Collection,Element>(ofelement:Element,incollection:Elements)->Elements.Index?whereElements.Element==Element,Element:Equatable{// GOOD.forcurrentinelements{// ...}}
publicfuncindex<Elements:Collection,Element>(ofelement:Element,incollection:Elements)->Elements.Index?whereElements.Element==Element,Element:Equatable{// AVOID.forcurrentinelements{// ...}}
For declarations that contain awhere
clause followed by generic constraints,additional rules apply:
- If the generic constraint list exceeds the column limit when placed on thesame line as the return type, then a line break is first insertedbeforethe
where
keyword and thewhere
keyword is indented at the same level asthe original line. - If the generic constraint list still exceeds the column limit after insertingthe line break above, then the constraint list is oriented vertically with aline break after the
where
keyword and a line break after the finalconstraint.
Concrete examples of this are shown in the relevant subsections below.
This line-wrapping style ensures that the different parts of a declaration arequickly and easily identifiable to the reader by using indentation and linebreaks, while also preserving the same indentation level for those partsthroughout the file. Specifically, it prevents the zig-zag effect that would bepresent if the arguments are indented based on opening parentheses, as is commonin other languages:
publicfuncindex<Elements:Collection,Element>(ofelement:Element,// AVOID.incollection:Elements)->Elements.Index?whereElements.Element==Element,Element:Equatable{doSomething()}
Function Declarations
modifiers funcname(formal arguments){modifiers funcname(formal arguments) ->result{modifiers funcname<generic arguments>(formal arguments) throws ->result{modifiers funcname<generic arguments>(formal arguments) throws ->resultwheregeneric constraints{
Applying the rules above from left to right gives us the followingline-wrapping:
publicfuncindex<Elements:Collection,Element>(ofelement:Element,incollection:Elements)->Elements.Index?whereElements.Element==Element,Element:Equatable{forcurrentinelements{// ...}}
Function declarations in protocols that are terminated with a closingparenthesis ()
) may place the parenthesis on the same line as the finalargumentor on its own line.
publicprotocolContrivedExampleDelegate{funccontrivedExample(_contrivedExample:ContrivedExample,willDoSomethingTosomeValue:SomeValue)}publicprotocolContrivedExampleDelegate{funccontrivedExample(_contrivedExample:ContrivedExample,willDoSomethingTosomeValue:SomeValue)}
If types are complex and/or deeply nested, individual elements in thearguments/constraints lists and/or the return type may also need to be wrapped.In these rare cases, the same line-wrapping rules apply to those parts as applyto the declaration itself.
publicfuncperformanceTrackingIndex<Elements:Collection,Element>(ofelement:Element,incollection:Elements)->(Element.Index?,PerformanceTrackingIndexStatistics.Timings,PerformanceTrackingIndexStatistics.SpaceUsed){// ...}
However,typealias
es or some other means are often a better way to simplifycomplex declarations whenever possible.
Type and Extension Declarations
The examples below apply equally toclass
,struct
,enum
,extension
, andprotocol
(with the obvious exception that all but the first do not havesuperclasses in their inheritance list, but they are otherwise structurallysimilar).
modifiers className{modifiers className:superclass and protocols{modifiers className<generic arguments>:superclass and protocols{modifiers className<generic arguments>:superclass and protocolswheregeneric constraints{
classMyClass:MySuperclass,MyProtocol,SomeoneElsesProtocol,SomeFrameworkProtocol{// ...}classMyContainer<Element>:MyContainerSuperclass,MyContainerProtocol,SomeoneElsesContainerProtocol,SomeFrameworkContainerProtocol{// ...}classMyContainer<BaseCollection>:MyContainerSuperclass,MyContainerProtocol,SomeoneElsesContainerProtocol,SomeFrameworkContainerProtocolwhereBaseCollection:Collection{// ...}classMyContainer<BaseCollection>:MyContainerSuperclass,MyContainerProtocol,SomeoneElsesContainerProtocol,SomeFrameworkContainerProtocolwhereBaseCollection:Collection,BaseCollection.Element:Equatable,BaseCollection.Element:SomeOtherProtocolOnlyUsedToForceLineWrapping{// ...}
Function Calls
When a function call is line-wrapped, each argument is written on its own line,indented +2 from the original line.
As with function declarations, if the function call terminates its enclosingstatement and ends with a closing parenthesis ()
) (that is, it has no trailingclosure), then the parenthesis may be placedeither on the same line as thefinal argumentor on its own line.
letindex=index(of:veryLongElementVariableName,in:aCollectionOfElementsThatAlsoHappensToHaveALongName)letindex=index(of:veryLongElementVariableName,in:aCollectionOfElementsThatAlsoHappensToHaveALongName)
If the function call ends with a trailing closure and the closure’s signaturemust be wrapped, then place it on its own line and wrap the argument list inparentheses to distinguish it from the body of the closure below it.
someAsynchronousAction.execute(withDelay:howManySeconds,context:actionContext){(context,completion)indoSomething(withContext:context)completion()}
Control Flow Statements
When a control flow statement (such asif
,guard
,while
, orfor
) iswrapped, the first continuation line is indented to the same position as thetoken following the control flow keyword. Additional continuation lines areindented at that same position if they are syntactically parallel elements, orin +2 increments from that position if they are syntactically nested.
The open brace ({
) preceding the body of the control flow statement can eitherbe placed on the same line as the last continuation line or on the next line,at the same indentation level as the beginning of the statement. Forguard
statements, theelse {
must be kept together, either on the same line or onthe next line.
ifaBooleanValueReturnedByAVeryLongOptionalThing()&&aDifferentBooleanValueReturnedByAVeryLongOptionalThing()&&yetAnotherBooleanValueThatContributesToTheWrapping(){doSomething()}ifaBooleanValueReturnedByAVeryLongOptionalThing()&&aDifferentBooleanValueReturnedByAVeryLongOptionalThing()&&yetAnotherBooleanValueThatContributesToTheWrapping(){doSomething()}ifletvalue=aValueReturnedByAVeryLongOptionalThing(),letvalue2=aDifferentValueReturnedByAVeryLongOptionalThing(){doSomething()}ifletvalue=aValueReturnedByAVeryLongOptionalThing(),letvalue2=aDifferentValueReturnedByAVeryLongOptionalThingThatForcesTheBraceToBeWrapped(){doSomething()}guardletvalue=aValueReturnedByAVeryLongOptionalThing(),letvalue2=aDifferentValueReturnedByAVeryLongOptionalThing()else{doSomething()}guardletvalue=aValueReturnedByAVeryLongOptionalThing(),letvalue2=aDifferentValueReturnedByAVeryLongOptionalThing()else{doSomething()}forelementincollectionwhereelement.happensToHaveAVeryLongPropertyNameThatYouNeedToCheck{doSomething()}
Other Expressions
When line-wrapping other expressions that are not function calls (as describedabove), the second line (the one immediately following the first break) isindented exactly +2 from the original line.
When there are multiple continuation lines, indentation may be varied inincrements of +2 as needed. In general, two continuation lines use the sameindentation level if and only if they begin with syntactically parallelelements. However, if there are many continuation lines caused by long wrappedexpressions, consider splitting them into multiple statements using temporaryvariables when possible.
letresult=anExpression+thatIsMadeUpOf*aLargeNumber+ofTerms/andTherefore%mustBeWrapped+(andWeWill-keepMakingItLonger*soThatWeHave/aContrivedExample)
letresult=anExpression+thatIsMadeUpOf*aLargeNumber+ofTerms/andTherefore%mustBeWrapped+(andWeWill-keepMakingItLonger*soThatWeHave/aContrivedExample)
Horizontal Whitespace
Terminology note: In this section,horizontal whitespace refers tointerior space. These rules are never interpreted as requiring or forbiddingadditional space at the start of a line.
Beyond where required by the language or other style rules, and apart fromliterals and comments, a single Unicode space also appears in the followingplacesonly:
Separating any reserved word starting a conditional or switch statement (suchas
if
,guard
,while
, orswitch
) from the expression that follows itif that expression starts with an open parenthesis ((
).if(x==0&&y==0)||z==0{// ...}
if(x==0&&y==0)||z==0{// ...}
Before any closing curly brace (
}
) that follows code on the same line,before any open curly brace ({
), and after any open curly brace ({
) thatis followed by code on the same line.letnonNegativeCubes=numbers.map{$0*$0*$0}.filter{$0>=0}
letnonNegativeCubes=numbers.map{$0*$0*$0}.filter{$0>=0}letnonNegativeCubes=numbers.map{$0*$0*$0}.filter{$0>=0}
On both sides of any binary or ternary operator, including the“operator-like” symbols described below, with exceptions noted at the end:
The
=
sign used in assignment, initialization of variables/properties,and default arguments in functions.varx=5funcsum(_numbers:[Int],initialValue:Int=0){// ...}
varx=5funcsum(_numbers:[Int],initialValue:Int=0){// ...}
The ampersand (
&
) in a protocol composition type.funcsayHappyBirthday(toperson:NameProviding&AgeProviding){// ...}
funcsayHappyBirthday(toperson:NameProviding&AgeProviding){// ...}
The operator symbol in a function declaring/implementing that operator.
staticfunc==(lhs:MyType,rhs:MyType)->Bool{// ...}
staticfunc==(lhs:MyType,rhs:MyType)->Bool{// ...}
The arrow (
->
) preceding the return type of a function.funcsum(_numbers:[Int])->Int{// ...}
funcsum(_numbers:[Int])->Int{// ...}
Exception: There is no space on either side of the dot (
.
) used toreference value and type members.letwidth=view.bounds.width
letwidth=view.bounds.width
Exception: There is no space on either side of the
..<
or...
operators used in range expressions.fornumberin1...5{// ...}letsubstring=string[index..<string.endIndex]
fornumberin1...5{// ...}letsubstring=string[index..<string.endIndex]
After, but not before, the comma (
,
) in parameter lists and intuple/array/dictionary literals.letnumbers=[1,2,3]
letnumbers=[1,2,3]letnumbers=[1,2,3]letnumbers=[1,2,3]
After, but not before, the colon (
:
) inSuperclass/protocol conformance lists and generic constraints.
structHashTable:Collection{// ...}structAnyEquatable<Wrapped:Equatable>:Equatable{// ...}
structHashTable:Collection{// ...}structAnyEquatable<Wrapped:Equatable>:Equatable{// ...}
Function argument labels and tuple element labels.
lettuple:(x:Int,y:Int)funcsum(_numbers:[Int]){// ...}
lettuple:(x:Int,y:Int)lettuple:(x:Int,y:Int)funcsum(_numbers:[Int]){// ...}funcsum(_numbers:[Int]){// ...}
Variable/property declarations with explicit types.
letnumber:Int=5
letnumber:Int=5letnumber:Int=5
Shorthand dictionary type names.
varnameAgeMap:[String:Int]=[]
varnameAgeMap:[String:Int]=[]varnameAgeMap:[String:Int]=[]
Dictionary literals.
letnameAgeMap=["Ed":40,"Timmy":9]
letnameAgeMap=["Ed":40,"Timmy":9]letnameAgeMap=["Ed":40,"Timmy":9]
At least two spaces before and exactly one space after the double slash(
//
) that begins an end-of-line comment.letinitialFactor=2// Warm up the modulator.
letinitialFactor=2// Warm up the modulator.
Outside, but not inside, the brackets of an array or dictionary literals andthe parentheses of a tuple literal.
letnumbers=[1,2,3]
letnumbers=[1,2,3]
Horizontal Alignment
Terminology note:Horizontal alignment is the practice of adding avariable number of additional spaces in your code with the goal of makingcertain tokens appear directly below certain other tokens on previous lines.
Horizontal alignment is forbidden except when writing obviously tabular datawhere omitting the alignment would be harmful to readability. In other cases(for example, lining up the types of stored property declarations in astruct
orclass
), horizontal alignment is an invitation for maintenance problems if anew member is introduced that requires every other member to be realigned.
structDataPoint{varvalue:IntvarprimaryColor:UIColor}
structDataPoint{varvalue:IntvarprimaryColor:UIColor}
Vertical Whitespace
A single blank line appears in the following locations:
Between consecutive members of a type: properties, initializers, methods,enum cases, and nested types,except that:
- A blank line is optional between two consecutive stored properties or twoenum cases whose declarations fit entirely on a single line. Such blanklines can be used to createlogical groupings of these declarations.
- A blank line is optional between two extremely closely related propertiesthat do not otherwise meet the criterion above; for example, a privatestored property and a related public computed property.
- Only as needed between statements to organize code into logicalsubsections.
- Optionally before the first member or after the last member of a type(neither is encouraged nor discouraged).
- Anywhere explicitly required by other sections of this document.
Multiple blank lines are permitted, but never required (nor encouraged). Ifyou do use multiple consecutive blank lines, do so consistently throughout yourcode base.
Parentheses
Parentheses arenot used around the top-most expression that follows anif
,guard
,while
, orswitch
keyword.
ifx==0{print("x is zero")}if(x==0||y==1)&&z==2{print("...")}
if(x==0){print("x is zero")}if((x==0||y==1)&&z==2){print("...")}
Optional grouping parentheses are omitted only when the author and the revieweragree that there is no reasonable chance that the code will be misinterpretedwithout them, nor that they would have made the code easier to read. It isnotreasonable to assume that every reader has the entire Swift operator precedencetable memorized.
Formatting Specific Constructs
Non-Documentation Comments
Non-documentation comments always use the double-slash format (//
), never theC-style block format (/* ... */
).
Properties
Local variables are declared close to the point at which they are first used(within reason) to minimize their scope.
With the exception of tuple destructuring, everylet
orvar
statement(whether a property or a local variable) declares exactly one variable.
vara=5varb=10let(quotient,remainder)=divide(100,9)
vara=5,b=10
Switch Statements
Case statements are indented at thesame level as the switch statement towhich they belong; the statements inside the case blocks are then indented +2spaces from that level.
switchorder{case.ascending:print("Ascending")case.descending:print("Descending")case.same:print("Same")}
switchorder{case.ascending:print("Ascending")case.descending:print("Descending")case.same:print("Same")}
switchorder{case.ascending:print("Ascending")case.descending:print("Descending")case.same:print("Same")}
Enum Cases
In general, there is only onecase
per line in anenum
. The comma-delimitedform may be used only when none of the cases have associated values or rawvalues, all cases fit on a single line, and the cases do not need furtherdocumentation because their meanings are obvious from their names.
publicenumToken{casecommacasesemicoloncaseidentifier}publicenumToken{casecomma,semicolon,identifier}publicenumToken{casecommacasesemicoloncaseidentifier(String)}
publicenumToken{casecomma,semicolon,identifier(String)}
When all cases of anenum
must beindirect
, theenum
itself is declaredindirect
and the keyword is omitted on the individual cases.
publicindirectenumDependencyGraphNode{caseuserDefined(dependencies:[DependencyGraphNode])casesynthesized(dependencies:[DependencyGraphNode])}
publicenumDependencyGraphNode{indirectcaseuserDefined(dependencies:[DependencyGraphNode])indirectcasesynthesized(dependencies:[DependencyGraphNode])}
When anenum
case does not have associated values, empty parentheses are neverpresent.
publicenumBinaryTree<Element>{indirectcasenode(element:Element,left:BinaryTree,right:BinaryTree)caseempty// GOOD.}
publicenumBinaryTree<Element>{indirectcasenode(element:Element,left:BinaryTree,right:BinaryTree)caseempty()// AVOID.}
The cases of an enum must follow a logical ordering that the author couldexplain if asked. If there is no obviously logical ordering, use alexicographical ordering based on the cases’ names.
In the following example, the cases are arranged in numerical order based on theunderlying HTTP status code and blank lines are used to separate groups.
publicenumHTTPStatus:Int{caseok=200casebadRequest=400casenotAuthorized=401casepaymentRequired=402caseforbidden=403casenotFound=404caseinternalServerError=500}
The following version of the same enum is less readable. Although the cases areordered lexicographically, the meaningful groupings of related values has beenlost.
publicenumHTTPStatus:Int{casebadRequest=400caseforbidden=403caseinternalServerError=500casenotAuthorized=401casenotFound=404caseok=200casepaymentRequired=402}
Trailing Closures
Functions should not be overloaded such that two overloads differonly by thename of their trailing closure argument. Doing so prevents using trailingclosure syntax—when the label is not present, a call to the function witha trailing closure is ambiguous.
Consider the following example, which prohibits using trailing closure syntax tocallgreet
:
funcgreet(enthusiasticallynameProvider:()->String){print("Hello,\(nameProvider())! It's a pleasure to see you!")}funcgreet(apatheticallynameProvider:()->String){print("Oh, look. It's\(nameProvider()).")}greet{"John"}// error: ambiguous use of 'greet'
This example is fixed by differentiating some part of the function name otherthan the closure argument—in this case, the base name:
funcgreetEnthusiastically(_nameProvider:()->String){print("Hello,\(nameProvider())! It's a pleasure to see you!")}funcgreetApathetically(_nameProvider:()->String){print("Oh, look. It's\(nameProvider()).")}greetEnthusiastically{"John"}greetApathetically{"not John"}
If a function call has multiple closure arguments, thennone are called usingtrailing closure syntax;all are labeled and nested inside the argumentlist’s parentheses.
UIView.animate(withDuration:0.5,animations:{// ...},completion:{finishedin// ...})
UIView.animate(withDuration:0.5,animations:{// ...}){finishedin// ...}
If a function has a single closure argument and it is the final argument, thenit isalways called using trailing closure syntax, except in the followingcases to resolve ambiguity or parsing errors:
- As described above, labeled closure arguments must be used to disambiguatebetween two overloads with otherwise identical arguments lists.
- Labeled closure arguments must be used in control flow statements where thebody of the trailing closure would be parsed as the body of the control flowstatement.
Timer.scheduledTimer(timeInterval:30,repeats:false){timerinprint("Timer done!")}ifletfirstActive=list.first(where:{$0.isActive}){process(firstActive)}
Timer.scheduledTimer(timeInterval:30,repeats:false,block:{timerinprint("Timer done!")})// This example fails to compile.ifletfirstActive=list.first{$0.isActive}{process(firstActive)}
When a function called with trailing closure syntax takes no other arguments,empty parentheses (()
) after the function name arenever present.
letsquares=[1,2,3].map{$0*$0}
letsquares=[1,2,3].map({$0*$0})letsquares=[1,2,3].map(){$0*$0}
Trailing Commas
Trailing commas in array and dictionary literals arerequired when eachelement is placed on its own line. Doing so produces cleaner diffs when itemsare added to those literals later.
letconfigurationKeys=["bufferSize","compression","encoding",// GOOD.]
letconfigurationKeys=["bufferSize","compression","encoding"// AVOID.]
Numeric Literals
It is recommended but not required that long numeric literals (decimal,hexadecimal, octal, and binary) use the underscore (_
) separator to groupdigits for readability when the literal has numeric value or when there exists adomain-specific grouping.
Recommended groupings are three digits for decimal (thousands separators), fourdigits for hexadecimal, four or eight digits for binary literals, orvalue-specific field boundaries when they exist (such as three digits for octalfile permissions).
Do not group digits if the literal is an opaque identifier that does not have ameaningful numeric value.
Attributes
Parameterized attributes (such as@availability(...)
or@objc(...)
) are eachwritten on their own line immediately before the declaration to which theyapply, are lexicographically ordered, and are indented at the same level as thedeclaration.
@available(iOS 9.0, *)publicfunccoolNewFeature(){// ...}
@available(iOS 9.0, *)publicfunccoolNewFeature(){// ...}
Attributes without parameters (for example,@objc
without arguments,@IBOutlet
, or@NSManaged
) are lexicographically ordered andmay be placedon the same line as the declaration if and only if they would fit on that linewithout requiring the line to be rewrapped. If placing an attribute on the sameline as the declaration would require a declaration to be wrapped thatpreviously did not need to be wrapped, then the attribute is placed on its ownline.
publicclassMyViewController:UIViewController{@IBOutletprivatevartableView:UITableView!}
Naming
Apple’s API Style Guidelines
Apple’sofficial Swift naming and API design guidelineshosted on swift.org are considered part of this style guide and are followed asif they were repeated here in their entirety.
Naming Conventions Are Not Access Control
Restricted access control (internal
,fileprivate
, orprivate
) is preferredfor the purposes of hiding information from clients, rather than namingconventions.
Naming conventions (such as prefixing a leading underscore) are only used inrare situations when a declaration must be given higher visibility than isotherwise desired in order to work around language limitations—forexample, a type that has a method that is only intended to be called by otherparts of a library implementation that crosses module boundaries and musttherefore be declaredpublic
.
Identifiers
In general, identifiers contain only 7-bit ASCII characters. Unicode identifiersare allowed if they have a clear and legitimate meaning in the problem domainof the code base (for example, Greek letters that represent mathematicalconcepts) and are well understood by the team who owns the code.
letsmile="😊"letdeltaX=newX-previousXletΔx=newX-previousX
let😊="😊"
Initializers
For clarity, initializer arguments that correspond directly to a stored propertyhave the same name as the property. Explicitself.
is used during assignmentto disambiguate them.
publicstructPerson{publicletname:StringpublicletphoneNumber:String// GOOD.publicinit(name:String,phoneNumber:String){self.name=nameself.phoneNumber=phoneNumber}}
publicstructPerson{publicletname:StringpublicletphoneNumber:String// AVOID.publicinit(nameotherName:String,phoneNumberotherPhoneNumber:String){name=otherNamephoneNumber=otherPhoneNumber}}
Static and Class Properties
Static and class properties that return instances of the declaring type arenot suffixed with the name of the type.
publicclassUIColor{publicclassvarred:UIColor{// GOOD.// ...}}publicclassURLSession{publicclassvarshared:URLSession{// GOOD.// ...}}
publicclassUIColor{publicclassvarredColor:UIColor{// AVOID.// ...}}publicclassURLSession{publicclassvarsharedSession:URLSession{// AVOID.// ...}}
When a static or class property evaluates to a singleton instance of thedeclaring type, the namesshared
anddefault
are commonly used. This styleguide does not require specific names for these; the author should choose a namethat makes sense for the type.
Global Constants
Like other variables, global constants arelowerCamelCase
. Hungarian notation,such as a leadingg
ork
, is not used.
letsecondsPerMinute=60
letSecondsPerMinute=60letkSecondsPerMinute=60letgSecondsPerMinute=60letSECONDS_PER_MINUTE=60
Delegate Methods
Methods on delegate protocols and delegate-like protocols (such as data sources)are named using the linguistic syntax described below, which is inspired byCocoa’s protocols.
The term “delegate’s source object” refers to the object that invokes methodson the delegate. For example, a
UITableView
is the source object thatinvokes methods on theUITableViewDelegate
that is set as the view’sdelegate
property.
All methods take the delegate’s source object as the first argument.
For methods that take the delegate’s source object as theironly argument:
If the method returns
Void
(such as those used to notify the delegate thatan event has occurred), then the method’s base name is thedelegate’ssource type followed by anindicative verb phrase describing theevent. The argument isunlabeled.funcscrollViewDidBeginScrolling(_scrollView:UIScrollView)
If the method returns
Bool
(such as those that make an assertion about thedelegate’s source object itself), then the method’s name is thedelegate’ssource type followed by anindicative or conditional verb phrasedescribing the assertion. The argument isunlabeled.funcscrollViewShouldScrollToTop(_scrollView:UIScrollView)->Bool
If the method returns some other value (such as those querying forinformation about a property of the delegate’s source object), then themethod’s base name is anoun phrase describing the property beingqueried. The argument islabeled with a preposition or phrase with atrailing preposition that appropriately combines the noun phrase and thedelegate’s source object.
funcnumberOfSections(inscrollView:UIScrollView)->Int
For methods that takeadditional arguments after the delegate’s sourceobject, the method’s base name is the delegate’s source typeby itself andthe first argument isunlabeled. Then:
If the method returns
Void
, the second argument islabeled with anindicative verb phrase describing the event that has the argument as itsdirect object or prepositional object, and any other arguments (ifpresent) provide further context.functableView(_tableView:UITableView,willDisplayCellcell:UITableViewCell,forRowAtindexPath:IndexPath)
If the method returns
Bool
, the second argument islabeled with anindicative or conditional verb phrase that describes the return value interms of the argument, and any other arguments (if present) provide furthercontext.functableView(_tableView:UITableView,shouldSpringLoadRowAtindexPath:IndexPath,withcontext:UISpringLoadedInteractionContext)->Bool
If the method returns some other value, the second argument islabeledwith a noun phrase and trailing preposition that describes the returnvalue in terms of the argument, and any other arguments (if present) providefurther context.
functableView(_tableView:UITableView,heightForRowAtindexPath:IndexPath)->CGFloat
Apple’s documentation ondelegates and data sourcesalso contains some good general guidance about such names.
Programming Practices
Common themes among the rules in this section are: avoid redundancy, avoidambiguity, and prefer implicitness over explicitness unless being explicitimproves readability and/or reduces ambiguity.
Compiler Warnings
Code should compile without warnings when feasible. Any warnings that are ableto be removed easily by the author must be removed.
A reasonable exception is deprecation warnings, where it may not be possible toimmediately migrate to the replacement API, or where an API may be deprecatedfor external users but must still be supported inside a library during adeprecation period.
Initializers
Forstruct
s, Swift synthesizes a non-public memberwiseinit
that takesarguments forvar
properties and for anylet
properties that lack defaultvalues. When that initializer is suitable (that is, apublic
one is notneeded), it is used and no explicit initializer is written.
The initializers declared by the specialExpressibleBy*Literal
compilerprotocols are never called directly.
structKilometers:ExpressibleByIntegerLiteral{init(integerLiteralvalue:Int){// ...}}letk1:Kilometers=10// GOOD.letk2=10asKilometers// ALSO GOOD.
structKilometers:ExpressibleByIntegerLiteral{init(integerLiteralvalue:Int){// ...}}letk=Kilometers(integerLiteral:10)// AVOID.
Explicitly calling.init(...)
is allowed only when the receiver of the call isa metatype variable. In direct calls to the initializer using the literal typename,.init
is omitted. (Referring to the initializer directly by usingMyType.init
syntax to convert it to a closure is permitted.)
letx=MyType(arguments)lettype=lookupType(context)letx=type.init(arguments)letx=makeValue(factory:MyType.init)
letx=MyType.init(arguments)
Properties
Theget
block for a read-only computed property is omitted and its body isdirectly nested inside the property declaration.
vartotalCost:Int{returnitems.sum{$0.cost}}
vartotalCost:Int{get{returnitems.sum{$0.cost}}}
Types with Shorthand Names
Arrays, dictionaries, and optional types are written in their shorthand formwhenever possible; that is,[Element]
,[Key: Value]
, andWrapped?
. Thelong formsArray<Element>
,Dictionary<Key, Value>
, andOptional<Wrapped>
are only written when required by the compiler; for example, the Swift parserrequiresArray<Element>.Index
and does not accept[Element].Index
.
funcenumeratedDictionary<Element>(fromvalues:[Element],start:Array<Element>.Index?=nil)->[Int:Element]{// ...}
funcenumeratedDictionary<Element>(fromvalues:Array<Element>,start:Optional<Array<Element>.Index>=nil)->Dictionary<Int,Element>{// ...}
Void
is atypealias
for the empty tuple()
, so from an implementationpoint of view they are equivalent. In function type declarations (such asclosures, or variables holding a function reference), the return type is alwayswritten asVoid
, never as()
. In functions declared with thefunc
keyword,theVoid
return type is omitted entirely.
Empty argument lists are always written as()
, never asVoid
. (In fact,the function signatureVoid -> Result
is an error in Swift because functionarguments must be surrounded by parentheses, and(Void)
has a differentmeaning: an argument list with a single empty-tuple argument.)
funcdoSomething(){// ...}letcallback:()->Void
funcdoSomething()->Void{// ...}funcdoSomething2()->(){// ...}letcallback:()->()
Optional Types
Sentinel values are avoided when designing algorithms (for example, an “index”of −1 when an element was not found in a collection). Sentinel values caneasily and accidentally propagate through other layers of logic because the typesystem cannot distinguish between them and valid outcomes.
Optional
is used to convey a non-error result that is either a value or theabsence of a value. For example, when searching a collection for a value, notfinding the value is still avalid and expected outcome, not an error.
funcindex(ofthing:Thing,inthings:[Thing])->Int?{// ...}ifletindex=index(of:thing,in:lotsOfThings){// Found it.}else{// Didn't find it.}
funcindex(ofthing:Thing,inthings:[Thing])->Int{// ...}letindex=index(of:thing,in:lotsOfThings)ifindex!=-1{// Found it.}else{// Didn't find it.}
Optional
is also used for error scenarios when there is a single, obviousfailure state; that is, when an operation may fail for a single domain-specificreason that is clear to the client. (The domain-specific restriction is meant toexclude severe errors that are typically out of the user’s control to properlyhandle, such as out-of-memory errors.)
For example, converting a string to an integer would fail if thestring does not represent a valid integer that fits into the type’s bit width:
structInt17{init?(_string:String){// ...}}
Conditional statements that test that anOptional
is non-nil
but do notaccess the wrapped value are written as comparisons tonil
. The followingexample is clear about the programmer’s intent:
ifvalue!=nil{print("value was not nil")}
This example, while taking advantage of Swift’s pattern matching and bindingsyntax, obfuscates the intent by appearing to unwrap the value and thenimmediately throw it away.
iflet_=value{print("value was not nil")}
Error Types
Error types are used when there are multiple possible error states.
Throwing errors instead of merging them with the return type cleanly separatesconcerns in the API. Valid inputs and valid state produce valid outputs in theresult domain and are handled with standard sequential control flow. Invalidinputs and invalid state are treated as errors and are handled using therelevant syntactic constructs (do
-catch
andtry
). For example:
structDocument{enumReadError:Error{casenotFoundcasepermissionDeniedcasemalformedHeader}init(path:String)throws{// ...}}do{letdocument=tryDocument(path:"important.data")}catchDocument.ReadError.notFound{// ...}catchDocument.ReadError.permissionDenied{// ...}catch{// ...}
Such a design forces the caller to consciously acknowledge the failure case by:
- wrapping the calling code in a
do
-catch
block and handling error cases towhichever degree is appropriate, - declaring the function in which the call is made as
throws
and letting theerror propagate out, or - using
try?
when the specific reason for failure is unimportant and only theinformation about whether the call failed is needed.
In general, with exceptions noted below, force-try!
is forbidden; it isequivalent totry
followed byfatalError
but without a meaningful message.If an error outcome would mean that the program is in such an unrecoverablestate that immediate termination is the only reasonable action, it is better tousedo
-catch
ortry?
and provide more context in the error message toassist debugging if the operation does fail.
Exception: Force-
try!
is allowed in unit tests and test-only code. It isalso allowed in non-test code when it is unmistakably clear that an errorwould only be thrown because ofprogrammer error; we specifically definethis to mean a single expression that could be evaluated without context inthe Swift REPL. For example, consider initializing a regular expression from aa string literal:letregex=try!NSRegularExpression(pattern:"a*b+c?")
The
NSRegularExpression
initializer throws an error if the regularexpression is malformed, but when it is a string literal, the error would onlyoccur if the programmer mistyped it. There is no benefit to writing extraerror handling logic here.If the pattern above were not a literal but instead were dynamic or derivedfrom user input,
try!
shouldnot be used and errors should be handledgracefully.
Force Unwrapping and Force Casts
Force-unwrapping and force-casting are often code smells and are stronglydiscouraged. Unless it is extremely clear from surrounding code why such anoperation is safe, a comment should be present that describes the invariant thatensures that the operation is safe. For example,
letvalue=getSomeInteger()// ...intervening code...// This force-unwrap is safe because `value` is guaranteed to fall within the// valid enum cases because it came from some data source that only permits// those raw values.returnSomeEnum(rawValue:value)!
Exception: Force-unwraps are allowed in unit tests and test-only codewithout additional documentation. This keeps such code free of unnecessarycontrol flow. In the event that
nil
is unwrapped or a cast operation is toan incompatible type, the test will fail which is the desired result.
Implicitly Unwrapped Optionals
Implicitly unwrapped optionals are inherently unsafe and should be avoidedwhenever possible in favor of non-optional declarations or regularOptional
types. Exceptions are described below.
User-interface objects whose lifetimes are based on the UI lifecycle instead ofbeing strictly based on the lifetime of the owning object are allowed to useimplicitly unwrapped optionals. Examples of these include@IBOutlet
properties connected to objects in a XIB file or storyboard, properties that areinitialized externally like in theprepareForSegue
implementation of a callingview controller, and properties that are initialized elsewhere during a class’slife cycle, like views in a view controller’sviewDidLoad
method. Making suchproperties regular optionals can put too much burden on the user to unwrap thembecause they are guaranteed to be non-nil and remain that way once the objectsare ready for use.
classSomeViewController:UIViewController{@IBOutletvarbutton:UIButton!overridefuncviewDidLoad(){populateLabel(for:button)}privatefuncpopulateLabel(forbutton:UIButton){// ...}}
Implicitly unwrapped optionals can also surface in Swift code when usingObjective-C APIs that lack the appropriate nullability attributes. If possible,coordinate with the owners of that code to add those annotations so that theAPIs are imported cleanly into Swift. If this is not possible, try to keep thefootprint of those implicitly unwrapped optionals as small as possible in yourSwift code; that is, do not propagate them through multiple layers of your ownabstractions.
Implicitly unwrapped optionals are also allowed in unit tests. This is forreasons similar to the UI object scenario above—the lifetime of testfixtures often begins not in the test’s initializer but in thesetUp()
methodof a test so that they can be reset before the execution of each test.
Access Levels
Omitting an explicit access level is permitted on declarations. For top-leveldeclarations, the default access level isinternal
. For nested declarations,the default access level is the lesser ofinternal
and the access level of theenclosing declaration.
Specifying an explicit access level at the file level on an extension isforbidden. Each member of the extension has its access level specified if it isdifferent than the default.
extensionString{publicvarisUppercase:Bool{// ...}publicvarisLowercase:Bool{// ...}}
publicextensionString{varisUppercase:Bool{// ...}varisLowercase:Bool{// ...}}
Nesting and Namespacing
Swift allowsenum
s,struct
s, andclass
es to be nested, so nesting ispreferred (instead of naming conventions) to express scoped and hierarchicalrelationships among types when possible. For example, flagenum
s or errortypes that are associated with a specific type are nested in that type.
classParser{enumError:Swift.Error{caseinvalidToken(String)caseunexpectedEOF}funcparse(text:String)throws{// ...}}
classParser{funcparse(text:String)throws{// ...}}enumParseError:Error{caseinvalidToken(String)caseunexpectedEOF}
Swift does not currently allow protocols to be nested in other types or viceversa, so this rule does not apply to situations such as the relationshipbetween a controller class and its delegate protocol.
Declaring anenum
without cases is the canonical way to define a “namespace”to group a set of related declarations, such as constants or helper functions.Thisenum
automatically has no instances and does not require that extraboilerplate code be written to prevent instantiation.
enumDimensions{staticlettileMargin:CGFloat=8staticlettilePadding:CGFloat=4staticlettileContentSize:CGSize(width:80,height:64)}
structDimensions{privateinit(){}staticlettileMargin:CGFloat=8staticlettilePadding:CGFloat=4staticlettileContentSize:CGSize(width:80,height:64)}
guard
s for Early Exits
Aguard
statement, compared to anif
statement with an inverted condition,provides visual emphasis that the condition being tested is a special case thatcauses early exit from the enclosing scope.
Furthermore,guard
statements improve readability by eliminating extra levelsof nesting (the “pyramid of doom”); failure conditions are closely coupled tothe conditions that trigger them and the main logic remains flush left withinits scope.
This can be seen in the following examples; in the first, there is a clearprogression that checks for invalid states and exits, then executes the mainlogic in the successful case. In the second example withoutguard
, the mainlogic is buried at an arbitrary nesting level and the thrown errors areseparated from their conditions by a great distance.
funcdiscombobulate(_values:[Int])throws->Int{guardletfirst=values.firstelse{throwDiscombobulationError.arrayWasEmpty}guardfirst>=0else{throwDiscombobulationError.negativeEnergy}varresult=0forvalueinvalues{result+=invertedCombobulatoryFactory(of:value)}returnresult}
funcdiscombobulate(_values:[Int])throws->Int{ifletfirst=values.first{iffirst>=0{varresult=0forvalueinvalues{result+=invertedCombobulatoryFactor(of:value)}returnresult}else{throwDiscombobulationError.negativeEnergy}}else{throwDiscombobulationError.arrayWasEmpty}}
Aguard
-continue
statement can also be useful in a loop to avoid increasedindentation when the entire body of the loop should only be executed in somecases (but see also thefor
-where
discussion below.)
for
-where
Loops
When the entirety of afor
loop’s body would be a singleif
block testing acondition of the element, the test is placed in thewhere
clause of thefor
statement instead.
foritemincollectionwhereitem.hasProperty{// ...}
foritemincollection{ifitem.hasProperty{// ...}}
fallthrough
inswitch
Statements
When multiplecase
s of aswitch
would execute the same statements, thecase
patterns are combined into ranges or comma-delimited lists. Multiplecase
statements that do nothing butfallthrough
to acase
below are notallowed.
switchvalue{case1:print("one")case2...4:print("two to four")case5,7:print("five or seven")default:break}
switchvalue{case1:print("one")case2:fallthroughcase3:fallthroughcase4:print("two to four")case5:fallthroughcase7:print("five or seven")default:break}
In other words, there is never acase
whose body containsonly thefallthrough
statement. Cases containingadditional statements which thenfallthrough to the next case are permitted.
Pattern Matching
Thelet
andvar
keywords are placed individually in front ofeach elementin a pattern that is being matched. The shorthand version oflet
/var
thatprecedes and distributes across the entire pattern is forbidden because it canintroduce unexpected behavior if a value being matched in a pattern is itself avariable.
enumDataPoint{caseunlabeled(Int)caselabeled(String,Int)}letlabel="goodbye"// `label` is treated as a value here because it is not preceded by `let`, so// the pattern below matches only data points that have the label "goodbye".switchDataPoint.labeled("hello",100){case.labeled(label,letvalue):// ...}// Writing `let` before each individual binding clarifies that the intent is to// introduce a new binding (shadowing the local variable within the case) rather// than to match against the value of the local variable. Thus, this pattern// matches data points with any string label.switchDataPoint.labeled("hello",100){case.labeled(letlabel,letvalue):// ...}
In the example below, if the author’s intention was to match using the value ofthelabel
variable above, that has been lost becauselet
distributes acrossthe entire pattern and thus shadows the variable with a binding that applies toany string value:
switchDataPoint.labeled("hello",100){caselet.labeled(label,value):// ...}
Labels of tuple arguments andenum
associated values are omitted when bindinga value to a variable with the same name as the label.
enumBinaryTree<Element>{indirectcasesubtree(left:BinaryTree<Element>,right:BinaryTree<Element>)caseleaf(element:Element)}switchtreeNode{case.subtree(letleft,letright):// ...case.leaf(letelement):// ...}
Including the labels adds noise that is redundant and lacking usefulinformation:
switchtreeNode{case.subtree(left:letleft,right:letright):// ...case.leaf(element:letelement):// ...}
Tuple Patterns
Assigning variables through a tuple pattern (sometimes referred to as atupleshuffle) is only permitted if the left-hand side of the assignment isunlabeled.
let(a,b)=(y:4,x:5.0)
let(x:a,y:b)=(y:4,x:5.0)
Labels on the left-hand side closely resemble type annotations, and can lead toconfusing code.
// This declares two variables, `Int`, which is a `Double` with value 5.0, and// `Double`, which is an `Int` with value 4.// `x` and `y` are not variables.let(x:Int,y:Double)=(y:4,x:5.0)
Numeric and String Literals
Integer and string literals in Swift do not have an intrinsic type. For example,5
by itself is not anInt
; it is a special literal value that can expressany type that conforms toExpressibleByIntegerLiteral
and only becomes anInt
if type inference does not map it to a more specific type. Likewise, theliteral"x"
is neitherString
norCharacter
norUnicodeScalar
, but itcan become any of those types depending on its context, falling back toString
as a default.
Thus, when a literal is used to initialize a value of a type other than itsdefault, and when that type cannot be inferred otherwise by context, specify thetype explicitly in the declaration or use anas
expression to coerce it.
// Without a more explicit type, x1 will be inferred as type Int.letx1=50// These are explicitly type Int32.letx2:Int32=50letx3=50asInt32// Without a more explicit type, y1 will be inferred as type String.lety1="a"// These are explicitly type Character.lety2:Character="a"lety3="a"asCharacter// These are explicitly type UnicodeScalar.lety4:UnicodeScalar="a"lety5="a"asUnicodeScalarfuncwriteByte(_byte:UInt8){// ...}// Inference also occurs for function arguments, so 50 is a UInt8 without// explicitly coercion.writeByte(50)
The compiler will emit errors appropriately for invalid literal coercions if,for example, a number does not fit into the integer type or a multi-characterstring is coerced to a character. So while the following examples emit errors,they are “good” because the errors are caught at compile-time and for the rightreasons.
// error: integer literal '9223372036854775808' overflows when stored into 'Int64'leta=0x8000_0000_0000_0000asInt64// error: cannot convert value of type 'String' to type 'Character' in coercionletb="ab"asCharacter
Using initializer syntax for these types of coercions can lead to misleadingcompiler errors, or worse, hard-to-debug runtime errors.
// This first tries to create an `Int` (signed) from the literal and then// convert it to a `UInt64`. Even though this literal fits into a `UInt64`, it// doesn't fit into an `Int` first, so it doesn't compile.leta1=UInt64(0x8000_0000_0000_0000)// This invokes `Character.init(_: String)`, thus creating a `String` "a" at// runtime (which involves a slow heap allocation), extracting the character// from it, and then releasing it. This is significantly slower than a proper// coercion.letb=Character("a")// As above, this creates a `String` and then `Character.init(_: String)`// attempts to extract the single character from it. This fails a precondition// check and traps at runtime.letc=Character("ab")
Playground Literals
The graphically-rendered playground literals#colorLiteral(...)
,#imageLiteral(...)
, and#fileLiteral(...)
are forbidden in non-playgroundproduction code. They are permitted in playground sources.
letcolor=UIColor(red:1.0,green:1.0,blue:1.0,alpha:1.0)
letcolor=#colorLiteral(red:1.0,green:1.0,blue:1.0,alpha:1.0)
Trapping vs. Overflowing Arithmetic
The standard (trapping-on-overflow) arithmetic and bitwise operators (+
,-
,*
,<<
, and>>
) are used for most normal operations, rather than themasking operations (preceded by&
). Trapping on overflow is safer because itprevents bad data from propagating through other layers of the system.
// GOOD. Overflow will not cause the balance to go negative.letnewBankBalance=oldBankBalance+recentHugeProfit
// AVOID. Overflow will cause the balance to go negative if the summands are// large.letnewBankBalance=oldBankBalance&+recentHugeProfit
Masking operations are comparatively rare but are permitted (and in factnecessary for correctness) in problem domains that use modular arithmetic, suchas cryptography, big-integer implementations, hash functions, and so forth.
varhashValue:Int{// GOOD. What matters here is the distribution of the bit pattern rather than// the actual numeric value.returnfoo.hashValue&+31*(bar.hashValue&+31&*baz.hashValue)}
varhashValue:Int{// INCORRECT. This will trap arbitrarily and unpredictably depending on the// hash values of the individual terms.returnfoo.hashValue+31*(bar.hashValue+31*baz.hashValue)}
Masking operations are also permitted in performance-sensitive code where thevalues are already known to not cause overflow (or where overflow is not aconcern). In this case, comments should be used to indicate why the use ofmasking operations is important. Additionally, consider adding debugpreconditions to check these assumptions without affecting performance ofoptimized builds.
Defining New Operators
When used unwisely, custom-defined operators can significantly reduce thereadability of code because such operators often lack the historical context ofthe more common ones built into the standard library.
In general, defining custom operators should be avoided. However, it is allowedwhen an operator has a clear and well-defined meaning in the problem domainand when using an operator significantly improves the readability of the codewhen compared to function calls. For example, since*
is the onlymultiplication operator defined by Swift (not including the masking version), anumeric matrix library may define additional operators to support otheroperations like cross product and dot product.
An example of a prohibited use case is defining custom<~~
and~~>
operatorsto decode and encode JSON data. Such operators are not native to the problemdomain of processing JSON and even an experienced Swift engineer would havedifficulty understanding the purpose of the code without seeking outdocumentation of those operators.
If you must use third-party code of unquestionable value that provides an APIonly available through custom operators, you arestrongly encouraged toconsider writing a wrapper that defines more readable methods that delegate tothe custom operators. This will significantly reduce the learning curve requiredto understand how such code works for new teammates and other code reviewers.
Overloading Existing Operators
Overloading operators is permitted when your use of the operator is semanticallyequivalent to the existing uses in the standard library. Examples of permitteduse cases are implementing the operator requirements forEquatable
andHashable
, or defining a newMatrix
type that supports arithmetic operations.
If you wish to overload an existing operator with a meaning other than itsnatural meaning, follow the guidance inDefining New Operators to determine whether this ispermitted. In other words, if the new meaning is well-established in the problemdomain and the use of the operator is a readability improvement over othersyntactic constructs, then it is permitted.
An example of a prohibited case of operator repurposing would be to overload*
and+
to build an ad hoc regular expression API. Such an API would not providestrong enough readability benefits compared to simply representing the entireregular expression as a string.
Documentation Comments
General Format
Documentation comments are written using the format where each line is precededby a triple slash (///
). Javadoc-style block comments (/** ... */
) are notpermitted.
/// Returns the numeric value of the given digit represented as a Unicode scalar.////// - Parameters:/// - digit: The Unicode scalar whose numeric value should be returned./// - radix: The radix, between 2 and 36, used to compute the numeric value./// - Returns: The numeric value of the scalar.funcnumericValue(ofdigit:UnicodeScalar,radix:Int=10)->Int{// ...}
/** * Returns the numeric value of the given digit represented as a Unicode scalar. * * - Parameters: * - digit: The Unicode scalar whose numeric value should be returned. * - radix: The radix, between 2 and 36, used to compute the numeric value. * - Returns: The numeric value of the scalar. */funcnumericValue(ofdigit:UnicodeScalar,radix:Int=10)->Int{// ...}/**Returns the numeric value of the given digit represented as a Unicode scalar.- Parameters: - digit: The Unicode scalar whose numeric value should be returned. - radix: The radix, between 2 and 36, used to compute the numeric value.- Returns: The numeric value of the scalar.*/funcnumericValue(ofdigit:UnicodeScalar,radix:Int=10)->Int{// ...}
Single-Sentence Summary
Documentation comments begin with a briefsingle-sentence summary thatdescribes the declaration. (This sentence may span multiple lines, but if itspans too many lines, the author should consider whether the summary can besimplified and details moved to a new paragraph.)
If more detail is needed than can be stated in the summary, additionalparagraphs (each separated by a blank line) are added after it.
The single-sentence summary is not necessarily a complete sentence; for example,method summaries are generally written as verb phraseswithout “this method[…]” because it is already implied as the subject and writing it out would beredundant. Likewise, properties are often written as noun phraseswithout“this property is […]”. In any case, however, they are still terminated with aperiod.
/// The background color of the view.varbackgroundColor:UIColor/// Returns the sum of the numbers in the given array.////// - Parameter numbers: The numbers to sum./// - Returns: The sum of the numbers.funcsum(_numbers:[Int])->Int{// ...}
/// This property is the background color of the view.varbackgroundColor:UIColor/// This method returns the sum of the numbers in the given array.////// - Parameter numbers: The numbers to sum./// - Returns: The sum of the numbers.funcsum(_numbers:[Int])->Int{// ...}
Parameter, Returns, and Throws Tags
Clearly document the parameters, return value, and thrown errors of functionsusing theParameter(s)
,Returns
, andThrows
tags, in that order. None everappears with an empty description. When a description does not fit on a singleline, continuation lines are indented 2 spaces in from the position of thehyphen starting the tag.
The recommended way to write documentation comments in Xcode is to place thetext cursor on the declaration and pressCommand + Option + /. This willautomatically generate the correct format with placeholders to be filled in.
Parameter(s)
andReturns
tags may be omitted only if the single-sentencebrief summary fully describes the meaning of those items and including the tagswould only repeat what has already been said.
The content following theParameter(s)
,Returns
, andThrows
tags should beterminated with a period, even when they are phrases instead of completesentences.
When a method takes a single argument, the singular inline form of theParameter
tag is used. When a method takes multiple arguments, the groupedplural formParameters
is used and each argument is written as an item in anested list with only its name as the tag.
/// Returns the output generated by executing a command.////// - Parameter command: The command to execute in the shell environment./// - Returns: A string containing the contents of the invoked process's/// standard output.funcexecute(command:String)->String{// ...}/// Returns the output generated by executing a command with the given string/// used as standard input.////// - Parameters:/// - command: The command to execute in the shell environment./// - stdin: The string to use as standard input./// - Returns: A string containing the contents of the invoked process's/// standard output.funcexecute(command:String,stdin:String)->String{// ...}
The following examples are incorrect, because they use the plural form ofParameters
for a single parameter or the singular formParameter
formultiple parameters.
/// Returns the output generated by executing a command.////// - Parameters:/// - command: The command to execute in the shell environment./// - Returns: A string containing the contents of the invoked process's/// standard output.funcexecute(command:String)->String{// ...}/// Returns the output generated by executing a command with the given string/// used as standard input.////// - Parameter command: The command to execute in the shell environment./// - Parameter stdin: The string to use as standard input./// - Returns: A string containing the contents of the invoked process's/// standard output.funcexecute(command:String,stdin:String)->String{// ...}
Apple’s Markup Format
Use ofApple’s markup formatis strongly encouraged to add rich formatting to documentation. Such markuphelps to differentiate symbolic references (like parameter names) fromdescriptive text in comments and is rendered by Xcode and other documentationgeneration tools. Some examples of frequently used directives are listed below.
- Paragraphs are separated using a single line that starts with
///
and isotherwise blank. - *Single asterisks* and_single underscores_ surround text that shouldbe rendered in italic/oblique type.
- **Double asterisks** and__double underscores__ surround textthat should be rendered in boldface.
- Names of symbols or inline code are surrounded in
`backticks`
. - Multi-line code (such as example usage) is denoted by placing three backticks(
`` `) on the lines before and after the code block.
Where to Document
At a minimum, documentation comments are present for every open or publicdeclaration, and every open or public member of such a declaration, withspecific exceptions noted below:
Individual cases of an
enum
often are not documented if their meaning isself-explanatory from their name. Cases with associated values, however,should document what those values mean if it is not obvious.A documentation comment is not always present on a declaration that overridesa supertype declaration or implements a protocol requirement, or on adeclaration that provides the default implementation of a protocol requirementin an extension.
It is acceptable to document an overridden declaration to describe newbehavior from the declaration that it overrides. In no case should thedocumentation for the override be a mere copy of the base declaration’sdocumentation.
A documentation comment is not always present on test classes and testmethods. However, they can be useful for functional test classes and forhelper classes/methods shared by multiple tests.
A documentation comment is not always present on an extension declaration(that is, the
extension
itself). You may choose to add one if it helpclarify the purpose of the extension, but avoid meaningless or misleadingcomments.In the following example, the comment is just repetition of what is alreadyobvious from the source code:
/// Add `Equatable` conformance.extensionMyType:Equatable{// ...}
The next example is more subtle, but it is an example of documentation that isnot scalable because the extension or the conformance could be updated in thefuture. Consider that the type may be made
Comparable
at the time of thatwriting in order to sort the values, but that is not the only possible use ofthat conformance and client code could use it for other purposes in thefuture./// Make `Candidate` comparable so that they can be sorted.extensionCandidate:Comparable{// ...}
In general, if you find yourself writing documentation that simply repeatsinformation that is obvious from the source and sugaring it with words like“a representation of,” then leave the comment out entirely.
However, it isnot appropriate to cite this exception to justify omittingrelevant information that a typical reader might need to know. For example, fora property namedcanonicalName
, don’t omit its documentation (with therationale that it would only say/// The canonical name.
) if a typical readermay have no idea what the term “canonical name” means in that context. Use thedocumentation as an opportunity to define the term.