Skip to content

Commit

Permalink
Documentation revision for Swift 2.0.
Browse files Browse the repository at this point in the history
Closes NSHipster#336, resolves #335.
  • Loading branch information
natecook1000 committed Oct 1, 2015
1 parent 7efc411 commit 128d04f
Showing 1 changed file with 137 additions and 62 deletions.
199 changes: 137 additions & 62 deletions 2015-05-05-swift-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ authors:
- Nate Cook
category: Swift
tags: swift
excerpt: "Code structure and organization is a matter of pride for developers. Clear and consistent code signifies clear and consistent thought. Read on to learn about the recent changes to documentation with Xcode 6 & Swift."
excerpt: "Code structure and organization is a matter of pride for developers. Clear and consistent code signifies clear and consistent thought. Read on to learn about the recent changes to documentation with Xcode 7 & Swift 2.0."
revisions:
"2014-07-28": Original publication.
"2015-05-05": Extended detail on supported markup; revised examples.
"2015-09-30": Revised for Xcode 7 & Swift 2.0.
status:
swift: 1.2
swift: 2.0
reviewed: September 30, 2015
---

Code structure and organization is a matter of pride for developers. Clear and consistent code signifies clear and consistent thought. Even though the compiler lacks a discerning palate when it comes to naming, whitespace, or documentation, it makes all of the difference for human collaborators.
Expand All @@ -30,7 +32,7 @@ With the announcements of WWDC 2014, the developer documentation was overhauled

In the midst of Swift code, Headerdoc comments are not parsed correctly when invoking Quick Documentation (`⌥ʘ`):

~~~{swift}
```swift
/**
Lorem ipsum dolor sit amet.
Expand All @@ -39,105 +41,159 @@ In the midst of Swift code, Headerdoc comments are not parsed correctly when inv
@return Sed do eiusmod tempor.
*/
func foo(bar: String) -> AnyObject { ... }
~~~
```

