Skip to content

Latest commit

 

History

History
628 lines (454 loc) · 17.6 KB

README.md

File metadata and controls

628 lines (454 loc) · 17.6 KB

Swift Code Style

The overarching goals are conciseness, readability, and simplicity.

1. Naming

Use descriptive names with camel case for classes, methods, variables, etc.

  • Class, struct and enum names should start with a upper case letter
  • Method and variables names should start with a lower case letter

Preferred:

private let maximumWidgetCount = 100

class WidgetContainer {
    var widgetButton: UIButton
    let widgetHeightPercentage = 0.85
}

Not Preferred:

let MAX_WIDGET_COUNT = 100

class app_widgetContainer {
    var wBut: UIButton
    let wHeightPct = 0.85
}

For functions and init methods, prefer named parameters for all arguments unless the context is very clear. Include external parameter names if it makes function calls more readable.

func dateFromString(dateString: String) -> NSDate
func convertPointAt(column column: Int, row: Int) -> CGPoint
func timedAction(delay delay: NSTimeInterval, perform action: SKAction) -> SKAction!

// would be called like this:
dateFromString("2014-03-14")
convertPointAt(column: 42, row: 13)
timedAction(delay: 1.0, perform: someOtherAction)

For methods, follow the standard Apple convention of referring to the first parameter in the method name:

class Guideline {
    func combineWithString(incoming: String, options: Dictionary?) { ... }
    func upvoteBy(amount: Int) { ... }
}

1.1 Enumerations

Use UpperCamelCase for enumeration values:

enum Shape {
    case Rectangle
    case Square
    case Triangle
    case Circle
}

1.2 Class Prefixes

Swift types are automatically namespaced by the module that contains them and you should not add a class prefix. If two names from different modules collide you can disambiguate by prefixing the type name with the module name.

// Both MyModule and SomeModule contain UsefulClass
import SomeModule

let myClass = MyModule.UsefulClass()

2. Spacing

  • Indent using 4 spaces rather than tabs to conserve space and help prevent line wrapping.

  • Method braces and other braces (if/else/switch/while etc.) always open on the same line as the statement but close on a new line.

  • Tip: You can re-indent by selecting some code (or ⌘A to select all) and then Control-I (or Editor\Structure\Re-Indent in the menu). Some of the Xcode template code will have 4-space tabs hard coded, so this is a good way to fix that.

Preferred:

if user.isHappy {
    // Do something
} else {
    // Do something else
}

Not Preferred:

if user.isHappy
{
    // Do something
}
else {
    // Do something else
}
  • There should be exactly one blank line between methods to aid in visual clarity and organization. New line within methods should separate functionality, but having too many sections in a method often means you should refactor into several methods.
  • End files with a newline.
  • Make liberal use of vertical whitespace to divide code into logical chunks.
  • Don’t leave trailing whitespace. (Except on blank lines to keep the indentation level)
  • Trust Xcode default re-indentation

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

4. Classes and Structures

4.1 Which one to use?

Remember, structs have value semantics. Use structs for things that do not have an identity. An array that contains [a, b, c] is really the same as another array that contains [a, b, c] and they are completely interchangeable. It doesn't matter whether you use the first array or the second, because they represent the exact same thing. That's why arrays are structs.

Classes have reference semantics. Use classes for things that do have an identity or a specific life cycle. You would model a person as a class because two person objects are two different things. Just because two people have the same name and birthdate, doesn't mean they are the same person. But the person's birthdate would be a struct because a date of 3 March 1950 is the same as any other date object for 3 March 1950. The date itself doesn't have an identity.

Sometimes, things should be structs but need to conform to AnyObject or are historically modeled as classes already (NSDate, NSSet). Try to follow these guidelines as closely as possible.

4.2 Prefer structs over classes

Unless you require functionality that can only be provided by a class (like identity or deinitializers), implement a struct instead.

Note that inheritance is (by itself) usually not a good reason to use classes, because polymorphism can be provided by protocols, and implementation reuse can be provided through composition.

For example, this class hierarchy:

class Vehicle {
    let numberOfWheels: Int

    init(numberOfWheels: Int) {
        self.numberOfWheels = numberOfWheels
    }

    func maximumTotalTirePressure(pressurePerWheel: Float) -> Float {
        return pressurePerWheel * Float(numberOfWheels)
    }
}

class Bicycle: Vehicle {
    init() {
        super.init(numberOfWheels: 2)
    }
}

class Car: Vehicle {
    init() {
        super.init(numberOfWheels: 4)
    }
}

could be refactored into these definitions:

protocol Vehicle {
    var numberOfWheels: Int { get }
}

