Note: This repository is no longer being maintained.
This style guide is a product of our iOS team's more than a year writing, reviewing, and testing Swift code. It reflects the coding rules we have observed as "efficient" in our production apps.
The guidelines here try to accomplish/encourage practices that accomplish the following goals:
- to make it hard to write programmer errors, or at least make them hard to miss
- to increase readability and clarity of intent, with assumption that the code will be reviewed/consumed by a different person
- to minimize unnecessary code bloat
- to observe aesthetics that the majority of the team voted for
We understand that a quite a few of the guidelines below are controversial and may even be opposite what the Swift community generally observes, but we have tried and tested these practices in a team environment and they work for us.
That said, this is a live document. As our app grows, our team improves, and Swift evolves, our practices will adapt as well and this list will be updated if needed.
OK | NG |
---|---|
self.backgroundColor = UIColor.whiteColor()
self.completion = {
// ...
} |
self.backgroundColor = UIColor.whiteColor();
self.completion = {
// ...
}; |
Rationale: There is no practical advantage of using trailing semicolons. It is, however, a very good way to catch someone copy-pasting Objective-C code ;)
Set Xcode's Text Editing settings as shown:
Rationale: Maintains visually similar indentation across different text editors.
OK | NG |
---|---|
class Button {
// ...
}
// <-- One line here |
class Button {
// ...
} // <-- No new line after |
class Button {
// ...
}
// <-- One line here
// <-- Another line here |
Rationale: Prevents no-trailing-newline errors and reduces noise in commit diffs.
OK |
---|
class BaseViewController: UIViewController {
// ...
override viewDidLoad() {
// ...
}
override viewWillAppear(animated: Bool) {
// ...
}
} |
Rationale: Gives breathing room between code blocks.
OK | NG |
---|---|
func <| (lhs: Int, rhs: Int) -> Int {
// ...
}
let value = 1 <| 2 |
func <|(lhs: Int, rhs: Int) -> Int {
// ...
}
let value = 1<|2 |
Rationale: Readability.
OK | NG |
---|---|
func doSomething(value: Int) -> Int {
// ...
} |
func doSomething(value: Int)->Int {
// ...
} |
Rationale: Readability.
Commas (,
) should have no whitespace before it, and should have either one space or one newline after.
OK | NG |
---|---|
let array = [1, 2, 3] |
let array = [1,2,3]
let array = [1 ,2 ,3]
let array = [1 , 2 , 3] |
self.presentViewController(
controller,
animated: true,
completion: nil
) |
self.presentViewController(
controller ,
animated: true,completion: nil
) |
Rationale: Keeps comma-separated items visually separate.
Colons (:
) used to indicate type should have one space after it and should have no whitespace before it.
OK | NG |
---|---|
func createItem(item: Item) |
func createItem(item:Item)
func createItem(item :Item)
func createItem(item : Item) |
var item: Item? = nil |
var item:Item? = nil
var item :Item? = nil
var item : Item? = nil |
Rationale: The colon describes the object to its left, not the right. (Just how we write colons in english)
Colons (:
) for case
statements should have no whitespace before it, and should have either one space or one newline after it.
OK | NG |
---|---|
switch result {
case .Success:
self.completion()
case .Failure:
self.failure()
} |
switch result {
case .Success :
self.completion()
case .Failure:self.reportError()
} |
Rationale: Same as he previous rule, the colon describes the object to its left, not the right.
OK | NG |
---|---|
class Icon {
// ...
} |
class Icon{
// ...
} |
let block = { () -> Void in
// ...
} |
let block ={ () -> Void in
// ...
} |
Rationale: Separates the brace from the declaration.
Open braces ({
) for type declarations, functions, and closures should be followed by one empty line. Single-statement closures can be written in one line.
OK | NG |
---|---|
class Icon {
let image: UIImage
var completion: (() -> Void)
init(image: UIImage) {
self.image = image
self.completion = { [weak self] in self?.didComplete() }
}
func doSomething() {
self.doSomethingElse()
}
} |
class Icon {
let image: UIImage
init(image: UIImage) {
self.image = image
self.completion = { [weak self] in print("done"); self?.didComplete() }
}
func doSomething() { self.doSomethingElse() }
} |
Rationale: Gives breathing room when scanning for code.
Empty declarations should be written in empty braces ({}
), otherwise a comment should indicate the reason for the empty implementation.
OK | NG |
---|---|
extension Icon: Equatable {} |
extension Icon: Equatable {
} |
var didTap: () -> Void = {}
override func drawRect(rect: CGRect) {}
@objc dynamic func controllerDidChangeContent(controller: NSFetchedResultsController) {
// do nothing; delegate method required to enable tracking mode
} |
var didTap: () -> Void = { }
override func drawRect(rect: CGRect) {
}
@objc dynamic func controllerDidChangeContent(controller: NSFetchedResultsController) {
} |
Rationale: Makes it clear that the declaration was meant to be empty and not just a missing TODO
.
Close braces (}
) should not have empty lines before it. For single line expressions enclosed in braces, there should be one space between the last statement and the closing brace.
OK | NG |
---|---|
class Button {
var didTap: (sender: Button) -> Void = { _ in }
func tap() {
self.didTap()
}
} |
class Button {
var didTap: (sender: Button) -> Void = {_ in}
func tap() {
self.didTap()
}
} |
Rationale: Provides breathing room between declarations while keeping code compact.
Close braces (}
) unless on the same line as its corresponding open brace ({
), should be left-aligned with the statement that declared the open brace.
OK | NG |
---|---|
lazy var largeImage: UIImage = { () -> UIImage in
let image = // ...
return image
}() |
lazy var largeImage: UIImage = { () -> UIImage in
let image = // ...
return image
}() |
Rationale: Close braces left-aligned with their opening statements visually express their scopes pretty well. This rule is the basis for the succeeding formatting guidelines below.
The get
and set
statement and their close braces (}
) should all be left-aligned. If the statement in the braces can be expressed in a single line, the get
and set
declaration can be inlined.
The rules on braces apply.
OK | NG |
---|---|
struct Rectangle {
// ...
var right: Float {
get {
return self.x + self.width
}
set {
self.x = newValue - self.width
}
}
} |
struct Rectangle {
// ...
var right: Float {
get
{
return self.x + self.width
}
set
{
self.x = newValue - self.width
}
}
} |
struct Rectangle {
// ...
var right: Float {
get { return self.x + self.width }
set { self.x = newValue - self.width }
}
} |
struct Rectangle {
// ...
var right: Float {
get { return self.x + self.width }
set { self.x = newValue - self.width
print(self)
}
}
} |
Rationale: Combined with the rules on braces, this formatting provides very good consistency and scannability.
OK | NG |
---|---|
struct Rectangle {
// ...
var right: Float {
return self.x + self.width
}
} |
struct Rectangle {
// ...
var right: Float {
get {
return self.x + self.width
}
}
} |
Rationale: The return
statement provides enough clarity that lets us use the more compact form.
if
, else
, switch
, do
, catch
, repeat
, guard
, for
, while
, and defer
statements should be left-aligned with their respective close braces (}
).
The rules on braces apply.
OK | NG |
---|---|
if array.isEmpty {
// ...
}
else {
// ...
} |
if array.isEmpty {
// ...
} else {
// ...
} |
if array.isEmpty
{
// ...
}
else
{
// ...
} |
Rationale: Combined with the rules on braces, this formatting provides very good consistency and scannability. Close braces left-aligned with their respective control flow statements visually express their scopes pretty well.
case
statements should be left-aligned with the switch
statement. Single-line case
statements can be inlined and written compact. Multi-line case
statements should be indented below case:
and separated with one empty line.
The rules on braces apply.
OK | NG |
---|---|
switch result {
case .Success:
self.doSomething()
self.doSomethingElse()
case .Failure:
self.doSomething()
self.doSomethingElse()
} |
switch result {
case .Success: self.doSomething()
self.doSomethingElse()
case .Failure: self.doSomething()
self.doSomethingElse()
} |
switch result {
case .Success: self.doSomething()
case .Failure: self.doSomethingElse()
} |
switch result {
case .Success: self.doSomething()
case .Failure: self.doSomethingElse()
} |
Rationale: Reliance on Xcode's auto-indentation. For multi-line statements, separating case
s with empty lines enhance visual separation.
OK | NG |
---|---|
if array.isEmpty {
// ...
} |
if (array.isEmpty) {
// ...
} |
Rationale: This isn't Objective-C.
OK | NG |
---|---|
guard let strongSelf = self else {
return
}
// do many things with strongSelf |
if let strongSelf = self {
// do many things with strongSelf
} |
Rationale: The more nested scopes to keep track of, the heavier the burden of scanning code.
Naming rules are mostly based on Apple's naming conventions, since we'll end up consuming their API anyway.
OK | NG |
---|---|
class ImageButton {
enum ButtonState {
// ...
}
} |
class image_button {
enum buttonState {
// ...
}
} |
Rationale: Adopt Apple's naming rules for uniformity.
OK | NG |
---|---|
enum ErrorCode {
case Unknown
case NetworkNotFound
case InvalidParameters
}
struct CacheOptions : OptionSetType {
static let None = CacheOptions(rawValue: 0)
static let MemoryOnly = CacheOptions(rawValue: 1)
static let DiskOnly = CacheOptions(rawValue: 2)
static let All: CacheOptions = [.MemoryOnly, .DiskOnly]
// ...
} |
enum ErrorCode {
case unknown
case network_not_found
case invalidParameters
}
struct CacheOptions : OptionSetType {
static let none = CacheOptions(rawValue: 0)
static let memory_only = CacheOptions(rawValue: 1)
static let diskOnly = CacheOptions(rawValue: 2)
static let all: CacheOptions = [.memory_only, .diskOnly]
// ...
} |
Rationale: Adopt Apple's naming rules for uniformity.
Variables and functions should be in lowerCamelCase, including statics and constants. An exception is acronyms, which should be UPPERCASE.
OK | NG |
---|---|
var webView: UIWebView?
var URLString: String?
func didTapReloadButton() {
// ..
} |
var web_view: UIWebView?
var urlString: String?
func DidTapReloadButton() {
// ..
} |
Rationale: Adopt Apple's naming rules for uniformity. As for acronyms, the readability makes keeping them upper-case worth it.
Avoid single-character names for types, variables, and functions. The only place they are allowed is as indexes in iterators.
OK | NG |
---|---|
for (i, value) in array.enumerate() {
// ... "i" is well known
} |
for (i, v) in array.enumerate() {
// ... what's "v"?
} |
Rationale: There is always a better name than single-character names. Even with i
, it is still more readable to use index
instead.
Avoid abbreviations as much as possible. (although some are allowed such as min
/max
)
OK | NG |
---|---|
let errorCode = error.code |
let err = error.code |
Rationale: Clarity is prioritized over slight brevity.
OK | NG |
---|---|
class Article {
var title: String
} |
class Article {
var text: String
// is this the title or the content text?
} |
Better | |
class NewsArticle {
var headlineTitle: String
} |
Rationale: Clarity is prioritized over slight brevity. Also, the more specific the name, the less likely they are to collide with other symbols.
OK | NG |
---|---|
var requestURL: NSURL
var sourceURLString: String
func loadURL(URL: NSURL) {
// ...
}
func loadURLString(URLString: String) {
// ...
} |
var requestURL: NSURL
var sourceURL: String
func loadURL(URL: NSURL) {
// ...
}
func loadURL(URL: String) {
// ...
} |
Rationale: Saves a few seconds checking header declarations for the correct type.
OK | NG |
---|---|
class User {
// ...
}
enum Result {
// ...
}
protocol Queryable {
// ...
} |
class UserClass {
// ...
}
enum ResultEnum {
// ...
}
protocol QueryableProtocol {
// ...
} |
Rationale: The extra suffix is redundant. It should be noted though that Objective-C protocols with the same name as an existing Objective-C class are bridged to Swift with a ~Protocol
suffix (e.g. NSObject
and NSObjectProtocol
). But they are irrelevant to this guideline as they are automatically generated by the Swift compiler.
OK | NG |
---|---|
import Foundation
import UIKit
import Alamofire
import Cartography
import SwiftyJSON |
import Foundation
import Alamofire
import SwiftyJSON
import UIKit
import Cartography |
Rationale: Reduce merge conflicts when dependencies change between branches.
All type declarations such as class
, struct
, enum
, extension
, and protocol
s, should be marked with // MARK: - <name of declaration>
(with hyphen)
OK | NG |
---|---|
// MARK: - Icon
class Icon {
// MARK: - CornerType
enum CornerType {
case Square
case Rounded
}
// ...
} |
// Icon
class Icon {
// MARK: CornerType
enum CornerType {
case Square
case Rounded
}
// ...
} |
Rationale: Makes it easy to jump to specific types when using Xcode's Source Navigator.
All properties and methods should be grouped into the superclass/protocol they implement and should be tagged with // MARK: <superclass/protocol name>
. The rest should be marked as either // MARK: Public
, // MARK: Internal
, or // MARK: Private
.
OK |
---|
// MARK: - BaseViewController
class BaseViewController: UIViewController, UIScrollViewDelegate {
// MARK: Internal
weak var scrollView: UIScrollView?
// MARK: UIViewController
override func viewDidLoad() {
// ...
}
override func viewWillAppear(animated: Bool) {
// ...
}
// MARK: UIScrollViewDelegate
@objc dynamic func scrollViewDidScroll(scrollView: UIScrollView) {
// ...
}
// MARK: Private
private var lastOffset = CGPoint.zero
} |
Rationale: Makes it easy to locate where in the source code certain properties and functions are declared.
OK | NG |
---|---|
import UIKit
// MARK: - BaseViewController
class BaseViewController: UIViewController {
// MARK: Internal
weak var scrollView: UIScrollView?
// MARK: UIViewController
override func viewDidLoad() {
// ...
}
override func viewWillAppear(animated: Bool) {
// ...
}
// MARK: Private
private var lastOffset = CGPoint.zero
} |
import UIKit
// MARK: - BaseViewController
class BaseViewController: UIViewController {
// MARK: Internal
weak var scrollView: UIScrollView?
// MARK: UIViewController
override func viewDidLoad() {
// ...
}
override func viewWillAppear(animated: Bool) {
// ...
}
// MARK: Private
private var lastOffset = CGPoint.zero
} |
Rationale: Aesthetic. Gives breathing room between type declarations and function groups.
// MARK: Public
// MARK: Internal
- Class Inheritance (parent-most to child-most)
// MARK: NSObject
// MARK: UIResponder
// MARK: UIViewController
- Protocol Inheritance (parent-most to child-most)
// MARK: UITableViewDataSource
// MARK: UIScrollViewDelegate
// MARK: UITableViewDelegate
// MARK: Private
Rationale: Makes it easy to locate where in the source code certain implementations are declared. public
and internal
declarations are more likely to be referred to by API consumers, so are declared at the top.
@
properties (@NSManaged
,@IBOutlet
,@IBInspectable
,@objc
,@nonobjc
, etc.)lazy var
properties- computed
var
properties - other
var
properties let
properties@
functions (@NSManaged
,@IBAction
,@objc
,@nonobjc
, etc.)- other functions
Rationale: @
properties and functions are more likely to be referred to (such as when checking KVC keys or Selector
strings, or when cross-referencing with Interface Builder) so are declared higher.
In general, all Xcode warnings should not be ignored. These include things like using let
instead of var
when possible, using _
in place of unused variables, etc.
Comments should be answering some form of "why?" question. Anything else should be explainable by the code itself, or not written at all.
OK | NG |
---|---|
let leftMargin: CGFloat = 20
view.frame.x = leftMargin |
view.frame.x = 20 // left margin |
@objc dynamic func tableView(tableView: UITableView,
heightForHeaderInSection section: Int) -> CGFloat {
return 0.01 // tableView ignores 0
} |
@objc dynamic func tableView(tableView: UITableView,
heightForHeaderInSection section: Int) -> CGFloat {
return 0.01 // return small number
} |
Rationale: The best comment is the ones you don't need. If you have to write one be sure to explain the rationale behind the code, not just to simply state the obvious.
OK | NG |
---|---|
self.titleLabel.text = "Date Today:" // TODO: localize |
self.titleLabel.text = "Date Today:" |
Rationale: Features are usually debugged and tested in the native language and translated strings are usually tested separately. This guarantees that all unlocalized texts are accounted for and easy to find later on.
All Objective-C protocol
implementations, whether properties or methods, should be prefixed with @objc dynamic
OK | NG |
---|---|
@objc dynamic func scrollViewDidScroll(scrollView: UIScrollView) {
// ...
} |
func scrollViewDidScroll(scrollView: UIScrollView) {
// ...
} |
Rationale: Prevents horrible compiler optimization bugs. Trust us.
OK |
---|
@IBOutlet private dynamic weak var closeButton: UIButton?
@IBAction private dynamic func closeButtonTouchUpInside(sender: UIButton) {
// ...
} |
Rationale: The Swift compiler sometimes mangle these. Writing dynamic
guarantees safety, even when we declare them as private
.
OK |
---|
override func viewDidLoad() {
super.viewDidLoad()
let gesture = UITapGestureRecognizer(target: self, action: "tapGestureRecognized:")
self.view.addGestureRecognizer(gesture)
}
private dynamic func tapGestureRecognized(sender: UITapGestureRecognizer) {
// ...
} |
Rationale: Same reason as the preceding rule, the Swift compiler sometimes mangle these. Writing dynamic
guarantees safety, even when we declare them as private
.
All @IBOutlet
s should be declared weak
. They should also be wrapped as Optional
, not ImplicitlyUnwrappedOptional
.
OK | NG |
---|---|
@IBOutlet dynamic weak var profileIcon: UIImageView? |
@IBOutlet var profileIcon: UIImageView! |
Rationale: This guarantees safety even if subclasses opt to not create the view for the @IBOutlet
. This also protects against crashes caused by properties being accessed before viewDidLoad(_:)
.
Rationale: This helps prevent pollution of XCode's auto-completion. In theory this should also help the compiler make better optimizations and build faster.
For library modules: all declarations should explicitly specify either public
, internal
, or private
.
OK | NG |
---|---|
private let defaultTimeout: NSTimeInterval = 30
internal class NetworkRequest {
// ...
} |
let defaultTimeout: NSTimeInterval = 30
class NetworkRequest {
// ...
} |
Rationale: Makes the intent clear for API consumers.
For application modules: public
access is prohibited unless required by a protocol. The internal
keyword may or may not be written, but the private
keyword is required.
OK | NG |
---|---|
private let someGlobal = "someValue"
class AppDelegate {
// ...
private var isForeground = false
} |
public let someGlobal = "someValue"
public class AppDelegate {
// ...
var isForeground = false
} |
Rationale: A public
declaration in an app bundle does not make sense. In effect, declarations are assumed to be either internal
or private
, in which case it is sufficient to just require private
explicitly.
OK | NG |
---|---|
@objc internal class User: NSManagedObject {
// ...
@NSManaged internal dynamic var identifier: Int
// ...
@NSManaged private dynamic var internalCache: NSData?
} |
internal @objc class User: NSManagedObject {
// ...
@NSManaged dynamic internal var identifier: Int
// ...
private @NSManaged dynamic var internalCache: NSData?
} |
Rationale: Combined with the rules on declaration order, this improves readability when scanning code vertically.
Unless required, a variable/property declaration's type should be inferred from either the left or right side of the statement, but not both.
OK | NG |
---|---|
var backgroundColor = UIColor.whiteColor()
var iconView = UIImageView(image) |
var backgroundColor: UIColor = UIColor.whiteColor()
var iconView: UIImageView = UIImageView(image) |
var lineBreakMode = NSLineBreakMode.ByWordWrapping
// or
var lineBreakMode: NSLineBreakMode = .ByWordWrapping |
var lineBreakMode: NSLineBreakMode = NSLineBreakMode.ByWordWrapping |
Rationale: Prevent redundancy. This also reduces ambiguity when binding to generic types.
When literal types are involved (StringLiteralConvertible
, NilLiteralConvertible
, etc), it is encouraged to specify the type explicitly and is preferrable over casting with as
directly.
OK | NG |
---|---|
var radius: CGFloat = 0
var length = CGFloat(0) |
var radius: CGFloat = CGFloat(0)
var length = 0 as CGFloat // prefer initializer to casts |
Rationale: Prevent redundancy. This also reduces ambiguity when binding to generic types.
OK |
---|
let badgeNumber = unreadItems.count |
Checking if empty or not:
OK | NG |
---|---|
if sequence.isEmpty {
// ... |
if sequence.count <= 0 {
// ... |
Getting the first or last item:
OK | NG |
---|---|
let first = sequence.first
let last = sequence.last |
let first = sequence[0]
let last = sequence[sequence.count - 1] |
Removing the first or last item:
OK | NG |
---|---|
sequence.removeFirst()
sequence.removeLast() |
sequence.removeAtIndex(0)
sequence.removeAtIndex(sequence.count - 1) |
Iterating all indexes:
OK | NG |
---|---|
for i in sequence.indices {
// ...
} |
for i in 0 ..< sequence.count {
// ...
} |
Getting the first or last index:
OK | NG |
---|---|
let first = sequence.indices.first
let last = sequence.indices.last |
let first = 0
let last = sequence.count - 1 |
Iterating all indexes except the last n
indexes:
OK | NG |
---|---|
for i in sequence.indices.dropLast(n) {
// ...
} |
for i in 0 ..< (sequence.count - n) {
// ...
} |
Iterating all indexes except the first n
indexes:
OK | NG |
---|---|
for i in sequence.indices.dropFirst(n) {
// ...
} |
for i in n ..< sequence.count {
// ...
} |
In general, if you have to add or subtract to count
, there is probably a better, Swift-y way to do it.
Rationale: Clarity of intent, which in turn reduces programming mistakes (esp. off-by-one calculation errors).
In particular, this will cover the ever-debatable usage/non-usage of self
.
All instance properties and functions should be fully-qualified with self
, including within closures.
(See next rule for implications)
OK | NG |
---|---|
self.animatableViews.forEach { view in
self.animateView(view)
} |
animatableViews.forEach { view in
animateView(view)
} |
Rationale: We found that we made less mistakes when we just required self
all the time than if we have to decide wether to write it or not. That said, we are aware of the implications of this to retain cycles. See rule below.
For all non-@noescape
and non-animation closures, accessing self
within the closure requires a [weak self]
declaration.
OK | NG |
---|---|
self.request.downloadImage(
url,
completion: { [weak self] image in
self?.didDownloadImage(image)
}
) |
self.request.downloadImage(
url,
completion: { image in
self.didDownloadImage(image) // hello retain cycle
}
) |
Rationale: Combined with the self
-requirement rule above, retain cycle candidates are very easy to spot. Just look for closures that access self
and check for the missing [weak self]
.
OK | NG |
---|---|
self.request.downloadImage(
url,
completion: { [weak self] image in
self?.didDownloadImage(image)
}
) |
self.request.downloadImage(
url,
completion: { [unowned self] image in
self.didDownloadImage(image)
}
) |
Rationale: While unowned
is more convenient (you don't need to work with an Optional
) than weak
, it is also more prone to crashes. Nobody likes zombies.
If the validity of the weak self
in the closure is needed, bind using the variable `self`
to shadow the original.
OK | NG |
---|---|
self.request.downloadImage(
url,
completion: { [weak self] image in
guard let `self` = self else {
return
}
self.didDownloadImage(image)
self.reloadData()
self.doSomethingElse()
}
) |
self.request.downloadImage(
url,
completion: { [weak self] image in
guard let strongSelf = self else {
return
}
strongSelf.didDownloadImage(image)
strongSelf.reloadData()
strongSelf.doSomethingElse()
}
) |
Rationale: Keep the syntax highlighting ;)