iOS Development - Swift Coding Guidlines-MOBILOITTE
iOS Development - Swift Coding Guidlines-MOBILOITTE
Musts
Native Swift Types
● Use Swift types whenever possible (Array, Dictionary, Set, String, etc.) as opposed to the NS*types from
Objective-C. Many Objective-C types can be automatically converted to Swift types and vice versa. See
Working With Cocoa Data Types for more details.
● Incorrect
● let pageLabelText = NSString(format: "%@/%@", currentPage, pageCount)
● Correct
● let pageLabelText = "\(currentPage)/\(pageCount)"
let alsoPageLabelText = currentPage + "/" + pageCount
Class Prefixes
● Do not add a class prefix. Swift types are automatically namespaced by the module that contains them. If two
names from different modules collide you can disambiguate by prefixing the type name with the module
name.
● // SomeModule.swift
public class UsefulClass {
public class func helloWorld() {
print("helloWorld from SomeModule")
}
}
// MyApp.Swift
class UsefulClass {
class func helloWorld() {
print("helloWorld from MyApp")
}
}
import SomeModule
Optionals
Force Unwrapping
● Avoid force unwrapping optionals by using ! or as! as this will cause your app to crash if the value you are
trying to use is nil. Safely unwrap the optional first by using things like guard let, if let, guard let as?, if let as?,
and optional chaining. A rare reason to force-unwrap would be if you have a value you expect to never be nil
and you want your app to crash if the value actually is nil due to some implementation mistake. An example of
this would be an @IBOutlet that accidentally gets disconnected. However, consider this an edge-case and
rethink whether your own code could be refactored to not use force-unwrapping.
● Incorrect unwrap
● // URL init(string:) is a failable initializer and will crash at runtime with a force unwrap if initialization fails!
let url = URL(string: "http://www.example.com/")!
UIApplication.shared.open(url)
● Correct unwrap
● guard let url = URL(string: "http://www.example.com/") else {
return
}
UIApplication.shared.open(url)
● Incorrect downcast
● // segue.destination is declared to be of type UIViewController, so forcing a downcast to type
// DetailViewController here will crash if the type is not DetailViewController at runtime!
let detailViewController = segue.destination as! DetailViewController
detailViewController.person = person
● Correct downcast
● guard let detailViewController = segue.destination as? DetailViewController else {
return
}
detailViewController.person = person
● Incorrect optional chaining
● // delegate is an optional so force unwrapping here will crash if delegate is actually nil at runtime!
delegate!.didSelectItem(item)
● Correct optional chaining
● delegate?.didSelectItem(item)
if
let constantOne = valueOne,
let constantTwo = valueTwo,
let constantThree = valueThree {
// Code
}
● Put a line-break after guard, if, or while and list each constant or variable its own line.
● Incorrect
● guard let
constantOne = valueOne,
var variableOne = valueTwo,
let constantTwo = valueThree else {
return
}
if
let constantOne = valueOne,
let constantTwo = valueTwo,
let constantThree = valueThree,
var variableOne = valueFour,
var variableTwo = valueFive,
var variableThree = valueSix {
// Code
}
Error Handling
Forced-try Expression
● Avoid using the forced-try expression try! as a way to ignore errors from throwing methods as this will
crash your app if the error actually gets thrown. Safely handle errors using a do statement along with try and
catch. A rare reason to use the forced-try expression is similar to force unwrapping optionals; you actually
want the app to crash (ideally during debugging before the app ships) to indicate an implementation error. An
example of this would be loading a bundle resource that should always be there unless you forgot to include it
or rename it.
● Incorrect
● // This will crash at runtime if there is an error parsing the JSON data!
let json = try! JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
● Correct
● do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
} catch {
print(error)
}
Access Control
● Prefer private properties and methods whenever possible to encapsulate and limit access to internal object
state.
● For private declarations at the top level of a file that are outside of a type, explicitly specify the declaration as
fileprivate. This is functionally the same as marking these declarations private, but clarifies the scope:
● Incorrect
● import Foundation
struct Baz {
...
● Correct
● import Foundation
struct Baz {
...
● If you need to expose functionality to other modules, prefer public classes and class members whenever
possible to ensure functionality is not accidentally overridden. Better to expose the class to open for
subclassing when needed.
Spacing
● Open curly braces on the same line as the statement and close on a new line.
● Don't add empty lines after opening braces or before closing braces.
● Put else statements on the same line as the closing brace of the previous if block.
● Make all colons left-hugging (no space before but a space after) except when used with the ternary operator
(a space both before and after).
● Incorrect
● class SomeClass : SomeSuperClass
{
else {
...
}}}
● Correct
● class SomeClass: SomeSuperClass {
private let someString: String
if someParam > 10 {
...
} else {
...
}
}
}
Closures
Shorthand Argument Syntax
● Only use shorthand argument syntax for simple one-line closure implementations:
● let doubled = [2, 3, 4].map { $0 * 2 } // [4, 6, 8]
● For all other cases, explicitly define the argument(s):
● let names = ["George Washington", "Martha Washington", "Abe Lincoln"]
let emails = names.map { fullname in
let dottedName = fullname.replacingOccurrences(of: " ", with: ".")
return dottedName.lowercased() + "@whitehouse.gov"
}
Capture lists
● Use capture lists to resolve strong reference cycles in closures:
● UserAPI.registerUser(user) { [weak self] result in
if result.success {
self?.doSomethingWithResult(result)
}
}
Protocols
Protocol Conformance
● When adding protocol conformance to a type, use a separate extension for the protocol methods. This keeps
the related methods grouped together with the protocol and can simplify instructions to add a protocol to a
type with its associated methods.
● Use a // MARK: - SomeDelegate comment to keep things well organized.
● Incorrect
● class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// All methods
}
● Correct
● class MyViewcontroller: UIViewController {
...
}
// MARK: - UITableViewDataSource
// MARK: - UIScrollViewDelegate
Delegate Protocols
● Limit delegate protocols to classes only by adding class to the protocol's inheritance list (as discussed in
Class-Only Protocols).
● If your protocol should have optional methods, it must be declared with the @objc attribute.
● Declare protocol definitions near the class that uses the delegate, not the class that implements the delegate
methods.
● If more than one class uses the same protocol, declare it in its own file.
● Use weak optional vars for delegate variables to avoid retain cycles.
● //SomeTableCell.swift
Trailing Comma
● For array and dictionary literals, unless the literal is very short, split it into multiple lines, with the opening
symbols on their own line, each item or key-value pair on its own line, and the closing symbol on its own line.
Put a trailing comma after the last item or key-value pair to facilitate future insertion/editing. Xcode will handle
alignment sanely.
● Correct
● let anArray = [
object1,
object2,
object3,
]
let aDictionary = [
"key1": value1,
"key2": value2,
]
● Incorrect
● let anArray = [
object1,
object2,
object3 //no trailing comma
]
let aDictionary = ["key1": value1, "key2": value2] //how can you even read that?!
Constants
● Define constants for unchanging pieces of data in the code. Some examples are CGFloat constants for cell
heights, string constants for cell identifiers, key names (for KVC and dictionaries), or segue identifiers.
● Where possible, keep constants private to the file they are related to.
● File-level constants must be declared with fileprivate let.
● File-level constants must be capital camel-cased to indicate that they are named constants instead of
properties.
● If the constant will be used outside of one file, fileprivate must be omitted.
● If the constant will be used outside of the module, it must be declared public (mostly useful for Pods or shared
libraries).
● If the constant is declared within a class or struct, it must be declared static to avoid declaring one constant
per instance.
● //SomeTableCell.swift
...
Function Parameters
● Name functions with parameters so that it is clear what the first parameter is.
○ If that is not possible, provide an explicit label for the first parameter.
● Avoid using the underscore character to prevent explicit external parameter names.
● Incorrect
● class Person {
init(_ firstName: String, _ lastName: String) {
// No external parameter names when called: Person("Some", "Guy")
}
func set(name: String) {
// It's not clear what the parameter is: set("Mr Bill")
}
Semicolons
● Forget them. They're dead to us.
● Don't you dare put multiple statements on one line.
Typealiases
● Create typealiases to give semantic meaning to commonly used datatypes and closures.
● typealias is equivalent to typedef in C and should be used for making names for types.
● Where appropriate, nest typealiases inside parent types (classes, structs, etc.) to which they relate.
● typealias IndexRange = Range<Int>
Flow Control
● For single conditional statements, do not use parentheses.
● Use parentheses around compound conditional statements for clarity or to make the order of operations
explicit.
● When using optional booleans and optional NSNumbers that represent booleans, check for true or false
rather than using the nil-coalescing operator:
● Incorrect
● if user.isCurrent?.boolValue ?? false {
// isCurrent is true
} else {
// isCurrent is nil or false
}
● Correct
● if user.isCurrent?.boolValue == true {
// isCurrent is true
} else {
// isCurrent is nil or false
}
Switch Statements
● break is not needed between case statements (they don't fall through by default)
● Use multiple values on a single case where it is appropriate:
● var someCharacter: Character
...
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
...
}
Loops
● Use the enumerated() function if you need to loop over a Sequence and use the index:
● for (index, element) in someArray.enumerated() {
...
}
● Use map when transforming Arrays (flatMap for Arrays of Optionals or Arrays of Arrays):
● let array = [1, 2, 3, 4, 5]
let stringArray = array.map { item in
return "item \(item)"
}
return item * 2
}
Shoulds
Declaring Variables
● When declaring variables you should be verbose, but not too verbose.
● Avoid repeating type information. Once should be enough.
● Make the name descriptive.
● Incorrect
● var someDictionary: Dictionary = [String: String]() //Dictionary is redundant
var somePoint: CGPoint = CGPoint(x:100, y: 200) //CGPoint is repeated
var b = Bool(false) //b is not a descriptive name
● Correct
● var someArray = [String]()
var someArray: [String] = []
var someDictionary = [String: Int]()
var someDictionary: [String : Int] = [:]
var countOfCats: UInt32 = 12
var isMadeOfCheese = false
var somePoint = CGPoint(x:100, y: 200)
Optionals
guard let vs. if let
● Use guard let over if let where possible. This improves readability by focusing on the happy-pathand can
also reduce nesting by keeping your code flatter:
● Incorrect
● func openURL(string: String) {
if let url = URL(string: string) {
// Nested
UIApplication.shared.open(url)
}
}
● Correct
● func openURL(string: String) {
guard let url = URL(string: string) else {
return
}
// Flat
UIApplication.shared.open(url)
}
● Since guard let needs to exit the current scope upon failure, if let is better suited for situations where you still
need to move forward after failing to unwrap an optional:
● if let anImage = UIImage(named: ImageNames.background) {
imageView.image = anImage
}
// Do more configuration
// ...
Spacing
● Use a newline for each logical step when chaining 3 or more methods together in a fluent style:
● Incorrect
● func foo() -> Int {
let nums: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
return nums
.map { $0 * 2 }
.filter { $0 % 2 == 0 }
.reduce(0, +)
}
● For guard statements, the else { may be placed on its own line after the last condition (rather than on the
same line as the last condition). For a single-condition guard, if the else { is on its own line, the condition
should be on its own line, too. If the else clause of a guard statement is a simple returnstatement, it may be all
on one line.
● Incorrect
● func openURL(string: String) {
guard let url = URL(string: string)
else {
return
}
// Flat
UIApplication.shared.open(url)
}
● guard let constantOne = valueOne,
let constantTwo = valueTwo,
let constantThree = valueThree
else { return }
● Correct (lots of choices here)
● func openURL(string: String) {
guard let url = URL(string: string) else {
return
}
// Flat
UIApplication.shared.open(url)
}
● func openURL(string: String) {
guard let url = URL(string: string) else { return }
// Flat
UIApplication.shared.open(url)
}
● func openURL(string: String) {
guard
let url = URL(string: string)
else {
return
}
// Flat
UIApplication.shared.open(url)
}
● func openURL(string: String) {
guard
let url = URL(string: string)
else { return }
// Flat
UIApplication.shared.open(url)
}
● guard
let constantOne = valueOne,
let constantTwo = valueTwo,
let constantThree = valueThree else { return }
● guard
let constantOne = valueOne,
let constantTwo = valueTwo,
let constantThree = valueThree
else { return }
● When grouping multiple cases in a switch statement, prefer putting each case on its own line, unless there
are only a few cases and they are short.
● Incorrect
● switch something {
case .oneLongCase, .anotherLongCase, .thereAreMoreCases, .thisIsWayTooFarToTheRight:
return true
case .sanity:
return false
}
● Correct
● switch something {
case .oneLongCase,
.anotherLongCase,
.thereAreMoreCases,
.thisIsInASanerPlace:
return false
case .sanity:
return true
}
Usage of self
● Except where necessary, avoid using self.. If you have a local variable that conflicts with a property name or
are in a context where self is captured, you may need to use self..
Loops
● If you have an Array of Arrays and want to loop over all contents, consider a for in loop using
joined(separator:) instead of nested loops:
● let arraysOfNames = [["Moe", "Larry", "Curly"], ["Groucho", "Chico", "Harpo", "Zeppo"]]
● Recommended
● for name in arraysOfNames.joined() {
print("\(name) is an old-timey comedian")
}
● Discouraged
● for names in arraysOfNames {
for name in names {
print("\(name) is an old-timey comedian")
}
}
Closures
● Avoid unnecessary parentheses around closure parameters.
● Incorrect
● functionWithAClosure { (result) in
...
}
● functionWithAClosure { (result) -> Int in
...
}
● Correct
● functionWithAClosure { result in
...
}
● functionWithAClosure { result -> Int in
...
}
● functionWithAClosure { (result: String) in
...
}
Tuples
● The word is pronounced like "tuh-ple"
● Rhymes with "couple" and "supple"
● Name the members of your tuples when creating or decomposing tuples:
● let foo = (something: "cats", somethingElse: 909_099)
let (something, somethingElse) = foo
Constants
● Declare constants with static let to ensure static storage.
● Prefer declaring constants in the scope in which they will be used rather than in a central shared constants file
like Constants.swift.
● Be wary of large constants files as they can become unmanageable over time. Refactor related parts of the
main constants file into separate files for that situation.
● Use enums to group related constants together in a namespace. The name of the enum should be singular,
and each constant should be written using camelCase. (Using a case-less enum prevents useless instances
from being created.)
● enum SegueIdentifier {
static let onboarding = "OnboardingSegue"
static let login = "LoginSegue"
static let logout = "LogoutSegue"
}
enum StoryboardIdentifier {
static let main = "Main"
static let onboarding = "Onboarding"
static let settings = "Settings"
}
print(SegueIdentifier.login) // "LoginSegue"
● Where appropriate, constants can also be grouped using an enum with a rawValue type that is relevant to the
type you need to work with. An enum with a rawValue of type String will implicitly assign its rawValue from
the name of the case if nothing is already explicitly defined for the rawValue. This can be useful when all the
names of the cases match with the value of the constant. Be aware that if you use enum cases for constants
in this way, you need to explicitly use rawValue every time you need to access the value of the constant:
● enum UserJSONKeys: String {
case username
case email
case role
// Explicitly defined rawValue
case identifier = "id"
...
}
print(UserJSONKeys.username.rawValue) // "username"
print(UserJSONKeys.identifier.rawValue) // "id"
Default Initializers
● Use default initializers where possible.
Classes vs Structs
● Most of your custom data types should be classes.
● Some situations where you may want to use structs:
○ When creating simple, lightweight data types.
○ When creating types that are composed of other value types.
○ When you don't need inheritance.
● Refer to the Swift Programming Language Guidlines for detailed info on this topic.
//note the formatting of this example is further changed from the suggestion for better readability
UIView.animate(withDuration: 0.5,
animations: {
...
},
completion: { complete in
//the return type is inferred to be `Void` and `complete` does not need parens
}
)