From 47b3bee9cc65d00b5f400d17ad343b28f1dace84 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Tue, 1 Aug 2023 16:15:20 -0700 Subject: [PATCH 1/4] Add rule to prefer for loops over functional forEach { ... } method --- README.md | 59 +++++++++++++++++++ .../AirbnbSwiftFormatTool/airbnb.swiftformat | 1 + 2 files changed, 60 insertions(+) diff --git a/README.md b/README.md index 2bae3ff..047a0a5 100644 --- a/README.md +++ b/README.md @@ -1542,6 +1542,8 @@ _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.** [![SwiftFormat: trailingClosures](https://img.shields.io/badge/SwiftFormat-trailingClosures-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#trailingClosures)
@@ -1562,6 +1564,63 @@ _You can enable the following settings in Xcode by running [this script](resourc planets.first { $0.isGasGiant } ``` +
+ +* (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/master/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 example, for loops support using the `break` keyword to terminate the loop. This isn't possible when just using `forEach`: + + ```swift + let planets = [.mercury, .venus, .earth, .mars, .jupiter, .saturn, .uranus, .neptune] + + for planet in planets { + if planet.isGasGiant { + // We can only terraform the terrestrial planets, so stop once we reach a gas giant. + break + } + + planet.teraform() + } + + planets.forEach { planet in + if planet.isGasGiant { + // We have no way to terminate the loop, since `return` just continues to the next item in the array + } + + planet.terraform() + } + ``` + + ```swift + // WRONG + planets.forEach { planet in + planet.terraform() + } + + // WRONG + planets.forEach { + $0.terraform() + } + + // RIGHT + for planet in planets { + planet.terraform() + } + + // ALSO FINE, since forEach is useful when paired with other functional methods in a chain. + planets + .filter { !$0.isGasGiant } + .map { PlanetTerraformer(planet: $0) } + .forEach { $0.terraform() } + ``` + +
+ ### Operators * (link) **Infix operators should have a single space on either side.** Prefer parenthesis to visually group statements with many operators rather than varying widths of whitespace. This rule does not apply to range operators (e.g. `1...3`) and postfix or prefix operators (e.g. `guest?` or `-1`). [![SwiftLint: operator_usage_whitespace](https://img.shields.io/badge/SwiftLint-operator__usage__whitespace-007A87.svg)](https://realm.github.io/SwiftLint/operator_usage_whitespace) diff --git a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat index 1f1ad53..0eaf311 100644 --- a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat +++ b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat @@ -88,3 +88,4 @@ --rules trailingClosures --rules elseOnSameLine --rules sortTypealiases +--rules forLoop From bda51a6f6f21ac3def55347b0a772fbac014feec Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Wed, 2 Aug 2023 15:04:20 -0700 Subject: [PATCH 2/4] Update README.md --- README.md | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/README.md b/README.md index 047a0a5..0f8c08a 100644 --- a/README.md +++ b/README.md @@ -1573,28 +1573,7 @@ _You can enable the following settings in Xcode by running [this script](resourc #### 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 example, for loops support using the `break` keyword to terminate the loop. This isn't possible when just using `forEach`: - - ```swift - let planets = [.mercury, .venus, .earth, .mars, .jupiter, .saturn, .uranus, .neptune] - - for planet in planets { - if planet.isGasGiant { - // We can only terraform the terrestrial planets, so stop once we reach a gas giant. - break - } - - planet.teraform() - } - - planets.forEach { planet in - if planet.isGasGiant { - // We have no way to terminate the loop, since `return` just continues to the next item in the array - } - - planet.terraform() - } - ``` + 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). ```swift // WRONG From 8195dd0695b9df1573d4b714052637e3507ff707 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 4 Aug 2023 21:20:49 -0700 Subject: [PATCH 3/4] Update README.md Co-authored-by: Michael Bachand --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f8c08a..fb370bc 100644 --- a/README.md +++ b/README.md @@ -1566,7 +1566,7 @@ _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. [![SwiftFormat: forLoop](https://img.shields.io/badge/SwiftFormat-forLoop-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#forLoop) +* (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/master/Rules.md#forLoop)
From fc195157a35557828bade18aedae56cf4b89a827 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Mon, 7 Aug 2023 11:17:03 -0700 Subject: [PATCH 4/4] Clean up --- Package.swift | 4 +- README.md | 69 ++++++++++--------- .../AirbnbSwiftFormatTool/airbnb.swiftformat | 1 + 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/Package.swift b/Package.swift index 2335472..64ab015 100644 --- a/Package.swift +++ b/Package.swift @@ -38,8 +38,8 @@ let package = Package( .binaryTarget( name: "SwiftFormat", - url: "https://github.com/calda/SwiftFormat/releases/download/0.52-beta-2/SwiftFormat.artifactbundle.zip", - checksum: "0cfa2c39a1d5eb7dd5d129f1eb0d525971bedac47d2864022d4f29a54e3cd0aa"), + url: "https://github.com/calda/SwiftFormat/releases/download/0.52-beta-3/SwiftFormat.artifactbundle.zip", + checksum: "ecca7f964e7dcf2d846633cf394c0cffc7628a5ff89d85d2e206f41142f0a859"), .binaryTarget( name: "SwiftLintBinary", diff --git a/README.md b/README.md index fb370bc..630c6b4 100644 --- a/README.md +++ b/README.md @@ -1239,6 +1239,41 @@ _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. [![SwiftFormat: forLoop](https://img.shields.io/badge/SwiftFormat-forLoop-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/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). + + ```swift + // WRONG + planets.forEach { planet in + planet.terraform() + } + + // WRONG + planets.forEach { + $0.terraform() + } + + // RIGHT + for planet in planets { + planet.terraform() + } + + // ALSO FINE, since forEach is useful when paired with other functional methods in a chain. + planets + .filter { !$0.isGasGiant } + .map { PlanetTerraformer(planet: $0) } + .forEach { $0.terraform() } + ``` + +
+ + ### Functions * (link) **Omit `Void` return types from function definitions.** [![SwiftFormat: redundantVoidReturnType](https://img.shields.io/badge/SwiftFormat-redundantVoidReturnType-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#redundantVoidReturnType) @@ -1566,40 +1601,6 @@ _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. [![SwiftFormat: forLoop](https://img.shields.io/badge/SwiftFormat-forLoop-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/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). - - ```swift - // WRONG - planets.forEach { planet in - planet.terraform() - } - - // WRONG - planets.forEach { - $0.terraform() - } - - // RIGHT - for planet in planets { - planet.terraform() - } - - // ALSO FINE, since forEach is useful when paired with other functional methods in a chain. - planets - .filter { !$0.isGasGiant } - .map { PlanetTerraformer(planet: $0) } - .forEach { $0.terraform() } - ``` - -
- ### Operators * (link) **Infix operators should have a single space on either side.** Prefer parenthesis to visually group statements with many operators rather than varying widths of whitespace. This rule does not apply to range operators (e.g. `1...3`) and postfix or prefix operators (e.g. `guest?` or `-1`). [![SwiftLint: operator_usage_whitespace](https://img.shields.io/badge/SwiftLint-operator__usage__whitespace-007A87.svg)](https://realm.github.io/SwiftLint/operator_usage_whitespace) diff --git a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat index 0eaf311..7bd21dd 100644 --- a/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat +++ b/Sources/AirbnbSwiftFormatTool/airbnb.swiftformat @@ -32,6 +32,7 @@ --someAny disabled # opaqueGenericParameters --elseposition same-line #elseOnSameLine --guardelse next-line #elseOnSameLine +--oneLineForEach wrap #forLoop # We recommend a max width of 100 but _strictly enforce_ a max width of 130 --maxwidth 130 # wrap