Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0001.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# (API-0001) Write a documentation comment for every declaration

Insights gained by writing documentation can have a profound impact on your design, so don't put it off.

## Overview

* **Use Swift's [dialect of Markdown](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/).**

* **Begin with a summary** that describes the entity being declared.
Often, an API can be completely understood from its declaration and
its summary.

```swift
/// **Returns a "view" of `self` containing the same elements in**
/// **reverse order.**
func reversed() -> ReverseCollection<Self>
```

* **Focus on the summary**; it's the most important part. Many
excellent documentation comments consist of nothing more than a
great summary.

* **Use a single sentence fragment** if possible, ending with a
period. Do not use a complete sentence.

* **Describe what a function or method *does* and what it
*returns***, omitting null effects and `Void` returns:

```swift
/// **Inserts** `newHead` at the beginning of `self`.
mutating func prepend(_ newHead: Int)

/// **Returns** a `List` containing `head` followed by the elements
/// of `self`.
func prepending(_ head: Element) -> List

/// **Removes and returns** the first element of `self` if non-empty;
/// returns `nil` otherwise.
mutating func popFirst() -> Element?
```

Note: in rare cases like `popFirst` above, the summary is formed
of multiple sentence fragments separated by semicolons.

* **Describe what a subscript *accesses***:

```swift
/// **Accesses** the `index`th element.
subscript(index: Int) -> Element { get set }
```

* **Describe what an initializer *creates***:

```swift
/// **Creates** an instance containing `n` repetitions of `x`.
init(count n: Int, repeatedElement x: Element)
```

* For all other declarations, **describe what the declared entity *is***.

```swift
/// **A collection that** supports equally efficient insertion/removal
/// at any position.
struct List {

/// **The element at the beginning** of `self`, or `nil` if self is
/// empty.
var first: Element?
...
```

* **Optionally, continue** with one or more paragraphs and bullet
items. Paragraphs are separated by blank lines and use complete
sentences.

The following example shows the summary, additional discussion,
parameters section, and symbol commands:

```swift
/// Writes the textual representation of each // ← Summary
/// element of `items` to the standard output.
/// // ← Blank line
/// The textual representation for each item `x` // ← Additional discussion
/// is generated by the expression `String(x)`.
///
/// - **Parameter separator**: text to be printed // ⎫
/// between items. // ⎟
/// - **Parameter terminator**: text to be printed // ⎬ Parameters section
/// at the end. // ⎟
/// // ⎭
/// - **Note**: To print without a trailing // ⎫
/// newline, pass `terminator: ""` // ⎟
/// // ⎬ Symbol commands
/// - **SeeAlso**: `CustomDebugStringConvertible`, // ⎟
/// `CustomStringConvertible`, `debugPrint`. // ⎭
public func print<Target: OutputStreamType>(
_ items: Any..., separator: String = " ", terminator: String = "\n")
```

* **Use recognized
[symbol documentation markup](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-SW1)
elements** to add information beyond the summary, whenever
appropriate.

* **Know and use recognized bullet items with
[symbol command syntax](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-SW13).** Popular development
tools such as Xcode give special treatment to bullet items that
start with the following keywords:

| [Attention](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Attention.html) | [Author](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Author.html) | [Authors](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Authors.html) | [Bug](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Bug.html) |
| [Complexity](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Complexity.html) | [Copyright](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Copyright.html) | [Date](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Date.html) | [Experiment](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Experiment.html) |
| [Important](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Important.html) | [Invariant](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Invariant.html) | [Note](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Note.html) | [Parameter](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Parameter.html) |
| [Parameters](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Parameters.html) | [Postcondition](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Postcondition.html) | [Precondition](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Precondition.html) | [Remark](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Remark.html) |
| [Requires](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Requires.html) | [Returns](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Returns.html) | [SeeAlso](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/SeeAlso.html) | [Since](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Since.html) |
| [Throws](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Throws.html) | [ToDo](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Todo.html) | [Version](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Version.html) | [Warning](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Warning.html) |
24 changes: 24 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# (API-0002) Include words to avoid ambiguity

