Skip to content

Commit

Permalink
Add rules for how to wrap attributes on property declarations (#254)
Browse files Browse the repository at this point in the history
  • Loading branch information
calda committed Dec 13, 2023
1 parent 013c1b4 commit bc6aa7c
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 3 deletions.
4 changes: 2 additions & 2 deletions Package.swift
Expand Up @@ -42,8 +42,8 @@ let package = Package(

.binaryTarget(
name: "SwiftFormat",
url: "https://github.com/calda/SwiftFormat/releases/download/0.53-beta-4/SwiftFormat.artifactbundle.zip",
checksum: "8506e9f6a434127f9eabe62c0a0349ccfd1e12e7cd7a96ba6a0c8f9d4a596099"),
url: "https://github.com/calda/SwiftFormat/releases/download/0.53-beta-8/SwiftFormat.artifactbundle.zip",
checksum: "42b2612305c5bffe57102d6c1a2e137309b5cf246638ff9ff75e3260a6795a19"),

.binaryTarget(
name: "SwiftLintBinary",
Expand Down
166 changes: 165 additions & 1 deletion README.md
Expand Up @@ -655,26 +655,190 @@ _You can enable the following settings in Xcode by running [this script](resourc

</details>

* <a id='attributes-on-prev-line'></a>(<a href='#attributes-on-prev-line'>link</a>) **Place function/type attributes on the line above the declaration**. [![SwiftFormat: wrapAttributes](https://img.shields.io/badge/SwiftFormat-wrapAttributes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#wrapAttributes)
* <a id='attributes-on-prev-line'></a>(<a href='#attributes-on-prev-line'>link</a>) **Place attributes for functions, types, and computed properties on the line above the declaration**. [![SwiftFormat: wrapAttributes](https://img.shields.io/badge/SwiftFormat-wrapAttributes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#wrapAttributes)

<details>

```swift
// WRONG
@objc class Spaceship {

@ViewBuilder var controlPanel: some View {
// ...
}

@discardableResult func fly() -> Bool {
// ...
}

}

// RIGHT
@objc
class Spaceship {

@ViewBuilder
var controlPanel: some View {
// ...
}

@discardableResult
func fly() -> Bool {
// ...
}

}
```

</details>

* <a id='simple-stored-property-attributes-on-same-line'></a>(<a href='#simple-stored-property-attributes-on-same-line'>link</a>) **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. [![SwiftFormat: wrapAttributes](https://img.shields.io/badge/SwiftFormat-wrapAttributes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#wrapAttributes)

<details>

```swift
// WRONG. These simple property wrappers should be written on the same line as the declaration.
struct SpaceshipDashboardView {

@State
private var warpDriveEnabled: Bool

@ObservedObject
private var lifeSupportService: LifeSupportService

@Environment(\.controlPanelStyle)
private var controlPanelStyle

}

// RIGHT
struct SpaceshipDashboardView {

@State private var warpDriveEnabled: Bool

@ObservedObject private var lifeSupportService: LifeSupportService

@Environment(\.controlPanelStyle) private var controlPanelStyle

}
```

```swift
// WRONG. These complex attached macros should be written on the previous line.
struct SolarSystemView {

@Query(sort: \.distance) var allPlanets: [Planet]

@Query(sort: \.age, order: .reverse) var moonsByAge: [Moon]

}

// RIGHT
struct SolarSystemView {

@Query(sort: \.distance)
var allPlanets: [Planet]

@Query(sort: \.age, order: .reverse)
var oldestMoons: [Moon]

}
```swift

```
// WRONG. These long, complex attributes should be written on the previous line.
struct RocketFactory {

@available(*, unavailable, message: "No longer in production") var saturn5Builder: Saturn5Builder

@available(*, deprecated, message: "To be retired by 2030") var atlas5Builder: Atlas5Builder

@available(*, iOS 18.0, tvOS 18.0, macOS 15.0, watchOS 11.0) var newGlennBuilder: NewGlennBuilder

}

// RIGHT
struct RocketFactory {

@available(*, unavailable, message: "No longer in production")
var saturn5Builder: Saturn5Builder

@available(*, deprecated, message: "To be retired by 2030")
var atlas5Builder: Atlas5Builder

@available(*, iOS 18.0, tvOS 18.0, macOS 15.0, watchOS 11.0)
var newGlennBuilder: NewGlennBuilder

}
```
#### Why?
Unlike other types of declarations, which have braces and span multiple lines, stored property declarations are often only a single line of code. Stored properties are often written sequentially without any blank lines between them. This makes the code compact without hurting readability, and allows for related properties to be grouped together in blocks:
```swift
struct SpaceshipDashboardView {
@State private var warpDriveEnabled: Bool
@State private var lifeSupportEnabled: Bool
@State private var artificialGravityEnabled: Bool
@State private var tractorBeamEnabled: Bool
@Environment(\.controlPanelStyle) private var controlPanelStyle
@Environment(\.toggleButtonStyle) private var toggleButtonStyle
}
```

If stored property attributes were written on the previous line (like other types of attributes), then the properties start to visually bleed together unless you add blank lines between them:

```swift
struct SpaceshipDashboardView {
@State
private var warpDriveEnabled: Bool
@State
private var lifeSupportEnabled: Bool
@State
private var artificialGravityEnabled: Bool
@State
private var tractorBeamEnabled: Bool

@Environment(\.controlPanelStyle)
private var controlPanelStyle
@Environment(\.toggleButtonStyle)
private var toggleButtonStyle
}
```

If you add blank lines, the list of properties becomes much longer and you lose the ability to group related properties together:

```swift
struct SpaceshipDashboardView {
@State
private var warpDriveEnabled: Bool

@State
private var lifeSupportEnabled: Bool

@State
private var artificialGravityEnabled: Bool

@State
private var tractorBeamEnabled: Bool

@Environment(\.controlPanelStyle)
private var controlPanelStyle

@Environment(\.toggleButtonStyle)
private var toggleButtonStyle
}
```

This doesn't apply to complex attributes with named arguments, or multiple unnamed arguments. These arguments are visually complex and typically encode a lot of information, so feel cramped and difficult to read when written on a single line:

```swift
// Despite being less than 100 characters long, these lines are very complex and feel unnecessarily long:
@available(*, unavailable, message: "No longer in production") var saturn5Builder: Saturn5Builder
@available(*, deprecated, message: "To be retired by 2030") var atlas5Builder: Atlas5Builder
@available(*, iOS 18.0, tvOS 18.0, macOS 15.0, watchOS 11.0) var newGlennBuilder: NewGlennBuilder
```

</details>
Expand Down
3 changes: 3 additions & 0 deletions Sources/AirbnbSwiftFormatTool/airbnb.swiftformat
Expand Up @@ -19,6 +19,9 @@
--closingparen same-line # wrapArguments
--wraptypealiases before-first # wrapArguments
--funcattributes prev-line # wrapAttributes
--computedvarattrs prev-line # wrapAttributes
--storedvarattrs same-line # wrapAttributes
--complexattrs prev-line # wrapAttributes
--typeattributes prev-line # wrapAttributes
--wrapternary before-operators # wrap
--structthreshold 20 # organizeDeclarations
Expand Down

0 comments on commit bc6aa7c

Please sign in to comment.