Swift Style Guide

Swift Style Guide

Updated for Swift 5.0

The goals are clarity, consistency and brevity, in that order.

  • Clarity at the point of use.
  • Codes should always be consistent within one data set. Pay attention to spelling and case; most frequent problems are with abbreviations.
  • Clarity is more important than brevity.
  • Write a documentation comment.

References

This guide mainly follows:

Formatting

Column Limit

Swift code has a column limit of 100 characters. Except as noted below, any line that would exceed this limit must be line-wrapped as described in Line-Wrapping.

Exceptions:

  1. Lines where obeying the column limit is not possible without breaking a meaningful unit of text that should not be broken (for example, a long URL in a comment).
  2. import statements.
  3. Code generated by another tool.

Braces

In general, braces follow Kernighan and Ritchie (K&R) style for non-empty blocks with exceptions for Swift-specific constructs and rules:

  • There is no line break before the opening brace ({).
  • There is a line break after the opening brace ({).
  • There is a line break before the closing brace (}).
  • There is a line break after the closing brace (}) iff that brace terminated a statement.
    • Exceptions: } else {

Semicolons

Semicolons (;) are not used. The only location where a semicolon may appear is inside a string literal or a comment.

One Statement Per Line

Line-Wrapping

Google gives clear picture here. The key point is to be careful to judge breakable or unbreakable units.

  1. If the entire declaration, statement, or expression fits on one line, then do that.
  2. Comma-delimited lists are only laid out in one direction: horizontally or vertically. In other words, all elements must fit on the same line, or each element must be on its own line. A horizontally-oriented list does not contain any line breaks, even before the first element or after the last element. Except in control flow statements, a vertically-oriented list contains a line break before the first element and after each element.
  3. A continuation line starting with an unbreakable token sequence is indented at the same level as the original line.
  4. A continuation line that is part of a vertically-oriented comma-delimited list is indented exactly +2 from the original line.
  5. When an open curly brace ({) follows a line-wrapped declaration or expression, it is on the same line as the final continuation line unless that line is indented at +2 from the original line. In that case, the brace is placed on its own line, to avoid the continuation lines from blending visually with the body of the subsequent block.

Some examples:

public func index<Elements: Collection, Element>(
  of element: Element,
  in collection: Elements
) -> Elements.Index?
where
  Elements.Element == Element,
  Element: Equatable {  // AVOID.
  for current in elements {
    // ...
  }
}
public func index<Elements: Collection, Element>(
  of element: Element,
  in collection: Elements
) -> Elements.Index?
where
  Elements.Element == Element,
  Element: Equatable
{  // GOOD.
  for current in elements {
    // ...
  }
}

For declarations that contain a where clause followed by generic constraints, additional rules apply:

If the generic constraint list exceeds the column limit when placed on the same line as the return type, then a line break is first inserted before the where keyword and the where keyword is indented at the same level as the original line. If the generic constraint list still exceeds the column limit after inserting the line break above, then the constraint list is oriented vertically with a line break after the where keyword and a line break after the final constraint.

Functions Calls

The followings are both good, choose one and stick to it consistently.

// good
let index = index(
  of: veryLongElementVariableName,
  in: aCollectionOfElementsThatAlsoHappensToHaveALongName)
// good
let index = index(
  of: veryLongElementVariableName,
  in: aCollectionOfElementsThatAlsoHappensToHaveALongName
)

Control Flow Statements

// bad
if cond1() &&
   muchlongerCond2() &&
   shortCond3()
{
  doSomething()
}
// good
if cond1() &&
   muchlongerCond2() &&
   shortCond3() {
  doSomething()
}
// bad
guard let value = returnValue()
      let value2 = returnValue2() else {
  doSomething()
}
// good
guard let value = returnValue()
      let value2 = returnValue2()
else {
  doSomething()
}

Other Expressions

When there are multiple continuation lines, indentation may be varied in increments of +2 as needed.

// bad
let result = anExpression + thatIsMadeUpOf * aLargeNumber +
    ofTerms / andTherefore % mustBeWrapped + (
        andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)
// good
let result = anExpression + thatIsMadeUpOf * aLargeNumber +
  ofTerms / andTherefore % mustBeWrapped + (
    andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)

Horizontal Whitespace

Here is common convention in most languages.

// bad
if(x==0 && y==0) || z==0 {

}
// good
if (x == 0 && y == 0) || z == 0 {

}
// bad
let nonNegativeCubes = numbers.map { $0 * $0 * $0 }.filter { $0 >= 0 }
// good
let nonNegativeCubes = numbers.map { $0 * $0 * $0 } .filter { $0 >= 0 }
// bad
func sum(_ numbers: [Int])->Int {
  // ...
}
// good
func sum(_ numbers: [Int]) -> Int {
  // ...
}
// bad
for number in 1 ... 5 {
  // ...
}

let substring = string[index ..< string.endIndex]
// good
for number in 1...5 {
  // ...
}

let substring = string[index..<string.endIndex]
// bad
let numbers = [1,2,3]
let numbers = [1 ,2 ,3]
let numbers = [1 , 2 , 3]
// good
let numbers = [1, 2, 3]
// bad
struct HashTable : Collection {
  // ...
}

struct AnyEquatable<Wrapped : Equatable> : Equatable {
  // ...
}
// good
struct HashTable: Collection {
  // ...
}

struct AnyEquatable<Wrapped: Equatable>: Equatable {
  // ...
}

Horizontal Alignment

// bad
struct DataPoint {
  var value:        Int
  var primaryColor: UIColor
}
// good
struct DataPoint {
  var value: Int
  var primaryColor: UIColor
}

Naming

Clarity

  • Include all the words needed to avoid ambiguity.

    Proper labeling can effectively reduce ambiguity.

    // bad
    extension List {
      public mutating func remove(position: Index) -> Element
    }
    
    friends.remove(x) // unclear: are we removing x?
    
    // good
    extension List {
      public mutating func remove(at position: Index) -> Element
    }
    
    friends.remove(at: x)
    
  • Omit needless words.

    // bad
    public mutating func removeElement(_ member: Element) -> Element?
    allViews.removeElement(cancelButton)
    
    // good
    public mutating func remove(_ member: Element) -> Element?
    allViews.remove(cancelButton)
    

    The naming removeElement is bad since the word Element doesn’t increase the readibility.

    Occasionally, it is necessary to repeat type information to avoid ambituity. However, in general it is better to use a word that describes a parameter’s role rather than its type.

  • Name variables, parameters, and associated types according to their roles.

    // bad
    var string = "Hello world"
    
    protocol ViewController {
      associatedtype ViewType: View
    }
    
    class ProductLine {
      func restock(from widgetFactory: WidetFactory)
    }
    
    // good
    var greeting = "Hello world"
    
    protocol ViewController {
      associatedtype ContentView: View
    }
    
    class ProductLine {
      func restock(from supplier: WidetFactory)
    }
    
  • Compensate for weak type information.

    To achieve clarity, precede each weakly typed parameter with a noun describing its role.

    // bad
    func add(_ observer: NSObject, for keyPath: String)
    grid.add(self, for: graphics) // vague
    
    // good
    func addObserver(_ observer: NSObject, forKeyPath path: String)
    grid.addObserver(self, forKeyPath: graphics) // clear
    
  • Class prefixes are not needed.

    // bad
    class MAUICollectionViewController: UICollectionViewController { ... }
    
    // good
    class UICollectionViewController { ... }
    

    Why? Because what you’ve declared is declared in current module, which is your current target. And UICollectionViewController from UIKit is declared in UIKit module.

    How to use it within current module?

    // good
    var customController = UICollectionViewController() // your custom class
    var uikitController = UIKit.UICollectionViewController() // class from UIKit
    
  • When creating custom delegate methods, an unnamed first parameter should be the delegate source.

    // bad
    func didSelectName(namePicker: NamePickerViewController, name: String)
    func namePickerShouldReload() -> Bool
    
    // good
    func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
    func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
    
  • Use type infered context to write shorter, clean code.

    // bad
    let selector = #selector(ViewController.viewDidLoad)
    view.backgroundColor = UIColor.yellow
    let toView = context.view(forKey: UITransitionContextViewKey.to)
    let view = UIView(frame: CGRect.zero)
    
    // good
    let selector = #selector(viewDidLoad)
    view.backgroundColor = .yellow
    let toView = context.view(forKey: .to)
    let view = UIView(frame: .zero)
    
  • Generic type parameters should be descriptive, UpperCamelCase names.

    When a type name doesn’t have a meaningful relationship or role, use a traditional single uppercase letter such as T, U, or V.

    // bad
    struct Stack<T> { ... }
    func write<target: OutputStream>(to target: inout target)
    func swap<Thing>(_ a: inout Thing, _ b: inout Thing)
    
    // good
    struct Stack<Element> { ... }
    func write<Target: OutputStream>(to target: inout Target)
    func swap<T>(_ a: inout T, _ b: inout T)
    
  • If a computed property is read-only, omit the get clause.

    // bad
    var area: Double {
      get {
        return width * height
      }
    }
    
    // good
    var area: Double {
      return width * height
    }
    
  • Use of final can sometimes clarify your intent and is worth the cost. In the below example, Box has a particular purpose and customization in a derived class is not intended. Marking it final makes that clear.

    final class Box<T> {
      let value: T
      init(_ value: T) {
        self.value = value
      }
    }
    

Fluency

  • Prefer grammatical English phrases.

    // bad
    x.insert(y, position: z)
    x.subViews(color: y)
    x.nounCapitalize()
    
    // good
    x.insert(y, at: z) // x, insert y at z
    x.subViews(havingColor: y) // x's subviews having color y
    x.capitalizingNouns() // x, capitalizing nouns
    
  • Begin names of factory methods with “make”.

    // bad
    newIterator()
    
    // good
    makeIterator()
    
  • The first argument to initializer and factory methods calls should not form a phrase.

    // bad
    let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
    let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)
    let ref = Link(to: destination)
    
    // good
    let foreground = Color(red: 32, green: 64, blue: 128)
    let newPart = factory.makeWidget(gears: 42, spindles: 14)
    let ref = Link(target: destination)
    
  • Name Mutating/Nonmutating method pairs consistently.

    Prefer to name the nonmutating variant using the verb’s past participle (usually appending “ed").

    // good
    // mutating
    mutating func reverse()
    x.reverse()
    
    // nonmutating: V-ed
    func reversed() -> Self
    let y = x.reversed()
    
  • Name the nonmutating variant using the verb’s present participle, by appending “ing.”

    // good
    // mutating
    mutating func stripNewlines()
    s.stripNewlines()
    
    // nonmutating: V-ing
    func strippingNewlines() -> String
    let oneLine = t.strippingNewlines()
    

    When the operation is naturally described by a noun, use the noun for the nonmutating method and apply the “form” prefix to name its mutating counterpart.

    // good
    // mutating: form + N
    y.formUnion(z)
    c.formSuccessor(&i)
    
    // nonmutating
    x = y.union(z)
    j = c.successor(i)
    
  • Uses of Boolean methods and properties should read as assertions about the receiver when the use is nonmutating.

    // good
    x.isEmpty
    line1.intersects(line2)
    
  • Protocols that describe what something is should read as nouns (e.g. Collection).

  • Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting).

  • The names of other types, properties, variables, and constants should read as nouns.

Terminology

  • Use US English spelling to match Apple’s API.

    // bad
    let colour = "yellow"
    
    // good
    let color = "yellow"
    
  • Avoid obscure terms.

    Don’t say “epidermis” if “skin” will serve your purpose.

  • Avoid abbreviations.

    However, don’t say verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x) since sin(x) has been in common use.