**Include all the words needed to avoid ambiguity** for a person reading code where the name is used.

## Overview

✅ For example, consider a method that removes the element at a
given position within a collection.

```swift
extension List {
public mutating func remove(at position: Index) -> Element
}
employees.remove(at: x)
```

⛔ If we were to omit the word `at` from the method signature, it could
imply to the reader that the method searches for and removes an
element equal to `x`, rather than using `x` to indicate the
position of the element to remove.

```swift
employees.remove(x) // unclear: are we removing x?
```
25 changes: 25 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0003.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# (API-0003) Omit needless words

Every word in a name should convey salient information at the use site.

## Overview

More words may be needed to clarify intent or disambiguate meaning, but those that are redundant with information the reader already possesses should be omitted. In particular, omit words that *merely repeat* type information.

⛔ In this case, the word `Element` adds nothing salient at the call site:

```swift
public mutating func removeElement(_ member: Element) -> Element?

allViews.removeElement(cancelButton)
```

✅ This API would be better:

```swift
public mutating func remove(_ member: Element) -> Element?

allViews.remove(cancelButton) // clearer
```

Occasionally, repeating type information is necessary to avoid ambiguity, but in general it is better to use a word that describes a parameter's *role* rather than its type. See <doc:API-0004> for details.
40 changes: 40 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0004.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# (API-0004) Name according to roles

**Name variables, parameters, and associated types according to their roles,** rather than their type constraints.

## Overview

⛔ Repurposing a type name fails to optimize clarity and expressivity:

```swift
var **string** = "Hello"
protocol ViewController {
associatedtype **View**Type : View
}
class ProductionLine {
func restock(from **widgetFactory**: WidgetFactory)
}
```

✅ Instead, strive to choose a name that expresses the entity's *role*:

```swift
var **greeting** = "Hello"
protocol ViewController {
associatedtype **ContentView** : View
}
class ProductionLine {
func restock(from **supplier**: WidgetFactory)
}
```

If an associated type is so tightly bound to its protocol constraint
that the protocol name *is* the role, avoid collision by appending
`Protocol` to the protocol name:

```swift
protocol Sequence {
associatedtype Iterator : Iterator**Protocol**
}
protocol Iterator**Protocol** { ... }
```
26 changes: 26 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0005.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# (API-0005) Compensate for weak type information

**Compensate for weak type information** to clarify a parameter's role.

## Overview

Especially when a parameter type is `NSObject`, `Any`, `AnyObject`,
or a fundamental type such as `Int` or `String`, type information and
context at the point of use may not fully convey intent. In this
example, the declaration may be clear, but the use site is vague.


```swift
func add(_ observer: NSObject, for keyPath: String)

grid.add(self, for: graphics) // vague
```

✅ To restore clarity, **precede each weakly typed parameter with a
noun describing its role**:

```swift
func add**Observer**(_ observer: NSObject, for**KeyPath** path: String)
grid.addObserver(self, forKeyPath: graphics) // clear
```
30 changes: 30 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0006.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# (API-0006) Prefer grammatical English phrases

**Prefer method and function names that make use sites form grammatical English phrases.**

## Overview


```swift
x.insert(y, at: z) // "x, insert y at z"
x.subviews(havingColor: y) // "x's subviews having color y"
x.capitalizingNouns() // "x, capitalizing nouns"
```


```swift
x.insert(y, position: z)
x.subviews(color: y)
x.nounCapitalize()
```

It is acceptable for fluency to degrade after the first argument or
two when those arguments are not central to the call's meaning:

```swift
AudioUnit.instantiate(
with: description,
**options: [.inProcess], completionHandler: stopProgressBar**)
```
3 changes: 3 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0007.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# (API-0007) Begin factory method names with "make"

