Swift Style Guide

Table Of Contents

Overview

Coding style is an important part of good software engineering practice. The goal is to write code that is clear and easy to understand, reducing the effort required to make future extensions or modifications.

In CS 326 we do not specify a detailed coding style that you must follow. However, we expect your code to be clear and easy to understand. This handout provides overall guidelines within which you should develop your own effective coding style.

Many other style guides are available. For example, you can see Apple’s Swift API Design Guidelines for one way to write your code in a comprehensible fashion — you might consider those rules while developing your own style.

Descriptive names

Names for modules, types, variables, and branch labels should document their meaning and/or use. This does not mean that names need to be very long. For example, names like i and j are fine for indexes in short loops, since programmers understand the meanings and uses of these variables by convention.

You should follow the standard Swift convention of capitalizing names of classes, structs, protocols, and enums, but starting method, property, constants, and variable names with a lower case letter. The Swift API Design Guidelines provide some common Naming Conventions that you may want to use when naming your classes, methods, etc.

Consistent indentation and spacing

Indenting code consistently makes it easy to see where if statements and for loops end, etc. You should choose consistent strategies; for example, be consistent about whether you put the open curly brace on the same line as the if or on the next line, or what your try-catch-finally blocks look like. Examine the staff-supplied code, and the code printed in books, for sample styles; feel free to develop your own if it makes you more comfortable.

In XCode, Ctrl-I will indent your code, according to its built-in indentation rules (which you may or may not like).

Consistent (and adequate) horizontal spacing also helps the reader. There is no reason to try to save a column or two by eliminating spaces. You should leave a space after the comma that separates method arguments. You should leave a space between for, if, or while and the following open parenthesis; otherwise, the statement looks too much like a method call, which is confusing. In general, you should place only one statement on each line. Remember that people reading your code may have a monitor of a different width, or may choose to print your code for a code review (the TAs may do this!). 80 columns is a commonly-accepted width in industry, and we require this for the convenience of the TAs when marking up your printouts. A longer line once in a while is acceptable, but not as a general rule. When you break lines, do so at logical, not arbitrary, locations.

Local variables

Local variables should have the smallest possible scope. Don't declare it at the beginning of the method and then have a lot of unrelated code intervening between its declaration and its first use. As another example, if you have a variable that is used within each loop iteration, declare it inside the loop to make clear to readers that there are no loop-carried dependencies.

Types

Type inference

In general, it is ok to let Swift infer the types of variables. However, you should add explicit types in cases where you believe it adds clarity to the code.

Type casting

As a general rule, you should never have type casts in code you write

However, there are some idioms in Swift that require casts, particularly when it comes to particular methods in UIKit and in handling JSON data. Avoid casts in all cases except when they are absolutely necessary.

Informative comments

Don't make the mistake of writing comments everywhere — a bad or useless comment is worse than no comment. If information is obvious from the code, adding a comment merely creates extra work for the reader. For example, this is a useless comment that would only help someone who does not know the programming language:

i = i + 1    // increment

Good comments add information to the code in a concise and clear way. For example, comments are informative if they:

  • Enable the reader to avoid reading some code. The following comment saves the reader the effort of figuring out the effect of some complicated formulas, and states the programmer's intention so the formulas can be checked later on.

        // Compute the standard deviation of list elements that are
        // less than the cutoff value.
        for i in 0..<n {
            ...
        }

    An important application of this type of comment is to document the arguments and return values of methods so clients need not read the implementation to understand how to use the method.

  • Explain an obscure step or algorithm. This is especially important when the effects of some step are not immediately obvious in the code itself. You should explain tricky algorithms, operations with side effects, magic numbers in the code, etc.

        // Signal that a new transfer request is complete and ready
        // to process.  The manager thread will begin the disk transfer
        // the next time it wakes up and notices that this variable has changed.
        bufferManager.requests += 1
  • Record assumptions. Under what assumptions does a piece of code work properly?

        // The buffer contains at least one character.
        // (If the buffer is empty, the interrupt handler returns without
        // invoking this function.)
        c = buffer.nextCharacter()
  • Record limitations and incomplete code. Frequently, the first version of code is incomplete; it is important to record which code is known to be incorrect. If you run out of time on an assignment and turn in a program that does not function correctly on all inputs, we will expect your code to show that you understand its limitations.

        if (n > 0) {
            average = sum / n
        } else {
            // TODO: Need to use decayed average from previous iteration.
            // For now, just use an arbitrary value.
            average = 15
        }