![Unrecognized Headerdoc](http://nshipster.s3.amazonaws.com/swift-documentation-headerdoc.png)

What _is_ parsed, however, is something markedly different:

![New Recognized Format](http://nshipster.s3.amazonaws.com/swift-documentation-new-format.png)

~~~{swift}
```swift
/**
Lorem ipsum dolor sit amet.
:param: bar Consectetur adipisicing elit.
- parameter bar: Consectetur adipisicing elit.
:returns: Sed do eiusmod tempor.
- returns: Sed do eiusmod tempor.
*/
func foo(bar: String) -> AnyObject { ... }
~~~
```

So what is this strange new documentation format? It turns out that SourceKit (the private framework powering Xcode, previously known for its high FPS crashes) includes a basic parser for [reStructuredText](http://docutils.sourceforge.net/docs/user/rst/quickref.html). Only a subset of the [specification](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#field-lists) is implemented, but there's enough in there to cover basic formatting.
So what is this not-so-strange new documentation format? After a yearlong sojourn in the lands of [reStructuredText](http://docutils.sourceforge.net/docs/user/rst/quickref.html), Xcode 7 has settled on a Swift-flavored version of [Markdown](https://daringfireball.net/projects/markdown/).


#### Basic Markup

Documentation comments are distinguished by using `/** ... */` for multi-line comments or `/// ...` for single-line comments. Inside comment blocks, paragraphs are separated by blank lines. Unordered lists can be made with several bullet characters: `-`, `+`, `*`, ``, etc, while ordered lists use Arabic numerals (1, 2, 3, ...) followed by a period `1.` or right parenthesis `1)` or surrounded by parentheses on both sides `(1)`:
Documentation comments are distinguished by using `/** ... */` for multi-line comments or `///` for single-line comments. Inside comment blocks, the conventions you've gotten used to when writing Markdown everywhere else apply:

~~~{swift}
- Paragraphs are separated by blank lines
- Unordered lists can use a variety of bullet characters: `-`, `+`, `*`, ``
- Ordered lists use Arabic numerals (1, 2, 3, ...) followed by a period `1.` or right parenthesis `1)`:
- Headers can be marked with preceding `#` signs or by underlining with `=` or `-`.
- Even [links](https://daringfireball.net/projects/markdown/syntax#link) and [images](https://daringfireball.net/projects/markdown/syntax#img) work, with web-based images pulled down and displayed directly in Xcode.

```swift
/**
# Lists
You can apply *italic*, **bold**, or `code` inline styles.
## Unordered Lists
- Lists are great,
- but perhaps don't nest
- Sub-list formatting
- isn't the best.
## Ordered Lists
1. Ordered lists, too
2. for things that are sorted;
3. Arabic numerals
4. are the only kind supported.
*/
~~~
```

#### Definition & Field Lists
#### Parameters & Return Values

Defininition and field lists are displayed similarly in Xcode's Quick Documentation popup, with definition lists a little more compact:

~~~{swift}
/**
Definition list
A list of terms and their definitions.
Format
Terms left-justified, definitions indented underneath.
:Field header:
Field lists are spaced out a little more.
:Another field: Field lists can start the body immediately, without a line break and indentation.
Subsequent indented lines are treated as part of the body, too.
*/
~~~
Xcode 7 recognizes and makes separate from a symbol's description a few special fields. The parameters, return value, and a new "throws" section (to go with Swift 2.0's new `throws` keyword) are broken out in the Quick Help popover and inspector when styled as a bulleted item followed by a colon (`:`).

Two special fields are used to document parameters and return values: `:param:` and `:returns:`, respectively. `:param:` is followed by the name of the paramenter, then the description. Return values don't have a name, so the description begins immediately after `:returns:`:
- **Parameters:** Start the line with `Parameter <param name>: ` and the description of the parameter.
- **Return values:** Start the line with `Returns: ` and information about the return value.
- **Thrown errors:** Start the line with `Throws: ` and a description of the errors that can be thrown. Since Swift doesn't type-check thrown errors beyond `ErrorType` conformance, it's especially important to document errors properly.

~~~{swift}
```swift
/**
Repeats a string `times` times.
:param: str The string to repeat.
:param: times The number of times to repeat `str`.
- Parameter str: The string to repeat.
- Parameter times: The number of times to repeat `str`.
:returns: A new string with `str` repeated `times` times.
- Throws: `MyError.InvalidTimes` if the `times` parameter
is less than zero.
- Returns: A new string with `str` repeated `times` times.
*/
func repeatString(str: String, times: Int) -> String {
return join("", Array(count: times, repeatedValue: str))
func repeatString(str: String, times: Int) throws -> String {
guard times >= 0 else { throw MyError.InvalidTimes }
return Repeat(count: 5, repeatedValue: "Hello").joinWithSeparator("")
}
```

A longer list of parameters can be broken out into a sublist by using the `Parameters:` prefix. Simply indent each parameter in a bulleted list below.

```swift
/// Returns the magnitude of a vector in three dimensions
/// from the given components.
///
/// - Parameters:
/// - x: The *x* component of the vector.
/// - y: The *y* component of the vector.
/// - z: The *z* component of the vector.
func magnitude3D(x: Double, y: Double, z: Double) -> Double {
return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2))
}
~~~
```

#### Description Fields

Swift-flavored Markdown includes another set of field headers to break out particular sections of a type or method's description, formatted just as `Returns` and `Throws` above. Loosely organized, the recognized headers are:

- *Algorithm/Safety Information:* `Precondition`, `Postcondition`, `Requires`, `Invariant`, `Complexity`, `Important`, `Warning`
- *Metadata*: `Author`, `Authors`, `Copyright`, `Date`, `SeeAlso`, `Since`, `Version`
- *General Notes & Exhortations:* `Attention`, `Bug`, `Experiment`, `Note`, `Remark`, `ToDo`

No matter which you choose, all fields are rendered as a bold header followed by a block of text:

> **Field Header:**
> The text of the subfield is displayed starting on the next line.

#### Code blocks

Code blocks can be embedded in documentation comments as well, which can be useful for demonstrating proper usage or implementation details. Inset the code block by at least two spaces:
Code blocks can be embedded in documentation comments as well, which can be useful for demonstrating proper usage or implementation details. Inset the code block by at least four spaces:

~~~{swift}
```swift
/**
The area of the `Shape` instance.
Computation depends on the shape of the instance. For a triangle, `area` will be equivalent to:
Computation depends on the shape of the instance.
For a triangle, `area` will be equivalent to:
let height = triangle.calculateHeight()
let area = triangle.base * height / 2
let height = triangle.calculateHeight()
let area = triangle.base * height / 2
*/
var area: CGFloat { get }
~~~
```

Fenced code blocks are also recognized, with three backticks (`\``) or tildes (`~`) marking the beginning and end of a block:

```swift
/**
The perimeter of the `Shape` instance.
Computation depends on the shape of the instance, and is
equivalent to:
```
// Circles:
let perimeter = circle.radius * 2 * CGFloat(M_PI)
// Other shapes:
let perimeter = shape.sides.map { $0.length }
.reduce(0, combine: +)
```
*/
var perimeter: CGFloat { get }
```

## Documentation Is My New Bicycle

How does this look when applied to an entire class? Quite nice, actually:

~~~{swift}
```swift
import Foundation

/// 🚲 A two-wheeled, human-powered mode of transportation.
Expand Down Expand Up @@ -198,12 +254,14 @@ class Bicycle {
/**
Initializes a new bicycle with the provided parts and specifications.
:param: style The style of the bicycle
:param: gearing The gearing of the bicycle
:param: handlebar The handlebar of the bicycle
:param: centimeters The frame size of the bicycle, in centimeters
- Parameters:
- style: The style of the bicycle
- gearing: The gearing of the bicycle
- handlebar: The handlebar of the bicycle
- frameSize: The frame size of the bicycle, in centimeters
:returns: A beautiful, brand-new, custom built just for you.
- Returns: A beautiful, brand-new bicycle, custom built
just for you.
*/
init(style: Style, gearing: Gearing, handlebar: Handlebar, frameSize centimeters: Int) {
self.style = style
Expand All @@ -218,7 +276,7 @@ class Bicycle {
/**
Take a bike out for a spin.
:param: meters The distance to travel in meters.
- Parameter meters: The distance to travel in meters.
*/
func travel(distance meters: Double) {
if meters > 0 {
Expand All @@ -227,7 +285,7 @@ class Bicycle {
}
}
}
~~~
```

Option-click on the `Style` `enum` declaration, and the description renders beautifully with a bulleted list:

Expand All @@ -254,11 +312,11 @@ To show these new tags in action, here's how the `Bicycle` class could be extend

![Xcode 6 Documentation Source Navigator MARK / TODO / FIXME](http://nshipster.s3.amazonaws.com/swift-documentation-xcode-source-navigator.png)

~~~{swift}
// MARK: Printable
```swift
// MARK: CustomStringConvertible

extension Bicycle: Printable {
var description: String {
extension Bicycle: CustomStringConvertible {
public var description: String {
var descriptors: [String] = []

switch self.style {
Expand Down Expand Up @@ -297,26 +355,43 @@ extension Bicycle: Printable {

// TODO: Allow bikes to be named?

return join(", ", descriptors)
return descriptors.joinWithSeparator(", ")
}
}
~~~
```

Bringing everything together in code:

~~~{swift}
```swift
let bike = Bicycle(style: .Road, gearing: .Freewheel(speeds: 8), handlebar: .Drop, frameSize: 53)

bike.travel(distance: 1_500) // Trip around the town
bike.travel(distance: 200) // Trip to the store

println(bike)
print(bike)
// "A road bike for streets or trails, with a 8-speed freewheel gear, and classic, drop handlebars, on a 53" frame, with a total of 1700.0 meters traveled over 2 trips."
~~~
```


## Jazzy

[Jazzy](https://github.com/realm/jazzy) is a terrific open-source command-line utility that transforms your project's documentation comments into a set of Apple-like HTML documentation. Jazzy uses Xcode's SourceKitService to read your beautifully written type and method descriptions. Install Jazzy as a gem, then simply run from the root of your project folder.

```text
$ [sudo] gem install jazzy
$ jazzy
Running xcodebuild
Parsing ...
building site
jam out ♪♫ to your fresh new docs in `docs`
```

[Take a peek](/swift-documentation-example/) at a Jazzy-generated docset for the `Bicycle` class.


* * *

Although the tooling and documentation around Swift is still rapidly evolving, one would be wise to adopt good habits early, by using the new light markup language conventions for documentation, as well as `MARK: ` comments in Swift code going forward.
Although the tooling and documentation around Swift is still rapidly evolving, one would be wise to adopt good habits early, by using the new Markdown capabilities for documentation, as well as `MARK: ` comments in Swift code going forward.

Go ahead and add it to your `TODO: ` list.

Expand Down

0 comments on commit 128d04f

Please sign in to comment.