Note: This repository is no longer being maintained.
Swift Style Guide
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.
Table of Contents
Styles and Conventions
Formatting
Semicolons
;
) are not allowed.
Trailing semicolons (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 ;)
Whitespaces
Use 4 spaces for tabs.
Set Xcode's Text Editing settings as shown:
Rationale: Maintains visually similar indentation across different text editors.
All source files should end with a single trailing newline (only).
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.
All functions should be at least one empty line apart each other.
OK |
---|
class BaseViewController: UIViewController {
// ...
override viewDidLoad() {
// ...
}
override viewWillAppear(animated: Bool) {
// ...
}
} |
Rationale: Gives breathing room between code blocks.
Use single spaces around operator definitions and operator calls.
OK | NG |
---|---|
func <| (lhs: Int, rhs: Int) -> Int {
// ...
}
let value = 1 <| 2 |
func <|(lhs: Int, rhs: Int) -> Int {
// ...
}
let value = 1<|2 |
Rationale: Readability.
->
) both in functions and in closures.
Use single spaces around return arrows (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.
Commas (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.
Colons (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)
:
) for case
statements should have no whitespace before it, and should have either one space or one newline after it.
Colons (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.
Braces
{
) should be one space following the previous non-whitespace character.
Open braces (OK | NG |
---|---|
class Icon {
// ...
} |
class Icon{
// ...
} |
let block = { () -> Void in
// ...
} |
let block ={ () -> Void in
// ...
} |
Rationale: Separates the brace from the declaration.
{
) for type declarations, functions, and closures should be followed by one empty line. Single-statement closures can be written in one line.
Open braces (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.
{}
), otherwise a comment should indicate the reason for the empty implementation.
Empty declarations should be written in empty braces (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
.
}
) 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.
Close braces (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.
}
) unless on the same line as its corresponding open brace ({
), should be left-aligned with the statement that declared the open brace.
Close braces (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.
Properties
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 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.
get
clause.
Read-only computed properties should ommit the 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.
Control Flow Statements
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.
if
, switch
, for
, and while
statements should not be enclosed in parentheses (()
).
Conditions for OK | NG |
---|---|
if array.isEmpty {
// ...
} |
if (array.isEmpty) {
// ...
} |
Rationale: This isn't Objective-C.
return
ing early when possible.
Try to avoid nesting statements by 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
Naming rules are mostly based on Apple's naming conventions, since we'll end up consuming their API anyway.
Capitalization
class
, struct
, enum
, protocol
) should be in UpperCamelCase.
Type names (OK | NG |
---|---|
class ImageButton {
enum ButtonState {
// ...
}
} |
class image_button {
enum buttonState {
// ...
}
} |
Rationale: Adopt Apple's naming rules for uniformity.
enum
values and OptionSetType
values should be in UpperCamelCase.
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.
Semantics
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.
some are allowed such as min
/max
)
Avoid abbreviations as much as possible. (although OK | NG |
---|---|
let errorCode = error.code |
let err = error.code |
Rationale: Clarity is prioritized over slight brevity.
Choose a name that communicates as much information about what it is and what it's for.
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.
NSURL
s by appending the suffix ~String
.
When pertaining to URLs, distinguish strings from actual 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.
class
, struct
, enum
, protocol
, etc.) in their names.
Do not pertain to constructs (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.
Dependencies
Import Statements
import
statements for OS frameworks and external frameworks should be separated and alphabetized.
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.
Declaration Order
class
, struct
, enum
, extension
, and protocol
s, should be marked with // MARK: - <name of declaration>
(with hyphen)
All type declarations such as 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.
// MARK: <superclass/protocol name>
. The rest should be marked as either // MARK: Public
, // MARK: Internal
, or // MARK: Private
.
All properties and methods should be grouped into the superclass/protocol they implement and should be tagged with 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.
// MARK:
tags should have two empty lines above and one empty line below.
All 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:
tags should be ordered as follows:
The groupings for // 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.
Under each grouping above, declarations should be ordered as follows:
@
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.
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 |
---|---|
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.
// TODO: localize
All temporary, unlocalized strings should be marked with 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.
Protection from Dynamism
protocol
implementations, whether properties or methods, should be prefixed with @objc dynamic
All Objective-C OK | NG |
---|---|
@objc dynamic func scrollViewDidScroll(scrollView: UIScrollView) {
// ...
} |
func scrollViewDidScroll(scrollView: UIScrollView) {
// ...
} |
Rationale: Prevents horrible compiler optimization bugs. Trust us.
IBAction
s and IBOutlet
s should be declared dynamic
All 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
.
Selector
s should be marked dynamic
All properties used for KVC/KVO and all functions used as 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
.
@IBOutlet
s should be declared weak
. They should also be wrapped as Optional
, not ImplicitlyUnwrappedOptional
.
All 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(_:)
.
Access Modifiers
private
by default and only expose as internal
or public
as the needs arise.
Design declarations as Rationale: This helps prevent pollution of XCode's auto-completion. In theory this should also help the compiler make better optimizations and build faster.
public
, internal
, or private
.
For library modules: all declarations should explicitly specify either OK | NG |
---|---|
private let defaultTimeout: NSTimeInterval = 30
internal class NetworkRequest {
// ...
} |
let defaultTimeout: NSTimeInterval = 30
class NetworkRequest {
// ...
} |
Rationale: Makes the intent clear for API consumers.
public
access is prohibited unless required by a protocol. The internal
keyword may or may not be written, but the private
keyword is required.
For application modules: 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.
@
modifiers.
Access modifiers should be written before all other non-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.
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 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.
StringLiteralConvertible
, NilLiteralConvertible
, etc), it is encouraged to specify the type explicitly and is preferrable over casting with as
directly.
When literal types are involved (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.
Collections / SequenceTypes
.count
should only be used when the count value itself is needed
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).
Protection from Retain Cycles
In particular, this will cover the ever-debatable usage/non-usage of self
.
self
, including within closures.
All instance properties and functions should be fully-qualified with (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.
@noescape
and non-animation closures, accessing self
within the closure requires a [weak self]
declaration.
For all non-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]
.
unowned
to capture references in closures.
Never use 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.
self
in the closure is needed, bind using the variable `self`
to shadow the original.
If the validity of the weak 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 ;)