A handful of Swift style rules #swiftlang

Revisiting some old style rules with new twists.

The Rule of Lily: “When a trailing closure argument is functional, use parentheses. When it is procedural, use braces.”

myCollection.map({blah}).filter({blah}).etc
myCollection.forEach {} // or 
dispatch_after(when, queue) {}

The consistency of style communicates whether closures return values. There’s an ongoing dispute as to whether a space should be left before  trailing braces.

The Rule of Self: “Implicit member expressions enable you to omit self when the compiler can unambiguously infer member types. Use self whenever a method call reflexively refers back to an instance.”

Consider the following for-loop’s where clause. The contains method call lacks an explicit subject.  What is doing the containing? A container isn’t included as method parameter so it has to be the calling instance.

for (flagLessOne, string) in strings.enumerate() 
    where contains(
        Features(rawValue: 1<<(flagLessOne + 1))) {
    nameArray.append(string)
}

Fully qualifying the call clarifies the subject ambiguity and vastly improves readability:

for (flagLessOne, string) in strings.enumerate() 
    where self.contains(
        Features(rawValue: 1<<(flagLessOne + 1))) {
    nameArray.append(string)
}

The Rule of Conditional Binding Cascades: “Unless you’re mixing var and let conditional bindings, use a single if let or if var introduction. Add liberal whitespace as needed.”

Instead of:

if let x = x, let y = y, let z = z {blah}

use:

if let x = x, y = y, z = z {blah}

Removing extraneous let keywords simplifies the binding cascade, and Xcode will nicely co-align these for you:

if let
    x = x,
    y = y,
    z = z {
    ...blah...
}

Although cascaded bindings avoids the pre-Swift 2 “pyramids of doom”, they tend towards “constipated blocks of horror”. When faced with excess serial bindings, interleave them with spaces and comments, as in the following example, or use a sequence of guard statements.

if let
    // Access JSON as dictionary 
    json = json as? NSDictionary,

    // Retrieve results array
    resultsList = json["results"] as? NSArray,

    // Extract first item
    results = resultsList.firstObject as? NSDictionary,

    // Extract name and price
    name = results["trackName"] as? String, 
    price = results["price"] as? NSNumber {

    // ... blah blah ...
  }

The Rule of  Pattern Matching Keywords: “When everything’s a binding, unify your bindings.”

Combine multiple pattern matching bindings by moving keywords out of tuples. Instead of:

if case (let x?, let y?) = myOptionalTuple {
    print(x, y)
}

Use:

if case let (x?, y?) = myOptionalTuple {
    print(x, y)
}

The Rule of isEmpty: “If you’re testing a collection’s count, you’re probably doing it wrong.”  Prefer isEmpty to count == 0.

The Rule of Void: “Use Void return types, not ().” A function returns -> Void and not -> ().

func doThis() -> Void 
func notThis() -> ()

The Rule of !: “Every time you use an exclamation point in Swift, a kitten dies.” Wherever possible, avoid forced casts and forced unwrapping.

The Rule of Collection Creation: “Use explicit typing and empty collections.” Types to the left of the assignment, empty instances to the right.

Instead of:

var x = [String: Int]() // and
var y = [Double]()
var z = Set<String>()
var mySet = MyOptionSet()

use

var x: [String: Int] = [:]
var y: [Double] = []
var z: Set<String> = []
var mySet: MyOptionSet = []

Cite.

The “Mike Ash” colon rule: “Space to the right. No space to the left.” Or no soup for you!

Prefer

[key: value] // and
struct Foo: MyProtocol

to

[key : value]
struct Foo : MyProtocol

The Rules of Moving-On-From-Objective-C

  • Don’t add Objective-C-style parentheses around if and switch conditions or with return keywords.
  • Use camel case for allTheConstants and not ALL_CAPS
  • Prefer Swift constructors to legacy ones, e.g. CGPoint(x: 1, y:1) over CGPointMake(1, 1)
  • Avoid terminal semicolons even though they compile. They make you look bad and you should feel bad for using them.

Update: Yes, I don’t just talk the talk, I walk the walk with these rules in my code:

Screen Shot 2015-11-18 at 10.31.13 AM

github repo

8 Comments

  • Nice writeup with some good rules of engagement.

  • Can you share the script with the rules you use?

  • I’d add a rule to use named tuples for multiple return values:

    func daytimeOnDate(date: NSDate, atCoordinate: CLLocationCoordinate2D) -> (sunrise: NSDate, sunset: NSDate)

  • Regarding the Rule of Void, why is returning void better than not returning anything?

    func doThis() -> Void
    func doThis()

    In Apple’s Swift book when a function has nothing to return, they omit the ->, like the second doThis() listing in my comment.

  • […] 作者:Erica Sadun,原文链接,原文日期:2015-11-17译者:mmoaay;校对:lfb_CD;定稿:shanks […]

  • In swift use 80, 100 or 120 column per line