Conventions

General Conventinos

  • Document the complexity of any computed property that is not O(1).

  • Prefer methods and properties to free functions.

    // When there's no obvious self
    min(x, y, z)
    
    // When the function is an unconstrained generic
    print(x)
    
    // When function syntax is part of the established domain notation
    sin(x)
    
  • Follow case convention.

    • Names of types and protocols are UpperCamelCase.
    • Everything else if lowerCamelCase.

    Acronyms and initialisms that commonly appear as all upper case in American English should be uniformly up- or down-cased according to case conventions:

    // good
    var utf8Bytes: [UTF8.CodeUnit]
    var isRepresentableAsASCII = true
    var userSMTPServer: SecureSMTPServer
    
  • Methods can share a base name.

    Overloading is good if the methods do essentially the same things.

    The following three all do the same things:

    // good
    extension Shape {
      func contains(_ other: Point) -> Bool { ... }
      func contains(_ other: Shape) -> Bool { ... }
      func contains(_ other: LineSegment) -> Bool { ... }
    }
    

    And since geometric types and collections are separate domains, this is also fine in the same program:

    // good
    extension Collection where Element: Equatable {
      func contains(_ sought: Element) -> Bool { ... }
    }
    

    However, these index methods have different semantics, and should have been named differently:

    // bad
    extension Database {
      // Rebuilds the database's search index
      func index() { ... }
    
      // Returns the `n`th row in the given table.
      func index(_ n: Int, inTable: TableID) -> TableRow { ... }
    }
    

    Avoid “overloading on return type”:

    // bad
    extension Box {
      func value() -> Int? { ... }
      func value() -> String? { ... }
    }
    

