Swift Style Guide: April 2016 Update

We’ve updated our popular Swift Style Guide for Swift 2.2 – check out the changes inside! By Ray Fix.

Save for later
Share

Good news – we have updated our popular Swift Style Guide for Swift 2.2!

Here at raywenderlich.com, we want to have a consistent style for you in our tutorials, books, videos, and conference materials. We believe it improves readability, and makes our content easier and quicker to understand.

At the same time, we want to avoid making a style guide so strict that an author feels like they are wearing a straight-jacket!

We’ve tried our best to walk this line carefully, and you may even wish to adopt some of these standards in your own projects. That said, our style guide is different than other style guides; we’re a tutorial site, which means our priorities are making our code easily readable in printed books and on the web. Many of the decisions are made with an eye toward conserving space for print, legibility, and tutorial writing.

Since our last major style update in April 2015, readers and team members opened dozens of issues against the guide and generated long threads of discussion suggesting how things could be made better. While we could not adopt all of the suggestions, we are truly grateful for feedback and the thoughts it generated. We hope you find this to be a compelling update.

Let’s look at some of the highlights.

Crush the Warnings

In a way, the Swift evolution community sets its own minimal style guidelines by causing the compiler to emit warnings on deprecated language features. You should consider warnings to be errors and fix them.

I am always surprised when I jump on a new project and find that the previous developer didn’t take the time to fix the warnings. We all have deadlines, but don’t let this be you. :]

Swift 2.2 brought several deprecations. For example, the ++ and -- operators along with along with the C-style for-loop are slated for removal in Swift 3. Rather than falling back to using a while-loop, go ahead and use enumerate() or the full power of ranges. It’s easy and fun. For example:

// list the index and item
for (index, item) in items.enumerate() {
  print("\(index): \(item)")
}

// count up from zero up to items.count by twos
for index in 0.stride(to: items.count, by: 2) {
  print(index)
}

// count down from three to zero
for index in (0...3).reverse() {
  print(index)
}

Another deprecation includes using strings instead of the new #selector expression. Xcode’s “fix it” button helps you out here, but it always uses the fully qualified names. Usually you can take advantage of context to make it shorter and more readable. Make the extra effort.

Less is more: The Swift evolution community is doing great work not only expanding features of Swift but also removing the less useful ones and simplifying the language as well. 🎉

Mind the Golden Path

The Golden Path (or sometimes Happy Path) rule dates back to our Obj-C style guide. It refers to the notion that you don’t want to nest conditional statements too deeply. That quickly gets hard to read.

// Not preferred
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {

  if let context = context {
    if let inputData = inputData {
      // use context and input to compute the frequencies

      return frequencies
    }
    else {
      throw FFTError.NoInputData
    }
  }
  else {
    throw FFTError.NoContext
  }
}

If you can help it, don’t nest at all. Embrace the concept of the early exit.

Every major release of Swift: 1.0 with optional chaining, 1.2 with multiple let optional binding and 2.0 with the guard, throws and defer statements have given you increasingly better tools to build a clean, readable golden paths through your methods. Use them!

// Preferred
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {

  guard let context = context else { throw FFTError.NoContext }
  guard let inputData = inputData else { throw FFTError.NoInputData }

  // use context and input to compute the frequencies

  return frequencies
}

Advantages of Being Lazy

Swift is a “safe” language. This means, among other things, that your objects are always guaranteed to be fully initialized before you use them. The rules can sometimes get a bit tricky. In fact, you might want to consult out our Swift Initialization in Depth tutorial if you want to understand the finer points of the process.

With UIViewControllers, full initialization is often delayed until the views load. Lazy initialization is a handy (and often safer) approach to use.

Consider the following stored property in your view controller:

lazy var locationManager: CLLocationManager = {
    let manager = CLLocationManager()
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.delegate = self
    manager.requestAlwaysAuthorization()
    return manager
}()

This allows you to defer initialization until you actually refer to the stored property which might have side-effects such as a permissions popup to appear.

Lifetime Extension

caretodance

When you are dealing with asynchronous requests such as a network call and then use that data to update your UI it is easy to inadvertently create a retain cycle. (Heap leak!)

Swift cleverly makes you explicitly type out self to clue you in that you might be doing this. Swift also lets you supply a capture list to break the retain cycle.

Be very careful using [unowned self] in your capture list. If your UI doesn’t outlive your network request, you just created an asynchronous crasher. Instead, prefer the more bulletproof [weak self].

In your closure, rather than optionally unwrap self each time, extend lifetime by performing the so-called strong-weak dance. This results in fewer possible code paths so is more obviously correct. An example of the canonical strong-weak dance is:

resource.request().onComplete { [weak self] response in
  guard let strongSelf = self else { return }
  let model = strongSelf.updateModel(response)
  strongSelf.updateUI(model)
}

We converged on the constant name strongSelf by looking at what a few successful open source projects (such as Alamofire) were doing. Perhaps in the future there will be a language feature to help minimize this boilerplate. For now, extending lifetime using the strong-weak dance ensures that you don’t create a retain cycle and that you don’t crash.