func maximumTotalTirePressure(vehicle: Vehicle, pressurePerWheel: Float) -> Float {
    return pressurePerWheel * Float(vehicle.numberOfWheels)
}

struct Bicycle: Vehicle {
    let numberOfWheels = 2
}

struct Car: Vehicle {
    let numberOfWheels = 4
}

Rationale: Value types are simpler, easier to reason about, and behave as expected with the let keyword.

4.3 Make classes final by default

Classes should start as final, and only be changed to allow subclassing if a valid need for inheritance has been identified. Even in that case, as many definitions as possible within the class should be final as well, following the same rules.

Rationale: Composition is usually preferable to inheritance, and opting in to inheritance hopefully means that more thought will be put into the decision.

4.4 Protocol Conformance

When adding protocol conformance to a class, prefer adding a separate class 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.

Also, it is recommended to add // MARK: - comment to keep things well-organized!

Preferred:

class MyViewcontroller: UIViewController {
    // class stuff here
}

// MARK: - UITableViewDataSource
extension MyViewcontroller: UITableViewDataSource {
    // table view data source methods
}

// MARK: - UITableViewDelegate
extension MyViewcontroller: UITableViewDelegate {
    // table view delegate methods
} 

Not Preferred:

class MyViewcontroller: UIViewController, UITableViewDataSource, UITableViewDelegate {
    // all methods
}

4.5 Refer explicitly to self when required

When accessing properties or methods on self, alway use self this way the developper knows that he is using instance properties/method:

private class History {
	var events: [Event]

	func rewrite() {
		self.events = []
	}
}

4.6 Prefer implicit getters on read-only properties and subscripts

When possible, omit the get keyword on read-only computed properties and read-only subscripts.

So, write these:

var myGreatProperty: Int {
	return 4
}

subscript(index: Int) -> T {
    return objects[index]
}

… not these:

var myGreatProperty: Int {
	get {
		return 4
	}
}

subscript(index: Int) -> T {
    get {
        return objects[index]
    }
}

Rationale: The intent and meaning of the first version is clear, and results in less code.

4.7 Omit type parameters where possible

Methods of parameterized types can omit type parameters on the receiving type when they’re identical to the receiver’s. For example:

struct Composite<T> {
	
	func compose(other: Composite<T>) -> Composite<T> {
		return Composite<T>(self, other)
	}
}

could be rendered as:

struct Composite<T> {
	
	func compose(other: Composite) -> Composite {
		return Composite(self, other)
	}
}

5. Function Declarations

Keep short function declarations on one line including the opening brace:

func reticulateSplines(spline: [Double]) -> Bool {
   // reticulate code goes here
}

For functions with long signatures, add line breaks at appropriate points and add an extra indent on subsequent lines:

func reticulateSplines(spline: [Double],
    adjustmentFactor: Double,
    translateConstant: Int,
    comment: String) -> Bool {
        // reticulate code goes here
}

5.1 Avoid functions that have a span greater than the height of your screen

If your function is bigger, split it in smaller functions

6. Closure Expressions

Use trailing closure syntax only if there's a single closure expression parameter at the end of the argument list. Give the closure parameters descriptive names.

Preferred:

UIView.animateWithDuration(1.0) {
    self.myView.alpha = 0
}

UIView.animateWithDuration(1.0,
    animations: {
        self.myView.alpha = 0
    },
    completion: { finished in
        self.myView.removeFromSuperview()
    }
)

Not Preferred:

UIView.animateWithDuration(1.0, animations: {
    self.myView.alpha = 0
})

UIView.animateWithDuration(1.0,
    animations: {
      self.myView.alpha = 0
    }) { f in
      self.myView.removeFromSuperview()
}

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

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

7. Types

Always use Swift's native types when available. Swift offers bridging to Objective-C so you can still use the full set of methods as needed.

Preferred:

let width = 120.0                                    // Double
let widthString = (width as NSNumber).stringValue    // String

Not Preferred:

let width: NSNumber = 120.0                          // NSNumber
let widthString: NSString = width.stringValue        // NSString

In Sprite Kit code, use CGFloat if it makes the code more succinct by avoiding too many conversions.

7.1 Type Inference

Prefer compact code and let the compiler infer the type for a constant or variable, unless you need a specific type other than the default such as CGFloat or Int16.

Preferred:

let message = "Click the button"
let currentBounds = computeViewBounds()
var names = [String]()
let maximumWidth: CGFloat = 106.5

Not Preferred:

let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
var names: [String] = []

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

8. Prefer let-bindings over var-bindings wherever possible