Parameters

  • Choose parameter names to serve documentation.

    // bad
    // Return an `Array` containing the elements of `self`
    // that satisfy `includedInResult`.
    func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element]
    
    // Replace the range of elements indicated by `r` with
    // the contents of `with`.
    mutating func replaceRange(_ r: Range, with: [E])
    
    // good
    // Return an `Array` containing the elements of `self`
    // that satisfy `predicate`.
    func filter(_ predicate: (Element) -> Bool) -> [Generator.Element]
    
    // Replace the given `subRange` of elements with `newElements`.
    mutating func replaceRange(_ subRange: Range, with newElements: [E])
    
  • Take advantage of defaulted parameters.

    // bad
    let order = lastName.compare(royalFamilyName, options: [], range: nil, locale: nil)
    
    // good
    let order = lastName.compare(royalFamilyName)
    
  • Prefer to locate parameters with defaults toward the end.

Argument Labels

  • Omit all labels when arguments can’t be usefully distinguished.

    // good
    min(number1, number2)
    zip(sequence1, sequence2)
    
  • In initializers that perform value preserving type conversions, omit the first argument label.

    // good
    Int64(someUInt32)
    
  • When the first argument forms part of a prepositional phrase, give it an argument label.

    // good
    x.removeBoxes(havingLength: 12)
    

    An exception arises when the first two arguments represent parts of a single abstraction:

    // bad
    a.move(toX: b, y: c)
    a.fade(fromRed: b, green: c, blue: d)
    
    // good
    a.moveTo(x: b, y: c)
    a.fadeFrom(red: b, green: c, blue: d)
    
  • Otherwise, if the first argument forms part of a grammatical phrase, omit its label.

    // good
    x.addSubview(y)
    
    // bad
    view.dismiss(false) // Don't dismiss? Dismiss a Bool?
    words.split(12) // Split the number 12?
    
    // good
    view.dismiss(animated: false)
    let text = words.split(maxSplits: 12)
    let studentByName = students.sorted(isOrderedBefore: Student.namePrecedes)
    
  • Label all other argument

