From ac8e1e935ba0f445f8920013437275915150316e Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Wed, 11 Mar 2026 12:47:25 -0700 Subject: [PATCH 1/3] initial copy from swift.org website content --- .../api-design-guidelines.md | 1069 +++++++++++++++++ 1 file changed, 1069 insertions(+) create mode 100644 api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md diff --git a/api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md b/api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md new file mode 100644 index 0000000..9f19c26 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md @@ -0,0 +1,1069 @@ +--- +layout: page +title: API Design Guidelines +official_url: https://swift.org/documentation/api-design-guidelines/ +redirect_from: /documentation/api-design-guidelines.html +--- + + + + +{% capture expand %}{::nomarkdown} + +{:/nomarkdown}{% endcapture %} +{% assign detail = '
' %} +{% assign enddetail = '
' %} + +
+To facilitate use as a quick reference, the details of many guidelines +can be expanded individually. Details are never hidden when this page +is printed. + +
+ +## Table of Contents +{:.no_toc} + +* TOC +{:toc} + +## Introduction + +Delivering a clear, consistent developer experience when writing Swift code is largely defined by the names and idioms that appear in APIs. +These design guidelines explain how to make sure that your code feels like a part of the larger Swift ecosystem. + +## Fundamentals + +* **Clarity at the point of use** is your most important goal. + Entities such as methods and properties are declared only once but + *used* repeatedly. Design APIs to make those uses clear and + concise. When evaluating a design, reading a declaration is seldom + sufficient; always examine a use case to make sure it looks + clear in context. + {:#clarity-at-the-point-of-use} + +* **Clarity is more important than brevity.** Although Swift + code can be compact, it is a *non-goal* + to enable the smallest possible code with the fewest characters. + Brevity in Swift code, where it occurs, is a side-effect of the + strong type system and features that naturally reduce boilerplate. + {:#clarity-over-brevity} + +* **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. + {:#write-doc-comment} + +
+ If you are having trouble describing your API's + functionality in simple terms, **you may have designed the wrong API.** +
+ + {{expand}} + {{detail}} + {% assign ref = 'https://developer.apple.com/library/prerelease/mac/documentation/Xcode/Reference/xcode_markup_formatting_ref/' %} + {% capture SymbolDoc %}{{ref}}SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-{% endcapture %} + + * **Use Swift's [dialect of Markdown]({{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 + ~~~ + + {{expand}} + {{detail}} + + * **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? + ... + ~~~ + + {{enddetail}} + + * **Optionally, continue** with one or more paragraphs and bullet + items. Paragraphs are separated by blank lines and use complete + sentences. + + ~~~ 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( + _ items: Any..., separator: String = " ", terminator: String = "\n") + ~~~ + + {{expand}} + {{detail}} + + * **Use recognized + [symbol documentation markup]({{SymbolDoc}}SW1) + elements** to add information beyond the summary, whenever + appropriate. + + * **Know and use recognized bullet items with + [symbol command syntax]({{SymbolDoc}}SW13).** Popular development + tools such as Xcode give special treatment to bullet items that + start with the following keywords: + + | [Attention]({{ref}}Attention.html) | [Author]({{ref}}Author.html) | [Authors]({{ref}}Authors.html) | [Bug]({{ref}}Bug.html) | + | [Complexity]({{ref}}Complexity.html) | [Copyright]({{ref}}Copyright.html) | [Date]({{ref}}Date.html) | [Experiment]({{ref}}Experiment.html) | + | [Important]({{ref}}Important.html) | [Invariant]({{ref}}Invariant.html) | [Note]({{ref}}Note.html) | [Parameter]({{ref}}Parameter.html) | + | [Parameters]({{ref}}Parameters.html) | [Postcondition]({{ref}}Postcondition.html) | [Precondition]({{ref}}Precondition.html) | [Remark]({{ref}}Remark.html) | + | [Requires]({{ref}}Requires.html) | [Returns]({{ref}}Returns.html) | [SeeAlso]({{ref}}SeeAlso.html) | [Since]({{ref}}Since.html) | + | [Throws]({{ref}}Throws.html) | [ToDo]({{ref}}Todo.html) | [Version]({{ref}}Version.html) | [Warning]({{ref}}Warning.html) | + + {{enddetail}} + + {{enddetail}} + +## Naming + +### Promote Clear Usage + +* **Include all the words needed to avoid ambiguity** for a person + reading code where the name is used. + {:#include-words-to-avoid-ambiguity} + + {{expand}} + {{detail}} + 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) + ~~~ + {:.good} + + 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? + ~~~ + {:.bad} + + {{enddetail}} + +* **Omit needless words.** Every word in a name should convey salient + information at the use site. + {:#omit-needless-words} + + {{expand}} + {{detail}} + 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. + + ~~~ swift + public mutating func removeElement(_ member: Element) -> Element? + + allViews.removeElement(cancelButton) + ~~~ + {:.bad} + + In this case, the word `Element` adds nothing salient at the call + site. This API would be better: + + ~~~ swift + public mutating func remove(_ member: Element) -> Element? + + allViews.remove(cancelButton) // clearer + ~~~ + {:.good} + + 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 the next + item for details. + {{enddetail}} + +* **Name variables, parameters, and associated types according to + their roles,** rather than their type constraints. + {:#name-according-to-roles} + + {{expand}} + {{detail}} + ~~~ swift + var **string** = "Hello" + protocol ViewController { + associatedtype **View**Type : View + } + class ProductionLine { + func restock(from **widgetFactory**: WidgetFactory) + } + ~~~ + {:.bad} + + Repurposing a type name in this way fails to optimize clarity and + expressivity. 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) + } + ~~~ + {:.good} + + 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** { ... } + ~~~ + {{enddetail}} + +* **Compensate for weak type information** to clarify a parameter's role. + {:#weak-type-information} + + {{expand}} + {{detail}} + 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 + ~~~ + {:.bad} + + 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 + ~~~ + {:.good} + {{enddetail}} + + +### Strive for Fluent Usage + +* **Prefer method and function names that make use sites form + grammatical English phrases.** + {:#methods-and-functions-read-as-phrases} + + {{expand}} + {{detail}} + ~~~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” + ~~~ + {:.good} + + ~~~swift + x.insert(y, position: z) + x.subviews(color: y) + x.nounCapitalize() + ~~~ + {:.bad} + + 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**) + ~~~ + {{enddetail}} + +* **Begin names of factory methods with “`make`”,** + e.g. `x.makeIterator()`. + {:#begin-factory-name-with-make} + +* The first argument to **initializer and + [factory methods](https://en.wikipedia.org/wiki/Factory_method_pattern) calls** + should not form a phrase starting with the base name, + e.g. `x.makeWidget(cogCount: 47)` + {:#init-factory-phrase-ends-with-basename} + + {{expand}} + {{detail}} + 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) + ~~~ + {:.good} + + 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)** + ~~~ + {:.bad} + + In practice, this guideline along with those for + [argument labels](#argument-labels) means the first argument will + have a label unless the call is performing a + [value preserving type conversion](#type-conversion). + + ~~~swift + let rgbForeground = RGBColor(cmykForeground) + ~~~ + {{enddetail}} + +* **Name functions and methods according to their side-effects** + {:#name-according-to-side-effects} + + * 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)`| + + {{expand}} + {{detail}} + + * 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() + ~~~ + + {{enddetail}} + + * 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)`| + +* **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)`. + {:#boolean-assertions} + +* **Protocols that describe *what something is* should read as + nouns** (e.g. `Collection`). + {:#protocols-describing-what-is-should-read-as-nouns} + +* **Protocols that describe a *capability* + should be named using the suffixes `able`, `ible`, or `ing`** + (e.g. `Equatable`, `ProgressReporting`). + {:#protocols-describing-capability-should-use-suffixes} + +* The names of other **types, properties, variables, and constants + should read as nouns.** + {:#name-of-others-should-read-as-nouns} + +### Use Terminology Well + +**Term of Art** +: *noun* - a word or phrase that has a precise, specialized meaning + within a particular field or profession. + +* **Avoid obscure terms** if a more common word conveys meaning just + as well. Don't say “epidermis” if “skin” will serve your purpose. + Terms of art are an essential communication tool, but should only be + used to capture crucial meaning that would otherwise be lost. + {:#avoid-obscure-terms} + +* **Stick to the established meaning** if you do use a term of art. + {:#stick-to-established-meaning} + + {{expand}} + {{detail}} + The only reason to use a technical term rather than a more common + word is that it *precisely* expresses something that would + otherwise be ambiguous or unclear. Therefore, an API should use + the term strictly in accordance with its accepted meaning. + + * **Don't surprise an expert**: anyone already familiar with the term + will be surprised and probably angered if we appear to have + invented a new meaning for it. + {:#do-not-surprise-an-expert} + + * **Don't confuse a beginner**: anyone trying to learn the term is + likely to do a web search and find its traditional meaning. + {:#do-not-confuse-a-beginner} + {{enddetail}} + +* **Avoid abbreviations.** Abbreviations, especially non-standard + ones, are effectively terms-of-art, because understanding depends on + correctly translating them into their non-abbreviated forms. + {:#avoid-abbreviations} + + > The intended meaning for any abbreviation you use should be + > easily found by a web search. + +* **Embrace precedent.** Don't optimize terms for the total beginner + at the expense of conformance to existing culture. + {:#embrace-precedent} + + {{expand}} + {{detail}} + It is better to name a contiguous data structure `Array` than to + use a simplified term such as `List`, even though a beginner + might grasp the meaning of `List` more easily. Arrays are + fundamental in modern computing, so every programmer knows—or + will soon learn—what an array is. Use a term that most + programmers are familiar with, and their web searches and + questions will be rewarded. + + Within a particular programming *domain*, such as mathematics, a + widely precedented term such as `sin(x)` is preferable to an + explanatory phrase such as + `verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)`. + Note that in this case, precedent outweighs the guideline to + avoid abbreviations: although the complete word is `sine`, + “sin(*x*)” has been in common use among programmers for decades, + and among mathematicians for centuries. + {{enddetail}} + +## Conventions + +### General Conventions + +* **Document the complexity of any computed property that is not + O(1).** People often assume that property access involves no + significant computation, because they have stored properties as a + mental model. Be sure to alert them when that assumption may be + violated. + {:#document-computed-property-complexity} + +* **Prefer methods and properties to free functions.** Free functions + are used only in special cases: + {:#prefer-method-and-properties-to-functions} + + {{expand}} + {{detail}} + + 1. When there's no obvious `self`: + + ~~~ + min(x, y, z) + ~~~ + + 2. When the function is an unconstrained generic: + + ~~~ + print(x) + ~~~ + + 3. When function syntax is part of the established domain notation: + + ~~~ + sin(x) + ~~~ + + {{enddetail}} + +* **Follow case conventions.** Names of types and protocols are + `UpperCamelCase`. Everything else is `lowerCamelCase`. + {:#follow-case-conventions} + + {{expand}} + {{detail}} + + [Acronyms and initialisms](https://en.wikipedia.org/wiki/Acronym) + that commonly appear as all upper case in American English should be + uniformly up- or down-cased according to case conventions: + + ~~~swift + var **utf8**Bytes: [**UTF8**.CodeUnit] + var isRepresentableAs**ASCII** = true + var user**SMTP**Server: Secure**SMTP**Server + ~~~ + + Other acronyms should be treated as ordinary words: + + ~~~swift + var **radar**Detector: **Radar**Scanner + var enjoys**Scuba**Diving = true + ~~~ + {{enddetail}} + + +{% comment %} +* **Be conscious of grammatical ambiguity**. Many words can act as + either a noun or a verb, e.g. “insert,” “record,” “contract,” and + “drink.” Consider how these dual roles may affect the clarity of + your API. + {:#be-conscious-of-grammatical-ambiguity} +{% endcomment %} + +* **Methods can share a base name** when they share the same basic + meaning or when they operate in distinct domains. + {:#similar-methods-can-share-a-base-name} + + {{expand}} + {{detail}} + For example, the following is encouraged, since the methods do essentially + the same things: + + ~~~ swift + extension Shape { + /// Returns `true` if `other` is within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **Point**) -> Bool { ... } + + /// Returns `true` if `other` is entirely within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **Shape**) -> Bool { ... } + + /// Returns `true` if `other` is within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **LineSegment**) -> Bool { ... } + } + ~~~ + {:.good} + + And since geometric types and collections are separate domains, + this is also fine in the same program: + + ~~~ swift + extension Collection where Element : Equatable { + /// Returns `true` if `self` contains an element equal to + /// `sought`; otherwise, `false`. + func **contains**(_ sought: Element) -> Bool { ... } + } + ~~~ + {:.good} + + However, these `index` methods have different semantics, and should + have been named differently: + + ~~~ swift + extension Database { + /// Rebuilds the database's search index + func **index**() { ... } + + /// Returns the `n`th row in the given table. + func **index**(_ n: Int, inTable: TableID) -> TableRow { ... } + } + ~~~ + {:.bad} + + Lastly, avoid “overloading on return type” because it causes + ambiguities in the presence of type inference. + + ~~~ swift + extension Box { + /// Returns the `Int` stored in `self`, if any, and + /// `nil` otherwise. + func **value**() -> Int? { ... } + + /// Returns the `String` stored in `self`, if any, and + /// `nil` otherwise. + func **value**() -> String? { ... } + } + ~~~ + {:.bad} + + {{enddetail}} + +### Parameters +{:#parameter-names} + +~~~swift +func move(from **start**: Point, to **end**: Point) +~~~ + +* **Choose parameter names to serve documentation**. Even though + parameter names do not appear at a function or method's point of + use, they play an important explanatory role. + {:#choose-parameter-names-to-serve-doc} + + {{expand}} + {{detail}} + Choose these names to make documentation easy to read. For example, + these names make documentation read naturally: + + ~~~swift + /// Return an `Array` containing the elements of `self` + /// that satisfy `**predicate**`. + func filter(_ **predicate**: (Element) -> Bool) -> [Generator.Element] + + /// Replace the given `**subRange**` of elements with `**newElements**`. + mutating func replaceRange(_ **subRange**: Range, with **newElements**: [E]) + ~~~ + {:.good} + + These, however, make the documentation awkward and ungrammatical: + + ~~~swift + /// Return an `Array` containing the elements of `self` + /// that satisfy `**includedInResult**`. + func filter(_ **includedInResult**: (Element) -> Bool) -> [Generator.Element] + + /// Replace the **range of elements indicated by `r`** with + /// the contents of `**with**`. + mutating func replaceRange(_ **r**: Range, **with**: [E]) + ~~~ + {:.bad} + + {{enddetail}} + +* **Take advantage of defaulted parameters** when it simplifies common + uses. Any parameter with a single commonly-used value is a + candidate for a default. + {:#take-advantage-of-defaulted-parameters} + + {{expand}} + {{detail}} + Default arguments improve readability by + hiding irrelevant information. For example: + + ~~~ swift + let order = lastName.compare( + royalFamilyName**, options: [], range: nil, locale: nil**) + ~~~ + {:.bad} + + can become the much simpler: + + ~~~ swift + let order = lastName.**compare(royalFamilyName)** + ~~~ + {:.good} + + Default arguments are generally preferable to the use of method + families, because they impose a lower cognitive burden on anyone + trying to understand the API. + + ~~~ swift + extension String { + /// *...description...* + public func compare( + _ other: String, options: CompareOptions **= []**, + range: Range? **= nil**, locale: Locale? **= nil** + ) -> Ordering + } + ~~~ + {:.good} + + The above may not be simple, but it is much simpler than: + + ~~~ swift + extension String { + /// *...description 1...* + public func **compare**(_ other: String) -> Ordering + /// *...description 2...* + public func **compare**(_ other: String, options: CompareOptions) -> Ordering + /// *...description 3...* + public func **compare**( + _ other: String, options: CompareOptions, range: Range) -> Ordering + /// *...description 4...* + public func **compare**( + _ other: String, options: StringCompareOptions, + range: Range, locale: Locale) -> Ordering + } + ~~~ + {:.bad} + + Every member of a method family needs to be separately documented + and understood by users. To decide among them, a user needs to + understand all of them, and occasional surprising relationships—for + example, `foo(bar: nil)` and `foo()` aren't always synonyms—make + this a tedious process of ferreting out minor differences in + mostly identical documentation. Using a single method with + defaults provides a vastly superior programmer experience. + {{enddetail}} + +* **Prefer to locate parameters with defaults toward the end** of the + parameter list. Parameters without defaults are usually more + essential to the semantics of a method, and provide a stable initial + pattern of use where methods are invoked. + {:#parameter-with-defaults-towards-the-end} + +* **If your API will run in production, prefer `#fileID`** over alternatives. + `#fileID` saves space and protects developers’ privacy. Use `#filePath` in + APIs that are never run by end users (such as test helpers and scripts) if + the full path will simplify development workflows or be used for file I/O. + Use `#file` to preserve source compatibility with Swift 5.2 or earlier. + +### Argument Labels + +~~~swift +func move(**from** start: Point, **to** end: Point) +x.move(**from:** x, **to:** y) +~~~ + +* **Omit all labels when arguments can't be usefully distinguished**, + e.g. `min(number1, number2)`, `zip(sequence1, sequence2)`. + {:#no-labels-for-indistinguishable-arguments} + +* **In initializers that perform value preserving type conversions, omit the + first argument label**, e.g. `Int64(someUInt32)` + {:#type-conversion} + + {{expand}} + {{detail}} + The first argument should always be the source of the conversion. + + ~~~ + extension String { + // Convert `x` into its textual representation in the given radix + init(**_** x: BigInt, radix: Int = 10) ← Note the initial underscore + } + + text = "The value is: " + text += **String(veryLargeNumber)** + text += " and in hexadecimal, it's" + text += **String(veryLargeNumber, radix: 16)** + ~~~ + + In “narrowing” type conversions, though, a label that describes + the narrowing is recommended. + + ~~~ swift + extension UInt32 { + /// Creates an instance having the specified `value`. + init(**_** value: Int16) ← Widening, so no label + /// Creates an instance having the lowest 32 bits of `source`. + init(**truncating** source: UInt64) + /// Creates an instance having the nearest representable + /// approximation of `valueToApproximate`. + init(**saturating** valueToApproximate: UInt64) + } + ~~~ + + > A value preserving type conversion is a + > [monomorphism](https://en.wikipedia.org/wiki/Monomorphism), i.e. + > every difference in the + > value of the source results in a difference in the value of the + > result. + > For example, conversion from `Int8` to `Int64` is value + > preserving because every distinct `Int8` value is converted to a + > distinct `Int64` value. Conversion in the other direction, however, + > cannot be value preserving: `Int64` has more possible values than + > can be represented in an `Int8`. + > + > Note: the ability to retrieve the original value has no bearing + > on whether a conversion is value preserving. + + {{enddetail}} + +* **When the first argument forms part of a + [prepositional phrase](https://en.wikipedia.org/wiki/Adpositional_phrase#Prepositional_phrases), + give it an argument label**. The argument label should normally begin at the + [preposition](https://en.wikipedia.org/wiki/Preposition), + e.g. `x.removeBoxes(havingLength: 12)`. + {:#give-prepositional-phrase-argument-label} + + {{expand}} + {{detail}} + An exception arises when the first two arguments represent parts of + a single abstraction. + + ~~~swift + a.move(**toX:** b, **y:** c) + a.fade(**fromRed:** b, **green:** c, **blue:** d) + ~~~ + {:.bad} + + In such cases, begin the argument label *after* the preposition, to + keep the abstraction clear. + + ~~~swift + a.moveTo(**x:** b, **y:** c) + a.fadeFrom(**red:** b, **green:** c, **blue:** d) + ~~~ + {:.good} + {{enddetail}} + +* **Otherwise, if the first argument forms part of a grammatical + phrase, omit its label**, appending any preceding words to the base + name, e.g. `x.addSubview(y)` + {:#omit-first-argument-if-partial-phrase} + + {{expand}} + {{detail}} + This guideline implies that if the first argument *doesn't* form + part of a grammatical phrase, it should have a label. + + ~~~swift + view.dismiss(**animated:** false) + let text = words.split(**maxSplits:** 12) + let studentsByName = students.sorted(**isOrderedBefore:** Student.namePrecedes) + ~~~ + {:.good} + + Note that it's important that the phrase convey the correct meaning. + The following would be grammatical but would express the wrong + thing. + + ~~~swift + view.dismiss(false) Don't dismiss? Dismiss a Bool? + words.split(12) Split the number 12? + ~~~ + {:.bad} + + Note also that arguments with default values can be omitted, and + in that case do not form part of a grammatical phrase, so they + should always have labels. + {{enddetail}} + +* **Label all other arguments**. + +## Special Instructions + +* **Label tuple members and name closure parameters** where they + appear in your API. + {:#label-closure-parameters} + + {{expand}} + {{detail}} + These names have + explanatory power, can be referenced from documentation comments, + and provide expressive access to tuple members. + + ~~~ swift + /// Ensure that we hold uniquely-referenced storage for at least + /// `requestedCapacity` elements. + /// + /// If more storage is needed, `allocate` is called with + /// **`byteCount`** equal to the number of maximally-aligned + /// bytes to allocate. + /// + /// - Returns: + /// - **reallocated**: `true` if a new block of memory + /// was allocated; otherwise, `false`. + /// - **capacityChanged**: `true` if `capacity` was updated; + /// otherwise, `false`. + mutating func ensureUniqueStorage( + minimumCapacity requestedCapacity: Int, + allocate: (_ **byteCount**: Int) -> UnsafePointer<Void> + ) -> (**reallocated:** Bool, **capacityChanged:** Bool) + ~~~ + + Names used for closure parameters should be chosen like + [parameter names](#parameter-names) for top-level functions. Labels for + closure arguments that appear at the call site are not supported. + {{enddetail}} + +* **Take extra care with unconstrained polymorphism** (e.g. `Any`, + `AnyObject`, and unconstrained generic parameters) to avoid + ambiguities in overload sets. + {:#unconstrained-polymorphism} + + {{expand}} + {{detail}} + For example, consider this overload set: + + ~~~ swift + struct Array { + /// Inserts `newElement` at `self.endIndex`. + public mutating func append(_ newElement: Element) + + /// Inserts the contents of `newElements`, in order, at + /// `self.endIndex`. + public mutating func append(_ newElements: S) + where S.Generator.Element == Element + } + ~~~ + {:.bad} + + These methods form a semantic family, and the argument types + appear at first to be sharply distinct. However, when `Element` + is `Any`, a single element can have the same type as a sequence of + elements. + + ~~~ swift + var values: [Any] = [1, "a"] + values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]? + ~~~ + {:.bad} + + To eliminate the ambiguity, name the second overload more + explicitly. + + ~~~ swift + struct Array { + /// Inserts `newElement` at `self.endIndex`. + public mutating func append(_ newElement: Element) + + /// Inserts the contents of `newElements`, in order, at + /// `self.endIndex`. + public mutating func append(**contentsOf** newElements: S) + where S.Generator.Element == Element + } + ~~~ + {:.good} + + Notice how the new name better matches the documentation comment. + In this case, the act of writing the documentation comment + actually brought the issue to the API author's attention. + {{enddetail}} + + + From 3dcde42a14c10e71e51aeed6fce11fab27e33253 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Wed, 11 Mar 2026 13:49:27 -0700 Subject: [PATCH 2/3] initial transformation --- .../Sources/APIGuidelines.docc/API-0001.md | 115 ++ .../Sources/APIGuidelines.docc/API-0002.md | 24 + .../Sources/APIGuidelines.docc/API-0003.md | 25 + .../Sources/APIGuidelines.docc/API-0004.md | 40 + .../Sources/APIGuidelines.docc/API-0005.md | 26 + .../Sources/APIGuidelines.docc/API-0006.md | 30 + .../Sources/APIGuidelines.docc/API-0007.md | 3 + .../Sources/APIGuidelines.docc/API-0008.md | 34 + .../Sources/APIGuidelines.docc/API-0009.md | 65 + .../Sources/APIGuidelines.docc/API-0010.md | 3 + .../Sources/APIGuidelines.docc/API-0011.md | 3 + .../Sources/APIGuidelines.docc/API-0012.md | 3 + .../Sources/APIGuidelines.docc/API-0013.md | 3 + .../Sources/APIGuidelines.docc/API-0014.md | 9 + .../Sources/APIGuidelines.docc/API-0015.md | 17 + .../Sources/APIGuidelines.docc/API-0016.md | 8 + .../Sources/APIGuidelines.docc/API-0017.md | 22 + .../Sources/APIGuidelines.docc/API-0018.md | 10 + .../Sources/APIGuidelines.docc/API-0019.md | 23 + .../Sources/APIGuidelines.docc/API-0020.md | 22 + .../Sources/APIGuidelines.docc/API-0021.md | 63 + .../Sources/APIGuidelines.docc/API-0022.md | 30 + .../Sources/APIGuidelines.docc/API-0023.md | 62 + .../Sources/APIGuidelines.docc/API-0024.md | 3 + .../Sources/APIGuidelines.docc/API-0025.md | 10 + .../Sources/APIGuidelines.docc/API-0026.md | 3 + .../Sources/APIGuidelines.docc/API-0027.md | 48 + .../Sources/APIGuidelines.docc/API-0028.md | 25 + .../Sources/APIGuidelines.docc/API-0029.md | 29 + .../Sources/APIGuidelines.docc/API-0030.md | 3 + .../Sources/APIGuidelines.docc/API-0031.md | 32 + .../Sources/APIGuidelines.docc/API-0032.md | 50 + .../APIGuidelines.docc/Documentation.md | 124 +- .../api-design-guidelines.md | 1069 ----------------- 34 files changed, 966 insertions(+), 1070 deletions(-) create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0001.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0002.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0003.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0004.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0005.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0006.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0007.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0008.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0009.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0010.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0011.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0012.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0013.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0014.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0015.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0016.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0017.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0018.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0019.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0020.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0021.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0022.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0023.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0024.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0025.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0026.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0027.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0028.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0029.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0030.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0031.md create mode 100644 api-guidelines/Sources/APIGuidelines.docc/API-0032.md delete mode 100644 api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0001.md b/api-guidelines/Sources/APIGuidelines.docc/API-0001.md new file mode 100644 index 0000000..0c7a60c --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0001.md @@ -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 + ``` + + * **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( + _ 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) | diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0002.md b/api-guidelines/Sources/APIGuidelines.docc/API-0002.md new file mode 100644 index 0000000..bdf1846 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0002.md @@ -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? +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0003.md b/api-guidelines/Sources/APIGuidelines.docc/API-0003.md new file mode 100644 index 0000000..0454237 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0003.md @@ -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 for details. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0004.md b/api-guidelines/Sources/APIGuidelines.docc/API-0004.md new file mode 100644 index 0000000..474ebaa --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0004.md @@ -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** { ... } +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0005.md b/api-guidelines/Sources/APIGuidelines.docc/API-0005.md new file mode 100644 index 0000000..c78cfea --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0005.md @@ -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 +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0006.md b/api-guidelines/Sources/APIGuidelines.docc/API-0006.md new file mode 100644 index 0000000..c7a82a5 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0006.md @@ -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**) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0007.md b/api-guidelines/Sources/APIGuidelines.docc/API-0007.md new file mode 100644 index 0000000..07525e5 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0007.md @@ -0,0 +1,3 @@ +# (API-0007) Begin factory method names with "make" + +**Begin names of factory methods with "`make`",** e.g. `x.makeIterator()`. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0008.md b/api-guidelines/Sources/APIGuidelines.docc/API-0008.md new file mode 100644 index 0000000..7eab0e3 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0008.md @@ -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]() means the first argument will +have a label unless the call is performing a +[value preserving type conversion](). + +```swift +let rgbForeground = RGBColor(cmykForeground) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0009.md b/api-guidelines/Sources/APIGuidelines.docc/API-0009.md new file mode 100644 index 0000000..6888cd1 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0009.md @@ -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)`| diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0010.md b/api-guidelines/Sources/APIGuidelines.docc/API-0010.md new file mode 100644 index 0000000..f5dbfdb --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0010.md @@ -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)`. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0011.md b/api-guidelines/Sources/APIGuidelines.docc/API-0011.md new file mode 100644 index 0000000..40b8cbf --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0011.md @@ -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`). diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0012.md b/api-guidelines/Sources/APIGuidelines.docc/API-0012.md new file mode 100644 index 0000000..f8b7eb6 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0012.md @@ -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`). diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0013.md b/api-guidelines/Sources/APIGuidelines.docc/API-0013.md new file mode 100644 index 0000000..3065b45 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0013.md @@ -0,0 +1,3 @@ +# (API-0013) Other names read as nouns + +The names of other **types, properties, variables, and constants should read as nouns.** diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0014.md b/api-guidelines/Sources/APIGuidelines.docc/API-0014.md new file mode 100644 index 0000000..4cf74e8 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0014.md @@ -0,0 +1,9 @@ +# (API-0014) Avoid obscure terms + +**Avoid obscure terms** if a more common word conveys meaning just as well. + +## Overview + +Don't say "epidermis" if "skin" will serve your purpose. +Terms of art are an essential communication tool, but should only be +used to capture crucial meaning that would otherwise be lost. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0015.md b/api-guidelines/Sources/APIGuidelines.docc/API-0015.md new file mode 100644 index 0000000..1085334 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0015.md @@ -0,0 +1,17 @@ +# (API-0015) Stick to the established meaning + +**Stick to the established meaning** if you do use a term of art. + +## Overview + +The only reason to use a technical term rather than a more common +word is that it *precisely* expresses something that would +otherwise be ambiguous or unclear. Therefore, an API should use +the term strictly in accordance with its accepted meaning. + +* **Don't surprise an expert**: anyone already familiar with the term + will be surprised and probably angered if we appear to have + invented a new meaning for it. + +* **Don't confuse a beginner**: anyone trying to learn the term is + likely to do a web search and find its traditional meaning. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0016.md b/api-guidelines/Sources/APIGuidelines.docc/API-0016.md new file mode 100644 index 0000000..3315080 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0016.md @@ -0,0 +1,8 @@ +# (API-0016) Avoid abbreviations + +**Avoid abbreviations.** Abbreviations, especially non-standard ones, are effectively terms-of-art, because understanding depends on correctly translating them into their non-abbreviated forms. + +## Overview + +> The intended meaning for any abbreviation you use should be +> easily found by a web search. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0017.md b/api-guidelines/Sources/APIGuidelines.docc/API-0017.md new file mode 100644 index 0000000..7549567 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0017.md @@ -0,0 +1,22 @@ +# (API-0017) Embrace precedent + +**Embrace precedent.** Don't optimize terms for the total beginner at the expense of conformance to existing culture. + +## Overview + +It is better to name a contiguous data structure `Array` than to +use a simplified term such as `List`, even though a beginner +might grasp the meaning of `List` more easily. Arrays are +fundamental in modern computing, so every programmer knows—or +will soon learn—what an array is. Use a term that most +programmers are familiar with, and their web searches and +questions will be rewarded. + +Within a particular programming *domain*, such as mathematics, a +widely precedented term such as `sin(x)` is preferable to an +explanatory phrase such as +`verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)`. +Note that in this case, precedent outweighs the guideline to +avoid abbreviations: although the complete word is `sine`, +"sin(*x*)" has been in common use among programmers for decades, +and among mathematicians for centuries. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0018.md b/api-guidelines/Sources/APIGuidelines.docc/API-0018.md new file mode 100644 index 0000000..59f9f15 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0018.md @@ -0,0 +1,10 @@ +# (API-0018) Document computed property complexity + +**Document the complexity of any computed property that is not O(1).** + +## Overview + +People often assume that property access involves no +significant computation, because they have stored properties as a +mental model. Be sure to alert them when that assumption may be +violated. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0019.md b/api-guidelines/Sources/APIGuidelines.docc/API-0019.md new file mode 100644 index 0000000..47e0b98 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0019.md @@ -0,0 +1,23 @@ +# (API-0019) Prefer methods and properties to free functions + +**Prefer methods and properties to free functions.** Free functions are used only in special cases. + +## Overview + +1. When there's no obvious `self`: + + ``` + min(x, y, z) + ``` + +2. When the function is an unconstrained generic: + + ``` + print(x) + ``` + +3. When function syntax is part of the established domain notation: + + ``` + sin(x) + ``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0020.md b/api-guidelines/Sources/APIGuidelines.docc/API-0020.md new file mode 100644 index 0000000..4ac7117 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0020.md @@ -0,0 +1,22 @@ +# (API-0020) Follow case conventions + +**Follow case conventions.** Names of types and protocols are `UpperCamelCase`. Everything else is `lowerCamelCase`. + +## Overview + +[Acronyms and initialisms](https://en.wikipedia.org/wiki/Acronym) +that commonly appear as all upper case in American English should be +uniformly up- or down-cased according to case conventions: + +```swift +var **utf8**Bytes: [**UTF8**.CodeUnit] +var isRepresentableAs**ASCII** = true +var user**SMTP**Server: Secure**SMTP**Server +``` + +Other acronyms should be treated as ordinary words: + +```swift +var **radar**Detector: **Radar**Scanner +var enjoys**Scuba**Diving = true +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0021.md b/api-guidelines/Sources/APIGuidelines.docc/API-0021.md new file mode 100644 index 0000000..2d6f7df --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0021.md @@ -0,0 +1,63 @@ +# (API-0021) Methods can share a base name + +**Methods can share a base name** when they share the same basic meaning or when they operate in distinct domains. + +## Overview + +✅ For example, the following is encouraged, since the methods do essentially +the same things: + +```swift +extension Shape { + /// Returns `true` if `other` is within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **Point**) -> Bool { ... } + + /// Returns `true` if `other` is entirely within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **Shape**) -> Bool { ... } + + /// Returns `true` if `other` is within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **LineSegment**) -> Bool { ... } +} +``` + +✅ And since geometric types and collections are separate domains, +this is also fine in the same program: + +```swift +extension Collection where Element : Equatable { + /// Returns `true` if `self` contains an element equal to + /// `sought`; otherwise, `false`. + func **contains**(_ sought: Element) -> Bool { ... } +} +``` + +⛔ However, these `index` methods have different semantics, and should +have been named differently: + +```swift +extension Database { + /// Rebuilds the database's search index + func **index**() { ... } + + /// Returns the `n`th row in the given table. + func **index**(_ n: Int, inTable: TableID) -> TableRow { ... } +} +``` + +⛔ Lastly, avoid "overloading on return type" because it causes +ambiguities in the presence of type inference. + +```swift +extension Box { + /// Returns the `Int` stored in `self`, if any, and + /// `nil` otherwise. + func **value**() -> Int? { ... } + + /// Returns the `String` stored in `self`, if any, and + /// `nil` otherwise. + func **value**() -> String? { ... } +} +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0022.md b/api-guidelines/Sources/APIGuidelines.docc/API-0022.md new file mode 100644 index 0000000..3b8346b --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0022.md @@ -0,0 +1,30 @@ +# (API-0022) Choose parameter names to serve documentation + +**Choose parameter names to serve documentation.** Even though parameter names do not appear at a function or method's point of use, they play an important explanatory role. + +## Overview + +Choose these names to make documentation easy to read. + +✅ For example, these names make documentation read naturally: + +```swift +/// Return an `Array` containing the elements of `self` +/// that satisfy `**predicate**`. +func filter(_ **predicate**: (Element) -> Bool) -> [Generator.Element] + +/// Replace the given `**subRange**` of elements with `**newElements**`. +mutating func replaceRange(_ **subRange**: Range, with **newElements**: [E]) +``` + +⛔ These, however, make the documentation awkward and ungrammatical: + +```swift +/// Return an `Array` containing the elements of `self` +/// that satisfy `**includedInResult**`. +func filter(_ **includedInResult**: (Element) -> Bool) -> [Generator.Element] + +/// Replace the **range of elements indicated by `r`** with +/// the contents of `**with**`. +mutating func replaceRange(_ **r**: Range, **with**: [E]) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0023.md b/api-guidelines/Sources/APIGuidelines.docc/API-0023.md new file mode 100644 index 0000000..8f1f581 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0023.md @@ -0,0 +1,62 @@ +# (API-0023) Take advantage of defaulted parameters + +**Take advantage of defaulted parameters** when it simplifies common uses. Any parameter with a single commonly-used value is a candidate for a default. + +## Overview + +Default arguments improve readability by hiding irrelevant information. + +⛔ For example: + +```swift +let order = lastName.compare( + royalFamilyName**, options: [], range: nil, locale: nil**) +``` + +✅ can become the much simpler: + +```swift +let order = lastName.**compare(royalFamilyName)** +``` + +Default arguments are generally preferable to the use of method +families, because they impose a lower cognitive burden on anyone +trying to understand the API. + +✅ + +```swift +extension String { + /// *...description...* + public func compare( + _ other: String, options: CompareOptions **= []**, + range: Range? **= nil**, locale: Locale? **= nil** + ) -> Ordering +} +``` + +⛔ The above may not be simple, but it is much simpler than: + +```swift +extension String { + /// *...description 1...* + public func **compare**(_ other: String) -> Ordering + /// *...description 2...* + public func **compare**(_ other: String, options: CompareOptions) -> Ordering + /// *...description 3...* + public func **compare**( + _ other: String, options: CompareOptions, range: Range) -> Ordering + /// *...description 4...* + public func **compare**( + _ other: String, options: StringCompareOptions, + range: Range, locale: Locale) -> Ordering +} +``` + +Every member of a method family needs to be separately documented +and understood by users. To decide among them, a user needs to +understand all of them, and occasional surprising relationships—for +example, `foo(bar: nil)` and `foo()` aren't always synonyms—make +this a tedious process of ferreting out minor differences in +mostly identical documentation. Using a single method with +defaults provides a vastly superior programmer experience. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0024.md b/api-guidelines/Sources/APIGuidelines.docc/API-0024.md new file mode 100644 index 0000000..dd3c0f2 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0024.md @@ -0,0 +1,3 @@ +# (API-0024) Locate parameters with defaults toward the end + +**Prefer to locate parameters with defaults toward the end** of the parameter list. Parameters without defaults are usually more essential to the semantics of a method, and provide a stable initial pattern of use where methods are invoked. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0025.md b/api-guidelines/Sources/APIGuidelines.docc/API-0025.md new file mode 100644 index 0000000..1a4a072 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0025.md @@ -0,0 +1,10 @@ +# (API-0025) Prefer #fileID in production + +**If your API will run in production, prefer `#fileID`** over alternatives. + +## Overview + +`#fileID` saves space and protects developers' privacy. Use `#filePath` in +APIs that are never run by end users (such as test helpers and scripts) if +the full path will simplify development workflows or be used for file I/O. +Use `#file` to preserve source compatibility with Swift 5.2 or earlier. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0026.md b/api-guidelines/Sources/APIGuidelines.docc/API-0026.md new file mode 100644 index 0000000..e7c9187 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0026.md @@ -0,0 +1,3 @@ +# (API-0026) Omit labels for indistinguishable arguments + +**Omit all labels when arguments can't be usefully distinguished**, e.g. `min(number1, number2)`, `zip(sequence1, sequence2)`. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0027.md b/api-guidelines/Sources/APIGuidelines.docc/API-0027.md new file mode 100644 index 0000000..0672826 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0027.md @@ -0,0 +1,48 @@ +# (API-0027) Omit first label for value-preserving type conversions + +**In initializers that perform value preserving type conversions, omit the first argument label**, e.g. `Int64(someUInt32)` + +## Overview + +The first argument should always be the source of the conversion. + +``` +extension String { + // Convert `x` into its textual representation in the given radix + init(**_** x: BigInt, radix: Int = 10) // ← Note the initial underscore +} + +text = "The value is: " +text += **String(veryLargeNumber)** +text += " and in hexadecimal, it's" +text += **String(veryLargeNumber, radix: 16)** +``` + +In "narrowing" type conversions, though, a label that describes +the narrowing is recommended. + +```swift +extension UInt32 { + /// Creates an instance having the specified `value`. + init(**_** value: Int16) // ← Widening, so no label + /// Creates an instance having the lowest 32 bits of `source`. + init(**truncating** source: UInt64) + /// Creates an instance having the nearest representable + /// approximation of `valueToApproximate`. + init(**saturating** valueToApproximate: UInt64) +} +``` + +> A value preserving type conversion is a +> [monomorphism](https://en.wikipedia.org/wiki/Monomorphism), i.e. +> every difference in the +> value of the source results in a difference in the value of the +> result. +> For example, conversion from `Int8` to `Int64` is value +> preserving because every distinct `Int8` value is converted to a +> distinct `Int64` value. Conversion in the other direction, however, +> cannot be value preserving: `Int64` has more possible values than +> can be represented in an `Int8`. +> +> Note: the ability to retrieve the original value has no bearing +> on whether a conversion is value preserving. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0028.md b/api-guidelines/Sources/APIGuidelines.docc/API-0028.md new file mode 100644 index 0000000..76529e2 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0028.md @@ -0,0 +1,25 @@ +# (API-0028) Label prepositional phrase arguments + +When the first argument forms part of a prepositional phrase, give it an argument label. The argument label should normally begin at the preposition, e.g. `x.removeBoxes(havingLength: 12)`. + +## Overview + +For more on prepositional phrases, see [prepositional phrase](https://en.wikipedia.org/wiki/Adpositional_phrase#Prepositional_phrases). For more on prepositions, see [preposition](https://en.wikipedia.org/wiki/Preposition). + +An exception arises when the first two arguments represent parts of +a single abstraction. + +⛔ + +```swift +a.move(**toX:** b, **y:** c) +a.fade(**fromRed:** b, **green:** c, **blue:** d) +``` + +✅ In such cases, begin the argument label *after* the preposition, to +keep the abstraction clear. + +```swift +a.moveTo(**x:** b, **y:** c) +a.fadeFrom(**red:** b, **green:** c, **blue:** d) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0029.md b/api-guidelines/Sources/APIGuidelines.docc/API-0029.md new file mode 100644 index 0000000..efec303 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0029.md @@ -0,0 +1,29 @@ +# (API-0029) Omit label when first argument forms a grammatical phrase + +**Otherwise, if the first argument forms part of a grammatical phrase, omit its label**, appending any preceding words to the base name, e.g. `x.addSubview(y)` + +## Overview + +This guideline implies that if the first argument *doesn't* form +part of a grammatical phrase, it should have a label. + +✅ + +```swift +view.dismiss(**animated:** false) +let text = words.split(**maxSplits:** 12) +let studentsByName = students.sorted(**isOrderedBefore:** Student.namePrecedes) +``` + +⛔ Note that it's important that the phrase convey the correct meaning. +The following would be grammatical but would express the wrong +thing. + +```swift +view.dismiss(false) // Don't dismiss? Dismiss a Bool? +words.split(12) // Split the number 12? +``` + +Note also that arguments with default values can be omitted, and +in that case do not form part of a grammatical phrase, so they +should always have labels. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0030.md b/api-guidelines/Sources/APIGuidelines.docc/API-0030.md new file mode 100644 index 0000000..417f243 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0030.md @@ -0,0 +1,3 @@ +# (API-0030) Label all other arguments + +**Label all other arguments.** diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0031.md b/api-guidelines/Sources/APIGuidelines.docc/API-0031.md new file mode 100644 index 0000000..ef3b813 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0031.md @@ -0,0 +1,32 @@ +# (API-0031) Label tuple members and name closure parameters + +**Label tuple members and name closure parameters** where they appear in your API. + +## Overview + +These names have +explanatory power, can be referenced from documentation comments, +and provide expressive access to tuple members. + +```swift +/// Ensure that we hold uniquely-referenced storage for at least +/// `requestedCapacity` elements. +/// +/// If more storage is needed, `allocate` is called with +/// **`byteCount`** equal to the number of maximally-aligned +/// bytes to allocate. +/// +/// - Returns: +/// - **reallocated**: `true` if a new block of memory +/// was allocated; otherwise, `false`. +/// - **capacityChanged**: `true` if `capacity` was updated; +/// otherwise, `false`. +mutating func ensureUniqueStorage( + minimumCapacity requestedCapacity: Int, + allocate: (_ **byteCount**: Int) -> UnsafePointer +) -> (**reallocated:** Bool, **capacityChanged:** Bool) +``` + +Names used for closure parameters should be chosen like +[parameter names]() for top-level functions. Labels for +closure arguments that appear at the call site are not supported. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0032.md b/api-guidelines/Sources/APIGuidelines.docc/API-0032.md new file mode 100644 index 0000000..ed595fe --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0032.md @@ -0,0 +1,50 @@ +# (API-0032) Take extra care with unconstrained polymorphism + +**Take extra care with unconstrained polymorphism** (e.g. `Any`, `AnyObject`, and unconstrained generic parameters) to avoid ambiguities in overload sets. + +## Overview + +⛔ For example, consider this overload set: + +```swift +struct Array { + /// Inserts `newElement` at `self.endIndex`. + public mutating func append(_ newElement: Element) + + /// Inserts the contents of `newElements`, in order, at + /// `self.endIndex`. + public mutating func append(_ newElements: S) + where S.Generator.Element == Element +} +``` + +These methods form a semantic family, and the argument types +appear at first to be sharply distinct. However, when `Element` +is `Any`, a single element can have the same type as a sequence of +elements. + +⛔ + +```swift +var values: [Any] = [1, "a"] +values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]? +``` + +✅ To eliminate the ambiguity, name the second overload more +explicitly. + +```swift +struct Array { + /// Inserts `newElement` at `self.endIndex`. + public mutating func append(_ newElement: Element) + + /// Inserts the contents of `newElements`, in order, at + /// `self.endIndex`. + public mutating func append(**contentsOf** newElements: S) + where S.Generator.Element == Element +} +``` + +Notice how the new name better matches the documentation comment. +In this case, the act of writing the documentation comment +actually brought the issue to the API author's attention. diff --git a/api-guidelines/Sources/APIGuidelines.docc/Documentation.md b/api-guidelines/Sources/APIGuidelines.docc/Documentation.md index 087098b..11785cc 100644 --- a/api-guidelines/Sources/APIGuidelines.docc/Documentation.md +++ b/api-guidelines/Sources/APIGuidelines.docc/Documentation.md @@ -1 +1,123 @@ -# ``APIGuidelines`` +# API Design Guidelines + +Delivering a clear, consistent developer experience when writing Swift code is largely defined by the names and idioms that appear in APIs. +These design guidelines explain how to make sure that your code feels like a part of the larger Swift ecosystem. + +## Fundamentals + +* **Clarity at the point of use** is your most important goal. + Entities such as methods and properties are declared only once but + *used* repeatedly. Design APIs to make those uses clear and + concise. When evaluating a design, reading a declaration is seldom + sufficient; always examine a use case to make sure it looks + clear in context. + +* **Clarity is more important than brevity.** Although Swift + code can be compact, it is a *non-goal* + to enable the smallest possible code with the fewest characters. + Brevity in Swift code, where it occurs, is a side-effect of the + strong type system and features that naturally reduce boilerplate. + +* **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. + +> Warning: +> If you are having trouble describing your API's +> functionality in simple terms, **you may have designed the wrong API.** + +## Naming + +### Promote Clear Usage + +### Strive for Fluent Usage + +### Use Terminology Well + +**Term of Art** +: *noun* - a word or phrase that has a precise, specialized meaning + within a particular field or profession. + +## Conventions + +### General Conventions + + + +### Parameters + +```swift +func move(from **start**: Point, to **end**: Point) +``` + +### Argument Labels + +```swift +func move(**from** start: Point, **to** end: Point) +x.move(**from:** x, **to:** y) +``` + +## Special Instructions + +## Topics + +### Fundamentals + +- + +### Naming — Promote Clear Usage + +- +- +- +- + +### Naming — Strive for Fluent Usage + +- +- +- +- +- +- +- +- + +### Naming — Use Terminology Well + +- +- +- +- + +### Conventions — General + +- +- +- +- + +### Conventions — Parameters + +- +- +- +- + +### Conventions — Argument Labels + +- +- +- +- +- + +### Special Instructions + +- +- diff --git a/api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md b/api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md deleted file mode 100644 index 9f19c26..0000000 --- a/api-guidelines/Sources/APIGuidelines.docc/api-design-guidelines.md +++ /dev/null @@ -1,1069 +0,0 @@ ---- -layout: page -title: API Design Guidelines -official_url: https://swift.org/documentation/api-design-guidelines/ -redirect_from: /documentation/api-design-guidelines.html ---- - - - - -{% capture expand %}{::nomarkdown} - -{:/nomarkdown}{% endcapture %} -{% assign detail = '
' %} -{% assign enddetail = '
' %} - -
-To facilitate use as a quick reference, the details of many guidelines -can be expanded individually. Details are never hidden when this page -is printed. - -
- -## Table of Contents -{:.no_toc} - -* TOC -{:toc} - -## Introduction - -Delivering a clear, consistent developer experience when writing Swift code is largely defined by the names and idioms that appear in APIs. -These design guidelines explain how to make sure that your code feels like a part of the larger Swift ecosystem. - -## Fundamentals - -* **Clarity at the point of use** is your most important goal. - Entities such as methods and properties are declared only once but - *used* repeatedly. Design APIs to make those uses clear and - concise. When evaluating a design, reading a declaration is seldom - sufficient; always examine a use case to make sure it looks - clear in context. - {:#clarity-at-the-point-of-use} - -* **Clarity is more important than brevity.** Although Swift - code can be compact, it is a *non-goal* - to enable the smallest possible code with the fewest characters. - Brevity in Swift code, where it occurs, is a side-effect of the - strong type system and features that naturally reduce boilerplate. - {:#clarity-over-brevity} - -* **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. - {:#write-doc-comment} - -
- If you are having trouble describing your API's - functionality in simple terms, **you may have designed the wrong API.** -
- - {{expand}} - {{detail}} - {% assign ref = 'https://developer.apple.com/library/prerelease/mac/documentation/Xcode/Reference/xcode_markup_formatting_ref/' %} - {% capture SymbolDoc %}{{ref}}SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-{% endcapture %} - - * **Use Swift's [dialect of Markdown]({{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 - ~~~ - - {{expand}} - {{detail}} - - * **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? - ... - ~~~ - - {{enddetail}} - - * **Optionally, continue** with one or more paragraphs and bullet - items. Paragraphs are separated by blank lines and use complete - sentences. - - ~~~ 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( - _ items: Any..., separator: String = " ", terminator: String = "\n") - ~~~ - - {{expand}} - {{detail}} - - * **Use recognized - [symbol documentation markup]({{SymbolDoc}}SW1) - elements** to add information beyond the summary, whenever - appropriate. - - * **Know and use recognized bullet items with - [symbol command syntax]({{SymbolDoc}}SW13).** Popular development - tools such as Xcode give special treatment to bullet items that - start with the following keywords: - - | [Attention]({{ref}}Attention.html) | [Author]({{ref}}Author.html) | [Authors]({{ref}}Authors.html) | [Bug]({{ref}}Bug.html) | - | [Complexity]({{ref}}Complexity.html) | [Copyright]({{ref}}Copyright.html) | [Date]({{ref}}Date.html) | [Experiment]({{ref}}Experiment.html) | - | [Important]({{ref}}Important.html) | [Invariant]({{ref}}Invariant.html) | [Note]({{ref}}Note.html) | [Parameter]({{ref}}Parameter.html) | - | [Parameters]({{ref}}Parameters.html) | [Postcondition]({{ref}}Postcondition.html) | [Precondition]({{ref}}Precondition.html) | [Remark]({{ref}}Remark.html) | - | [Requires]({{ref}}Requires.html) | [Returns]({{ref}}Returns.html) | [SeeAlso]({{ref}}SeeAlso.html) | [Since]({{ref}}Since.html) | - | [Throws]({{ref}}Throws.html) | [ToDo]({{ref}}Todo.html) | [Version]({{ref}}Version.html) | [Warning]({{ref}}Warning.html) | - - {{enddetail}} - - {{enddetail}} - -## Naming - -### Promote Clear Usage - -* **Include all the words needed to avoid ambiguity** for a person - reading code where the name is used. - {:#include-words-to-avoid-ambiguity} - - {{expand}} - {{detail}} - 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) - ~~~ - {:.good} - - 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? - ~~~ - {:.bad} - - {{enddetail}} - -* **Omit needless words.** Every word in a name should convey salient - information at the use site. - {:#omit-needless-words} - - {{expand}} - {{detail}} - 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. - - ~~~ swift - public mutating func removeElement(_ member: Element) -> Element? - - allViews.removeElement(cancelButton) - ~~~ - {:.bad} - - In this case, the word `Element` adds nothing salient at the call - site. This API would be better: - - ~~~ swift - public mutating func remove(_ member: Element) -> Element? - - allViews.remove(cancelButton) // clearer - ~~~ - {:.good} - - 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 the next - item for details. - {{enddetail}} - -* **Name variables, parameters, and associated types according to - their roles,** rather than their type constraints. - {:#name-according-to-roles} - - {{expand}} - {{detail}} - ~~~ swift - var **string** = "Hello" - protocol ViewController { - associatedtype **View**Type : View - } - class ProductionLine { - func restock(from **widgetFactory**: WidgetFactory) - } - ~~~ - {:.bad} - - Repurposing a type name in this way fails to optimize clarity and - expressivity. 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) - } - ~~~ - {:.good} - - 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** { ... } - ~~~ - {{enddetail}} - -* **Compensate for weak type information** to clarify a parameter's role. - {:#weak-type-information} - - {{expand}} - {{detail}} - 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 - ~~~ - {:.bad} - - 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 - ~~~ - {:.good} - {{enddetail}} - - -### Strive for Fluent Usage - -* **Prefer method and function names that make use sites form - grammatical English phrases.** - {:#methods-and-functions-read-as-phrases} - - {{expand}} - {{detail}} - ~~~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” - ~~~ - {:.good} - - ~~~swift - x.insert(y, position: z) - x.subviews(color: y) - x.nounCapitalize() - ~~~ - {:.bad} - - 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**) - ~~~ - {{enddetail}} - -* **Begin names of factory methods with “`make`”,** - e.g. `x.makeIterator()`. - {:#begin-factory-name-with-make} - -* The first argument to **initializer and - [factory methods](https://en.wikipedia.org/wiki/Factory_method_pattern) calls** - should not form a phrase starting with the base name, - e.g. `x.makeWidget(cogCount: 47)` - {:#init-factory-phrase-ends-with-basename} - - {{expand}} - {{detail}} - 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) - ~~~ - {:.good} - - 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)** - ~~~ - {:.bad} - - In practice, this guideline along with those for - [argument labels](#argument-labels) means the first argument will - have a label unless the call is performing a - [value preserving type conversion](#type-conversion). - - ~~~swift - let rgbForeground = RGBColor(cmykForeground) - ~~~ - {{enddetail}} - -* **Name functions and methods according to their side-effects** - {:#name-according-to-side-effects} - - * 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)`| - - {{expand}} - {{detail}} - - * 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() - ~~~ - - {{enddetail}} - - * 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)`| - -* **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)`. - {:#boolean-assertions} - -* **Protocols that describe *what something is* should read as - nouns** (e.g. `Collection`). - {:#protocols-describing-what-is-should-read-as-nouns} - -* **Protocols that describe a *capability* - should be named using the suffixes `able`, `ible`, or `ing`** - (e.g. `Equatable`, `ProgressReporting`). - {:#protocols-describing-capability-should-use-suffixes} - -* The names of other **types, properties, variables, and constants - should read as nouns.** - {:#name-of-others-should-read-as-nouns} - -### Use Terminology Well - -**Term of Art** -: *noun* - a word or phrase that has a precise, specialized meaning - within a particular field or profession. - -* **Avoid obscure terms** if a more common word conveys meaning just - as well. Don't say “epidermis” if “skin” will serve your purpose. - Terms of art are an essential communication tool, but should only be - used to capture crucial meaning that would otherwise be lost. - {:#avoid-obscure-terms} - -* **Stick to the established meaning** if you do use a term of art. - {:#stick-to-established-meaning} - - {{expand}} - {{detail}} - The only reason to use a technical term rather than a more common - word is that it *precisely* expresses something that would - otherwise be ambiguous or unclear. Therefore, an API should use - the term strictly in accordance with its accepted meaning. - - * **Don't surprise an expert**: anyone already familiar with the term - will be surprised and probably angered if we appear to have - invented a new meaning for it. - {:#do-not-surprise-an-expert} - - * **Don't confuse a beginner**: anyone trying to learn the term is - likely to do a web search and find its traditional meaning. - {:#do-not-confuse-a-beginner} - {{enddetail}} - -* **Avoid abbreviations.** Abbreviations, especially non-standard - ones, are effectively terms-of-art, because understanding depends on - correctly translating them into their non-abbreviated forms. - {:#avoid-abbreviations} - - > The intended meaning for any abbreviation you use should be - > easily found by a web search. - -* **Embrace precedent.** Don't optimize terms for the total beginner - at the expense of conformance to existing culture. - {:#embrace-precedent} - - {{expand}} - {{detail}} - It is better to name a contiguous data structure `Array` than to - use a simplified term such as `List`, even though a beginner - might grasp the meaning of `List` more easily. Arrays are - fundamental in modern computing, so every programmer knows—or - will soon learn—what an array is. Use a term that most - programmers are familiar with, and their web searches and - questions will be rewarded. - - Within a particular programming *domain*, such as mathematics, a - widely precedented term such as `sin(x)` is preferable to an - explanatory phrase such as - `verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)`. - Note that in this case, precedent outweighs the guideline to - avoid abbreviations: although the complete word is `sine`, - “sin(*x*)” has been in common use among programmers for decades, - and among mathematicians for centuries. - {{enddetail}} - -## Conventions - -### General Conventions - -* **Document the complexity of any computed property that is not - O(1).** People often assume that property access involves no - significant computation, because they have stored properties as a - mental model. Be sure to alert them when that assumption may be - violated. - {:#document-computed-property-complexity} - -* **Prefer methods and properties to free functions.** Free functions - are used only in special cases: - {:#prefer-method-and-properties-to-functions} - - {{expand}} - {{detail}} - - 1. When there's no obvious `self`: - - ~~~ - min(x, y, z) - ~~~ - - 2. When the function is an unconstrained generic: - - ~~~ - print(x) - ~~~ - - 3. When function syntax is part of the established domain notation: - - ~~~ - sin(x) - ~~~ - - {{enddetail}} - -* **Follow case conventions.** Names of types and protocols are - `UpperCamelCase`. Everything else is `lowerCamelCase`. - {:#follow-case-conventions} - - {{expand}} - {{detail}} - - [Acronyms and initialisms](https://en.wikipedia.org/wiki/Acronym) - that commonly appear as all upper case in American English should be - uniformly up- or down-cased according to case conventions: - - ~~~swift - var **utf8**Bytes: [**UTF8**.CodeUnit] - var isRepresentableAs**ASCII** = true - var user**SMTP**Server: Secure**SMTP**Server - ~~~ - - Other acronyms should be treated as ordinary words: - - ~~~swift - var **radar**Detector: **Radar**Scanner - var enjoys**Scuba**Diving = true - ~~~ - {{enddetail}} - - -{% comment %} -* **Be conscious of grammatical ambiguity**. Many words can act as - either a noun or a verb, e.g. “insert,” “record,” “contract,” and - “drink.” Consider how these dual roles may affect the clarity of - your API. - {:#be-conscious-of-grammatical-ambiguity} -{% endcomment %} - -* **Methods can share a base name** when they share the same basic - meaning or when they operate in distinct domains. - {:#similar-methods-can-share-a-base-name} - - {{expand}} - {{detail}} - For example, the following is encouraged, since the methods do essentially - the same things: - - ~~~ swift - extension Shape { - /// Returns `true` if `other` is within the area of `self`; - /// otherwise, `false`. - func **contains**(_ other: **Point**) -> Bool { ... } - - /// Returns `true` if `other` is entirely within the area of `self`; - /// otherwise, `false`. - func **contains**(_ other: **Shape**) -> Bool { ... } - - /// Returns `true` if `other` is within the area of `self`; - /// otherwise, `false`. - func **contains**(_ other: **LineSegment**) -> Bool { ... } - } - ~~~ - {:.good} - - And since geometric types and collections are separate domains, - this is also fine in the same program: - - ~~~ swift - extension Collection where Element : Equatable { - /// Returns `true` if `self` contains an element equal to - /// `sought`; otherwise, `false`. - func **contains**(_ sought: Element) -> Bool { ... } - } - ~~~ - {:.good} - - However, these `index` methods have different semantics, and should - have been named differently: - - ~~~ swift - extension Database { - /// Rebuilds the database's search index - func **index**() { ... } - - /// Returns the `n`th row in the given table. - func **index**(_ n: Int, inTable: TableID) -> TableRow { ... } - } - ~~~ - {:.bad} - - Lastly, avoid “overloading on return type” because it causes - ambiguities in the presence of type inference. - - ~~~ swift - extension Box { - /// Returns the `Int` stored in `self`, if any, and - /// `nil` otherwise. - func **value**() -> Int? { ... } - - /// Returns the `String` stored in `self`, if any, and - /// `nil` otherwise. - func **value**() -> String? { ... } - } - ~~~ - {:.bad} - - {{enddetail}} - -### Parameters -{:#parameter-names} - -~~~swift -func move(from **start**: Point, to **end**: Point) -~~~ - -* **Choose parameter names to serve documentation**. Even though - parameter names do not appear at a function or method's point of - use, they play an important explanatory role. - {:#choose-parameter-names-to-serve-doc} - - {{expand}} - {{detail}} - Choose these names to make documentation easy to read. For example, - these names make documentation read naturally: - - ~~~swift - /// Return an `Array` containing the elements of `self` - /// that satisfy `**predicate**`. - func filter(_ **predicate**: (Element) -> Bool) -> [Generator.Element] - - /// Replace the given `**subRange**` of elements with `**newElements**`. - mutating func replaceRange(_ **subRange**: Range, with **newElements**: [E]) - ~~~ - {:.good} - - These, however, make the documentation awkward and ungrammatical: - - ~~~swift - /// Return an `Array` containing the elements of `self` - /// that satisfy `**includedInResult**`. - func filter(_ **includedInResult**: (Element) -> Bool) -> [Generator.Element] - - /// Replace the **range of elements indicated by `r`** with - /// the contents of `**with**`. - mutating func replaceRange(_ **r**: Range, **with**: [E]) - ~~~ - {:.bad} - - {{enddetail}} - -* **Take advantage of defaulted parameters** when it simplifies common - uses. Any parameter with a single commonly-used value is a - candidate for a default. - {:#take-advantage-of-defaulted-parameters} - - {{expand}} - {{detail}} - Default arguments improve readability by - hiding irrelevant information. For example: - - ~~~ swift - let order = lastName.compare( - royalFamilyName**, options: [], range: nil, locale: nil**) - ~~~ - {:.bad} - - can become the much simpler: - - ~~~ swift - let order = lastName.**compare(royalFamilyName)** - ~~~ - {:.good} - - Default arguments are generally preferable to the use of method - families, because they impose a lower cognitive burden on anyone - trying to understand the API. - - ~~~ swift - extension String { - /// *...description...* - public func compare( - _ other: String, options: CompareOptions **= []**, - range: Range? **= nil**, locale: Locale? **= nil** - ) -> Ordering - } - ~~~ - {:.good} - - The above may not be simple, but it is much simpler than: - - ~~~ swift - extension String { - /// *...description 1...* - public func **compare**(_ other: String) -> Ordering - /// *...description 2...* - public func **compare**(_ other: String, options: CompareOptions) -> Ordering - /// *...description 3...* - public func **compare**( - _ other: String, options: CompareOptions, range: Range) -> Ordering - /// *...description 4...* - public func **compare**( - _ other: String, options: StringCompareOptions, - range: Range, locale: Locale) -> Ordering - } - ~~~ - {:.bad} - - Every member of a method family needs to be separately documented - and understood by users. To decide among them, a user needs to - understand all of them, and occasional surprising relationships—for - example, `foo(bar: nil)` and `foo()` aren't always synonyms—make - this a tedious process of ferreting out minor differences in - mostly identical documentation. Using a single method with - defaults provides a vastly superior programmer experience. - {{enddetail}} - -* **Prefer to locate parameters with defaults toward the end** of the - parameter list. Parameters without defaults are usually more - essential to the semantics of a method, and provide a stable initial - pattern of use where methods are invoked. - {:#parameter-with-defaults-towards-the-end} - -* **If your API will run in production, prefer `#fileID`** over alternatives. - `#fileID` saves space and protects developers’ privacy. Use `#filePath` in - APIs that are never run by end users (such as test helpers and scripts) if - the full path will simplify development workflows or be used for file I/O. - Use `#file` to preserve source compatibility with Swift 5.2 or earlier. - -### Argument Labels - -~~~swift -func move(**from** start: Point, **to** end: Point) -x.move(**from:** x, **to:** y) -~~~ - -* **Omit all labels when arguments can't be usefully distinguished**, - e.g. `min(number1, number2)`, `zip(sequence1, sequence2)`. - {:#no-labels-for-indistinguishable-arguments} - -* **In initializers that perform value preserving type conversions, omit the - first argument label**, e.g. `Int64(someUInt32)` - {:#type-conversion} - - {{expand}} - {{detail}} - The first argument should always be the source of the conversion. - - ~~~ - extension String { - // Convert `x` into its textual representation in the given radix - init(**_** x: BigInt, radix: Int = 10) ← Note the initial underscore - } - - text = "The value is: " - text += **String(veryLargeNumber)** - text += " and in hexadecimal, it's" - text += **String(veryLargeNumber, radix: 16)** - ~~~ - - In “narrowing” type conversions, though, a label that describes - the narrowing is recommended. - - ~~~ swift - extension UInt32 { - /// Creates an instance having the specified `value`. - init(**_** value: Int16) ← Widening, so no label - /// Creates an instance having the lowest 32 bits of `source`. - init(**truncating** source: UInt64) - /// Creates an instance having the nearest representable - /// approximation of `valueToApproximate`. - init(**saturating** valueToApproximate: UInt64) - } - ~~~ - - > A value preserving type conversion is a - > [monomorphism](https://en.wikipedia.org/wiki/Monomorphism), i.e. - > every difference in the - > value of the source results in a difference in the value of the - > result. - > For example, conversion from `Int8` to `Int64` is value - > preserving because every distinct `Int8` value is converted to a - > distinct `Int64` value. Conversion in the other direction, however, - > cannot be value preserving: `Int64` has more possible values than - > can be represented in an `Int8`. - > - > Note: the ability to retrieve the original value has no bearing - > on whether a conversion is value preserving. - - {{enddetail}} - -* **When the first argument forms part of a - [prepositional phrase](https://en.wikipedia.org/wiki/Adpositional_phrase#Prepositional_phrases), - give it an argument label**. The argument label should normally begin at the - [preposition](https://en.wikipedia.org/wiki/Preposition), - e.g. `x.removeBoxes(havingLength: 12)`. - {:#give-prepositional-phrase-argument-label} - - {{expand}} - {{detail}} - An exception arises when the first two arguments represent parts of - a single abstraction. - - ~~~swift - a.move(**toX:** b, **y:** c) - a.fade(**fromRed:** b, **green:** c, **blue:** d) - ~~~ - {:.bad} - - In such cases, begin the argument label *after* the preposition, to - keep the abstraction clear. - - ~~~swift - a.moveTo(**x:** b, **y:** c) - a.fadeFrom(**red:** b, **green:** c, **blue:** d) - ~~~ - {:.good} - {{enddetail}} - -* **Otherwise, if the first argument forms part of a grammatical - phrase, omit its label**, appending any preceding words to the base - name, e.g. `x.addSubview(y)` - {:#omit-first-argument-if-partial-phrase} - - {{expand}} - {{detail}} - This guideline implies that if the first argument *doesn't* form - part of a grammatical phrase, it should have a label. - - ~~~swift - view.dismiss(**animated:** false) - let text = words.split(**maxSplits:** 12) - let studentsByName = students.sorted(**isOrderedBefore:** Student.namePrecedes) - ~~~ - {:.good} - - Note that it's important that the phrase convey the correct meaning. - The following would be grammatical but would express the wrong - thing. - - ~~~swift - view.dismiss(false) Don't dismiss? Dismiss a Bool? - words.split(12) Split the number 12? - ~~~ - {:.bad} - - Note also that arguments with default values can be omitted, and - in that case do not form part of a grammatical phrase, so they - should always have labels. - {{enddetail}} - -* **Label all other arguments**. - -## Special Instructions - -* **Label tuple members and name closure parameters** where they - appear in your API. - {:#label-closure-parameters} - - {{expand}} - {{detail}} - These names have - explanatory power, can be referenced from documentation comments, - and provide expressive access to tuple members. - - ~~~ swift - /// Ensure that we hold uniquely-referenced storage for at least - /// `requestedCapacity` elements. - /// - /// If more storage is needed, `allocate` is called with - /// **`byteCount`** equal to the number of maximally-aligned - /// bytes to allocate. - /// - /// - Returns: - /// - **reallocated**: `true` if a new block of memory - /// was allocated; otherwise, `false`. - /// - **capacityChanged**: `true` if `capacity` was updated; - /// otherwise, `false`. - mutating func ensureUniqueStorage( - minimumCapacity requestedCapacity: Int, - allocate: (_ **byteCount**: Int) -> UnsafePointer<Void> - ) -> (**reallocated:** Bool, **capacityChanged:** Bool) - ~~~ - - Names used for closure parameters should be chosen like - [parameter names](#parameter-names) for top-level functions. Labels for - closure arguments that appear at the call site are not supported. - {{enddetail}} - -* **Take extra care with unconstrained polymorphism** (e.g. `Any`, - `AnyObject`, and unconstrained generic parameters) to avoid - ambiguities in overload sets. - {:#unconstrained-polymorphism} - - {{expand}} - {{detail}} - For example, consider this overload set: - - ~~~ swift - struct Array { - /// Inserts `newElement` at `self.endIndex`. - public mutating func append(_ newElement: Element) - - /// Inserts the contents of `newElements`, in order, at - /// `self.endIndex`. - public mutating func append(_ newElements: S) - where S.Generator.Element == Element - } - ~~~ - {:.bad} - - These methods form a semantic family, and the argument types - appear at first to be sharply distinct. However, when `Element` - is `Any`, a single element can have the same type as a sequence of - elements. - - ~~~ swift - var values: [Any] = [1, "a"] - values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]? - ~~~ - {:.bad} - - To eliminate the ambiguity, name the second overload more - explicitly. - - ~~~ swift - struct Array { - /// Inserts `newElement` at `self.endIndex`. - public mutating func append(_ newElement: Element) - - /// Inserts the contents of `newElements`, in order, at - /// `self.endIndex`. - public mutating func append(**contentsOf** newElements: S) - where S.Generator.Element == Element - } - ~~~ - {:.good} - - Notice how the new name better matches the documentation comment. - In this case, the act of writing the documentation comment - actually brought the issue to the API author's attention. - {{enddetail}} - - - From 054559d4ab6af774ed4b150de32530760b589b0c Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Wed, 11 Mar 2026 14:43:35 -0700 Subject: [PATCH 3/3] fairly direct translation into DocC, with minimal tweaks to align top-level formatting --- .../Sources/APIGuidelines.docc/API-0008.md | 2 +- .../Sources/APIGuidelines.docc/API-0014.md | 4 ++ .../Sources/APIGuidelines.docc/API-0021.md | 5 +++ .../Sources/APIGuidelines.docc/API-0031.md | 2 +- .../APIGuidelines.docc/Documentation.md | 44 ++----------------- 5 files changed, 14 insertions(+), 43 deletions(-) diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0008.md b/api-guidelines/Sources/APIGuidelines.docc/API-0008.md index 7eab0e3..59e99df 100644 --- a/api-guidelines/Sources/APIGuidelines.docc/API-0008.md +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0008.md @@ -25,7 +25,7 @@ let ref = **Link(to: destination)** ``` In practice, this guideline along with those for -[argument labels]() means the first argument will +argument labels (see ) means the first argument will have a label unless the call is performing a [value preserving type conversion](). diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0014.md b/api-guidelines/Sources/APIGuidelines.docc/API-0014.md index 4cf74e8..dd3c2be 100644 --- a/api-guidelines/Sources/APIGuidelines.docc/API-0014.md +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0014.md @@ -4,6 +4,10 @@ ## Overview +**Term of Art** +: *noun* - a word or phrase that has a precise, specialized meaning + within a particular field or profession. + Don't say "epidermis" if "skin" will serve your purpose. Terms of art are an essential communication tool, but should only be used to capture crucial meaning that would otherwise be lost. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0021.md b/api-guidelines/Sources/APIGuidelines.docc/API-0021.md index 2d6f7df..04e2a71 100644 --- a/api-guidelines/Sources/APIGuidelines.docc/API-0021.md +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0021.md @@ -4,6 +4,11 @@ ## Overview + + ✅ For example, the following is encouraged, since the methods do essentially the same things: diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0031.md b/api-guidelines/Sources/APIGuidelines.docc/API-0031.md index ef3b813..10242f8 100644 --- a/api-guidelines/Sources/APIGuidelines.docc/API-0031.md +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0031.md @@ -28,5 +28,5 @@ mutating func ensureUniqueStorage( ``` Names used for closure parameters should be chosen like -[parameter names]() for top-level functions. Labels for +parameter names (see ) for top-level functions. Labels for closure arguments that appear at the call site are not supported. diff --git a/api-guidelines/Sources/APIGuidelines.docc/Documentation.md b/api-guidelines/Sources/APIGuidelines.docc/Documentation.md index 11785cc..40b52fe 100644 --- a/api-guidelines/Sources/APIGuidelines.docc/Documentation.md +++ b/api-guidelines/Sources/APIGuidelines.docc/Documentation.md @@ -1,10 +1,10 @@ -# API Design Guidelines +# ``APIGuidelines`` + +## Overview Delivering a clear, consistent developer experience when writing Swift code is largely defined by the names and idioms that appear in APIs. These design guidelines explain how to make sure that your code feels like a part of the larger Swift ecosystem. -## Fundamentals - * **Clarity at the point of use** is your most important goal. Entities such as methods and properties are declared only once but *used* repeatedly. Design APIs to make those uses clear and @@ -26,44 +26,6 @@ These design guidelines explain how to make sure that your code feels like a par > If you are having trouble describing your API's > functionality in simple terms, **you may have designed the wrong API.** -## Naming - -### Promote Clear Usage - -### Strive for Fluent Usage - -### Use Terminology Well - -**Term of Art** -: *noun* - a word or phrase that has a precise, specialized meaning - within a particular field or profession. - -## Conventions - -### General Conventions - - - -### Parameters - -```swift -func move(from **start**: Point, to **end**: Point) -``` - -### Argument Labels - -```swift -func move(**from** start: Point, **to** end: Point) -x.move(**from:** x, **to:** y) -``` - -## Special Instructions - ## Topics ### Fundamentals