Use Structures over Classes whenever possible. Structures are always copied when they are passed around in your code, and do not use reference counting, so that makes them both faster and easier to reason about, because you know no one else can modify them.
Classes have additional capabilities that structures do not:
- Inheritance enables one class to inherit the characteristics of another.
- Type casting enables you to check and interpret the type of a class instance at runtime.
- Deinitializers enable an instance of a class to free up any resources it has assigned.
- Reference counting allows more than one reference to a class instance.
Unless you need one of the functionals listed above, use Structures.
Note that inheritance is (by itself) usually not a good reason to use classes, because polymorphism can be provided by protocols, and implementation reuse can be provided through composition.
Rationale: Value types are simpler, easier to reason about, and behave as expected with the let keyword.
####When specifying a type, always associate the colon with the identifier
class SmallBatchSustainableFairtrade: Coffee { ... }
let timeToCoffee: NSTimeInterval = 2
func makeCoffee(type: CoffeeType) -> Coffee { ... }
###Constants and Variables
Swift constants and variables associate a name with a value of a particular type. The value of a //constant// cannot be changed once it is set, whereas a //variable// can be set to a different value in the future.
var x = 1.2
let isBlue = true
Use ''let foo = …'' over ''var foo = …'' wherever possible (and when in doubt). Only use ''var'' if you absolutely have to (i.e. you know that the value might change, e.g. when using the ''weak'' storage modifier).
//Rationale:// The intent and meaning of both keywords is clear, but //let-by-default// results in safer and clearer code.
A ''let''-binding guarantees and //clearly signals to the programmer// that its value is supposed to and will never change. Subsequent code can thus make stronger assumptions about its usage. It becomes easier to reason about code. Had you used ''var'' while still making the assumption that the value never changed, you would have to manually check that. Accordingly, whenever you see a ''var'' identifier being used, assume that it will change and ask yourself why.
If you have an identifier ''foo'' of type ''FooType?'', don't force-unwrap it to get to the underlying value (''foo!'') if possible.
Instead, prefer this:
if let foo = 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:// Explicit ''if let''-binding of optionals results in safer code. Force unwrapping is more prone to lead to runtime crashes.
###Avoid using force casting
Prefer using conditional form of type-casting ''as?'' instead of force ''as!''.
Good:
let a = 12 as? Int64
// a being of Int64? type
Bad:
let a = 12 as! Int64
// a being of Int64! type
//Rationale:// Casting can fail, and handling the variable as a conditional can avoid the program from crashing.
###Use Swift native types =====
Always use Swift's native types when available. Swift offers bridging to Objective-C so you can still use the full set of methods as needed.
Good:
let width = 120.0 // Double
let widthString = (width as NSNumber).stringValue // String
Bad:
let width: NSNumber = 120.0 // NSNumber
let widthString: NSString = width.stringValue // NSString
###Start with action
For methods that represent an action an object takes, start the name with the action.
<code c>
func convertVariablesToFunctions() { ... }
<code c>
func variablesToFunctions() { ... }
It is usually a good ideea to split large functions into smaller ones. Prefer small concise functions over smaller ones.
Whenever you have a large function it can usually be split up into two more atomic functions.
A good rule of thumb is that any function which exceeds 15 lines of code is doing at least 2 different things. Exceptions to this rule are factory methods / methods which perform a big switch / methods which do one then more than 15 times.
Avoid creating methods with too many parameters. The ARM v6/v7/64 architectures allow for 3,5 and 7 parameters to be passed to a function via registers(fast storage), and following parameters are sent through RAM(slow storage). So remember, if you go beyond 3 parameters, you are not only making your method slower by using slower memory, you're also making it harder to read. [[https://www.mikeash.com/pyblog/friday-qa-2014-07-04-secrets-of-swifts-speed.html|Further Documentation]]
Good:
func outputDetailsForFile(file: File) { ... }
// File being an object that contains all those params
Bad:
func outputDetailsForPath(path: String, fileSize: Int, extension: String,
encoding enc: NSStringEncoding) { ... }
Rationale: Operators are usually special symbols(+-/) and when they dont have enough whitespaces they make code much harder to read. The white spaces rule is very universal and simple to remember: #####Binary operators: One space before and one space after Examples: Good:
func <|< <A>(lhs: A, rhs: A) -> A
Bad:
func <|<<A>(lhs: A, rhs: A) -> A
The same holds for arithmetic operators Good:
let size = viewWidth - 2 * FIRST_CONSTANT - secondConstant - totalConstant
Bad:
let size=viewWidth-2*FIRST_CONSTANT-secondConstant-totalConstant
The same rule for the return type of functions:
Good:
func sizeOfObject() -> Float {
}
Bad:
func sizeOfObject()->Float{
}
The same rule holds for the Swift ternary operator (from Apple's Swift guide) Good:
let rowHeight = hasHeader ? 50 : 20
Bad:
let rowHeight=hasHeader?50:20
Also, it stands for if else statements:
**Good:**
```swift
if (condition) {
performTask()
} else {
sleep()
}
Bad:
if(condition){
performTask()
}else{
sleep()
}
Prefer the One true bracing style. It is also used by Apple. : http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS
if condition {
// do stuff
} else {
// do other stuff
}
####Return early
If you have to meet certain criteria to continue execution, return early.
Good:
guard condition else { return error }
Bad:
if (condition) {
// condition satisfied code
} else {
if (anothertest) {
foo()
} else {
return error
}
}
When returning early prefer ''guard'' over ''if''.
####Comments
Preffer writing self explicit code which doesn't require comments. Comments suffer from rot and become obsolete soon after being written. Write comments only when you code fails to express itself.
IMPORTANT: If your code may create confusion for other developers, leaving a comment is mandatory.
Exception: comments used to generate documentation