Code Organization

Use extensions to organize your code into logical blocks of functionality. Each extension should be set off with a // MARK: - comment to keep things well-organized.

Protocol Conformance

  • In particular, when adding protocol conformance to a model, prefer adding a separate extension for the protocol methods.

    This keeps the related methods grouped together with the protocol and can simplify instructions to add a protocol to a class with its associated methods.

    // bad
    class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
      // all methods
    }
    
    // good
    class MyViewController: UIViewController {
        // class stuff here
    }
    
    // MARK: - UITableViewDataSource
    extension MyViewController: UITableViewDataSource {
        // table view data source methods
    }
    
    // MARK: - UIScrollViewDelegate
    extension MyViewController: UIScrollViewDelegate {
        // scroll view delegate methods
    }
    

Comments

  • When they are needed, use comments to explain why a particular piece of code does something. Comments must be kept up-to-date or deleted.

  • Avoid block comments inline with code, as the code should be as self-documenting as possible. Exception: This does not apply to those comments used to generate documentation.

  • Avoid the use of C-style comments (/* ... */). Prefer the use of double- (//) or triple-slash (///).

Function Declarations

  • Keep short function declarations on one line including the opening brace.

    // good
    func reticulateSplines(spline: [Double]) -> Bool {
      // code goes here
    }
    
  • For functions with lot’s of parameters, put each parameter on a new line and add an extra indent on subsequent lines.

    // good
    func reticulateSplines(
      spline: [Double],
      adjustmentFactor: Double,
      translateConstant: Int, comment: String
    ) -> Bool {
      // code goes here
    }
    
  • Don’t use (Void) to represent the lack of an input; simply use (). Use Void instead of () for closure and function outputs.

    // bad
    func updateConstraints() -> () { ... }
    
    typealias CompletionHandler = (result) -> ()
    
    // good
    func updateConstraints() -> Void { ... }
    
    typealias CompletionHandler = (result) -> Void
    

Function Calls

  • Mirror the style of function declarations at call sites.

    // good
    let success = reticulateSplines(splines)
    
  • If the call site must be wrapped, put each parameter on a new line, indented one additional level.

    // good
    let success = reticulateSplines(
      spline: splines,
      adjustmentFactor: 1.3,
      translateConstant: 2,
      comment: "normalize the display")
    

Closure Expressions

  • For single-expression closures where the context is clear, use implicit returns.

    // good
    attendeeList.sort { a, b in
      a > b
    }
    

Types

Constants are defined using the let keyword and variables with the var keyword. Always use let instead of var if the value of the variable will not change.

Tip: A good technique is to define everything using let and only change it to var if the compiler complains!

  • To declare a type property as a constant simply use static let.

    // bad
    let e = 2.7182818 // pollutes global namespace
    let root2 = 1.414
    
    let hypotenuse = side * root2 // what is root2?
    
    // good
    enum Math {
      static let e = 2.7182818
      static let root2 = 1.414
    }
    
    let hypotenuse = side * Math.root2
    

Optionals

  • Declare variables and function return types as optional with ? where a nil value is acceptable.

  • Use implicitly unwrapped types declared with ! only for instance variables that you know will be initialized later before use, such as subviews that will be set up in viewDidLoad(). Prefer optional binding to implicitly unwrapped optionals in most other cases.

  • When accessing an optional value, use optional chaining if the value is only accessed once or if there are many optionals in the chain:

    textContainer?.textLabel?.setNeedsDisplay()
    
  • Use optional binding when it’s more convenient to unwrap once and perform multiple operations:

    if let textContainer = textContainer { ... }
    
  • When naming optional variables and properties, avoid naming them like optionalString or maybeView since their optional-ness is already in the type declaration.

  • For optional binding, shadow the original name whenever possible rather than using names like unwrappedView or actualLabel.

    // bad
    var optionalSubview: UIView?
    var volume: Double?
    
    if let unwrappedSubview = optionalSubview {
      if let realVolume = volume {
        // do something with unwrappedSubview and realVolume
      }
    }
    
    // another example
    UIView.animate(withDuration: 2.0) { [weak self] in
      guard let strongSelf = self else { return }
      strongSelf.alpha = 1.0
    }
    
    // good
    var subview: UIView?
    var volume: Double?
    
    // later on...
    if let subview = subview, let volume = volume {
      // do something with unwrapped subview and volume
    }
    
    // another example
    UIView.animate(withDuration: 2.0) { [weak self] in
      guard let self = self else { return }
      self.alpha = 1.0
    }
    

Lazy Initialization

  • Consider using lazy initialization for finer grained control over object lifetime. This is especially true for UIViewController that loads views lazily. You can either use a closure that is immediately called { }() or call a private factory method. Example:

    lazy var locationManager = makeLocationManager()
    
    private func makeLocationManager() -> CLLocationManager {
      let manager = CLLocationManager()
      manager.desiredAccuracy = kCLLocationAccuracyBest
      manager.delegate = self
      manager.requestAlwaysAuthorization()
      return manager
    }
    

Notes:

  • [unowned self] is not required here. A retain cycle is not created.
  • Location manager has a side-effect for popping up UI to ask the user for permission so fine grain control makes sense here.

Type Inference

  • Prefer compact code and let the compiler infer the type for constants or variables of single instances. Type inference is also appropriate for small, non-empty arrays and dictionaries. When required, specify the specific type such as CGFloat or Int16.

    // bad
    let message: String = "Click the button"
    let currentBounds: CGRect = computeViewBounds()
    var names = [String]()
    
    // good
    let message = "Click the button"
    let currentBounds = computeViewBounds()
    var names = ["Mic", "Sam", "Christine"]
    let maximumWidth: CGFloat = 106.5
    

Type Annotation for Empty Arrays and Dictionaries

  • For empty arrays and dictionaries, use type annotation. (For an array or dictionary assigned to a large, multi-line literal, use type annotation.)

    //bad
    var names = [String]()
    var lookup = [String: Int]()
    
    // good
    var names: [String] = []
    var lookup: [String: Int] = [:]
    

NOTE: Following this guideline means picking descriptive names is even more important than before.

Syntactic Sugar

  • Prefer the shortcut versions of type declarations over the full generics syntax.

    // bad
    var deviceModels: Array<String>
    var employees: Dictionary<Int, String>
    var faxNumber: Optional<Int>
    
    // good
    var deviceModels: [String]
    var employees: [Int: String]
    var faxNumber: Int?
    

Functions vs Methods

  • Free functions, which aren’t attached to a class or type, should be used sparingly. When possible, prefer to use a method instead of a free function. This aids in readability and discoverability.

  • Free functions are most appropriate when they aren’t associated with any particular type or instance.

    // bad
    let sorted = mergeSort(items)  // hard to discover
    launch(&rocket)
    
    // good
    let sorted = items.mergeSorted()  // easily discoverable
    rocket.launch()  // acts on the model
    
  • Free Function Exceptions

    let tuples = zip(a, b)  // feels natural as a free function (symmetry)
    let value = max(x, y, z)  // another free function that feels natural
    

Memory Management

Code (even non-production, tutorial demo code) should not create reference cycles. Analyze your object graph and prevent strong cycles with weak and unowned references. Alternatively, use value types (struct, enum) to prevent cycles altogether.

Extending object lifetime

  • Extend object lifetime using the [weak self] and guard let self = self else { return } idiom. [weak self] is preferred to [unowned self] where it is not immediately obvious that self outlives the closure. Explicitly extending lifetime is preferred to optional chaining.

    // bad
    // might crash if self is released before response returns
    resource.request().onComplete { [unowned self] response in
      let model = self.updateModel(response)
      self.updateUI(model)
    }
    
    // bad
    // deallocate could happen between updating the model and updating UI
    resource.request().onComplete { [weak self] response in
      let model = self?.updateModel(response)
      self?.updateUI(model)
    }
    
    // good
    resource.request().onComplete { [weak self] response in
      guard let self = self else {
        return
      }
      let model = self.updateModel(response)
      self.updateUI(model)
    }
    

Access Control

  • Full access control annotation in tutorials can distract from the main topic and is not required. Using private and fileprivate appropriately, however, adds clarity and promotes encapsulation. Prefer private to fileprivate; use fileprivate only when the compiler insists.

  • Only explicitly use open, public, and internal when you require a full access control specification.

  • Use access control as the leading property specifier. The only things that should come before access control are the static specifier or attributes such as @IBAction, @IBOutlet and @discardableResult.

    // bad
    fileprivate let message = "Great Scott!"
    
    class TimeMachine {
      lazy dynamic private var fluxCapacitor = FluxCapacitor()
    }
    
    // good
    private let message = "Great Scott!"
    
    class TimeMachine {
      private dynamic lazy var fluxCapacitor = FluxCapacitor()
    }
    

Control Flow

  • Prefer the for-in style of for loop over the while-condition-increment style.

    // bad
    var i = 0
    while i < 3 {
      print("Hello three times")
      i += 1
    }
    
    // bad
    var i = 0
    while i < attendeeList.count {
      let person = attendeeList[i]
      print("\(person) is at position #\(i)")
      i += 1
    }
    
    
    // good
    for _ in 0..<3 {
      print("Hello three times")
    }
    
    for (index, person) in attendeeList.enumerated() {
      print("\(person) is at position #\(index)")
    }
    
    for index in stride(from: 0, to: items.count, by: 2) {
      print(index)
    }
    
    for index in (0...3).reversed() {
      print(index)
    }
    

Ternary Operator

  • The Ternary operator, ?: , should only be used when it increases clarity or code neatness. A single condition is usually all that should be evaluated. Evaluating multiple conditions is usually more understandable as an if statement or refactored into instance variables. In general, the best use of the ternary operator is during assignment of a variable and deciding which value to use.

    // bad
    result = a > b ? x = c > d ? c : d : y
    
    // good
    let value = 5
    result = value != 0 ? x : y
    
    let isHorizontal = true
    result = isHorizontal ? x : y
    

Multi-line String Literals

  • When building a long string literal, you’re encouraged to use the multi-line string literal syntax. Open the literal on the same line as the assignment but do not include text on that line. Indent the text block one additional level.

    // bad
    let message = """You cannot charge the flux \
      capacitor with a 9V battery.
      You must use a super-charger \
      which costs 10 credits. You currently \
      have \(credits) credits available.
      """
    
    // good
    let message = """
      You cannot charge the flux \
      capacitor with a 9V battery.
      You must use a super-charger \
      which costs 10 credits. You currently \
      have \(credits) credits available.
      """
    

Special Instructions

  • Label tuple members and name closure parameters

  • Take extra care with unconstrained polymorphism

    // bad
    struct Array {
      // Inserts `newElement` at `self.endIndex`.
      public mutating func append(_ newElement: Element)
    
      // Inserts the contents of `newElements`, in order, at
      // `self.endIndex`.
      public mutating func append(_ newElements: S)
        where S.Generator.Element == Element
    }
    
    var values: [Any] = [1, "a"]
    values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?
    
    // good
    struct Array {
      // Inserts `newElement` at `self.endIndex`.
      public mutating func append(_ newElement: Element)
    
      // Inserts the contents of `newElements`, in order, at
      // `self.endIndex`.
      public mutating func append(contentsOf newElements: S)
        where S.Generator.Element == Element
    }
    
    var values: [Any] = [1, "a"]
    values.append([2, 3, 4]) // it's [1, "a", [2, 3, 4]]!
    

Swift

4231 Words

2019-05-25 20:00 -0400