Use let foo = … over var foo = … wherever possible (and when in doubt). Only use var if you absolutely have to (i.e. you know that the value might change, e.g. when using the weak storage modifier).

Rationale: The intent and meaning of both keywords is clear, but let-by-default results in safer and clearer code.

A let-binding guarantees and clearly signals to the programmer that its value is supposed to and will never change. Subsequent code can thus make stronger assumptions about its usage.

It becomes easier to reason about code. Had you used var while still making the assumption that the value never changed, you would have to manually check that.

Accordingly, whenever you see a var identifier being used, assume that it will change and ask yourself why.

9. Optionals

9.1 Avoid Using Force-Unwrapping of Optionals

If you have an identifier foo of type FooType? or FooType!, don't force-unwrap it to get to the underlying value (foo!) if possible.

Instead, prefer this:

if let foo = foo {
    // Use unwrapped `foo` value in here
} else {
    // If appropriate, handle the case where the optional is nil
}

Alternatively, you might want to use Swift's Optional Chaining in some of these cases, such as:

// Call the function if `foo` is not nil. If `foo` is nil, ignore we ever tried to make the call
foo?.callSomethingIfFooIsNotNil()

Rationale: Explicit if let-binding of optionals results in safer code. Force unwrapping is more prone to lead to runtime crashes.

9.2 Avoid Using Implicitly Unwrapped Optionals

Where possible, use let foo: FooType? instead of let foo: FooType! if foo may be nil (Note that in general, ? can be used instead of !).

Rationale: Explicit optionals result in safer code. Implicitly unwrapped optionals have the potential of crashing at runtime.

9.3 Avoid imbrication of if-let

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 when appropriate rather than using names like unwrappedView or actualLabel.

Preferred:

var subview: UIView?
var volume: Double?

// later on...
if let subview = subview, volume = volume {
    // do something with unwrapped subview and volume
}

10. Use guard whenever possible instead of if when it linearize your code

Prefered

guard let value = opt else { return error }
// continue code

Not Prefered

if let value = opt {
    // very long if block
} else {
    return error
}

11. Struct Initializers

Use the native Swift struct initializers rather than the legacy CGGeometry constructors.

Preferred:

let bounds = CGRect(x: 40, y: 20, width: 120, height: 80)
let centerPoint = CGPoint(x: 96, y: 42)

Not Preferred:

let bounds = CGRectMake(40, 20, 120, 80)
let centerPoint = CGPointMake(96, 42)

Prefer the struct-scope constants CGRect.infiniteRect, CGRect.nullRect, etc. over global constants CGRectInfinite, CGRectNull, etc. For existing variables, you can use the shorter .zeroRect.

12. Syntactic Sugar

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

Preferred:

var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?

Not Preferred:

var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>

13. Control Flow

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

Preferred:

for _ in 0..<3 {
    println("Hello three times")
}

for (index, person) in enumerate(attendeeList) {
    println("\(person) is at position #\(index)")
}

Not Preferred:

for var i = 0; i < 3; i++ {
    println("Hello three times")
}

for var i = 0; i < attendeeList.count; i++ {
    let person = attendeeList[i]
    println("\(person) is at position #\(i)")
}

14. Semicolons

Swift does not require a semicolon after each statement in your code. They are only required if you wish to combine multiple statements on a single line.

Do not write multiple statements on a single line separated with semicolons.

The only exception to this rule is the for-conditional-increment construct, which requires semicolons. However, alternative for-in constructs should be used where possible.

Preferred:

let swift = "not a scripting language"

Not Preferred:

let swift = "not a scripting language";

NOTE: Swift is very different to JavaScript, where omitting semicolons is generally considered unsafe

15. Access control

  • Use private by default, internal when needed and public with precaution

15.1 Always specify access control explicitly for top-level definitions

Top-level functions, types, and variables should always have explicit access control specifiers:

public var whoopsGlobalState: Int
internal struct TheFez {}
private func doTheThings(things: [Thing]) {}

However, definitions within those can leave access control implicit, where appropriate:

internal struct TheFez {
     var owner: Person = Joshaber()
}

Rationale: It's rarely appropriate for top-level definitions to be specifically internal, and being explicit ensures that careful thought goes into that decision. Within a definition, reusing the same access control specifier is just duplicative, and the default is usually reasonable.

15.2 Always specify access control extension at the method level

Preferred:

extension Optional {
    private var isEmpty: Bool {
        return self == nil
    }
}

Not Preferred:

private extension Optional {
    var isEmpty: Bool {
        return self == nil
    }
}

16. Language

Use US English spelling to match Apple's API.

Preferred:

let color = "red"

Not Preferred:

let colour = "red"

Credits