Hints:

  • Don't write code first and then comment it — comment it as you go along. It is easier to comment it while you are thinking about it and still remember its details, and you are unlikely to go back and do it later. In fact, it's best to comment the code before you write it — doing so may expose weaknesses in your ideas and save you time on the implementation.
  • We do not require you to write comments on every program piece. However, your grade depends substantially on the clarity of your code, and some piece of the program that seems clear to you may not be clear to the reader. Therefore, we strongly recommend that you add explanatory comments to all classes, properties, and methods, especially public ones — it will likely be to your advantage to do so.

Swift Markdown comments

Every class, every interface, every public method and property, and every nontrivial non-public method and property, should have an explanatory comment. These comments should be written in Swift’s Markdown format.

Such documentation is useful even on non-public members, for two reasons. First, even though you would never supply this to your clients, it is helpful for the development team to have this handy. Second, an XCode displays the documentation for a member when you hover over a use, and this is as useful for private and public members.)

Comments should be grammatical English. If more than a few words, a comment should consist of complete sentences that start with a capital letter and end with a period.

The Apple Formatting Reference shows a number of examples of how to document Swift code with markdown.

It is important to use this syntax to document your code so that your comments will appear properly formatted inside XCode help system. (There are a number of documentation generators to create HTML from comments as well, such as jazzy, which I use to create the documentation for some of the labs.)

For every method, you should include at least the Parameter and Returns markdown entries. For CS 326, we use a few additional tags that are not recognized by XCode, such as Requires, Modifies, and Effects. We’ll cover how to include those tags when we discuss specifications in lecture.

TODO comments

If you want to leave yourself a note about a piece of code that you need to fix, preface the comment with TODO:. You will notice that TODO will appears in the menu that pops up from the “Jump Bar” at the top of the editor when you click on the name of the file you are editing.

Commenting out code

Sometimes, you want to temporarily or permanently comment out some code.

Swift has two ways to write comments: /*...*/ comments out everything between the comment delimiters, and // starts a comment that ends at the end of the line. You should use // to comment out code. Reserve /*...*/ comments for actual comments, in which case the opening tag should have an additional asterisk: /**.

In Eclipse, you can comment/uncomment a block of code by highlighting the region and pressing Ctrl+/.

The rest of this section explains why you should use line comments such as // instead of block comments such as /*...*/: because block comments do not nest. For example, if you already did:

let a = "A" 
let b = "B"
/* let b = "BB" */
let c = "C"
let d = "D"

But then you wanted to comment out the creation of variables b and c using a block comment, you would have:

let a = "A "
/* let b = "B"
/* let b = "BB" */
let c = "C"
*/
let d = "D"

(The two block comment characters that have been added are in red and the code that is commented out by the new block comment is underlined.) Notice that this failed to comment out the statement where c is created. Also, this code will no longer compile because there is a */ dangling by itself after the definition of c. This may seem easy to fix now, but if you have commented a large block of code, it may be a pain to find the nested block comment that is causing the compilation error. You can avoid this mess entirely by using the // comment:

let a = "A"
// let b = "B"
// // let b = "BB"
// let c = "C"
let d = "D"

This also makes it easier to uncomment smaller blocks of commented regions.

Documenting Code

Specification-level comments

Abstract data types. Every abstract data type (class, struct, enum, or protocol) should have:

  1. An overview section that gives a one or two line explanation of what objects of the type represent and whether they are mutable.
  2. A list of specification properties. There might be only one; for example, a set may have the property elems representing the set of elements. Each property should have a name, a type, and a short explanation. Your specification properties are likely to include any public properties of your class itself – these should be listed as well. You may find it useful to define extra derived properties that make it easier to write the specifications of methods; for each of these, you should indicate that it is derived and say how it is obtained from the other properties. There may be specification invariants that constrain the possible values of the specification properties; if so, you should specify them.

Method Specifications. All public methods of classes should have specifications; tricky private methods should also be specified. Method specifications should follow the requires, modifies, effects, parameters, returns structure described in the course material on specifications.

Implementation-level comments

Implementation notes. Class and struct comments should include the following elements:

  1. An abstraction function that defines each specification property in terms of the representation properties. Abstraction functions are only required for classes which are abstract data types, and not for classes like exceptions or some GUI widgets.
  2. A representation invariant. RIs are required for any class that has a representation (e.g., not most exceptions). We strongly recommend that you test invariants in a checkRep() method where feasible. Take care to include in your invariants assumptions about what can and cannot be null.
  3. For classes with complex representations, a note explaining the choice of representation (also called the representation rationale): what tradeoffs were made and what alternatives were considered and rejected (and why).

Runtime assertions. These should be used judiciously, as explained in lecture.