**Begin names of factory methods with "`make`",** e.g. `x.makeIterator()`.
34 changes: 34 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0008.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# (API-0008) Initializer and factory method first arguments

The first argument to initializer and factory method calls should not form a phrase starting with the base name, e.g. `x.makeWidget(cogCount: 47)`

## Overview

For more on factory methods, see [Factory method pattern](https://en.wikipedia.org/wiki/Factory_method_pattern).

✅ For example, the first arguments to these calls do not read as part of the same
phrase as the base name:

```swift
let foreground = **Color**(red: 32, green: 64, blue: 128)
let newPart = **factory.makeWidget**(gears: 42, spindles: 14)
let ref = **Link**(target: destination)
```

⛔ In the following, the API author has tried to create grammatical
continuity with the first argument.

```swift
let foreground = **Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)**
let newPart = **factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)**
let ref = **Link(to: destination)**
```

In practice, this guideline along with those for
argument labels (see <doc:API-0026>) means the first argument will
have a label unless the call is performing a
[value preserving type conversion](<doc:API-0027>).

```swift
let rgbForeground = RGBColor(cmykForeground)
```
65 changes: 65 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0009.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# (API-0009) Name according to side-effects

**Name functions and methods according to their side-effects.**

## Overview

* Those without side-effects should read as noun phrases,
e.g. `x.distance(to: y)`, `i.successor()`.

* Those with side-effects should read as imperative verb phrases,
e.g., `print(x)`, `x.sort()`, `x.append(y)`.

* **Name Mutating/nonmutating method pairs** consistently.
A mutating method will often have a nonmutating variant with
similar semantics, but that returns a new value rather than
updating an instance in-place.

* When the operation is **naturally described by a verb**, use the
verb's imperative for the mutating method and apply the "ed" or
"ing" suffix to name its nonmutating counterpart.

|Mutating|Nonmutating|
|-|-|
|`x.sort()`|`z = x.sorted()`|
|`x.append(y)`|`z = x.appending(y)`|

* Prefer to name the nonmutating variant using the verb's past
[participle](https://en.wikipedia.org/wiki/Participle) (usually
appending "ed"):

```swift
/// Reverses `self` in-place.
mutating func reverse()

/// Returns a reversed copy of `self`.
func revers**ed**() -> Self
...
x.reverse()
let y = x.reversed()
```

* When adding "ed" is not grammatical because the verb has a direct
object, name the nonmutating variant using the verb's present
[participle](https://en.wikipedia.org/wiki/Participle), by
appending "ing."

```swift
/// Strips all the newlines from `self`
mutating func stripNewlines()

/// Returns a copy of `self` with all the newlines stripped.
func strip**ping**Newlines() -> String
...
s.stripNewlines()
let oneLine = t.strippingNewlines()
```

* When the operation is **naturally described by a noun**, use the
noun for the nonmutating method and apply the "form" prefix to
name its mutating counterpart.

|Nonmutating|Mutating|
|-|-|
|`x = y.union(z)`|`y.formUnion(z)`|
|`j = c.successor(i)`|`c.formSuccessor(&i)`|
3 changes: 3 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0010.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# (API-0010) Boolean methods read as assertions

**Uses of Boolean methods and properties should read as assertions about the receiver** when the use is nonmutating, e.g. `x.isEmpty`, `line1.intersects(line2)`.
3 changes: 3 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0011.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# (API-0011) Protocols describing "what" read as nouns

**Protocols that describe *what something is* should read as nouns** (e.g. `Collection`).
3 changes: 3 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0012.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# (API-0012) Protocols describing capability use suffixes

**Protocols that describe a *capability* should be named using the suffixes `able`, `ible`, or `ing`** (e.g. `Equatable`, `ProgressReporting`).
3 changes: 3 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/API-0013.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# (API-0013) Other names read as nouns

The names of other **types, properties, variables, and constants should read as nouns.**
Loading