Skip to main content

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

NOT
be used.

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.