diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ae36a80..4bc038a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,6 +41,18 @@ jobs: - name: Run Unit Tests run: swift test + lint-markdown: + name: Lint Markdown + runs-on: macos-15 + steps: + - uses: actions/checkout@v6 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - uses: actions/setup-node@v4 + - name: Lint README.md + run: bundle exec rake lint:markdown + site-build: name: Build Site runs-on: macos-15 diff --git a/README.md b/README.md index 3108a2a..b9d718d 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,23 @@ Following this style guide should: -* Make it easier to read and begin understanding unfamiliar code. -* Make code easier to maintain. -* Reduce simple programmer errors. -* Reduce cognitive load while coding. -* Keep discussions on diffs focused on the code's logic rather than its style. +- Make it easier to read and begin understanding unfamiliar code. +- Make code easier to maintain. +- Reduce simple programmer errors. +- Reduce cognitive load while coding. +- Keep discussions on diffs focused on the code's logic rather than its style. Note that brevity is not a primary goal. Code should be made more concise only if other good code qualities (such as readability, simplicity, and clarity) remain equal or are improved. ## Guiding Tenets -* This guide is in addition to the official [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/). These rules should not contradict that document. -* These rules should not fight Xcode's ^ + I indentation behavior. -* We strive to make every rule lintable: - * If a rule changes the format of the code, it needs to be able to be reformatted automatically (either using [SwiftFormat](https://github.com/nicklockwood/SwiftFormat) or [SwiftLint](https://github.com/realm/SwiftLint) autocorrect). - * For rules that don't directly change the format of the code, we should have a lint rule that throws a warning. - * Exceptions to these rules should be rare and heavily justified. -* Format rules should be non-destructive. +- This guide is in addition to the official [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/). These rules should not contradict that document. +- These rules should not fight Xcode's ^ + I indentation behavior. +- We strive to make every rule lintable: + - If a rule changes the format of the code, it needs to be able to be reformatted automatically (either using [SwiftFormat](https://github.com/nicklockwood/SwiftFormat) or [SwiftLint](https://github.com/realm/SwiftLint) autocorrect). + - For rules that don't directly change the format of the code, we should have a lint rule that throws a warning. + - Exceptions to these rules should be rare and heavily justified. +- Format rules should be non-destructive. ## Swift Package Manager command plugin @@ -67,8 +67,9 @@ $ swift package format --swift-version 5.3 ``` The package plugin returns a non-zero exit code if there is a lint failure that requires attention. - - In `--lint` mode, any lint failure from any tool will result in a non-zero exit code. - - In standard autocorrect mode without `--lint`, only failures from SwiftLint lint-only rules will result in a non-zero exit code. + +- In `--lint` mode, any lint failure from any tool will result in a non-zero exit code. +- In standard autocorrect mode without `--lint`, only failures from SwiftLint lint-only rules will result in a non-zero exit code. @@ -77,9 +78,9 @@ The package plugin returns a non-zero exit code if there is a lint failure that 1. [Xcode Formatting](#xcode-formatting) 1. [Naming](#naming) 1. [Style](#style) - 1. [Functions](#functions) - 1. [Closures](#closures) - 1. [Operators](#operators) + 1. [Functions](#functions) + 1. [Closures](#closures) + 1. [Operators](#operators) 1. [Patterns](#patterns) 1. [File Organization](#file-organization) 1. [Objective-C Interoperability](#objective-c-interoperability) @@ -91,20 +92,21 @@ The package plugin returns a non-zero exit code if there is a lint failure that _You can enable the following settings in Xcode by running [this script](resources/xcode_settings.bash), e.g. as part of a "Run Script" build phase._ -* (link) **Each line should have a maximum column width of 100 characters.** +- (link) **Each line should have a maximum column width of 100 characters.**
[![SwiftFormat: wrap](https://img.shields.io/badge/SwiftFormat-wrap-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#wrap) #### Why? + Due to larger screen sizes, we have opted to choose a page guide greater than 80. We currently only "strictly enforce" (lint / auto-format) a maximum column width of 130 characters to limit the cases where manual clean up is required for reformatted lines that fall slightly above the threshold.
-* (link) **Use 2 spaces to indent lines.** +- (link) **Use 2 spaces to indent lines.**
@@ -112,7 +114,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Trim trailing whitespace in all lines.** +- (link) **Trim trailing whitespace in all lines.**
@@ -124,7 +126,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ## Naming -* (link) **Use PascalCase for type and protocol names, and lowerCamelCase for everything else.** +- (link) **Use PascalCase for type and protocol names, and lowerCamelCase for everything else.**
@@ -154,15 +156,11 @@ _You can enable the following settings in Xcode by running [this script](resourc let myFleet = SpaceFleet() ``` -
- _Exception: You may prefix a private property with an underscore if it is backing an identically-named property or method with a higher access level._ -
- #### Why? - There are specific scenarios where a backing property or method that is prefixed with an underscore could be easier to read than using a more descriptive name. + There are specific scenarios where a backing property or method that is prefixed with an underscore could be easier to read than using a more descriptive name. - Type erasure ```swift @@ -204,9 +202,9 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Name booleans like `isSpaceship`, `hasSpacesuit`, etc.** This makes it clear that they are booleans and not other types. +- (link) **Name booleans like `isSpaceship`, `hasSpacesuit`, etc.** This makes it clear that they are booleans and not other types. -* (link) **Acronyms in names (e.g. `URL`) should be all-caps except when it’s the start of a name that would otherwise be lowerCamelCase, in which case it should be uniformly lower-cased.** +- (link) **Acronyms in names (e.g. `URL`) should be all-caps except when it’s the start of a name that would otherwise be lowerCamelCase, in which case it should be uniformly lower-cased.**
@@ -244,7 +242,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Names should be written with their most general part first and their most specific part last.** The meaning of "most general" depends on context, but should roughly mean "that which most helps you narrow down your search for the item you're looking for." Most importantly, be consistent with how you order the parts of your name. +- (link) **Names should be written with their most general part first and their most specific part last.** The meaning of "most general" depends on context, but should roughly mean "that which most helps you narrow down your search for the item you're looking for." Most importantly, be consistent with how you order the parts of your name.
@@ -264,7 +262,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Include a hint about type in a name if it would otherwise be ambiguous.** +- (link) **Include a hint about type in a name if it would otherwise be ambiguous.**
@@ -280,7 +278,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Event-handling functions should be named like past-tense sentences.** The subject can be omitted if it's not needed for clarity. +- (link) **Event-handling functions should be named like past-tense sentences.** The subject can be omitted if it's not needed for clarity.
@@ -312,7 +310,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid Objective-C-style acronym prefixes.** This is no longer needed to avoid naming conflicts in Swift. +- (link) **Avoid Objective-C-style acronym prefixes.** This is no longer needed to avoid naming conflicts in Swift.
@@ -330,10 +328,11 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid `*Controller` in names of classes that aren't view controllers.** +- (link) **Avoid `*Controller` in names of classes that aren't view controllers.**
#### Why? + Controller is an overloaded suffix that doesn't provide information about the responsibilities of the class.
@@ -342,7 +341,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ## Style -* (link) **Don't include types where they can be easily inferred.** +- (link) **Don't include types where they can be easily inferred.**
@@ -393,7 +392,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer letting the type of a variable or property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.** +- (link) **Prefer letting the type of a variable or property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.**
@@ -475,7 +474,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Don't use `self` unless it's necessary for disambiguation or required by the language.** +- (link) **Don't use `self` unless it's necessary for disambiguation or required by the language.**
@@ -515,7 +514,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Bind to `self` when upgrading from a weak reference.** +- (link) **Bind to `self` when upgrading from a weak reference.**
@@ -549,7 +548,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Add a trailing comma after the last element of multi-line, multi-element comma-separated lists.* This includes arrays, dictionaries, function declarations, function calls, etc. Don't include a trailing comma if the list spans only a single line, or contains only a single element. +- (link) **Add a trailing comma after the last element of multi-line, multi-element comma-separated lists.** This includes arrays, dictionaries, function declarations, function calls, etc. Don't include a trailing comma if the list spans only a single line, or contains only a single element.
@@ -623,7 +622,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **There should be no spaces inside the brackets of collection literals.** +- (link) **There should be no spaces inside the brackets of collection literals.**
@@ -641,7 +640,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Name members of tuples for extra clarity.** Rule of thumb: if you've got more than 3 fields, you should probably be using a struct. +- (link) **Name members of tuples for extra clarity.** Rule of thumb: if you've got more than 3 fields, you should probably be using a struct.
@@ -672,7 +671,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Colons should always be followed by a space, but not preceded by a space**. +- (link) **Colons should always be followed by a space, but not preceded by a space**.
@@ -719,7 +718,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Place a space on either side of a return arrow for readability.** +- (link) **Place a space on either side of a return arrow for readability.**
@@ -751,7 +750,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit unnecessary parentheses.** +- (link) **Omit unnecessary parentheses.**
@@ -773,7 +772,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit enum associated values from case statements when all arguments are unlabeled.** +- (link) **Omit enum associated values from case statements when all arguments are unlabeled.**
@@ -799,62 +798,61 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **When destructuring an enum case or a tuple, place the `let` keyword inline, adjacent to each individual property assignment.** +- (link) **When destructuring an enum case or a tuple, place the `let` keyword inline, adjacent to each individual property assignment.**
[![SwiftFormat: hoistPatternLet](https://img.shields.io/badge/SwiftFormat-hoistPatternLet-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#hoistPatternLet) - ```swift - // WRONG - switch result { - case let .success(value): - ... - case let .error(errorCode, errorReason): - ... - } - - // WRONG - guard let case .success(value) else { - return - } + ```swift + // WRONG + switch result { + case let .success(value): + ... + case let .error(errorCode, errorReason): + ... + } - // RIGHT - switch result { - case .success(let value): - ... - case .error(let errorCode, let errorReason): - ... - } + // WRONG + guard let case .success(value) else { + return + } - // RIGHT - guard case .success(let value) else { - return - } - ``` + // RIGHT + switch result { + case .success(let value): + ... + case .error(let errorCode, let errorReason): + ... + } - #### Why? + // RIGHT + guard case .success(let value) else { + return + } + ``` - 1. **Consistency**: We should prefer to either _always_ inline the `let` keyword or _never_ inline the `let` keyword. In Airbnb's Swift codebase, we [observed](https://github.com/airbnb/swift/pull/126#discussion_r631979244) that inline `let` is used far more often in practice (especially when destructuring enum cases with a single associated value). + #### Why? + 1. **Consistency**: We should prefer to either _always_ inline the `let` keyword or _never_ inline the `let` keyword. In Airbnb's Swift codebase, we [observed](https://github.com/airbnb/swift/pull/126#discussion_r631979244) that inline `let` is used far more often in practice (especially when destructuring enum cases with a single associated value). - 2. **Clarity**: Inlining the `let` keyword makes it more clear which identifiers are part of the conditional check and which identifiers are binding new variables, since the `let` keyword is always adjacent to the variable identifier. + 2. **Clarity**: Inlining the `let` keyword makes it more clear which identifiers are part of the conditional check and which identifiers are binding new variables, since the `let` keyword is always adjacent to the variable identifier. - ```swift - // `let` is adjacent to the variable identifier, so it is immediately obvious - // at a glance that these identifiers represent new variable bindings - case .enumCaseWithSingleAssociatedValue(let string): - case .enumCaseWithMultipleAssociatedValues(let string, let int): + ```swift + // `let` is adjacent to the variable identifier, so it is immediately obvious + // at a glance that these identifiers represent new variable bindings + case .enumCaseWithSingleAssociatedValue(let string): + case .enumCaseWithMultipleAssociatedValues(let string, let int): - // The `let` keyword is quite far from the variable identifiers, - // so it is less obvious that they represent new variable bindings - case let .enumCaseWithSingleAssociatedValue(string): - case let .enumCaseWithMultipleAssociatedValues(string, int): + // The `let` keyword is quite far from the variable identifiers, + // so it is less obvious that they represent new variable bindings + case let .enumCaseWithSingleAssociatedValue(string): + case let .enumCaseWithMultipleAssociatedValues(string, int): - ``` + ```
-* (link) **Place attributes for functions, types, and computed properties on the line above the declaration**. +- (link) **Place attributes for functions, types, and computed properties on the line above the declaration**.
@@ -893,7 +891,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Place simple attributes for stored properties on the same line as the rest of the declaration**. Complex attributes with named arguments, or more than one unnamed argument, should be placed on the previous line. +- (link) **Place simple attributes for stored properties on the same line as the rest of the declaration**. Complex attributes with named arguments, or more than one unnamed argument, should be placed on the previous line.
@@ -1046,7 +1044,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Place modifiers for a declaration on the same line as the rest of the declaration**. +- (link) **Place modifiers for a declaration on the same line as the rest of the declaration**.
@@ -1074,7 +1072,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Multi-line arrays should have each bracket on a separate line.** Put the opening and closing brackets on separate lines from any of the elements of the array. Also add a trailing comma on the last element. +- (link) **Multi-line arrays should have each bracket on a separate line.** Put the opening and closing brackets on separate lines from any of the elements of the array. Also add a trailing comma on the last element.
@@ -1103,7 +1101,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) [Long](https://github.com/airbnb/swift#column-width) type aliases of protocol compositions should wrap before the `=` and before each individual `&`. +- (link) **[Long](#column-width) type aliases of protocol compositions should wrap before the `=` and before each individual `&`.**
@@ -1135,7 +1133,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Sort protocol composition type aliases alphabetically.** +- (link) **Sort protocol composition type aliases alphabetically.**
@@ -1165,7 +1163,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) Omit the right-hand side of the expression when unwrapping an optional property to a non-optional property with the same name. +- (link) **Omit the right-hand side of the expression when unwrapping an optional property to a non-optional property with the same name.**
@@ -1201,7 +1199,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Else statements should start on the same line as the previous condition's closing brace, unless the conditions are separated by a blank line or comments.** +- (link) **Else statements should start on the same line as the previous condition's closing brace, unless the conditions are separated by a blank line or comments.**
@@ -1259,14 +1257,15 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Multi-line conditional statements should break after the leading keyword.** Indent each individual statement by [2 spaces](https://github.com/airbnb/swift#spaces-over-tabs). +- (link) **Multi-line conditional statements should break after the leading keyword.** Indent each individual statement by [2 spaces](#spaces-over-tabs).
[![SwiftFormat: wrapArguments](https://img.shields.io/badge/SwiftFormat-wrapArguments-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#wrapArguments) #### Why? - Breaking after the leading keyword resets indentation to the standard [2-space grid](https://github.com/airbnb/swift#spaces-over-tabs), + + Breaking after the leading keyword resets indentation to the standard [2-space grid](#spaces-over-tabs), which helps avoid fighting Xcode's ^ + I indentation behavior. ```swift @@ -1320,7 +1319,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Add a line break after the assignment operator (`=`) before a multi-line `if` or `switch` expression**, and indent the following `if` / `switch` expression. If the declaration fits on a single line, a line break is not required. +- (link) **Add a line break after the assignment operator (`=`) before a multi-line `if` or `switch` expression**, and indent the following `if` / `switch` expression. If the declaration fits on a single line, a line break is not required.
@@ -1386,7 +1385,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **When initializing a new property with the result of a conditional statement (e.g. an `if` or `switch` statement), use a single `if`/`switch` expression where possible** rather than defining an uninitialized property and initializing it on every branch of the following conditional statement. +- (link) **When initializing a new property with the result of a conditional statement (e.g. an `if` or `switch` statement), use a single `if`/`switch` expression where possible** rather than defining an uninitialized property and initializing it on every branch of the following conditional statement.
@@ -1497,7 +1496,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Insert a blank line following a switch case with a multi-line body.** Spacing within an individual switch statement should be consistent. If any case has a multi-line body then all cases should include a trailing blank line. The last switch case doesn't need a blank line, since it is already followed by a closing brace. +- (link) **Insert a blank line following a switch case with a multi-line body.** Spacing within an individual switch statement should be consistent. If any case has a multi-line body then all cases should include a trailing blank line. The last switch case doesn't need a blank line, since it is already followed by a closing brace.
@@ -1624,13 +1623,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit redundant `break` statements in switch cases.** +- (link) **Omit redundant `break` statements in switch cases.**
[![SwiftFormat: redundantBreak](https://img.shields.io/badge/SwiftFormat-redundantBreak-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantBreak) #### Why? + Swift automatically breaks out of a switch case after executing its code, so explicit `break` statements are usually unnecessary and add visual clutter. ```swift @@ -1655,7 +1655,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Add a line break before the `else` keyword in a multi-line guard statement.** For single-line guard statements, keep the `else` keyword on the same line as the `guard` keyword. The open brace should immediately follow the `else` keyword. +- (link) **Add a line break before the `else` keyword in a multi-line guard statement.** For single-line guard statements, keep the `else` keyword on the same line as the `guard` keyword. The open brace should immediately follow the `else` keyword.
@@ -1686,7 +1686,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Indent the body and closing triple-quote of multiline string literals**, unless the string literal begins on its own line in which case the string literal contents and closing triple-quote should have the same indentation as the opening triple-quote. +- (link) **Indent the body and closing triple-quote of multiline string literals**, unless the string literal begins on its own line in which case the string literal contents and closing triple-quote should have the same indentation as the opening triple-quote.
@@ -1724,7 +1724,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Use constructors instead of Make() functions for NSRange and others.** +- (link) **Use constructors instead of Make() functions for NSRange and others.**
@@ -1740,7 +1740,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **For standard library types with a canonical shorthand form (`Optional`, `Array`, `Dictionary`), prefer using the shorthand form over the full generic form.** +- (link) **For standard library types with a canonical shorthand form (`Optional`, `Array`, `Dictionary`), prefer using the shorthand form over the full generic form.**
@@ -1760,7 +1760,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit explicit `.init` when not required.** +- (link) **Omit explicit `.init` when not required.**
@@ -1776,7 +1776,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) The opening brace following a single-line expression should be on the same line as the rest of the statement. +- (link) **The opening brace following a single-line expression should be on the same line as the rest of the statement.**
@@ -1813,7 +1813,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) The opening brace following a multi-line expression should wrap to a new line. +- (link) **The opening brace following a multi-line expression should wrap to a new line.**
@@ -1838,7 +1838,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Braces should be surrounded by a single whitespace character (either a space, or a newline) on each side.** +- (link) **Braces should be surrounded by a single whitespace character (either a space, or a newline) on each side.**
@@ -1872,7 +1872,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) For function calls and declarations, there should be no spaces before or inside the parentheses of the argument list. +- (link) **For function calls and declarations, there should be no spaces before or inside the parentheses of the argument list.**
@@ -1892,7 +1892,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Comment blocks should use single-line comments (`//` for code comments and `///` for documentation comments)**, rather than multi-line comments (`/* ... */` and `/** ... */`). +- (link) **Comment blocks should use single-line comments (`//` for code comments and `///` for documentation comments)**, rather than multi-line comments (`/* ... */` and `/** ... */`).
@@ -1944,7 +1944,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Use doc comments (`///`) instead of regular comments (`//`) before declarations within type bodies or at the top level.** +- (link) **Use doc comments (`///`) instead of regular comments (`//`) before declarations within type bodies or at the top level.**
@@ -2067,7 +2067,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Place doc comments for a declaration before any attributes or modifiers.** +- (link) **Place doc comments for a declaration before any attributes or modifiers.**
@@ -2096,7 +2096,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) Include spaces or newlines before and after comment delimiters (`//`, `///`, `/*`, and `*/`) +- (link) **Include spaces or newlines before and after comment delimiters** (`//`, `///`, `/*`, and `*/`)
@@ -2128,7 +2128,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) Include a single space in an empty set of braces (`{ }`). +- (link) **Include a single space in an empty set of braces** (`{ }`).
@@ -2158,13 +2158,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer using `for` loops over the functional `forEach(…)` method**, unless using `forEach(…)` as the last element in a functional chain. +- (link) **Prefer using `for` loops over the functional `forEach(…)` method**, unless using `forEach(…)` as the last element in a functional chain.
[![SwiftFormat: forLoop](https://img.shields.io/badge/SwiftFormat-forLoop-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#forLoop) #### Why? + For loops are more idiomatic than the `forEach(…)` method, and are typically familiar to all developers who have experience with C-family languages. For loops are also more expressive than the `forEach(…)` method. For loops support the `return`, `continue`, and `break` control flow keywords, while `forEach(…)` only supports `return` (which has the same behavior as `continue` in a for loop). @@ -2194,7 +2195,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit the `internal` keyword** when defining types, properties, or functions with an internal access control level. +- (link) **Omit the `internal` keyword** when defining types, properties, or functions with an internal access control level.
@@ -2216,7 +2217,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid using `public` access control in `internal` types.** In this case the `public` modifier is redundant and has no effect. +- (link) **Avoid using `public` access control in `internal` types.** In this case the `public` modifier is redundant and has no effect.
@@ -2240,7 +2241,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ### Functions -* (link) **Omit `Void` return types from function definitions.** +- (link) **Omit `Void` return types from function definitions.**
@@ -2260,7 +2261,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Separate [long](https://github.com/airbnb/swift#column-width) function declarations with line breaks before each argument label, and before the closing parenthesis (`)`).** +- (link) **Separate [long](#column-width) function declarations with line breaks before each argument label, and before the closing parenthesis (`)`).**
@@ -2337,7 +2338,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **[Long](https://github.com/airbnb/swift#column-width) function calls should also break on each argument.** Put the closing parenthesis on its own line. +- (link) **[Long](#column-width) function calls should also break on each argument.** Put the closing parenthesis on its own line.
@@ -2383,78 +2384,79 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Name unused function parameters as underscores (`_`).** +- (link) **Name unused function parameters as underscores (`_`).** -
+
[![SwiftFormat: unusedArguments](https://img.shields.io/badge/SwiftFormat-unusedArguments-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#unusedArguments) - #### Why? - Naming unused function parameters as underscores makes it more clear when the parameter is unused within the function body. - This can make it easier to catch subtle logical errors, and can highlight opportunities to simplify method signatures. + #### Why? - ```swift - // WRONG + Naming unused function parameters as underscores makes it more clear when the parameter is unused within the function body. + This can make it easier to catch subtle logical errors, and can highlight opportunities to simplify method signatures. - // In this method, the `newCondition` parameter is unused. - // This is actually a logical error, and is easy to miss, but compiles without warning. - func updateWeather(_ newCondition: WeatherCondition) -> Weather { - var updatedWeather = self - updatedWeather.condition = condition // this mistake inadvertently makes this method unable to change the weather condition - return updatedWeather - } - - // In this method, the `color` parameter is unused. - // Is this a logical error (e.g. should it be passed through to the `universe.generateStars` method call), - // or is this an unused argument that should be removed from the method signature? - func generateUniverseWithStars( - at location: Point, - count: Int, - color: StarColor, - withAverageDistance averageDistance: Float - ) { - let universe = generateUniverse() - universe.generateStars( - at: location, - count: count, - withAverageDistance: averageDistance - ) - } - ``` + ```swift + // WRONG - ```swift - // RIGHT + // In this method, the `newCondition` parameter is unused. + // This is actually a logical error, and is easy to miss, but compiles without warning. + func updateWeather(_ newCondition: WeatherCondition) -> Weather { + var updatedWeather = self + updatedWeather.condition = condition // this mistake inadvertently makes this method unable to change the weather condition + return updatedWeather + } + + // In this method, the `color` parameter is unused. + // Is this a logical error (e.g. should it be passed through to the `universe.generateStars` method call), + // or is this an unused argument that should be removed from the method signature? + func generateUniverseWithStars( + at location: Point, + count: Int, + color: StarColor, + withAverageDistance averageDistance: Float + ) { + let universe = generateUniverse() + universe.generateStars( + at: location, + count: count, + withAverageDistance: averageDistance + ) + } + ``` - // Automatically reformatting the unused parameter to be an underscore - // makes it more clear that the parameter is unused, which makes it - // easier to spot the logical error. - func updateWeather(_: WeatherCondition) -> Weather { - var updatedWeather = self - updatedWeather.condition = condition - return updatedWeather - } + ```swift + // RIGHT - // The underscore makes it more clear that the `color` parameter is unused. - // This method argument can either be removed if truly unnecessary, - // or passed through to `universe.generateStars` to correct the logical error. - func generateUniverseWithStars( - at location: Point, - count: Int, - color _: StarColor, - withAverageDistance averageDistance: Float - ) { - let universe = generateUniverse() - universe.generateStars( - at: location, - count: count, - withAverageDistance: averageDistance - ) - } - ``` + // Automatically reformatting the unused parameter to be an underscore + // makes it more clear that the parameter is unused, which makes it + // easier to spot the logical error. + func updateWeather(_: WeatherCondition) -> Weather { + var updatedWeather = self + updatedWeather.condition = condition + return updatedWeather + } + + // The underscore makes it more clear that the `color` parameter is unused. + // This method argument can either be removed if truly unnecessary, + // or passed through to `universe.generateStars` to correct the logical error. + func generateUniverseWithStars( + at location: Point, + count: Int, + color _: StarColor, + withAverageDistance averageDistance: Float + ) { + let universe = generateUniverse() + universe.generateStars( + at: location, + count: count, + withAverageDistance: averageDistance + ) + } + ``` -
+
-* (link) **Remove blank lines between chained functions.** +- (link) **Remove blank lines between chained functions.**
@@ -2500,13 +2502,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit redundant typed `throws` annotations from function definitions.** +- (link) **Omit redundant typed `throws` annotations from function definitions.**
[![SwiftFormat: redundantTypedThrows](https://img.shields.io/badge/SwiftFormat-redundantTypedThrows-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantTypedThrows) #### Why? + `throws(Never)` is equivalent to a non-throwing function, and `throws(Error)` is equivalent to non-typed `throws`. These redundant annotations add unnecessary complexity to function signatures. ```swift @@ -2533,7 +2536,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ### Closures -* (link) **Favor `Void` return types over `()` in closure declarations.** If you must specify a `Void` return type in a function declaration, use `Void` rather than `()` to improve readability. +- (link) **Favor `Void` return types over `()` in closure declarations.** If you must specify a `Void` return type in a function declaration, use `Void` rather than `()` to improve readability.
@@ -2553,31 +2556,32 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Name unused closure parameters as underscores (`_`).** +- (link) **Name unused closure parameters as underscores (`_`).** -
+
[![SwiftFormat: unusedArguments](https://img.shields.io/badge/SwiftFormat-unusedArguments-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#unusedArguments) - #### Why? - Naming unused closure parameters as underscores reduces the cognitive overhead required to read - closures by making it obvious which parameters are used and which are unused. + #### Why? - ```swift - // WRONG - someAsyncThing() { argument1, argument2, argument3 in - print(argument3) - } + Naming unused closure parameters as underscores reduces the cognitive overhead required to read + closures by making it obvious which parameters are used and which are unused. - // RIGHT - someAsyncThing() { _, _, argument3 in - print(argument3) - } - ``` + ```swift + // WRONG + someAsyncThing() { argument1, argument2, argument3 in + print(argument3) + } -
+ // RIGHT + someAsyncThing() { _, _, argument3 in + print(argument3) + } + ``` + +
-* (link) **Closures should have a single space or newline inside each brace.** Trailing closures should additionally have a single space or newline outside each brace. +- (link) **Closures should have a single space or newline inside each brace.** Trailing closures should additionally have a single space or newline outside each brace.
@@ -2617,7 +2621,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit `Void` return types from closure expressions.** +- (link) **Omit `Void` return types from closure expressions.**
@@ -2637,7 +2641,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer trailing closure syntax for closure arguments with no parameter name.** +- (link) **Prefer trailing closure syntax for closure arguments with no parameter name.**
@@ -2661,7 +2665,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid using `unowned` captures.** Instead prefer safer alternatives like `weak` captures, or capturing variables directly. +- (link) **Avoid using `unowned` captures.** Instead prefer safer alternatives like `weak` captures, or capturing variables directly.
@@ -2743,7 +2747,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ### Operators -* (link) **Infix operators should have a single space on either side.** However, in operator definitions, omit the trailing space between the operator and the open parenthesis. This rule does not apply to range operators (e.g. `1...3`). +- (link) **Infix operators should have a single space on either side.** However, in operator definitions, omit the trailing space between the operator and the open parenthesis. This rule does not apply to range operators (e.g. `1...3`).
@@ -2777,7 +2781,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **[Long](https://github.com/airbnb/swift#column-width) ternary operator expressions should wrap before the `?` and before the `:`**, putting each conditional branch on a separate line. +- (link) **[Long](#column-width) ternary operator expressions should wrap before the `?` and before the `:`**, putting each conditional branch on a separate line.
@@ -2800,11 +2804,11 @@ _You can enable the following settings in Xcode by running [this script](resourc let destinationPlanet = solarSystem.hasPlanetsInHabitableZone ? solarSystem.planetsInHabitableZone.first : solarSystem.uninhabitablePlanets.first - ``` + ```
-* (link) In conditional statements (`if`, `guard`, `while`), separate boolean conditions using commas (`,`) instead of `&&` operators. +- (link) **In conditional statements (`if`, `guard`, `while`), separate boolean conditions using commas (`,`) instead of `&&` operators.**
@@ -2840,7 +2844,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) When extending bound generic types, prefer using generic bracket syntax (`extension Collection`), or sugared syntax for applicable standard library types (`extension [Planet]`) instead of generic type constraints. +- (link) **When extending bound generic types, prefer using generic bracket syntax (`extension Collection`), or sugared syntax for applicable standard library types (`extension [Planet]`) instead of generic type constraints.**
@@ -2869,7 +2873,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid using semicolons.** Semicolons are not required at the end of a line, so should be omitted. While you can use semicolons to place two statements on the same line, it is more common and preferred to separate them using a newline instead. +- (link) **Avoid using semicolons.** Semicolons are not required at the end of a line, so should be omitted. While you can use semicolons to place two statements on the same line, it is more common and preferred to separate them using a newline instead.
@@ -2913,7 +2917,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ## Patterns -* (link) **Prefer initializing properties at `init` time whenever possible, rather than using implicitly unwrapped optionals.** A notable exception is UIViewController's `view` property. +- (link) **Prefer initializing properties at `init` time whenever possible, rather than using implicitly unwrapped optionals.** A notable exception is UIViewController's `view` property.
@@ -2945,15 +2949,16 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid performing any meaningful or time-intensive work in `init()`.** Avoid doing things like opening database connections, making network requests, reading large amounts of data from disk, etc. Create something like a `start()` method if these things need to be done before an object is ready for use. +- (link) **Avoid performing any meaningful or time-intensive work in `init()`.** Avoid doing things like opening database connections, making network requests, reading large amounts of data from disk, etc. Create something like a `start()` method if these things need to be done before an object is ready for use. -* (link) **Omit redundant memberwise initializers.** The compiler can synthesize memberwise initializers for structs, so explicit initializers that only assign parameters to properties with the same names should be omitted. Note that this only applies to `internal`, `fileprivate` and `private` initializers, since compiler-synthesized memberwise initializers are only generated for those access controls. +- (link) **Omit redundant memberwise initializers.** The compiler can synthesize memberwise initializers for structs, so explicit initializers that only assign parameters to properties with the same names should be omitted. Note that this only applies to `internal`, `fileprivate` and `private` initializers, since compiler-synthesized memberwise initializers are only generated for those access controls.
[![SwiftFormat: redundantMemberwiseInit](https://img.shields.io/badge/SwiftFormat-redundantMemberwiseInit-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantMemberwiseInit) #### Why? + Removing redundant memberwise initializers reduces boilerplate and makes the code more concise. The compiler-synthesized initializers are equivalent to the explicit ones, so there's no functional difference. ```swift @@ -3007,7 +3012,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Extract complex property observers into methods.** This reduces nestedness, separates side-effects from property declarations, and makes the usage of implicitly-passed parameters like `oldValue` explicit. +- (link) **Extract complex property observers into methods.** This reduces nestedness, separates side-effects from property declarations, and makes the usage of implicitly-passed parameters like `oldValue` explicit.
@@ -3043,7 +3048,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Extract complex callback blocks into methods**. This limits the complexity introduced by weak-self in blocks and reduces nestedness. If you need to reference self in the method call, make use of `guard` to unwrap self for the duration of the callback. +- (link) **Extract complex callback blocks into methods**. This limits the complexity introduced by weak-self in blocks and reduces nestedness. If you need to reference self in the method call, make use of `guard` to unwrap self for the duration of the callback.
@@ -3080,16 +3085,17 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer using `guard` at the beginning of a scope.** +- (link) **Prefer using `guard` at the beginning of a scope.**
#### Why? + It's easier to reason about a block of code when all `guard` statements are grouped together at the top rather than intermixed with business logic.
-* (link) **Use consistent ordering for modifiers.** Access modifiers like `public` and `private` come before other modifiers like `final` or `static`. +- (link) **Use consistent ordering for modifiers.** Access modifiers like `public` and `private` come before other modifiers like `final` or `static`.
@@ -3107,7 +3113,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Access control should be at the strictest level possible.** Prefer `public` to `open` and `private` to `fileprivate` unless you need that behavior. +- (link) **Access control should be at the strictest level possible.** Prefer `public` to `open` and `private` to `fileprivate` unless you need that behavior.
@@ -3166,7 +3172,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid global functions whenever possible.** Prefer methods within type definitions. +- (link) **Avoid global functions whenever possible.** Prefer methods within type definitions.
@@ -3196,16 +3202,17 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Use caseless `enum`s for organizing `public` or `internal` constants and functions into namespaces.** - * Avoid creating non-namespaced global constants and functions. - * Feel free to nest namespaces where it adds clarity. - * `private` globals are permitted, since they are scoped to a single file and do not pollute the global namespace. Consider placing private globals in an `enum` namespace to match the guidelines for other declaration types. +- (link) **Use caseless `enum`s for organizing `public` or `internal` constants and functions into namespaces.**
[![SwiftFormat: enumNamespaces](https://img.shields.io/badge/SwiftFormat-enumNamespaces-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#enumNamespaces) + - Avoid creating non-namespaced global constants and functions. + - Feel free to nest namespaces where it adds clarity. + - `private` globals are permitted, since they are scoped to a single file and do not pollute the global namespace. Consider placing private globals in an `enum` namespace to match the guidelines for other declaration types. #### Why? + Caseless `enum`s work well as namespaces because they cannot be instantiated, which matches their intent. ```swift @@ -3242,13 +3249,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Use Swift's automatic enum values unless they map to an external source.** Add a comment explaining why explicit values are defined. +- (link) **Use Swift's automatic enum values unless they map to an external source.** Add a comment explaining why explicit values are defined.
[![SwiftFormat: redundantRawValues](https://img.shields.io/badge/SwiftFormat-redundantRawValues-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantRawValues) #### Why? + To minimize user error, improve readability, and write code faster, rely on Swift's automatic enum values. If the value maps to an external source (e.g. it's coming from a network request) or is persisted across binaries, however, define the values explicitly, and document what these values are mapping to. This ensures that if someone adds a new value in the middle, they won't accidentally break things. @@ -3327,13 +3335,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Use optionals only when they have semantic meaning.** +- (link) **Use optionals only when they have semantic meaning.** -* (link) **Prefer immutable values whenever possible.** Use `map` and `compactMap` instead of appending to a new collection. Use `filter` instead of removing elements from a mutable collection. +- (link) **Prefer immutable values whenever possible.** Use `map` and `compactMap` instead of appending to a new collection. Use `filter` instead of removing elements from a mutable collection.
#### Why? + Mutable variables increase complexity, so try to keep them in as narrow a scope as possible. ```swift @@ -3363,11 +3372,12 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer immutable or computed static properties over mutable ones whenever possible.** Use stored `static let` properties or computed `static var` properties over stored `static var` properties whenever possible, as stored `static var` properties are global mutable state. +- (link) **Prefer immutable or computed static properties over mutable ones whenever possible.** Use stored `static let` properties or computed `static var` properties over stored `static var` properties whenever possible, as stored `static var` properties are global mutable state.
#### Why? + Global mutable state increases complexity and makes it harder to reason about the behavior of applications. It should be avoided when possible. ```swift @@ -3403,7 +3413,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Handle an unexpected but recoverable condition with an `assert` method combined with the appropriate logging in production. If the unexpected condition is not recoverable, prefer a `precondition` method or `fatalError()`.** This strikes a balance between crashing and providing insight into unexpected conditions in the wild. Only prefer `fatalError` over a `precondition` method when the failure message is dynamic, since a `precondition` method won't report the message in the crash report. +- (link) **Handle an unexpected but recoverable condition with an `assert` method combined with the appropriate logging in production. If the unexpected condition is not recoverable, prefer a `precondition` method or `fatalError()`.** This strikes a balance between crashing and providing insight into unexpected conditions in the wild. Only prefer `fatalError` over a `precondition` method when the failure message is dynamic, since a `precondition` method won't report the message in the crash report.
@@ -3437,13 +3447,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer defining simple generic constraints in the generic parameter list rather than in the where clause.** +- (link) **Prefer defining simple generic constraints in the generic parameter list rather than in the where clause.**
[![SwiftFormat: simplifyGenericConstraints](https://img.shields.io/badge/SwiftFormat-simplifyGenericConstraints-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#simplifyGenericConstraints) #### Why? + Inline generic constraints (``) are more concise and idiomatic than where clauses (` where T: Protocol`) for simple protocol conformances. Using inline constraints for simple cases makes generic declarations easier to read at a glance. Where clauses are reserved for complex constraints that cannot be expressed inline, like associated type constraints (`T.Element == Star`) or concrete type equality. ```swift @@ -3483,11 +3494,12 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Default type methods to `static`.** +- (link) **Default type methods to `static`.**
#### Why? + If a method needs to be overridden, the author should opt into that functionality by using the `class` keyword instead. ```swift @@ -3504,7 +3516,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Default classes to `final`.** +- (link) **Default classes to `final`.**
@@ -3530,7 +3542,6 @@ _You can enable the following settings in Xcode by running [this script](resourc Most classes are never overridden, and composition is generally preferred over inheritance. If a class does need to be subclassed, use one of these approaches to indicate to the linter that the class should not be marked `final`: - 1. If the class is already `public`, mark the class as `open`. `open` access control indicates that the class is allowed to be subclassed: ```swift @@ -3572,11 +3583,12 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) When switching over an enum, generally prefer enumerating all cases rather than using the `default` case. +- (link) **When switching over an enum, generally prefer enumerating all cases rather than using the `default` case.**
#### Why? + Enumerating every case requires developers and reviewers have to consider the correctness of every switch statement when new cases are added in the future. ```swift @@ -3639,13 +3651,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Check for nil rather than using optional binding if you don't need to use the value.** +- (link) **Check for nil rather than using optional binding if you don't need to use the value.**
[![SwiftLint: unused_optional_binding](https://img.shields.io/badge/SwiftLint-unused__optional__binding-007A87.svg)](https://realm.github.io/SwiftLint/unused_optional_binding) #### Why? + Checking for nil makes it immediately clear what the intent of the statement is. Optional binding is less explicit. ```swift @@ -3664,7 +3677,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit the `return` keyword when not required by the language.** +- (link) **Omit the `return` keyword when not required by the language.**
@@ -3744,7 +3757,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Use `AnyObject` instead of `class` in protocol definitions.** +- (link) **Use `AnyObject` instead of `class` in protocol definitions.**
@@ -3766,7 +3779,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Specify the access control for each declaration in an extension individually.** +- (link) **Specify the access control for each declaration in an extension individually.**
@@ -3807,20 +3820,22 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer dedicated logging systems like [`os_log`](https://developer.apple.com/documentation/os/logging) or [`swift-log`](https://github.com/apple/swift-log) over writing directly to standard out using `print(…)`, `debugPrint(…)`, or `dump(…)`.** +- (link) **Prefer dedicated logging systems like [`os_log`](https://developer.apple.com/documentation/os/logging) or [`swift-log`](https://github.com/apple/swift-log) over writing directly to standard out using `print(…)`, `debugPrint(…)`, or `dump(…)`.**
#### Why? + All log messages should flow into intermediate logging systems that can direct messages to the correct destination(s) and potentially filter messages based on the app's environment or configuration. `print(…)`, `debugPrint(…)`, or `dump(…)` will write all messages directly to standard out in all app configurations and can potentially leak personally identifiable information (PII).
-* (link) **Don't use `#file`. Use `#fileID` or `#filePath` as appropriate.** +- (link) **Don't use `#file`. Use `#fileID` or `#filePath` as appropriate.**
#### Why? + The behavior of the `#file` literal (or macro as of Swift 5.9) has evolved from evaluating to the full source file path (the behavior as of `#filePath`) to a human-readable string containing module and file name (the behavior of `#fileID`). Use the literal (or macro) with the most appropriate behavior for your use case. [Swift documentation](https://developer.apple.com/documentation/swift/file) @@ -3829,18 +3844,19 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Don't use `#filePath` in production code. Use `#fileID` instead.** +- (link) **Don't use `#filePath` in production code. Use `#fileID` instead.**
#### Why? + `#filePath` should only be used in non-production code where the full path of the source file provides useful information to developers. Because `#fileID` doesn’t embed the full path to the source file, it won't expose your file system and reduces the size of the compiled binary. [#filePath documentation](https://developer.apple.com/documentation/swift/filepath#overview)
-* (link) **Avoid single-expression closures that are always called immediately**. Instead, prefer inlining the expression. +- (link) **Avoid single-expression closures that are always called immediately**. Instead, prefer inlining the expression.
@@ -3874,411 +3890,411 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Omit the `get` clause from a computed property declaration that doesn't also have a `set`, `willSet`, or `didSet` clause.** +- (link) **Omit the `get` clause from a computed property declaration that doesn't also have a `set`, `willSet`, or `didSet` clause.** -
+
[![SwiftFormat: redundantGet](https://img.shields.io/badge/SwiftFormat-redundantGet-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantGet) - ```swift - // WRONG - var universe: Universe { - get { - Universe() - } - } - - // RIGHT - var universe: Universe { + ```swift + // WRONG + var universe: Universe { + get { Universe() } + } - // RIGHT - var universe: Universe { - get { multiverseService.current } - set { multiverseService.current = newValue } - } - ``` + // RIGHT + var universe: Universe { + Universe() + } -
+ // RIGHT + var universe: Universe { + get { multiverseService.current } + set { multiverseService.current = newValue } + } + ``` -* (link) **Prefer using opaque generic parameters (with `some`) over verbose named generic parameter syntax where possible.** +
-
+- (link) **Prefer using opaque generic parameters (with `some`) over verbose named generic parameter syntax where possible.** + +
[![SwiftFormat: opaqueGenericParameters](https://img.shields.io/badge/SwiftFormat-opaqueGenericParameters-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#opaqueGenericParameters) - #### Why? + #### Why? - Opaque generic parameter syntax is significantly less verbose and thus more legible than the full named generic parameter syntax. + Opaque generic parameter syntax is significantly less verbose and thus more legible than the full named generic parameter syntax. - ```swift - // WRONG - func spaceshipDashboard( - warpDrive: WarpDriveView, - captainsLog: CaptainsLogView - ) -> some View { - ... - } + ```swift + // WRONG + func spaceshipDashboard( + warpDrive: WarpDriveView, + captainsLog: CaptainsLogView + ) -> some View { + ... + } - func generate(_ planets: Planets) where Planets: Collection, Planets.Element == Planet { - ... - } + func generate(_ planets: Planets) where Planets: Collection, Planets.Element == Planet { + ... + } - // RIGHT - func spaceshipDashboard( - warpDrive: some View, - captainsLog: some View - ) -> some View { - ... - } + // RIGHT + func spaceshipDashboard( + warpDrive: some View, + captainsLog: some View + ) -> some View { + ... + } - func generate(_ planets: some Collection) { - ... - } + func generate(_ planets: some Collection) { + ... + } - // Also fine, since there isn't an equivalent opaque parameter syntax for expressing - // that two parameters in the type signature are of the same type: - func terraform(_ planetaryBody: Body, into terraformedBody: Body) { - ... - } + // Also fine, since there isn't an equivalent opaque parameter syntax for expressing + // that two parameters in the type signature are of the same type: + func terraform(_ planetaryBody: Body, into terraformedBody: Body) { + ... + } - // Also fine, since the generic parameter name is referenced in the function body so can't be removed: - func terraform(_ planetaryBody: Body) { - planetaryBody.generateAtmosphere(Body.idealAtmosphere) - } - ``` + // Also fine, since the generic parameter name is referenced in the function body so can't be removed: + func terraform(_ planetaryBody: Body) { + planetaryBody.generateAtmosphere(Body.idealAtmosphere) + } + ``` - #### `some Any` + #### `some Any` - Fully-unconstrained generic parameters are somewhat uncommon, but are equivalent to `some Any`. For example: + Fully-unconstrained generic parameters are somewhat uncommon, but are equivalent to `some Any`. For example: - ```swift - func assertFailure( - _ result: Result, - file: StaticString = #filePath, - line: UInt = #line - ) { - if case .failure(let error) = result { - XCTFail(error.localizedDescription, file: file, line: line) - } + ```swift + func assertFailure( + _ result: Result, + file: StaticString = #filePath, + line: UInt = #line + ) { + if case .failure(let error) = result { + XCTFail(error.localizedDescription, file: file, line: line) } + } - // is equivalent to: - func assertFailure( - _ result: Result, - file: StaticString = #filePath, - line: UInt = #line - ) { - if case .failure(let error) = result { - XCTFail(error.localizedDescription, file: file, line: line) - } + // is equivalent to: + func assertFailure( + _ result: Result, + file: StaticString = #filePath, + line: UInt = #line + ) { + if case .failure(let error) = result { + XCTFail(error.localizedDescription, file: file, line: line) } - ``` + } + ``` - `some Any` is somewhat unintuitive, and the named generic parameter is useful in this situation to compensate for the weak type information. Because of this, prefer using named generic parameters instead of `some Any`. + `some Any` is somewhat unintuitive, and the named generic parameter is useful in this situation to compensate for the weak type information. Because of this, prefer using named generic parameters instead of `some Any`. -
+
-* (link) **Prefer to avoid using `@unchecked Sendable`**. Use a standard `Sendable` conformance instead where possible. If working with a type from a module that has not yet been updated to support Swift Concurrency, suppress concurrency-related errors using `@preconcurrency import`. +- (link) **Prefer to avoid using `@unchecked Sendable`**. Use a standard `Sendable` conformance instead where possible. If working with a type from a module that has not yet been updated to support Swift Concurrency, suppress concurrency-related errors using `@preconcurrency import`. -
+
- `@unchecked Sendable` provides no guarantees about the thread safety of a type, and instead unsafely suppresses compiler errors related to concurrency checking. + `@unchecked Sendable` provides no guarantees about the thread safety of a type, and instead unsafely suppresses compiler errors related to concurrency checking. - There are typically other, safer methods for suppressing concurrency-related errors: + There are typically other, safer methods for suppressing concurrency-related errors: - ### 1. Use `Sendable` instead of `@unchecked Sendable`, with `@MainActor` if appropriate + ### 1. Use `Sendable` instead of `@unchecked Sendable`, with `@MainActor` if appropriate - A `Sendable` conformance is the preferred way to declare that a type is thread-safe. The compiler will emit an error if a type conforming to `Sendable` is not thread-safe. For example, simple value types and immutable classes can always safely conform to `Sendable`, but mutable classes cannot: + A `Sendable` conformance is the preferred way to declare that a type is thread-safe. The compiler will emit an error if a type conforming to `Sendable` is not thread-safe. For example, simple value types and immutable classes can always safely conform to `Sendable`, but mutable classes cannot: - ```swift - // RIGHT: Simple value types are thread-safe. - struct Planet: Sendable { - var mass: Double - } + ```swift + // RIGHT: Simple value types are thread-safe. + struct Planet: Sendable { + var mass: Double + } - // RIGHT: Immutable classes are thread-safe. - final class Planet: Sendable { - let mass: Double - } + // RIGHT: Immutable classes are thread-safe. + final class Planet: Sendable { + let mass: Double + } - // WRONG: Mutable classes are not thread-safe. - final class Planet: Sendable { - // ERROR: stored property 'mass' of 'Sendable'-conforming class 'Planet' is mutable - var mass: Double - } + // WRONG: Mutable classes are not thread-safe. + final class Planet: Sendable { + // ERROR: stored property 'mass' of 'Sendable'-conforming class 'Planet' is mutable + var mass: Double + } - // WRONG: @unchecked is unnecessary because the compiler can prove that the type is thread-safe. - struct Planet: @unchecked Sendable { - var mass: Double - } - ``` + // WRONG: @unchecked is unnecessary because the compiler can prove that the type is thread-safe. + struct Planet: @unchecked Sendable { + var mass: Double + } + ``` - Mutable classes can be made `Sendable` and thread-safe if they are isolated to a single actor / thread / concurrency domain. Any mutable class can be made `Sendable` by isolating it to a global actor using an annotation like `@MainActor` (which isolates it to the main actor): + Mutable classes can be made `Sendable` and thread-safe if they are isolated to a single actor / thread / concurrency domain. Any mutable class can be made `Sendable` by isolating it to a global actor using an annotation like `@MainActor` (which isolates it to the main actor): - ```swift - // RIGHT: A mutable class isolated to the main actor is thread-safe. - @MainActor - final class Planet: Sendable { - var mass: Double - } + ```swift + // RIGHT: A mutable class isolated to the main actor is thread-safe. + @MainActor + final class Planet: Sendable { + var mass: Double + } - // WRONG: @unchecked Sendable is unsafe because mutable classes are not thread-safe. - struct Planet: @unchecked Sendable { - var mass: Double - } - ``` + // WRONG: @unchecked Sendable is unsafe because mutable classes are not thread-safe. + struct Planet: @unchecked Sendable { + var mass: Double + } + ``` - ### 2. Use `@preconcurrency import` + ### 2. Use `@preconcurrency import` - If working with a non-`Sendable` type from a module that hasn't yet adopted Swift concurrency, suppress concurrency-related errors using `@preconcurrency import`. + If working with a non-`Sendable` type from a module that hasn't yet adopted Swift concurrency, suppress concurrency-related errors using `@preconcurrency import`. - ```swift - /// Defined in `UniverseKit` module - class Planet: PlanetaryBody { - var star: Star - } - ``` + ```swift + /// Defined in `UniverseKit` module + class Planet: PlanetaryBody { + var star: Star + } + ``` - ```swift - // WRONG: Unsafely marking a non-thread-safe class as Sendable only to suppress errors - import PlanetaryBody + ```swift + // WRONG: Unsafely marking a non-thread-safe class as Sendable only to suppress errors + import PlanetaryBody - extension PlanetaryBody: @unchecked Sendable { } + extension PlanetaryBody: @unchecked Sendable { } - // RIGHT - @preconcurrency import PlanetaryBody - ``` + // RIGHT + @preconcurrency import PlanetaryBody + ``` - ### 3. Restructure code so the compiler can verify that it is thread-safe + ### 3. Restructure code so the compiler can verify that it is thread-safe - If possible, restructure code so that the compiler can verify that it is thread safe. This lets you use a `Sendable` conformance instead of an unsafe `@unchecked Sendable` conformance. + If possible, restructure code so that the compiler can verify that it is thread safe. This lets you use a `Sendable` conformance instead of an unsafe `@unchecked Sendable` conformance. - When conforming to `Sendable`, the compiler will emit an error in the future if you attempt to make a change that is not thread-safe. This guarantee is lost when using `@unchecked Sendable`, which makes it easier to accidentally introduce changes which are not thread-safe. + When conforming to `Sendable`, the compiler will emit an error in the future if you attempt to make a change that is not thread-safe. This guarantee is lost when using `@unchecked Sendable`, which makes it easier to accidentally introduce changes which are not thread-safe. - For example, given this set of classes: + For example, given this set of classes: - ```swift - class PlanetaryBody { - let mass: Double - } + ```swift + class PlanetaryBody { + let mass: Double + } - class Planet: PlanetaryBody { - let star: Star - } + class Planet: PlanetaryBody { + let star: Star + } - // NOT IDEAL: no compiler-enforced thread safety. - extension PlanetaryBody: @unchecked Sendable { } - ``` + // NOT IDEAL: no compiler-enforced thread safety. + extension PlanetaryBody: @unchecked Sendable { } + ``` - the compiler can't verify `PlanetaryBody` is `Sendable` because it is not `final`. Instead of using `@unchecked Sendable`, you could restructure the code to not use subclassing: + the compiler can't verify `PlanetaryBody` is `Sendable` because it is not `final`. Instead of using `@unchecked Sendable`, you could restructure the code to not use subclassing: - ```swift - // BETTER: Compiler-enforced thread safety. - protocol PlanetaryBody: Sendable { - var mass: Double { get } - } + ```swift + // BETTER: Compiler-enforced thread safety. + protocol PlanetaryBody: Sendable { + var mass: Double { get } + } - final class Planet: PlanetaryBody, Sendable { - let mass: Double - let star: Star - } - ``` + final class Planet: PlanetaryBody, Sendable { + let mass: Double + let star: Star + } + ``` - ### Using `@unchecked Sendable` when necessary + ### Using `@unchecked Sendable` when necessary - Sometimes it is truly necessary to use `@unchecked Sendable`. In these cases, you can add a `// swiftlint:disable:next no_unchecked_sendable` annotation with an explanation for how we know the type is thread-safe, and why we have to use `@unchecked Sendable` instead of `Sendable`. + Sometimes it is truly necessary to use `@unchecked Sendable`. In these cases, you can add a `// swiftlint:disable:next no_unchecked_sendable` annotation with an explanation for how we know the type is thread-safe, and why we have to use `@unchecked Sendable` instead of `Sendable`. - A canonical, safe use case of `@unchecked Sendable` is a class where the mutable state is protected by some other thread-safe mechanism like a lock. This type is thread-safe, but the compiler cannot verify this. + A canonical, safe use case of `@unchecked Sendable` is a class where the mutable state is protected by some other thread-safe mechanism like a lock. This type is thread-safe, but the compiler cannot verify this. - ```swift - struct Atomic { - /// `value` is thread-safe because it is manually protected by a lock. - var value: Value { ... } - } + ```swift + struct Atomic { + /// `value` is thread-safe because it is manually protected by a lock. + var value: Value { ... } + } - // WRONG: disallowed by linter - extension Atomic: @unchecked Sendable { } + // WRONG: disallowed by linter + extension Atomic: @unchecked Sendable { } - // WRONG: suppressing lint error without an explanation - // swiftlint:disable:next no_unchecked_sendable - extension Atomic: @unchecked Sendable { } + // WRONG: suppressing lint error without an explanation + // swiftlint:disable:next no_unchecked_sendable + extension Atomic: @unchecked Sendable { } - // RIGHT: suppressing the linter with an explanation why the type is thread-safe - // Atomic is thread-safe because its underlying mutable state is protected by a lock. - // swiftlint:disable:next no_unchecked_sendable - extension Atomic: @unchecked Sendable { } - ``` + // RIGHT: suppressing the linter with an explanation why the type is thread-safe + // Atomic is thread-safe because its underlying mutable state is protected by a lock. + // swiftlint:disable:next no_unchecked_sendable + extension Atomic: @unchecked Sendable { } + ``` - It is also reasonable to use `@unchecked Sendable` for types that are thread-safe in existing usage but can't be refactored to support a proper `Sendable` conformance (e.g. due to backwards compatibility constraints): + It is also reasonable to use `@unchecked Sendable` for types that are thread-safe in existing usage but can't be refactored to support a proper `Sendable` conformance (e.g. due to backwards compatibility constraints): - ```swift - class PlanetaryBody { - let mass: Double - } + ```swift + class PlanetaryBody { + let mass: Double + } - class Planet: PlanetaryBody { - let star: Star - } + class Planet: PlanetaryBody { + let star: Star + } - // WRONG: disallowed by linter - extension PlanetaryBody: @unchecked Sendable { } + // WRONG: disallowed by linter + extension PlanetaryBody: @unchecked Sendable { } - // WRONG: suppressing lint error without an explanation - // swiftlint:disable:next no_unchecked_sendable - extension PlanetaryBody: @unchecked Sendable { } + // WRONG: suppressing lint error without an explanation + // swiftlint:disable:next no_unchecked_sendable + extension PlanetaryBody: @unchecked Sendable { } - // RIGHT: suppressing the linter with an explanation why the type is thread-safe - // PlanetaryBody cannot conform to Sendable because it is non-final and has subclasses. - // PlanetaryBody itself is safely Sendable because it only consists of immutable values. - // All subclasses of PlanetaryBody are also simple immutable values, so are safely Sendable as well. - // swiftlint:disable:next no_unchecked_sendable - extension PlanetaryBody: @unchecked Sendable { } - ``` + // RIGHT: suppressing the linter with an explanation why the type is thread-safe + // PlanetaryBody cannot conform to Sendable because it is non-final and has subclasses. + // PlanetaryBody itself is safely Sendable because it only consists of immutable values. + // All subclasses of PlanetaryBody are also simple immutable values, so are safely Sendable as well. + // swiftlint:disable:next no_unchecked_sendable + extension PlanetaryBody: @unchecked Sendable { } + ``` -
+
-* (link) **Avoid defining properties that are then returned immediately.** Instead, return the value directly. +- (link) **Avoid defining properties that are then returned immediately.** Instead, return the value directly. -
+
[![SwiftFormat: redundantProperty](https://img.shields.io/badge/SwiftFormat-redundantProperty-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantProperty) - ### Why? + ### Why? - Property declarations that are immediately returned are typically redundant and unnecessary. Sometimes these are unintentionally created as the byproduct of refactoring. Cleaning them up automatically simplifies the code. In some cases this also results in the `return` keyword itself being unnecessary, further simplifying the code. + Property declarations that are immediately returned are typically redundant and unnecessary. Sometimes these are unintentionally created as the byproduct of refactoring. Cleaning them up automatically simplifies the code. In some cases this also results in the `return` keyword itself being unnecessary, further simplifying the code. - ```swift - // WRONG - var spaceship: Spaceship { - let spaceship = spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) - return spaceship - } + ```swift + // WRONG + var spaceship: Spaceship { + let spaceship = spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) + return spaceship + } - // RIGHT - var spaceship: Spaceship { - spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) - } + // RIGHT + var spaceship: Spaceship { + spaceshipBuilder.build(warpDrive: warpDriveBuilder.build()) + } - // WRONG - var spaceship: Spaceship { - let warpDrive = warpDriveBuilder.build() - let spaceship = spaceshipBuilder.build(warpDrive: warpDrive) - return spaceship - } + // WRONG + var spaceship: Spaceship { + let warpDrive = warpDriveBuilder.build() + let spaceship = spaceshipBuilder.build(warpDrive: warpDrive) + return spaceship + } - // RIGHT - var spaceship: Spaceship { - let warpDrive = warpDriveBuilder.build() - return spaceshipBuilder.build(warpDrive: warpDrive) - } - ``` + // RIGHT + var spaceship: Spaceship { + let warpDrive = warpDriveBuilder.build() + return spaceshipBuilder.build(warpDrive: warpDrive) + } + ``` -
+
-* (link) **Prefer using a generated Equatable implementation when comparing all properties of a type.** For structs, prefer using the compiler-synthesized Equatable implementation when possible. +- (link) **Prefer using a generated Equatable implementation when comparing all properties of a type.** For structs, prefer using the compiler-synthesized Equatable implementation when possible. -
+
[![SwiftFormat: redundantEquatable](https://img.shields.io/badge/SwiftFormat-redundantEquatable-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundantEquatable) - ### Why? + ### Why? - Manually-implemented Equatable implementations are verbose, and keeping them up-to-date is error-prone. For example, when adding a new property, it's possible to forget to update the Equatable implementation to compare it. + Manually-implemented Equatable implementations are verbose, and keeping them up-to-date is error-prone. For example, when adding a new property, it's possible to forget to update the Equatable implementation to compare it. - ```swift - /// WRONG: The `static func ==` implementation is redundant and error-prone. - struct Planet: Equatable { - let mass: Double - let orbit: OrbitalElements - let rotation: Double + ```swift + /// WRONG: The `static func ==` implementation is redundant and error-prone. + struct Planet: Equatable { + let mass: Double + let orbit: OrbitalElements + let rotation: Double - static func ==(lhs: Planet, rhs: Planet) -> Bool { - lhs.mass == rhs.mass - && lhs.orbit == rhs.orbit - && lhs.rotation == rhs.rotation - } + static func ==(lhs: Planet, rhs: Planet) -> Bool { + lhs.mass == rhs.mass + && lhs.orbit == rhs.orbit + && lhs.rotation == rhs.rotation } + } - /// RIGHT: The `static func ==` implementation is synthesized by the compiler. - struct Planet: Equatable { - let mass: Double - let orbit: OrbitalElements - let rotation: Double - } + /// RIGHT: The `static func ==` implementation is synthesized by the compiler. + struct Planet: Equatable { + let mass: Double + let orbit: OrbitalElements + let rotation: Double + } - /// ALSO RIGHT: The `static func ==` implementation differs from the implementation that - /// would be synthesized by the compiler and compared all properties, so is not redundant. - struct CelestialBody: Equatable { - let id: UUID - let orbit: OrbitalElements + /// ALSO RIGHT: The `static func ==` implementation differs from the implementation that + /// would be synthesized by the compiler and compared all properties, so is not redundant. + struct CelestialBody: Equatable { + let id: UUID + let orbit: OrbitalElements - static func ==(lhs: Planet, rhs: Planet) -> Bool { - lhs.id == rhs.id - } + static func ==(lhs: Planet, rhs: Planet) -> Bool { + lhs.id == rhs.id } - ``` + } + ``` - In projects that provide an `@Equatable` macro, prefer using that macro to generate the `static func ==` for classes rather than implementing it manually. + In projects that provide an `@Equatable` macro, prefer using that macro to generate the `static func ==` for classes rather than implementing it manually. - ```swift - /// WRONG: The `static func ==` implementation is verbose and error-prone. - final class Planet: Equatable { - let mass: Double - let orbit: OrbitalElements - let rotation: Double + ```swift + /// WRONG: The `static func ==` implementation is verbose and error-prone. + final class Planet: Equatable { + let mass: Double + let orbit: OrbitalElements + let rotation: Double - static func ==(lhs: Planet, rhs: Planet) -> Bool { - lhs.mass == rhs.mass - && lhs.orbit == rhs.orbit - && lhs.rotation == rhs.rotation - } + static func ==(lhs: Planet, rhs: Planet) -> Bool { + lhs.mass == rhs.mass + && lhs.orbit == rhs.orbit + && lhs.rotation == rhs.rotation } + } - /// RIGHT: The `static func ==` implementation is generated by the `@Equatable` macro. - @Equatable - final class struct Planet: Equatable { - let mass: Double - let orbit: OrbitalElements - let rotation: Double - } - ``` + /// RIGHT: The `static func ==` implementation is generated by the `@Equatable` macro. + @Equatable + final class struct Planet: Equatable { + let mass: Double + let orbit: OrbitalElements + let rotation: Double + } + ``` -
+
-* (link) **Prefer using the `@Entry` macro to define properties inside `EnvironmentValues`**. When adding properties to SwiftUI `EnvironmentValues`, prefer using the compiler-synthesized property implementation when possible. +- (link) **Prefer using the `@Entry` macro to define properties inside `EnvironmentValues`**. When adding properties to SwiftUI `EnvironmentValues`, prefer using the compiler-synthesized property implementation when possible. -
+
[![SwiftFormat: environmentEntry](https://img.shields.io/badge/SwiftFormat-environmentEntry-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/develop/Rules.md#environmentEntry) - ### Why? + ### Why? - Manually-implemented environment keys are verbose and it is considered a legacy pattern. `@Entry` was specifically intended to be a replacement considering it was backported to iOS 13. + Manually-implemented environment keys are verbose and it is considered a legacy pattern. `@Entry` was specifically intended to be a replacement considering it was backported to iOS 13. - ```swift - /// WRONG: The `EnvironmentValues` property depends on `IsSelectedEnvironmentKey` - struct IsSelectedEnvironmentKey: EnvironmentKey { - static var defaultValue: Bool { false } - } + ```swift + /// WRONG: The `EnvironmentValues` property depends on `IsSelectedEnvironmentKey` + struct IsSelectedEnvironmentKey: EnvironmentKey { + static var defaultValue: Bool { false } + } - extension EnvironmentValues { - var isSelected: Bool { - get { self[IsSelectedEnvironmentKey.self] } - set { self[IsSelectedEnvironmentKey.self] = newValue } - } + extension EnvironmentValues { + var isSelected: Bool { + get { self[IsSelectedEnvironmentKey.self] } + set { self[IsSelectedEnvironmentKey.self] = newValue } } + } - /// RIGHT: The `EnvironmentValues` property uses the @Entry macro - extension EnvironmentValues { - @Entry var isSelected: Bool = false - } - ``` + /// RIGHT: The `EnvironmentValues` property uses the @Entry macro + extension EnvironmentValues { + @Entry var isSelected: Bool = false + } + ``` -
+
-* (link) **Avoid using `()` as a type**. Prefer `Void`. +- (link) **Avoid using `()` as a type**. Prefer `Void`.
@@ -4291,9 +4307,10 @@ _You can enable the following settings in Xcode by running [this script](resourc // RIGHT let result: Result ``` +
-* (link) **Avoid using `Void()` as an instance of `Void`**. Prefer `()`. +- (link) **Avoid using `Void()` as an instance of `Void`**. Prefer `()`.
@@ -4308,9 +4325,10 @@ _You can enable the following settings in Xcode by running [this script](resourc // RIGHT completion(.success(())) ``` +
-* (link) **Prefer using `count(where: { ... })` over `filter { ... }.count`**. +- (link) **Prefer using `count(where: { ... })` over `filter { ... }.count`**.
@@ -4325,32 +4343,34 @@ _You can enable the following settings in Xcode by running [this script](resourc // RIGHT let planetsWithMoons = planets.count(where: { !$0.moons.isEmpty }) ``` +
-* (link) **If available in your project, prefer using a `#URL(_:)` macro instead of force-unwrapping `URL(string:)!` initializer`**. +- (link) **If available in your project, prefer using a `#URL(_:)` macro instead of force-unwrapping `URL(string:)!` initializer**. -
+
[![SwiftFormat: urlMacro](https://img.shields.io/badge/SwiftFormat-urlMacro-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#urlMacro) - #### Why? + #### Why? - The `#URL` macro provides compile-time validation of URL strings, eliminating runtime crashes from invalid URLs while maintaining clean syntax for static URL creation. + The `#URL` macro provides compile-time validation of URL strings, eliminating runtime crashes from invalid URLs while maintaining clean syntax for static URL creation. - ```swift - // WRONG - let url = URL(string: "https://example.com")! + ```swift + // WRONG + let url = URL(string: "https://example.com")! - // RIGHT - let url = #URL("https://example.com") - ``` -
+ // RIGHT + let url = #URL("https://example.com") + ``` + +
**[⬆ back to top](#table-of-contents)** ## File Organization -* (link) **Alphabetize and deduplicate module imports within a file. Place all imports at the top of the file below the header comments. Do not add additional line breaks between import statements. Add a single empty line before the first import and after the last import.** +- (link) **Alphabetize and deduplicate module imports within a file.** Place all imports at the top of the file below the header comments. Do not add additional line breaks between import statements. Add a single empty line before the first import and after the last import.
@@ -4383,12 +4403,8 @@ _You can enable the following settings in Xcode by running [this script](resourc import Foundation ``` -
- _Exception: `@testable import` should be grouped after the regular import and separated by an empty line._ -
- ```swift // WRONG @@ -4416,7 +4432,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Limit consecutive whitespace to one blank line or space (excluding indentation).** Favor the following formatting guidelines over whitespace of varying heights or widths. +- (link) **Limit consecutive whitespace to one blank line or space (excluding indentation).** Favor the following formatting guidelines over whitespace of varying heights or widths.
@@ -4447,8 +4463,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
- -* (link) **Files should end in a newline.** +- (link) **Files should end in a newline.**
@@ -4456,13 +4471,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Declarations that include scopes spanning multiple lines should be separated from adjacent declarations in the same scope by a newline.** Insert a single blank line between multi-line scoped declarations (e.g. types, extensions, functions, computed properties, etc.) and other declarations at the same indentation level. +- (link) **Declarations that include scopes spanning multiple lines should be separated from adjacent declarations in the same scope by a newline.** Insert a single blank line between multi-line scoped declarations (e.g. types, extensions, functions, computed properties, etc.) and other declarations at the same indentation level.
[![SwiftFormat: blankLinesBetweenScopes](https://img.shields.io/badge/SwiftFormat-blankLinesBetweenScopes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#blankLinesBetweenScopes) #### Why? + Dividing scoped declarations from other declarations at the same scope visually separates them, making adjacent declarations easier to differentiate from the scoped declaration. ```swift @@ -4508,7 +4524,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Remove blank lines at the top and bottom of scopes**, excluding type bodies which can optionally include blank lines. +- (link) **Remove blank lines at the top and bottom of scopes**, excluding type bodies which can optionally include blank lines.
@@ -4546,14 +4562,13 @@ _You can enable the following settings in Xcode by running [this script](resourc
- -* (link) **Each type and extension which implements a conformance should be preceded by a `MARK` comment.** - * Types should be preceded by a `// MARK: - TypeName` comment. - * Extensions that add a conformance should be preceded by a `// MARK: - TypeName + ProtocolName` comment. - * Extensions that immediately follow the type being extended should omit that type's name and instead use `// MARK: ProtocolName`. - * If there is only one type or extension in a file, the `MARK` comment can be omitted. - * If the extension in question is empty (e.g. has no declarations in its body), the `MARK` comment can be omitted. - * For extensions that do not add new conformances, consider adding a `MARK` with a descriptive comment. +- (link) **Each type and extension which implements a conformance should be preceded by a `MARK` comment.** + - Types should be preceded by a `// MARK: - TypeName` comment. + - Extensions that add a conformance should be preceded by a `// MARK: - TypeName + ProtocolName` comment. + - Extensions that immediately follow the type being extended should omit that type's name and instead use `// MARK: ProtocolName`. + - If there is only one type or extension in a file, the `MARK` comment can be omitted. + - If the extension in question is empty (e.g. has no declarations in its body), the `MARK` comment can be omitted. + - For extensions that do not add new conformances, consider adding a `MARK` with a descriptive comment.
@@ -4575,18 +4590,18 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Use `// MARK:` to separate the contents of type definitions and extensions into the sections listed below, in order.** All type definitions and extensions should be divided up in this consistent way, allowing a reader of your code to easily jump to what they are interested in. - * `// MARK: Lifecycle` for `init` and `deinit` methods. - * `// MARK: Open` for `open` properties and methods. - * `// MARK: Public` for `public` properties and methods. - * `// MARK: Package` for `package` properties and methods. - * `// MARK: Internal` for `internal` properties and methods. - * `// MARK: Fileprivate` for `fileprivate` properties and methods. - * `// MARK: Private` for `private` properties and methods. - * If the type in question is an enum, its cases should go above the first `// MARK:`. - * Do not subdivide each of these sections into subsections, as it makes the method dropdown more cluttered and therefore less useful. Instead, group methods by functionality and use smart naming to make clear which methods are related. If there are enough methods that sub-sections seem necessary, consider refactoring your code into multiple types. - * If all of the type or extension's definitions belong to the same category (e.g. the type or extension only consists of `internal` properties), it is OK to omit the `// MARK:`s. - * If the type in question is a simple value type (e.g. fewer than 20 lines), it is OK to omit the `// MARK:`s, as it would hurt legibility. +- (link) **Use `// MARK:` to separate the contents of type definitions and extensions into the sections listed below, in order.** All type definitions and extensions should be divided up in this consistent way, allowing a reader of your code to easily jump to what they are interested in. + - `// MARK: Lifecycle` for `init` and `deinit` methods. + - `// MARK: Open` for `open` properties and methods. + - `// MARK: Public` for `public` properties and methods. + - `// MARK: Package` for `package` properties and methods. + - `// MARK: Internal` for `internal` properties and methods. + - `// MARK: Fileprivate` for `fileprivate` properties and methods. + - `// MARK: Private` for `private` properties and methods. + - If the type in question is an enum, its cases should go above the first `// MARK:`. + - Do not subdivide each of these sections into subsections, as it makes the method dropdown more cluttered and therefore less useful. Instead, group methods by functionality and use smart naming to make clear which methods are related. If there are enough methods that sub-sections seem necessary, consider refactoring your code into multiple types. + - If all of the type or extension's definitions belong to the same category (e.g. the type or extension only consists of `internal` properties), it is OK to omit the `// MARK:`s. + - If the type in question is a simple value type (e.g. fewer than 20 lines), it is OK to omit the `// MARK:`s, as it would hurt legibility.
@@ -4594,141 +4609,140 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Within each top-level section, place content in the following order.** This allows a new reader of your code to more easily find what they are looking for. - * Nested types and type aliases - * Static properties - * Static property with body - * Class properties with body - * SwiftUI dynamic properties (@State, @Environment, @Binding, etc), grouped by type - * Instance properties - * Instance properties with body - * Static methods - * Class methods - * Instance methods +- (link) **Within each top-level section, place content in the following order.** This allows a new reader of your code to more easily find what they are looking for. + - Nested types and type aliases + - Static properties + - Static property with body + - Class properties with body + - SwiftUI dynamic properties (@State, @Environment, @Binding, etc), grouped by type + - Instance properties + - Instance properties with body + - Static methods + - Class methods + - Instance methods
[![SwiftFormat: organizeDeclarations](https://img.shields.io/badge/SwiftFormat-organizeDeclarations-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#organizeDeclarations) - Computed properties and properties with property observers should appear at the end of the set of declarations of the same kind. (e.g. instance properties.) + Computed properties and properties with property observers should appear at the end of the set of declarations of the same kind. (e.g. instance properties.) - ```swift - // WRONG - class PlanetView: UIView { + ```swift + // WRONG + class PlanetView: UIView { - static var startOfTime { -CGFloat.greatestFiniteMagnitude / 0 } + static var startOfTime { -CGFloat.greatestFiniteMagnitude / 0 } - var atmosphere: Atmosphere { - didSet { - print("oh my god, the atmosphere changed") - } + var atmosphere: Atmosphere { + didSet { + print("oh my god, the atmosphere changed") } + } - override class var layerClass: AnyClass { - PlanetLayer.self - } + override class var layerClass: AnyClass { + PlanetLayer.self + } - var gravity: CGFloat + var gravity: CGFloat - static let speedOfLight: CGFloat = 300_000 - } + static let speedOfLight: CGFloat = 300_000 + } - // RIGHT - class PlanetView: UIView { + // RIGHT + class PlanetView: UIView { - static let speedOfLight: CGFloat = 300_000 - static var startOfTime { -CGFloat.greatestFiniteMagnitude / 0 } + static let speedOfLight: CGFloat = 300_000 + static var startOfTime { -CGFloat.greatestFiniteMagnitude / 0 } - override class var layerClass: AnyClass { - PlanetLayer.self - } + override class var layerClass: AnyClass { + PlanetLayer.self + } - var gravity: CGFloat - var atmosphere: Atmosphere { - didSet { - print("oh my god, the atmosphere changed") - } + var gravity: CGFloat + var atmosphere: Atmosphere { + didSet { + print("oh my god, the atmosphere changed") } - } - ``` + } + } + ``` - SwiftUI Properties are a special type of property that lives inside SwiftUI views. These views conform to the [`DynamicProperty`](https://developer.apple.com/documentation/swiftui/dynamicproperty) protocol and cause the view's body to re-compute. Given this common functionality and also a similar syntax, it is preferred to group them. + SwiftUI Properties are a special type of property that lives inside SwiftUI views. These views conform to the [`DynamicProperty`](https://developer.apple.com/documentation/swiftui/dynamicproperty) protocol and cause the view's body to re-compute. Given this common functionality and also a similar syntax, it is preferred to group them. - ```swift - // WRONG + ```swift + // WRONG - struct CustomSlider: View { + struct CustomSlider: View { - // MARK: Internal + // MARK: Internal - var body: some View { - ... - } + var body: some View { + ... + } - // MARK: Private + // MARK: Private - @Binding private var value: Value - private let range: ClosedRange - @Environment(\.sliderStyle) private var style - private let step: Double.Stride - @Environment(\.layoutDirection) private var layoutDirection - } + @Binding private var value: Value + private let range: ClosedRange + @Environment(\.sliderStyle) private var style + private let step: Double.Stride + @Environment(\.layoutDirection) private var layoutDirection + } - // RIGHT + // RIGHT - struct CustomSlider: View { + struct CustomSlider: View { - // MARK: Internal + // MARK: Internal - var body: some View { - ... - } + var body: some View { + ... + } - // MARK: Private + // MARK: Private - @Environment(\.sliderStyle) private var style - @Environment(\.layoutDirection) private var layoutDirection - @Binding private var value: Value + @Environment(\.sliderStyle) private var style + @Environment(\.layoutDirection) private var layoutDirection + @Binding private var value: Value - private let range: ClosedRange - private let step: Double.Stride - } - ``` + private let range: ClosedRange + private let step: Double.Stride + } + ``` - Additionally, within the grouping of SwiftUI properties, it is preferred that the properties are also grouped by their dynamic property type. The group order applied by the formatter is determined by the first time a type appears: + Additionally, within the grouping of SwiftUI properties, it is preferred that the properties are also grouped by their dynamic property type. The group order applied by the formatter is determined by the first time a type appears: - ```swift - // WRONG - struct CustomSlider: View { + ```swift + // WRONG + struct CustomSlider: View { - @Binding private var value: Value - @State private var foo = Foo() - @Environment(\.sliderStyle) private var style - @State private var bar = Bar() - @Environment(\.layoutDirection) private var layoutDirection + @Binding private var value: Value + @State private var foo = Foo() + @Environment(\.sliderStyle) private var style + @State private var bar = Bar() + @Environment(\.layoutDirection) private var layoutDirection - private let range: ClosedRange - private let step: Double.Stride - } + private let range: ClosedRange + private let step: Double.Stride + } - // RIGHT - struct CustomSlider: View { + // RIGHT + struct CustomSlider: View { - @Binding private var value: Value - @State private var foo = Foo() - @State private var bar = Bar() - @Environment(\.sliderStyle) private var style - @Environment(\.layoutDirection) private var layoutDirection + @Binding private var value: Value + @State private var foo = Foo() + @State private var bar = Bar() + @Environment(\.sliderStyle) private var style + @Environment(\.layoutDirection) private var layoutDirection - private let range: ClosedRange - private let step: Double.Stride - } - ``` + private let range: ClosedRange + private let step: Double.Stride + } + ```
- -* (link) **Add empty lines between property declarations of different kinds.** (e.g. between static properties and instance properties.) +- (link) **Add empty lines between property declarations of different kinds.** (e.g. between static properties and instance properties.)
@@ -4749,15 +4763,15 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Only define a single property or enum case per line.** +- (link) **Only define a single property or enum case per line.**
[![SwiftFormat: singlePropertyPerLine](https://img.shields.io/badge/SwiftFormat-singlePropertyPerLine-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#singlePropertyPerLine) [![SwiftFormat: wrapEnumCases](https://img.shields.io/badge/SwiftFormat-wrapEnumCases-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#wrapEnumCases) #### Why? - - Declarations that define a single property are much more common, and more idiomatic. - - Only using the standard form of property declarations makes it easier to write and maintain tools that operate on source code, like macros, lint rules, and code autocorrect. + - Declarations that define a single property are much more common, and more idiomatic. + - Only using the standard form of property declarations makes it easier to write and maintain tools that operate on source code, like macros, lint rules, and code autocorrect. ```swift // WRONG @@ -4792,13 +4806,14 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Remove empty extensions that define no properties, functions, or conformances.** +- (link) **Remove empty extensions that define no properties, functions, or conformances.**
[![SwiftFormat: emptyExtensions](https://img.shields.io/badge/SwiftFormat-emptyExtensions-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#emptyExtensions) #### Why? + Improves readability since the code has no effect and should be removed for clarity. ```swift @@ -4817,7 +4832,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ## Objective-C Interoperability -* (link) **Prefer pure Swift classes over subclasses of NSObject.** If your code needs to be used by some Objective-C code, wrap it to expose the desired functionality. Use `@objc` on individual methods and variables as necessary rather than exposing all API on a class to Objective-C via `@objcMembers`. +- (link) **Prefer pure Swift classes over subclasses of NSObject.** If your code needs to be used by some Objective-C code, wrap it to expose the desired functionality. Use `@objc` on individual methods and variables as necessary rather than exposing all API on a class to Objective-C via `@objcMembers`.
@@ -4847,7 +4862,7 @@ _You can enable the following settings in Xcode by running [this script](resourc ## Testing -* (link) **In Swift Testing, don't prefix test case methods with "`test`".** +- (link) **In Swift Testing, don't prefix test case methods with "`test`".**
@@ -4878,9 +4893,10 @@ _You can enable the following settings in Xcode by running [this script](resourc func artificialGravityMatchesEarthGravity() { ... } } ``` +
-* (link) **Avoid `guard` statements in unit tests**. XCTest and Swift Testing have APIs for unwrapping an optional and failing the test, which are much simpler than unwrapping the optionals yourself. Use assertions instead of guarding on boolean conditions. +- (link) **Avoid `guard` statements in unit tests**. XCTest and Swift Testing have APIs for unwrapping an optional and failing the test, which are much simpler than unwrapping the optionals yourself. Use assertions instead of guarding on boolean conditions.
@@ -4924,7 +4940,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Prefer throwing tests to `try!`**. `try!` will crash your test suite like a force-unwrapped optional. XCTest and Swift Testing support throwing test methods, so use that instead. +- (link) **Prefer throwing tests to `try!`**. `try!` will crash your test suite like a force-unwrapped optional. XCTest and Swift Testing support throwing test methods, so use that instead.
@@ -4963,15 +4979,17 @@ _You can enable the following settings in Xcode by running [this script](resourc } } ``` +
-* (link) **In test suites, test cases should be `internal`, and helper methods and properties should be `private`**. +- (link) **In test suites, test cases should be `internal`, and helper methods and properties should be `private`**.
[![SwiftFormat: testSuiteAccessControl](https://img.shields.io/badge/SwiftFormat-testSuiteAccessControl-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#testSuiteAccessControl) #### Why? + Test suites and test cases don't need to be `public` to be picked up by XCTest / Swift Testing, so should be `internal`. Helpers and stored properties should be `private` since they are not accessed outside of the test suite. @@ -5056,7 +5074,7 @@ _You can enable the following settings in Xcode by running [this script](resourc
-* (link) **Avoid force-unwrapping in unit tests**. Force-unwrapping (`!`) will crash your test suite. Use safe alternatives like `try XCTUnwrap` or `try #require`, which will throw an error instead, or standard optional unwrapping (`?`). +- (link) **Avoid force-unwrapping in unit tests**. Force-unwrapping (`!`) will crash your test suite. Use safe alternatives like `try XCTUnwrap` or `try #require`, which will throw an error instead, or standard optional unwrapping (`?`).
@@ -5115,9 +5133,10 @@ _You can enable the following settings in Xcode by running [this script](resourc } } ``` +
-* (link) **Remove redundant `throws` and `async` effects from test cases**. If a test case doesn't throw any errors, or doesn't `await` any `async` method calls, then `throws` and `async` are redundant. +- (link) **Remove redundant `throws` and `async` effects from test cases**. If a test case doesn't throw any errors, or doesn't `await` any `async` method calls, then `throws` and `async` are redundant.
@@ -5160,13 +5179,14 @@ _You can enable the following settings in Xcode by running [this script](resourc } } ``` +
**[⬆ back to top](#table-of-contents)** ## Contributors - - [View Contributors](https://github.com/airbnb/swift/graphs/contributors) +- [View Contributors](https://github.com/airbnb/swift/graphs/contributors) **[⬆ back to top](#table-of-contents)** diff --git a/Rakefile b/Rakefile index 11c52dc..458edf8 100644 --- a/Rakefile +++ b/Rakefile @@ -8,6 +8,24 @@ namespace :lint do task :swift do sh 'swift package --allow-writing-to-package-directory format --lint' end + + desc 'Lints README.md' + task :markdown do + unless system('which npx > /dev/null 2>&1') + puts "Error: npx is not installed. Please run: brew install node" + exit 1 + end + + readme_path = 'README.md' + check_result = system('npx --yes prettier --check README.md') + + unless check_result + puts "" + puts "README.md has unformatted changes." + puts "Please run `bundle exec rake format:markdown` and commit the result." + exit 1 + end + end end namespace :format do @@ -15,6 +33,27 @@ namespace :format do task :swift do sh 'swift package --allow-writing-to-package-directory format' end + + desc 'Formats README.md' + task :markdown do + unless system('which npx > /dev/null 2>&1') + puts "Error: npx is not installed. Please run: brew install node" + exit 1 + end + + readme_path = 'README.md' + + # Check if there would be changes by running prettier in check mode + check_result = system('npx --yes prettier --check README.md > /dev/null 2>&1') + + if check_result + puts "No changes" + else + # Format the file + sh 'npx --yes prettier --write README.md' + puts "Formatted README.md" + end + end end namespace :update do