SwiftUI Coding Standard
SwiftUI Coding Standard
Semicolons
Trailing semicolons (;) are not allowed.
OK NG
self.backgroundColor = self.backgroundColor =
UIColor.whiteColor() UIColor.whiteColor();
self.completion = { 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 ;)
Whitespaces
All source files should end with a single trailing newline (only).
OK NG
class Button {
class Button {
// ...
// ...
}
} // <-- No new line after
// <-- One line here
class Button {
// ...
}
// <-- One line here
// <-- Another line here
Rationale: Prevents no-trailing-newline errors and reduces noise in
commit diffs.
All functions should be at least one empty line apart each other.
OK
}
Rationale: Gives breathing room between code blocks.
OK NG
func <| (lhs: Int, rhs: Int) -> Int { func <|(lhs: Int, rhs: Int) -> Int {
// ... // ...
} }
let value = 1 <| 2 let value = 1<|2
Rationale: Readability.
Use single spaces around return arrows (->) both in functions and
in closures.
OK NG
Commas
Commas (,) should have no whitespace before it, and should have
either one space or one newline after.
OK NG
self.presentViewController(
self.presentViewController(
controller,
controller ,
animated: true,
animated: true,completion: nil
completion: nil
)
)
Rationale: Keeps comma-separated items visually separate.
Colons
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)
switch result {
switch result {
case .Success:
case .Success :
self.completion()
self.completion()
case .Failure:
case .Failure:self.reportError()
self.failure()
}
}
Rationale: Same as he previous rule, the colon describes the object to its
left, not the right.
Braces
Open braces ({) should be one space following the previous non-
whitespace character.
OK NG
}
Rationale: Gives breathing room when scanning for code.
class Button {
class Button {
var didTap: (sender: Button) -> Void =
var didTap: (sender: Button) -> Void =
{_ in}
{ _ in }
func tap() {
func tap() {
self.didTap()
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 = { () -> lazy var largeImage: UIImage = { () ->
UIImage in UIImage in
let image = // ... let image = // ...
return 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.
Properties
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
get { get
{
return self.x + self.width return self.x + self.width
} }
set { set
{
self.x = newValue - self.width self.x = newValue - self.width
} }
} }
} }
struct Rectangle {
struct Rectangle { // ...
// ... var right: Float {
var right: Float {
get { return self.x + self.width }
get { return self.x + self.width } set { self.x = newValue - 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.
struct Rectangle {
// ...
struct Rectangle {
var right: Float {
// ...
var right: Float {
get {
return self.x + self.width
return self.x + self.width
}
}
}
}
}
Rationale: The return statement provides enough clarity that lets us use
the more compact form.
OK NG
if array.isEmpty {
if array.isEmpty {
// ...
// ...
}
} else {
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.
OK NG
switch result {
case .Success: switch result {
self.doSomething() case .Success: self.doSomething()
self.doSomethingElse() self.doSomethingElse()
case .Failure: case .Failure: self.doSomething()
self.doSomething() self.doSomethingElse()
self.doSomethingElse() }
}
switch result {
switch result {
case .Success: self.doSomething()
case .Success: self.doSomething()
case .Failure: self.doSomethingElse()
case .Failure: self.doSomethingElse()
}
}
Rationale: Reliance on Xcode's auto-indentation. For multi-line
statements, separating cases with empty lines enhance visual separation.
Conditions for if, switch, for, and while statements should not be
enclosed in parentheses (()).
OK NG
if array.isEmpty { if (array.isEmpty) {
// ... // ...
} }
Rationale: This isn't Objective-C.
Naming
Naming rules are mostly based on Apple's naming conventions, since we'll
end up consuming their API anyway.
Capitalization
Type names (class, struct, enum, protocol) should be
in UpperCamelCase.
OK NG
} }
Rationale: Adopt Apple's naming rules for uniformity.
} }
struct CacheOptions : OptionSetType { struct CacheOptions : OptionSetType {
static let None = static let none =
CacheOptions(rawValue: 0) CacheOptions(rawValue: 0)
static let MemoryOnly = static let memory_only =
CacheOptions(rawValue: 1) CacheOptions(rawValue: 1)
static let DiskOnly = static let diskOnly =
CacheOptions(rawValue: 2) CacheOptions(rawValue: 2)
static let All: CacheOptions = static let all: CacheOptions =
[.MemoryOnly, .DiskOnly] [.memory_only, .diskOnly]
// ... // ...
} }
Rationale: Adopt Apple's naming rules for uniformity.
OK NG
Semantics
Avoid single-character names for types, variables, and functions.
The only place they are allowed is as indexes in iterators.
OK NG
OK NG
class Article {
class Article {
var text: String
var title: 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.
Dependencies
Import Statements
importstatements for OS frameworks and external frameworks
should be separated and alphabetized.
OK NG
Declaration Order
All type declarations such as class, struct, enum, extension,
and protocols, should be marked with // MARK: - <name of
declaration> (with hyphen)
OK NG
} }
Rationale: Makes it easy to jump to specific types when using
Xcode's Source Navigator.
// MARK: - BaseViewController
class BaseViewController: UIViewController, UIScrollViewDelegate {
// MARK: Internal
// MARK: UIViewController
// MARK: UIScrollViewDelegate
// MARK: Private
}
Rationale: Makes it easy to locate where in the source code certain
properties and functions are declared.
All // MARK: tags should have two empty lines above and one
empty line below.
OK NG
import UIKit
// MARK: - BaseViewController
class BaseViewController:
import UIKit
UIViewController {
// MARK: - BaseViewController
// MARK: Internal
class BaseViewController:
UIViewController {
weak var scrollView: UIScrollView?
// MARK: Internal
weak var scrollView: UIScrollView?
// MARK: UIViewController
// MARK: UIViewController
override func viewDidLoad() {
override func viewDidLoad() {
// ...
// ...
}
}
override func viewWillAppear(animated:
override func viewWillAppear(animated:
Bool) {
Bool) {
// ...
// ...
}
}
// MARK: Private
private var lastOffset = CGPoint.zero
// 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)
o // MARK: NSObject
o // MARK: UIResponder
o // MARK: UIViewController
Protocol Inheritance (parent-most to child-most)
o // MARK: UITableViewDataSource
o // MARK: UIScrollViewDelegate
o // MARK: UITableViewDelegate
// MARK: Private
Best Practices
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
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
} }
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.
}
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 @IBOutlets should be declared weak. They should also be
wrapped as Optional, not ImplicitlyUnwrappedOptional.
OK NG
@IBOutlet dynamic weak var profileIcon: @IBOutlet var profileIcon:
UIImageView? 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(_:).
Access Modifiers
Design declarations as private by default and only expose
as internal or public as the needs arise.
Rationale: This helps prevent pollution of XCode's auto-completion. In
theory this should also help the compiler make better optimizations and
build faster.
Type Inference
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 lineBreakMode =
NSLineBreakMode.ByWordWrapping
var lineBreakMode: NSLineBreakMode =
// or
NSLineBreakMode.ByWordWrapping
var lineBreakMode: NSLineBreakMode
= .ByWordWrapping
Rationale: Prevent redundancy. This also reduces ambiguity when
binding to generic types.
Collections / SequenceTypes
.count should only be used when the count value itself is needed
OK
OK NG
OK NG
sequence.removeFirst() sequence.removeAtIndex(0)
sequence.removeLast() sequence.removeAtIndex(sequence.count - 1)
Iterating all indexes:
OK NG
OK NG
OK NG
} }
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(
self.request.downloadImage(
url,
url,
completion: { image in
completion: { [weak self] image in
self.didDownloadImage(image) // hello
self?.didDownloadImage(image)
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].
Never use unowned to capture references in closures.
OK NG
self.request.downloadImage( self.request.downloadImage(
url, url,
completion: { [weak self] image in completion: { [unowned self] image in
self?.didDownloadImage(image) 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( self.request.downloadImage(
url, url,
completion: { [weak self] image in completion: { [weak self] image in
guard let `self` = self else { guard let strongSelf = self else {
return return
} }
self.didDownloadImage(image) strongSelf.didDownloadImage(image)
self.reloadData() strongSelf.reloadData()
self.doSomethingElse() strongSelf.doSomethingElse()
} }
) )
Rationale: Keep the syntax highlighting ;)