A guide to our Swift style and conventions.
This is an attempt to encourage patterns that accomplish the following goals (in rough priority order):
- Increased rigor, and decreased likelihood of programmer error
- Increased clarity of intent
- Reduced verbosity
- Fewer debates about aesthetics
If you have suggestions, please see ourcontribution guidelines, then open a pull request. ⚡
- Tabs, not spaces.
- End files with a newline.
- Make liberal use of vertical whitespace to divide code into logical chunks.
- Don’t leave trailing whitespace.
- Not even leading indentation on blank lines.
Uselet foo =…
overvar foo =…
wherever possible (and when in doubt). Only usevar
if you absolutely have to (i.e. youknowthat the value might change, e.g. when using theweak
storage modifier).
Rationale:The intent and meaning of both keywords are clear, butlet-by-defaultresults in safer and clearer code.
Alet
-binding guarantees andclearly signals to the programmerthat its value will never change. Subsequent code can thus make stronger assumptions about its usage.
It becomes easier to reason about code. Had you usedvar
while still making the assumption that the value never changed, you would have to manually check that.
Accordingly, whenever you see avar
identifier being used, assume that it will change and ask yourself why.
When you have to meet certain criteria to continue execution, try to exit early. So, instead of this:
if n.isNumber{
// Use n here
}else{
return
}
use this:
guard n.isNumberelse{
return
}
// Use n here
You can also do it withif
statement, but usingguard
is preferred, becauseguard
statement withoutreturn
,break
orcontinue
produces a compile-time error, so exit is guaranteed.
If you have an identifierfoo
of typeFooType?
orFooType!
,don't force-unwrap it to get to the underlying value (foo!
) if possible.
Instead, prefer this:
ifletfoo=foo{
// Use unwrapped `foo` value in here
}else{
// If appropriate, handle the case where the optional is nil
}
Alternatively, you might want to use Swift's Optional Chaining in some of these cases, such as:
// Call the function if `foo` is not nil. If `foo` is nil, ignore we ever tried to make the call
foo?.callSomethingIfFooIsNotNil()
Rationale:Explicitif let
-binding of optionals results in safer code. Force unwrapping is more prone to lead to runtime crashes.
Where possible, uselet foo: FooType?
instead oflet foo: FooType!
iffoo
may be nil (Note that in general,?
can be used instead of!
).
Rationale:Explicit optionals result in safer code. Implicitly unwrapped optionals have the potential of crashing at runtime.
When possible, omit theget
keyword on read-only computed properties and
read-only subscripts.
So, write these:
varmyGreatProperty:Int{
return4
}
subscript(index:Int)->T{
returnobjects[index]
}
…not these:
varmyGreatProperty:Int{
get{
return4
}
}
subscript(index:Int)->T{
get{
returnobjects[index]
}
}
Rationale:The intent and meaning of the first version are clear, and results in less code.
Top-level functions, types, and variables should always have explicit access control specifiers:
publicvarwhoopsGlobalState:Int
internalstructTheFez{}
privatefuncdoTheThings(things:[Thing]){}
However, definitions within those can leave access control implicit, where appropriate:
internalstructTheFez{
varowner:Person=Joshaber()
}
Rationale:It's rarely appropriate for top-level definitions to be specificallyinternal
,and being explicit ensures that careful thought goes into that decision. Within a definition, reusing the same access control specifier is just duplicative, and the default is usually reasonable.
When specifying the type of an identifier, always put the colon immediately after the identifier, followed by a space and then the type name.
classSmallBatchSustainableFairtrade:Coffee{...}
lettimeToCoffee:NSTimeInterval=2
funcmakeCoffee(type:CoffeeType)->Coffee{...}
Rationale:The type specifier is saying something about theidentifierso it should be positioned with it.
Also, when specifying the type of a dictionary, always put the colon immediately after the key type, followed by a space and then the value type.
letcapitals:[Country:City]=[sweden:stockholm]
When accessing properties or methods onself
,leave the reference toself
implicit by default:
privateclassHistory{
varevents:[Event]
funcrewrite(){
events=[]
}
}
Only include the explicit keyword when required by the language—for example, in a closure, or when parameter names conflict:
extensionHistory{
init(events:[Event]){
self.events=events
}
varwhenVictorious:()->(){
return{
self.rewrite()
}
}
}
Rationale:This makes the capturing semantics ofself
stand out more in closures, and avoids verbosity elsewhere.
Unless you require functionality that can only be provided by a class (like identity or deinitializers), implement a struct instead.
Note that inheritance is (by itself) usuallynota good reason to use classes, because polymorphism can be provided by protocols, and implementation reuse can be provided through composition.
For example, this class hierarchy:
classVehicle{
letnumberOfWheels:Int
init(numberOfWheels:Int){
self.numberOfWheels=numberOfWheels
}
funcmaximumTotalTirePressure(pressurePerWheel:Float)->Float{
returnpressurePerWheel*Float(numberOfWheels)
}
}
classBicycle:Vehicle{
init(){
super.init(numberOfWheels:2)
}
}
classCar:Vehicle{
init(){
super.init(numberOfWheels:4)
}
}
could be refactored into these definitions:
protocolVehicle{
varnumberOfWheels:Int{get}
}
funcmaximumTotalTirePressure(vehicle:Vehicle,pressurePerWheel:Float)->Float{
returnpressurePerWheel*Float(vehicle.numberOfWheels)
}
structBicycle:Vehicle{
letnumberOfWheels=2
}
structCar:Vehicle{
letnumberOfWheels=4
}
Rationale:Value types are simpler, easier to reason about, and behave as expected with thelet
keyword.
Classes should start asfinal
,and only be changed to allow subclassing if a valid need for inheritance has been identified. Even in that case, as many definitions as possiblewithinthe class should befinal
as well, following the same rules.
Rationale:Composition is usually preferable to inheritance, and optinginto inheritance hopefully means that more thought will be put into the decision.
Methods of parameterized types can omit type parameters on the receiving type when they’re identical to the receiver’s. For example:
structComposite<T>{
…
funccompose(other:Composite<T>)->Composite<T>{
returnComposite<T>(self,other)
}
}
could be rendered as:
structComposite<T>{
…
funccompose(other:Composite)->Composite{
returnComposite(self,other)
}
}
Rationale:Omitting redundant type parameters clarifies the intent, and makes it obvious by contrast when the returned type takes different type parameters.
Use whitespace around operators when defining them. Instead of:
func<|(lhs:Int,rhs:Int)->Int
func<|<<A>(lhs:A,rhs:A)->A
write:
func<|(lhs:Int,rhs:Int)->Int
func<|<<A>(lhs:A,rhs:A)->A
Rationale:Operators consist of punctuation characters, which can make them difficult to read when immediately followed by the punctuation for a type or value parameter list. Adding whitespace separates the two more clearly.