0% found this document useful (0 votes)
22 views20 pages

SwiftUI Coding Standard

Uploaded by

premagarwal2611
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views20 pages

SwiftUI Coding Standard

Uploaded by

premagarwal2611
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 20

Table of Contents

 Styles and Conventions


o Formatting
 Semicolons (;)
 Whitespaces
 Commas (,)
 Colons (:)
 Braces ({})
 Properties
 Control Flow Statements
o Naming
 Capitalization
 Semantics
o Dependencies
 Import Statements
o Declaration Order
 Best Practices
o Comments
o Protection from Dynamism
o Access Modifiers
o Type Inference
o Collections / SequenceTypes
o Protection from Retain Cycles

Styles and Conventions


Formatting

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

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 {
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

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 { 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

func doSomething(value: Int) -> Int { func doSomething(value: Int)->Int {


// ... // ...
} }
Rationale: Readability.

Commas
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(
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)

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 {
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

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 { class Icon {


let image: UIImage let image: UIImage
var completion: (() -> Void) init(image: UIImage) {
self.image = image
init(image: UIImage) { self.completion = { [weak self] in
print("done"); self?.didComplete() }
self.image = image }
self.completion = { [weak self] in
self?.didComplete() }
}
func doSomething()
func doSomething() {
{ self.doSomethingElse() }
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) {} var didTap: () -> Void = { }
@objc dynamic func override func drawRect(rect: CGRect) {
controllerDidChangeContent(controller: }
NSFetchedResultsController) { @objc dynamic func
// do nothing; delegate method required controllerDidChangeContent(controller:
to enable tracking mode 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 {
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

struct Rectangle { struct Rectangle {


// ... // ...
var right: Float { var right: Float {

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.

Read-only computed properties should ommit the get clause.


OK NG

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.

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 {
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.

casestatements 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: 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.

Try to avoid nesting statements by returning early when possible.


OK NG

guard let strongSelf = self else {


if let strongSelf = self {
return
// do many things with strongSelf
}
}
// 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
Type names (class, struct, enum, protocol) should be
in UpperCamelCase.
OK NG

class ImageButton { class image_button {


enum ButtonState { enum buttonState {
// ... // ...
} }

} }
Rationale: Adopt Apple's naming rules for uniformity.

enum values and OptionSetType values should be


in UpperCamelCase.
OK NG

enum ErrorCode { enum ErrorCode {


case Unknown case unknown
case NetworkNotFound case network_not_found
case InvalidParameters case invalidParameters

} }
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.

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 web_view: UIWebView?


var URLString: String? var urlString: String?
func didTapReloadButton() { 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() { for (i, v) in array.enumerate() {


// ... "i" is well known // ... 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.

Choose a name that communicates as much information about


what it is and what it's for.

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.

When pertaining to URLs, distinguish strings from actual NSURLs


by appending the suffix ~String.
OK NG

var requestURL: NSURL var requestURL: NSURL


var sourceURLString: String var sourceURL: String
func loadURL(URL: NSURL) { func loadURL(URL: NSURL) {
// ... // ...
} }
func loadURLString(URLString: String) { func loadURL(URL: String) {
// ... // ...
} }
Rationale: Saves a few seconds checking header declarations for the
correct type.

Do not pertain to constructs (class, struct, enum, protocol, etc.) in


their names.
OK NG

class User { class UserClass {


// ... // ...
} }
enum Result { enum ResultEnum {
// ... // ...
} }
protocol Queryable { 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
importstatements for OS frameworks and external frameworks
should be separated and alphabetized.
OK NG

import Foundation import Foundation


import UIKit import Alamofire
import Alamofire import SwiftyJSON
import Cartography import UIKit
import SwiftyJSON import Cartography
Rationale: Reduce merge conflicts when dependencies change between
branches.

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

// MARK: - Icon // Icon


class Icon { class Icon {
// MARK: - CornerType // MARK: CornerType

enum CornerType { enum CornerType {

case Square case Square


case Rounded 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.

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.

The groupings for // MARK: tags should be ordered as follows:

 // 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

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 = 20 // left margin
view.frame.x = leftMargin

@objc dynamic func @objc dynamic func


tableView(tableView: UITableView, tableView(tableView: UITableView,
heightForHeaderInSection section: Int) - heightForHeaderInSection section: Int) -
> CGFloat { > CGFloat {
return 0.01 // tableView ignores 0 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.

All temporary, unlocalized strings should be marked with // TODO:


localize
OK NG

self.titleLabel.text = "Date Today:" // TODO: self.titleLabel.text = "Date


localize 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


All Objective-C protocol implementations, whether properties or
methods, should be prefixed with @objc dynamic
OK NG

@objc dynamic func


func scrollViewDidScroll(scrollView:
scrollViewDidScroll(scrollView:
UIScrollView) {
UIScrollView) {
// ...
// ...
}
}
Rationale: Prevents horrible compiler optimization bugs. Trust us.

All IBActions and IBOutlets should be declared dynamic


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.
All properties used for KVC/KVO and all functions used as Selectors
should be marked dynamic
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 @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.

For library modules: all declarations should explicitly specify


either public, internal, or private.
OK NG

private let defaultTimeout: NSTimeInterval let defaultTimeout: NSTimeInterval =


= 30 30
internal class NetworkRequest { 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" public let someGlobal = "someValue"


class AppDelegate { public class AppDelegate {
// ... // ...
private var isForeground = false 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.
Access modifiers should be written before all other non-
@ modifiers.
OK NG

@objc internal class User: internal @objc class User:


NSManagedObject { NSManagedObject {
// ... // ...
@NSManaged internal dynamic var @NSManaged dynamic internal var
identifier: Int identifier: Int
// ... // ...
@NSManaged private dynamic var private @NSManaged dynamic var
internalCache: NSData? 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 =


var backgroundColor =
UIColor.whiteColor()
UIColor.whiteColor()
var iconView: UIImageView =
var iconView = UIImageView(image)
UIImageView(image)

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.

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 radius: CGFloat = CGFloat(0)


var length = 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 first = sequence[0]


let last = sequence.last let last = sequence[sequence.count - 1]
Removing the first or last item:

OK NG

sequence.removeFirst() sequence.removeAtIndex(0)
sequence.removeLast() 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 first = 0


let last = sequence.indices.last 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.
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 animatableViews.forEach { view in


self.animateView(view) 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(
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 ;)

You might also like