Make sure to read Apple's API Design Guidelines.
Specifics from these guidelines + additional remarks are mentioned below.
This guide was last updated for Swift 5 on November 17, 2021.
- Swift Style Guide
- 1.1 Use 4 spaces for tabs.
- 1.2 Ensure that there is a newline at the end of every file.
- 1.3 Ensure that there is no trailing whitespace (Xcode->Preferences->Text Editing->Editing->Automatically trim trailing whitespace).
- 1.4 The opening brace should be followed by an empty line. Give breathing room when scanning for code.
- 1.5 Do not place opening braces on new lines - we use the Stroustrup style.
final class SomeObject {
// MARK: - Functions
func someFunction() {
if let x = x {
/* ... */
}
else if let y = y {
/* ... */
}
else if let z = z {
/* ... */
}
else {
/* ... */
}
}
/* ... */
}
-
1.6 Add one empty line between functions. Give breathing room between code blocks.
-
1.7 Empty declarations should be written in empty braces
{}
. Make it clear that the declaration was meant to be empty and not just a missing// TODO:
.
// PREFERRED
extension UIRectCorner: Hashable {}
// NOT PREFERRED
extension UIRectCorner: Hashable { }
// EVEN LESS PREFERRED
extension UIRectCorner: Hashable {
}
- 1.8 When writing a type for a property, constant, variable, a key for a dictionary, a function argument, a protocol conformance, or a superclass, don't add a space before the colon.
// Specifying type.
let communityViewController: CommunityViewController
// Dictionary syntax (note that we left-align as opposed to aligning colons).
let communityMembersDictionary: [String: Any] = [
"Founder": "Ruslan Skorb",
/* ... */
]
// Declaring a function.
func someFunction<T, U: SomeProtocol>(firstArgument: U, secondArgument: T) where T.RelatedType == U {
/* ... */
}
// Calling a function.
someFunction(someArgument: "R.SK Lab")
// Superclasses.
final class CommunityViewController: UIViewController {
/* ... */
}
// Protocols.
extension CommunityViewController: UICollectionViewDataSource {
/* ... */
}
- 1.9 In general, there should be a space following a comma.
let communityMembers = ["Ruslan Skorb", /* ... */]
- 1.10 There should be a space before and after a binary operator such as
+
,==
, or->
. There should also not be a space after a(
and before a)
.
let colorRed = topColorRed + percent * (bottomColorRed - topColorRed)
-
2.1 Use
PascalCase
for type names (e.g.struct
,enum
,class
,typealias
,associatedtype
, etc.). -
2.2 Use
camelCase
(initial lowercase letter) for function, method, property, constant, variable, argument names, enum cases, etc. -
2.3 When dealing with an acronym or other name that is usually written in all caps, actually use all caps in any names that use this in code. The exception is if this word is at the start of a name that needs to start with lowercase - in this case, use all lowercase for the acronym.
// "URL" is at the start of a constant name, so we use lowercase "url".
let urlSession = URLSession(configuration: .default)
// Prefer using ID to Id.
let appUserID = 1
// Prefer RSKLabLLC to RskLabLlc.
final public class RSKLabLLC {
/* ... */
}
- 2.4 Names should be descriptive and unambiguous.
// PREFERRED
class IBDesignableButton: UIButton {
/* ... */
}
// NOT PREFERRED
class CustomButton: UIButton {
/* ... */
}
- 2.5 Do not abbreviate, use shortened names, or single letter names.
// PREFERRED
@IBDesignable open class IBDesignableButton: UIButton {
// MARK: - Open Properties
@IBInspectable open var cornerRadius: CGFloat {
/* ... */
}
}
// NOT PREFERRED
@IBDesignable open class IBDesignBtn: UIButton {
// MARK: - Open Properties
@IBInspectable open var rad: CGFloat {
/* ... */
}
}
- 2.6 Include type information in constant or variable names when it is not obvious otherwise.
// PREFERRED
let createNoteBarButtonItem: UIBarButtonItem
// PREFERRED
// It is ok not to include `String` in the name of the property here
// because it's obvious from the property name that it's a string.
var searchTerm: String?
// NOT PREFERRED
// For the sake of consistency, we should put the type name at the end of the
// property name and not at the start.
let barButtonItemNotes: UIBarButtonItem
// NOT PREFERRED
// This is not a button, so the name should not end with `Button`,
// use `createNoteBarButtonItem` instead.
let createNoteButton: UIBarButtonItem
// NOT PREFERRED
// This is a label, so it should be `startLoggingLabel`.
let startLogging: Label
// NOT PREFERRED
// This is a table view controller - not a table view.
let notesTableView: NotesViewNotesTableViewController
// NOT PREFERRED
// As mentioned previously, we don't want to use abbreviations,
// so don't use `VC` instead of `ViewController`.
let notesTableVC: NotesViewNotesTableVC
- 2.7 Name your function with words that describe its behavior.
// PREFERRED
func remove(at index: Index) -> Element {
/* ... */
}
// NOT PREFERRED
func remove(index: Index) -> Element {
/* ... */
}
- 2.8 Name a
protocol
using the suffixProtocol
.
protocol ObjectProtocol {}
protocol RectProtocol: ObjectProtocol {
// MARK: - Properties
var height: CGFloat { get set }
/* ... */
}
-
3.1.1 Prefer
let
tovar
whenever possible. -
3.1.2 Prefer the composition of
map
,filter
,reduce
, etc. over iterating when transforming from one collection to another. Make sure to avoid using closures that have side effects when using these methods.
// PREFERRED
let strings = [1, 2, 3].flatMap {
String($0)
}
// ["1", "2", "3"]
// NOT PREFERRED
var strings: [String] = []
for integer in [1, 2, 3] {
strings.append(String(integer))
}
// PREFERRED
let evenNumbers = [4, 8, 15, 16, 23, 42].filter {
$0 % 2 == 0
}
// [4, 8, 16, 42]
// NOT PREFERRED
var evenNumbers: [Int] = []
for integer in [4, 8, 15, 16, 23, 42] {
if integer % 2 == 0 {
evenNumbers.append(integer)
}
}
-
3.1.3 Prefer not declaring types for constants or variables if they can be inferred anyway.
-
3.1.4 If a function returns multiple values, prefer returning a tuple to using
inout
arguments (it’s best to use labeled tuples for clarity on what you’re returning if it is not otherwise obvious). If you use a certain tuple more than once, consider using atypealias
. If you’re returning 3 or more items in a tuple, consider using astruct
orclass
instead.
var communityMemberName: (firstName: String, lastName: String) {
("Ruslan", "Skorb")
}
let firstName = communityMemberName.firstName
let lastName = communityMemberName.lastName
-
3.1.5 Be wary of retain cycles when creating delegates for your classes; typically, these properties should be declared
weak
. -
3.1.6 Be careful when calling
self
directly from an escaping closure as this can cause a retain cycle - use a capture list when this might be the case:
someFunctionWithEscapingClosure { [weak self] in
self?.doSomething()
}
lazy var storedClosure = { [unowned self] in
self.doSomething()
}
- 3.1.7 Don't place parentheses around control flow predicates.
// PREFERRED
if x == y {
/* ... */
}
// NOT PREFERRED
if (x == y) {
/* ... */
}
- 3.1.8 Avoid writing out an
enum
type where possible - use shorthand.
// PREFERRED
let cancelAlertAction = UIAlertAction(title: cancelAlertActionTitle, style: .cancel, handler: nil)
// NOT PREFERRED
let cancelAlertAction = UIAlertAction(title: cancelAlertActionTitle, style: UIAlertAction.Style.cancel, handler: nil)
- 3.1.9 When using a statement such as
else
,catch
, etc. that follows a block, put this keyword on a new line. Again, we are following the Stroustrup style here. Exampleif
/else
anddo
/catch
code is below.
if let appStoreReceiptURL = bundle.appStoreReceiptURL {
let data: Data
do {
data = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
}
catch {
/* ... */
}
/* ... */
}
else {
/* ... */
}
-
3.1.10 Prefer
static
toclass
when declaring a function or property that is associated with a class as opposed to an instance of that class. Only useclass
if you specifically need the functionality of overriding that function or property in a subclass, though consider using aprotocol
to achieve this instead. -
3.1.11 If you have a function that takes no arguments, has no side effects, and returns some object or value, prefer using a computed property instead.
- 3.2.1 Write the access modifier keyword first.
// PREFERRED
private static let somePrivateInt: Int
// NOT PREFERRED
static private let somePrivateInt: Int
- 3.2.2 The access modifier keyword should not be on a line by itself - keep it inline with what it is describing.
// PREFERRED
open struct CommunityMember {
/* ... */
}
// NOT PREFERRED
open
struct CommunityMember {
/* ... */
}
Prefer creating named functions to custom operators.
If you want to introduce a custom operator, make sure that you have a very good reason why you want to introduce a new operator into global scope as opposed to using some other construct.
You can override existing operators to support new types (especially ==
). However, your new definitions must preserve the semantics of the operator. For example, ==
must always test equality and return a boolean.
-
3.4.1 Since
switch
cases in Swift break by default, do not include thebreak
keyword if it is not needed. -
3.4.2 The
case
statements should line up with theswitch
statement itself as per default Swift standards. -
3.4.3 Prefer lists of possibilities (e.g.
case 1, 2, 3:
) to using thefallthrough
keyword where possible. -
3.4.4 If you have a default case that shouldn't be reached, preferably throw an error (or handle it in some other similar way such as asserting).
switch type {
case .delete:
/* ... */
case .insert:
/* ... */
case .move:
/* ... */
case .update:
/* ... */
@unknown default:
fatalError("Unknown `type`")
}
- 3.5.1 If you don't plan on actually using the value stored in an optional, but need to determine whether or not this value is
nil
, explicitly check this value againstnil
as opposed to usingif let
syntax.
// PREFERERED
if someOptional != nil {
// do something
}
// NOT PREFERRED
if let _ = someOptional {
// do something
}
When implementing protocols, there are three ways to organize your code:
- Using
// MARK: -
comments to separate your protocol implementation from the rest of your code. - Using an extension outside your
class
/struct
implementation code, but in the same source file. - Using an extension outside your
class
/struct
implementation code in another source file.
- 3.7.1 If making a read-only, computed property, provide the getter without the
get {}
around it.
override var flipsHorizontallyInOppositeLayoutDirection: Bool {
true
}
- 3.7.2 When using
get
,set
,willSet
, anddidSet
, indent these blocks. - 3.7.3 Though you can create a custom name for the new or old value for
willSet
/didSet
andset
, use the standardnewValue
/oldValue
identifiers that are provided by default.
private(set) var selectedViewController: UIViewController? {
didSet {
oldValue?.willMove(toParent: nil)
oldValue?.view.removeFromSuperview()
oldValue?.removeFromParent()
/* ... */
}
}
@objc dynamic var noteText: String? {
get {
noteTextController.noteText
}
set {
noteTextController.noteText = newValue
}
}
- 3.7.4 You can declare a singleton property as follows:
final class SomeObject {
/* ... */
// MARK: - Properties
static let shared = SomeObject()
/* ... */
}
- 3.8.1 If the types of the parameters are obvious, it is OK to omit the type name, but being explicit is also OK. Sometimes readability is enhanced by adding clarifying detail and sometimes by taking repetitive parts away - use your best judgment and be consistent.
// Omitting the type.
doSomething { result in
/* ... */
}
// Using shorthand in a map statement.
[1, 2, 3].flatMap {
String($0)
}
- 3.8.2 Keep parameter names on same line as the opening brace for closures.
- 3.9.1 Prefer using a
for item in items
syntax when possible as opposed to something likefor i in 0 ..< items.count
. If you need to access an array subscript directly, make sure to do proper bounds checking. You can usefor (index, value) in items.enumerated()
to get both the index and the value.
- 3.10.1 In general, we prefer to use an "early return" strategy where applicable as opposed to nesting code in
if
statements. Usingguard
statements for this use-case is often helpful and can improve the readability of the code.
// PREFERRED
func communityMember(at index: Int) -> CommunityMember? {
guard index >= 0 && index < communityMembers.count else {
// return early because the index is out of bounds
return nil
}
return communityMembers[index]
}
The following list should be the standard organization of all your Swift files:
Before the type declaration:
import CoreData.NSManagedObjectID
import MessageUI.MFMailComposeViewController
import RSKUIKit
Inside the type declaration:
// MARK: - <#Accessibility#> Properties
// MARK: - Deinitializer
// MARK: - <#Accessibility#> Initializers
// MARK: - <#Accessibility#> Functions
// MARK: - <#Protocol#>
Each section above should be organized by accessibility:
open
public
internal (by default, do not need to be specified explicitly)
fileprivate
private
After the type declaration:
<#accessibility#> extension
Import statements, properties, initializers and functions inside // MARK: -
, as well as protocols should be placed in alphabetical order.
All public
functions/classes/properties/constants/structs/enums/protocols/etc. should be documented.
After writing a doc comment, you should option click the function/property/class/etc. to make sure that everything is formatted correctly.
Be sure to check out the full set of features available in Swift's comment markup described in Apple's Documentation.
Guidelines:
-
5.1.1 Use a triple slash for doc comments (
///
). -
5.1.2 Add a new line before and after the doc comment that takes up more than one line.
-
5.1.3 When mentioning code, use code ticks - `
///
/// Initializes and returns a new object that conforms to `RectProtocol` with the specified `origin` and `size`.
///
/// - Parameters:
/// - origin: The coordinates of the origin of the rectangle.
/// - size: The size of the rectangle.
///
- 5.2.1 Always leave a space after
//
. - 5.2.2 When using
// MARK: -
, leave a newline after it.
class Task {
// MARK: - Properties
let attributesDispatchQueue: DispatchQueue
/* ... */
// MARK: - Initializers
init(targetQueue: DispatchQueue, workDispatchQoS: DispatchQoS) {
/* ... */
}
}