Coding Guidelines
Language
US English should be used.
Preferred:
let color = UIColor.cyanColor()
Not Preferred:
let colour = UIColor.cyanColor()
General
Use Xcode formatting
Wherever in doubt, use Xcode formatting - for applicable cases select
& ctrl+I
is always the correct choice.
Whitespace
- Tabs, not spaces.
- End files with a newline.
- Don't leave trailing whitespace.
- Not even leading indentation on blank lines.
- Use only one newline to separate code, multiple empty lines are prohibited
Semicolons
Semicolons should not be used. If you are forced to write them, consider if style is correct.
Prefer let
-bindings over var
-bindings wherever possible
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 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.
Return and break early
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.isNumber else {
return
}
// Use n here
You can also do it with if
statement, but using guard
is preferred, because guard
statement without return
, break
or continue
produces a compile-time error, so exit is guaranteed.
Avoid Using Force-Unwrapping of Optionals
If you have an identifier foo
of type FooType?
or FooType!
, don't force-unwrap it to get to the underlying value (foo!
) if possible.
Instead, prefer this:
var foo: Foo?
...
if let foo {
// Use unwrapped `foo` value in here
} else {
// If appropriate, handle the case where the optional is nil
}
or even better guard let if appropriate
var foo: Foo?
...
guard let foo else { return }
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 Implicitly Unwrapped Optionals
Where possible, use let foo: FooType?
instead of let foo: FooType!
if foo
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.
Only explicitly refer to self
when required
When accessing properties or methods on self
, leave the reference to self
implicit by default:
private class History {
var events: [Event]
func rewrite() {
events = []
}
}
Only include the explicit keyword when required by the language for example, in a closure, or when parameter names conflict:
extension History {
init(events: [Event]) {
self.events = events
}
var whenVictorious: () -> () {
return {
self.rewrite()
}
}
}
Rationale: This makes the capturing semantics of self
stand out more in closures, and avoids verbosity elsewhere.
Prefer implicit getters on read-only properties and subscripts
When possible, omit the get
keyword on read-only computed properties and
read-only subscripts.
So, write these:
var myGreatProperty: Int {
return 4
}
subscript(index: Int) -> T {
return objects[index]
}
and not these:
var myGreatProperty: Int {
get {
return 4
}
}
subscript(index: Int) -> T {
get {
return objects[index]
}
}
Rationale: The intent and meaning of the first version is clear, and results in less code.
Prefer structs over classes
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) usually not a 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:
class Vehicle {
let numberOfWheels: Int
init(numberOfWheels: Int) {
self.numberOfWheels = numberOfWheels
}
func maximumTotalTirePressure(pressurePerWheel: Float) -> Float {
return pressurePerWheel * Float(numberOfWheels)
}
}
class Bicycle: Vehicle {
init() {
super.init(numberOfWheels: 2)
}
}
class Car: Vehicle {
init() {
super.init(numberOfWheels: 4)
}
}
could be refactored into these definitions:
protocol Vehicle {
var numberOfWheels: Int { get }
}
func maximumTotalTirePressure(vehicle: Vehicle, pressurePerWheel: Float) -> Float {
return pressurePerWheel * Float(vehicle.numberOfWheels)
}
struct Bicycle: Vehicle {
let numberOfWheels = 2
}
struct Car: Vehicle {
let numberOfWheels = 4
}
Rationale: Value types are simpler, easier to reason about, and behave as expected with the let
keyword.
Make classes final by default
Marking class as final enables many compiler optimizations. It's easier to remove is subclassing is required. Always start with the least permissive option.
Preferred
final class MyClass {}
Not preferred
class Myclass {}
Make properties private when possible
In addition to encapsulation, private properties cannot be overridden which allows the compiler to introduce further optimizations, just like with final classes. Always start with the least permissive option.
Omit type parameters where possible
Methods of parameterized types can omit type parameters on the receiving type when they're identical to the receiver's. For example:
struct Composite<T> {
...
func compose(other: Composite<T>) -> Composite<T> {
return Composite<T>(self, other)
}
}
could be rendered as:
struct Composite<T> {
...
func compose(other: Composite) -> Composite {
return Composite(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 operator definitions
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.
Code Organization
Pragma marks
Use // MARK: - GroupOfMethods
to categorise methods in functional groupings and protocol/delegate implementations, following this general structure.
The descriptor of the pragma mark should be capitalised and use camel casing. If descriptor is used to group methods that implement some protocol, the name of that protocol should be used.
If needed, you can create subgroups under a single group by // MARK: GroupOfMethods
Preferred:
// MARK: - UITableViewDelegate
// MARK: - GroupOfMethods
// MARK: SubgroupOfMethods
Not Preferred:
// MARK: - Table View Delegate
// MARK: - groupOfMethods
// MARK: - group-Of-Methods
// MARK: Table View Delegate
// MARK: groupOfMethods
// MARK: group-Of-Methods
Also, -
sign should be used in descriptors.
Preferred:
// MARK: - GroupOfMethods
Not Preferred:
// MARK: GroupOfMethods
For some common cases, predefined descriptors should be used. Here is a list of predefined descriptors
Predefined Descriptors:
// MARK: - Lifecycle
// MARK: - Actions
// MARK: - Public static properties
// MARK: - Private static properties
// MARK: - Public propeties
// MARK: - Private properties
Class organization (applies to structs, enums...)
Starts with properties, first static/class, then instance.
Per type, start with public, then private. Internal is in most use-cases treated as public. In modules, use public, internal and then private.
Initializers follow properties, then lifecycle methods (if applicable)
Then follow methods, first public, then public protocol conformance methods followed by private methods. Button actions or targets in general (.editingChanged, gesture recognizer actions etc.) can most likely be private, but in any case put them in the correct mark per access level.
Deinit should be the last mark.
If needed/applicable, extension can follow main implementation, separated by appropriate mark
protocol ExampleProtocol: AnyObject {
var protocolProperty1: String { get set }
var protocolProperty2: String { get set }
func protocolMethod1()
func protocolMethod2()
}
class ViewController: UIViewController, ExampleProtocol {
// MARK: - Public class properties
static var classProperty = ""
// MARK: - Private class properties
private static var classPrivateProperty = ""
// MARK: - Public properties
var name = ""
var publicComputedProperty: String { "Hello from public" }
// MARK: - ExampleProtocol properties
var protocolProperty1 = ""
var protocolProperty2 = ""
// MARK: - Private properties
private var number = 1
private var privateComputedProperty: String { "Hello from private"}
// MARK: - Initialization
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError()
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
...
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
}
// MARK: - Public methods
func somePublicMethod() { }
// MARK: - ExampleProtocol methods
func protocolMethod1() { }
func protocolMethod2() { }
// MARK: - Private methods
private func privateMethod() { }
// MARK: - Actions
@objc private func buttonAction(_ sender: UIButton) { }
@objc private func textFieldChanged(_ sender: UITextField) { }
// MARK: - Deinit
deinit { }
}
// MARK - SomeOtherConformance
extension ViewController: SomeOtherConformance {
// MARK: - Public getters
// MARK: - Public methods
}
Naming
Apple naming conventions should be applied wherever possible.
Long, descriptive method and variable names are good, remember you are developing this app to be maintained.
Preferred:
let settingsButton: UIButton
Not Preferred:
let setBut: UIButton
let buttonSettings: UIButton
Variables
Variables should be named as descriptively as possible. Single letter variable names should be avoided.
Instance variables
Instance variables should be camel-case, with the leading word being lowercase. All instance variable should have A minimum possible scope. We really want to expose what is really necessary and encapsulate everything else.
When accessing instance variables from the methods within the class that defines those variables, prefix self.
should not be used. The one and only exception to this rule is the case when the method receives a parameter with the same name as the instance variable's name.
Preferred:
var instanceVariable1: String
var instanceVariable2: String
func method(instanceVariable1: String, parameter2: String) {
self.instanceVariable1 = instanceVariable1
instanceVariable2 = parameter2
}
Not Preferred:
var instanceVariable1: String
var instanceVariable2: String
func method(instanceVariable1: String, parameter2: String) {
self.instanceVariable1 = instanceVariable1
self.instanceVariable2 = parameter2
}
Booleans
Descriptive method names should be used. The word 'is' in front of a name should be used.
Preferred:
let isActive: Bool
Not Preferred:
let active: Bool
Numeric variables
Prefer using underscores for formatting numbers, as it's much easier to read.
Preferred
let revenue = 1_000_000
Not preferred
let revenue = 1000000
Parameter and variable declaration
When declaring variables parameters, there should be no space between variable/parameter name and colon. There should be a single free space character between colon and type.
Preferred:
let active: Bool
Not Preferred:
let active:Bool
let active :Bool
When initialising and declaring variables at the same time, there should be no type definition.
Preferred:
let active = true
Not Preferred:
let active: Bool = true
Dictionaries
Dictionary key/value types should be declared with a space on both sides of the colon.
Preferred
var dict: [String : AnyObject]
Not preferred
var dict: [String: AnyObject]
View controllers and view models
View controller's names should end with VC
suffix.
Preferred:
var destinationVC = UIViewController()
Not Preferred:
var destinationViewController = UIViewController()
var destination = UIViewController()
View controllers and view models usually come in pairs. View models should have same names as matching view controllers with different suffix. Suffix for view models should be VM
.
Each view controller should have its own view defined in a separate class whenever possible. A typical view controller initialisation and loading cycle should look something like this:
// Convenience getter for sign in view
private var signInView {
return self.signInView.subview as! GSSignInView
}
.
override func loadView() {
self.view = GSSignInView()
}
override func viewDidLoad() {
super.viewDidLoad()
...
bindViewModel()
}
.
private func bindViewModel() {
...
}
Matching view controllers, views and view models should be grouped in the same folder.
Constants and Defines
Class level constants should be kept in a single private enum called Constants
Example:
private enum Constants {
static let constant1 = "oO"
static let constant2 = "Oo"
}
Project level constants should be kept in Defines swift file. Defines swift file should also keep stuff like enums, and structs used around the project that cannot be semantically associated to specific classes.
Enum is used instead of struct we've been using previously for a simple reason that it can't be instantiated, so it's used as a pure namespace. Instantiating struct with Constants() would be no harm whatsoever, but it would also serve no purpose and even possibly be misleading.
Singletons
Methods for accessing singleton instances should be named shared
Preferred:
public static let shared: Manager = {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
return Manager(configuration: configuration)
}()
Not Preferred:
public static let sharedManager: Manager = {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
return Manager(configuration: configuration)
}()
Types
String
should be used instead of NSString
.
Core Data
Primary Keys
All primary keys should be named like this <entity_name>ID. Also, all primary keys should be of type String
Preferred:
patientID
Not Preferred:
ID
patientId
Numerical values
All numerical values should be of appropriate Swift numerical types, except for Int
. Integer values should be defined in core data as NSNumber
.
Images
Images should be used within xcassets resource bundles.
The naming of each individual should follow the same naming conventions as the naming of methods and variables with the exception that rather than using camel casing, a "-" should be used to separate different words
Preferred:
background-request-empty
Not Preferred:
backgroundRequestEmpty
Parentheses
There should be no spaces between parentheses and their contents. ("Parentheses should hug their content")
Preferred:
required init(argument: AnyObject) {
...
}
Not Preferred:
required init(argument: AnyObject ) {
...
}
Omit unneeded parentheses when possible:
Preferred
if value {
}
Not preferred
if (value) {
}
Curly braces should not hug content
Leading and trailing braces should have whitespace between them and the content for visual clarity. ("spaces let braces breathe")
Preferred
guard let item = item else { return }
ints.map { %0 * 2 }
Not Preferred
guard let item = item else {return}
items.map{$0 * 2}
Methods
Method's body should never start with an empty line.
Preferred:
func someMethod() {
print("preferred way")
}
Not Preferred:
func someMethod() {
`
print("wrong way")
}
When calling methods within the class where those methods are defined, self.
prefix should
Preferred:
func method1() {
method2()
}
func method2() {
...
}
Not Preferred:
func method1() {
self.method2()
}
func method2() {
...
}
Method declaration should always follow this pattern:
<scope(unless it's internal)> func <method name>(<first_argument_name>: <argument_typr>, <first_argument_name>: <argument_typr>, ..) {
method body..
}
Preferred:
func method1(parameter1: type1) {
...
}
private func method2() {
...
}
public func method3() {
...
}
Not Preferred:
internal method1(parameter1: type1) {
...
}
private func method2()
{
...
}
public func method3(){
...
}
Naming methods in api and business layer
All methods that represent calls to API should start with the name of the HTTP method that they are implementing (post, put, get, delete).
Names of methods in business layer (managers, model extension…) should start with CREATE, UPDATE, IMPORT or DELETE.
Chained method calls
When multiple method calls are chained on the same dictionary, each method call should go on its own line.
Preferred
let evenNumbersAsString = numbers
.filter {
...
}
.map {
...
}
Not Preferred:
let evenNumbersAsString = numbers.filter {
...
}
.map {
...
}
let evenNumbersAsString = numbers.filter {...}.map {...}
let evenNumbersAsString = numbers.filter
{
...
}
.map
{
...
}
In case the same code is used in multiple "map", "filter" or "reduce" calls, that code should be put in a separate function.
Delegates
All delegate methods should have a parameter that represents a caller of that method.
Preferred:
optional func scrollViewDidScroll(_ scrollView: UIScrollView)
Not Preferred:
optional func scrollViewDidScroll()
Action Target
In action target pattern (buttons, gesture recognisers...) name of method that implement action should end with word Action. Also, action method should always receive a sender (object that has triggered the action)
Preferred:
func floatingButtonAction(sender: UGButtonFloating!) {
...
}
Not Preferred:
func floatingButtonAction() {
...
}
func floatingButtonDidTap(sender: UGButtonFloating!)) {
...
}
func floatingButtonDidTap() {
...
}
Prefer Void to () for closure return type
as it enhances readability and makes it easier to detect return type.
Preferred
const closure = () -> Void
Not preferred
const closure = () -> ()
Omit return type when possible
(addition to the rule for Void return type for functions)
Preferred
func someFunction() {
}
Not preferred
func someFunction() -> Void {
}
##Consider omitting return keyword when not required
It is suggested, but not required, to omit the return keyword when allowed.
When using SwiftUI, this is not a consideration, but a requirement.
Preferred
func calculateSize() -> CGSize {
CGSize(...)
}
Not preferred
func calculateSize() -> CGSize {
return CGSize(...)
}
##Break function declaration to multiple lines when it makes sense
If so, every parameter and its type should be on one line and the end bracket with the return type should be on a newline. That way, each parameter can easily be seen, and the return type is apparent. Note: how many parameters need to exist before newlining is tentative, please use common sense.
Preferred
public static func post<Body: Model>(
method: HTTPMethod = .post,
baseURL: URL, path: String,
params: [URLQueryItem]? = nil,
body: Body?,
completion: @escaping (Result<Data, APIError>) -> Void
) -> Request {
let builder = PostRequestBuilder(method: method, baseURL: baseURL, path: path, params: params, body: body)
return Request(builder: builder, completion: completion)
}
Not preferred
public static func post<Body: Model>(method: HTTPMethod = .post, baseURL: URL, path: String, params: [URLQueryItem]? = nil, body: Body?, completion: @escaping (Result<Data, APIError>) -> Void) -> Request {
let builder = PostRequestBuilder(method: method, baseURL: baseURL, path: path, params: params, body: body)
return Request(builder: builder, completion: completion)
}
This is, although valid, difficult to scan for parameters
public static func post<Body: Model>(
method: HTTPMethod = .post,
baseURL: URL, path: String,
params: [URLQueryItem]? = nil,
body: Body?,
completion: @escaping (Result<Data, APIError>) -> Void) -> Request {
let builder = PostRequestBuilder(method: method, baseURL: baseURL, path: path, params: params, body: body)
return Request(builder: builder, completion: completion)
}
}
This implementation makes it relatively easy to see all the parameters, but the return type is difficult to see. Additionally, using default Xcode formatting, it's difficult to see the transition between the function parameters, return type and the implementation itself as they're indented the same. Even when there is no return type (omit Void), closing parenthesis will visually separate parameters from implementation
##Function calls
If the function has many parameters, consider breaking the call into multiple lines. Each parameter must be in a new line. Closing parenthesis must be on a new line.
Preferred
someFunc(
param1: value1,
param2: value2,
param3: value3,
param4: value4
)
let other = "Value"
This adds visual separation between the function call and possible further code (although a single line break is allowed by this guide)
Not preferred
someFunc(
param1: value1,
param2: value2,
param3: value3,
param4: value4)
let other = "Value"
##Multiple closures in methods
When having multiple closures in methods, try to determine if using a trailing closure for the last one, although valid, is sensible. E.g. for function
func someMethod(
param1: String,
param2: String,
closure1: () -> Void,
closure2: () -> Void) {
}
it's easier to read and determine what is the purpose of the last closure if written
Preferred
someMethod(
param1: "p1",
param2: "p2",
closure1: { (implementation1) },
closure2: { (implementation2) }
)
then
Suggested to consider not using
someMethod(
param1: "p1",
param2: "p2",
closure1: { (implementation1) }) {
(implementation2)
}
##Horizontal alignment
Colons must follow the variable name, do not introduce whitespace to match types.
Preferred
let items: Int
let memeberNames: String
Not preferred
let items : Int
let memeberNames: String
or
let items: Int
let memeberNames: String
##Trailing commas
Prefer adding trailing commas to the last element as it makes easier to adding elements to array.
Preferred
var names = [
"Name 1",
"Name 2",
"Name 3",
]
Not preferred
var names = [
"Name 1",
"Name 2",
"Name 3"
]
##Prefer named tuples
as it's easier to determine the element purpose and use.
Preferred
declaration:
var user: (name: String, email: String)
call site:
let name = user.name
Not preferred
declaration:
var user: (String, String)
call site:
let name = user.0
##Avoid void breaks
Although passing void as break is valid, using break is more expressive and easier to spot.
Preferred
switch something {
case 'a':
doSomething()
case 'b':
break
case 'c':
doSomethingElse()
}
Not preferred
switch something {
case 'a':
doSomething()
case 'b':
()
case 'c':
doSomethingElse()
}
##Avoid default clause in switch statements where possible
This will create a compile error when a new enum case is introduced.
Preferred
switch finiteEnum {
case .a:
doSomething1()
case .b:
doSomething2()
case .c:
doSomething3()
}
Not preferred
switch finiteEnum {
case .a:
doSomething()
default:
break
}
Guard and if let multiple item alignments
If there are multiple guard, if or if let statements, place all individual conditions on a new line, so they are lined up and easy to visually scan, comment out conditions or set breakpoints.
Preferred
guard
let value1 = value1,
let value2 = value2,
let value3 = value3
else { return }
if
let value1 = value1,
let value2 = value2,
let value3 = value3 {
}
Not preferred
guard let value1 = value1, let value2 = value2, let value3 = value3 else { return }
guard let value1 = value1,
let value2 = value2,
let value3 = value3
else { return }
if let value1 = value1,
let value2 = value2,
let value3 = value3 {
}
TODOs
Each TODO that we have in code should also have a matching task created in the task manager we are using for that project. A link to that task should be pasted in the comment.