Airbnb's Swift Style Guide
https://github.com/airbnb/swift.git
Following this style guide should:
This repo includes a Swift Package Manager command plugin that you can use to automatically reformat or lint your package according to the style guide. To use this command plugin with your package, all you need to do is add this repo as a dependency:
dependencies: [
.package(url: "https://github.com/airbnb/swift", from: "1.0.0"),
]
and then run the format command plugin in your package directory:
$ swift package format
Usage guide
# Supported in Xcode 14+. Prompts for permission to write to the package directory.
$ swift package format
# When using the Xcode 13 toolchain, or a noninteractive shell, you must use:
$ swift package --allow-writing-to-package-directory format
# To just lint without reformatting, you can use `--lint`:
$ swift package format --lint
# By default the command plugin runs on the entire package directory.
# You can exclude directories using `exclude`:
$ swift package format --exclude Tests
# Alternatively you can explicitly list the set of paths and/or SPM targets:
$ swift package format --paths Sources Tests Package.swift
$ swift package format --targets AirbnbSwiftFormatTool
# The plugin infers your package's minimum Swift version from the `swift-tools-version`
# in your `Package.swift`, but you can provide a custom value with `--swift-version`:
$ 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.
--lint mode, any lint failure from any tool will result in a non-zero exit code.--lint, only failures from SwiftLint lint-only rules will result in a non-zero exit code.You can enable the following settings in Xcode by running this script, e.g. as part of a "Run Script" build phase._
#### 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.
protocol SpaceThing {
...
}
class SpaceFleet: SpaceThing {
enum Formation {
...
}
class Spaceship {
...
}
var ships: [Spaceship] = []
static let worldName: String = "Earth"
func addShip(_ ship: Spaceship) {
...
}
}
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.
public final class AnyRequester<ModelType>: Requester {
public init<T: Requester>(_ requester: T) where T.ModelType == ModelType {
_executeRequest = requester.executeRequest
}
@discardableResult
public func executeRequest(
_ request: URLRequest,
onSuccess: @escaping (ModelType, Bool) -> Void,
onFailure: @escaping (Error) -> Void
) -> URLSessionCancellable {
return _executeRequest(request, onSuccess, onFailure)
}
private let _executeRequest: (
URLRequest,
@escaping (ModelType, Bool) -> Void,
@escaping (Error) -> Void
) -> URLSessionCancellable
}
final class ExperiencesViewController: UIViewController {
// We can't name this view since UIViewController has a view: UIView property.
private lazy var _view = CustomView()
loadView() {
self.view = _view
}
}
isSpaceship, hasSpacesuit, etc. This makes it clear that they are booleans and not other types.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.
// WRONG
class UrlValidator {
func isValidUrl(_ URL: URL) -> Bool {
...
}
func isProfileUrl(_ URL: URL, for userId: String) -> Bool {
...
}
}
let URLValidator = UrlValidator()
let isProfile = URLValidator.isProfileUrl(URLToTest, userId: IDOfUser)
// RIGHT
class URLValidator {
func isValidURL(_ url: URL) -> Bool {
...
}
func isProfileURL(_ url: URL, for userID: String) -> Bool {
...
}
}
let urlValidator = URLValidator()
let isProfile = urlValidator.isProfileURL(urlToTest, userID: idOfUser)
// WRONG
let rightTitleMargin: CGFloat
let leftTitleMargin: CGFloat
let bodyRightMargin: CGFloat
let bodyLeftMargin: CGFloat
// RIGHT
let titleMarginRight: CGFloat
let titleMarginLeft: CGFloat
let bodyMarginRight: CGFloat
let bodyMarginLeft: CGFloat
// WRONG
let title: String
let cancel: UIButton
// RIGHT
let titleText: String
let cancelButton: UIButton
// WRONG
class ExperiencesViewController {
private func handleBookButtonTap() {
...
}
private func modelChanged() {
...
}
}
// RIGHT
class ExperiencesViewController {
private func didTapBookButton() {
...
}
private func modelDidChange() {
...
}
}
// WRONG
class AIRAccount {
...
}
// RIGHT
class Account {
...
}
*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.
// WRONG
let sun: Star = Star(mass: 1.989e30)
let earth: Planet = Planet.earth
// RIGHT
let sun = Star(mass: 1.989e30)
let earth = Planet.earth
// NOT RECOMMENDED. However, since the linter doesn't have full type information, this is not enforced automatically.
let moon: Moon = earth.moon // returns `Moon`
// RIGHT
let moon = earth.moon
let moon: PlanetaryBody? = earth.moon
// WRONG: Most literals provide a default type that can be inferred.
let enableGravity: Bool = true
let numberOfPlanets: Int = 8
let sunMass: Double = 1.989e30
// RIGHT
let enableGravity = true
let numberOfPlanets = 8
let sunMass = 1.989e30
// WRONG: Types can be inferred from if/switch expressions as well if each branch has the same explicit type.
let smallestPlanet: Planet =
if treatPlutoAsPlanet {
Planet.pluto
} else {
Planet.mercury
}
// RIGHT
let smallestPlanet =
if treatPlutoAsPlanet {
Planet.pluto
} else {
Planet.mercury
}
Prefer using inferred types when the right-hand-side value is a static member with a leading dot (e.g. an init, a static property / function, or an enum case). This applies to both local variables and property declarations:
// WRONG
struct SolarSystemBuilder {
let sun: Star = .init(mass: 1.989e30)
let earth: Planet = .earth
func setUp() {
let galaxy: Galaxy = .andromeda
let system: SolarSystem = .init(sun, earth)
galaxy.add(system)
}
}
// RIGHT
struct SolarSystemBuilder {
let sun = Star(mass: 1.989e30)
let earth = Planet.earth
func setUp() {
let galaxy = Galaxy.andromeda
let system = SolarSystem(sun, earth)
galaxy.add(system)
}
}
Explicit types are still permitted in other cases:
// RIGHT: There is no right-hand-side value, so an explicit type is required.
let sun: Star
// RIGHT: The right-hand-side is not a static member of the left-hand type.
let moon: PlantaryBody = earth.moon
let sunMass: Float = 1.989e30
let planets: [Planet] = []
let venusMoon: Moon? = nil
There are some rare cases where the inferred type syntax has a different meaning than the explicit type syntax. In these cases, the explicit type syntax is still permitted:
extension String {
static let earth = "Earth"
}
// WRONG: fails with "error: type 'String?' has no member 'earth'"
let planetName = String?.earth
// RIGHT
let planetName: String? = .earth
struct SaturnOutline: ShapeStyle { ... }
extension ShapeStyle where Self == SaturnOutline {
static var saturnOutline: SaturnOutline {
SaturnOutline()
}
}
// WRONG: fails with "error: static member 'saturnOutline' cannot be used on protocol metatype '(any ShapeStyle).Type'"
let myShape2 = (any ShapeStyle).myShape
// RIGHT: If the property's type is an existential / protocol type, moving the type
// to the right-hand side will result in invalid code if the value is defined in an
// extension like `extension ShapeStyle where Self == SaturnOutline`.
// SwiftFormat autocorrect detects this case by checking for the existential `any` keyword.
let myShape1: any ShapeStyle = .saturnOutline
self unless it's necessary for disambiguation or required by the language.
final class Listing {
init(capacity: Int, allowsPets: Bool) {
// WRONG
self.capacity = capacity
self.isFamilyFriendly = !allowsPets // `self.` not required here
// RIGHT
self.capacity = capacity
isFamilyFriendly = !allowsPets
}
private let isFamilyFriendly: Bool
private var capacity: Int
private func increaseCapacity(by amount: Int) {
// WRONG
self.capacity += amount
// RIGHT
capacity += amount
// WRONG
self.save()
// RIGHT
save()
}
}
self when upgrading from a weak reference.
// WRONG
class MyClass {
func request(completion: () -> Void) {
API.request() { [weak self] response in
guard let strongSelf = self else { return }
// Do work
completion()
}
}
}
// RIGHT
class MyClass {
func request(completion: () -> Void) {
API.request() { [weak self] response in
guard let self else { return }
// Do work
completion()
}
}
}
// WRONG
let terrestrialPlanets = [
mercury,
venus,
earth,
mars
]
func buildSolarSystem(
innerPlanets: [Planet],
outerPlanets: [Planet]
) { ... }
buildSolarSystem(
innerPlanets: terrestrialPlanets,
outerPlanets: gasGiants
)
// RIGHT
let terrestrialPlanets = [
mercury,
venus,
earth,
mars,
]
func buildSolarSystem(
innerPlanets: [Planet],
outerPlanets: [Planet],
) { ... }
buildSolarSystem(
innerPlanets: terrestrialPlanets,
outerPlanets: gasGiants,
)
// WRONG: Omit the trailing comma in single-element lists.
let planetsWithLife = [
earth,
]
func buildSolarSystem(
_ planets: [Planet],
)
buildSolarSystem(
terrestrialPlanets + gasGiants,
)
// RIGHT
let planetsWithLife = [
earth
]
func buildSolarSystem(
_ planets: [Planet]
) { ... }
buildSolarSystem(
terrestrialPlanets + gasGiants
)
// WRONG
let innerPlanets = [ mercury, venus, earth, mars ]
let largestObjects = [ .star: sun, .planet: jupiter ]
// RIGHT
let innerPlanets = [mercury, venus, earth, mars]
let largestObjects = [.star: sun, .planet: jupiter]
// WRONG
func whatever() -> (Int, Int) {
return (4, 4)
}
let thing = whatever()
print(thing.0)
// RIGHT
func whatever() -> (x: Int, y: Int) {
return (x: 4, y: 4)
}
// THIS IS ALSO OKAY
func whatever2() -> (x: Int, y: Int) {
let x = 4
let y = 4
return (x, y)
}
let coord = whatever()
coord.x
coord.y
// WRONG
let planet:CelestialObject = sun.planets[0]
let planet : CelestialObject = sun.planets[0]
// RIGHT
let planet: CelestialObject = sun.planets[0]
// WRONG
class Planet : CelestialObject {
...
}
// RIGHT
class Planet: CelestialObject {
...
}
// WRONG
let moons: [Planet : Moon] = [
mercury : [],
venus : [],
earth : [theMoon],
mars : [phobos,deimos],
]
// RIGHT
let moons: [Planet: Moon] = [
mercury: [],
venus: [],
earth: [theMoon],
mars: [phobos,deimos],
]
// WRONG
func doSomething()->String {
...
}
// RIGHT
func doSomething() -> String {
...
}
// WRONG
func doSomething(completion: ()->Void) {
...
}
// RIGHT
func doSomething(completion: () -> Void) {
...
}
// WRONG
if (userCount > 0) { ... }
switch (someValue) { ... }
let evens = userCounts.filter { (number) in number.isMultiple(of: 2) }
let squares = userCounts.map() { $0 * $0 }
// RIGHT
if userCount > 0 { ... }
switch someValue { ... }
let evens = userCounts.filter { number in number.isMultiple(of: 2) }
let squares = userCounts.map { $0 * $0 }
// WRONG
if case .done(_) = result { ... }
switch animal {
case .dog(_, _, _):
...
}
// RIGHT
if case .done = result { ... }
switch animal {
case .dog:
...
}
let keyword inline, adjacent to each individual property assignment.
// WRONG
switch result {
case let .success(value):
...
case let .error(errorCode, errorReason):
...
}
// WRONG
guard let case .success(value) else {
return
}
// RIGHT
switch result {
case .success(let value):
...
case .error(let errorCode, let errorReason):
...
}
// RIGHT
guard case .success(let value) else {
return
}
#### Why?
let keyword or never inline the let keyword. In Airbnb's Swift codebase, we observed that inline let is used far more often in practice (especially when destructuring enum cases with a single associated value).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.// `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):
// 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 {
...
}
}
// 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
}
// 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]
}
// 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:
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:
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:
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:
// 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
// WRONG
public struct Spaceship {
nonisolated
public func fly() { ... }
@MainActor
public
func fly() { ... }
}
// RIGHT
public struct Spaceship {
nonisolated public func fly() { ... }
@MainActor
public func fly() { ... }
}
// WRONG
let rowContent = [listingUrgencyDatesRowContent(),
listingUrgencyBookedRowContent(),
listingUrgencyBookedShortRowContent()]
// WRONG
let rowContent = [
listingUrgencyDatesRowContent(),
listingUrgencyBookedRowContent(),
listingUrgencyBookedShortRowContent()
]
// RIGHT
let rowContent = [
listingUrgencyDatesRowContent(),
listingUrgencyBookedRowContent(),
listingUrgencyBookedShortRowContent(),
]
= and before each individual &.
// WRONG (too long)
public typealias Dependencies = CivilizationServiceProviding & LawsOfPhysicsProviding & PlanetBuilderProviding & UniverseBuilderProviding & UniverseSimulatorServiceProviding
// WRONG (naive wrapping)
public typealias Dependencies = CivilizationServiceProviding & LawsOfPhysicsProviding & PlanetBuilderProviding &
UniverseBuilderProviding & UniverseSimulatorServiceProviding
// WRONG (unbalanced)
public typealias Dependencies = CivilizationServiceProviding
& LawsOfPhysicsProviding
& PlanetBuilderProviding
& UniverseBuilderProviding
& UniverseSimulatorServiceProviding
// RIGHT
public typealias Dependencies
= CivilizationServiceProviding
& LawsOfPhysicsProviding
& PlanetBuilderProviding
& UniverseBuilderProviding
& UniverseSimulatorServiceProviding
#### Why?
Protocol composition type aliases are an unordered list with no natural ordering. Sorting alphabetically keeps these lists more organized, which is especially valuable for long protocol compositions.
// WRONG (not sorted)
public typealias Dependencies
= UniverseBuilderProviding
& LawsOfPhysicsProviding
& UniverseSimulatorServiceProviding
& PlanetBuilderProviding
& CivilizationServiceProviding
// RIGHT
public typealias Dependencies
= CivilizationServiceProviding
& LawsOfPhysicsProviding
& PlanetBuilderProviding
& UniverseBuilderProviding
& UniverseSimulatorServiceProviding
#### Why?
Following the rationale in SE-0345, this shorthand syntax removes unnecessary boilerplate while retaining clarity.
// WRONG
if
let galaxy = galaxy,
galaxy.name == "Milky Way"
{ ... }
guard
let galaxy = galaxy,
galaxy.name == "Milky Way"
else { ... }
// RIGHT
if
let galaxy,
galaxy.name == "Milky Way"
{ ... }
guard
let galaxy,
galaxy.name == "Milky Way"
else { ... }
// WRONG
if let galaxy {
...
}
else if let bigBangService {
...
}
else {
...
}
// RIGHT
if let galaxy {
...
} else if let bigBangService {
...
} else {
...
}
// RIGHT, because there are comments between the conditions
if let galaxy {
...
}
// If the galaxy hasn't been created yet, create it using the big bang service
else if let bigBangService {
...
}
// If the big bang service doesn't exist, fail gracefully
else {
...
}
// RIGHT, because there are blank lines between the conditions
if let galaxy {
...
}
else if let bigBangService {
// If the galaxy hasn't been created yet, create it using the big bang service
...
}
else {
// If the big bang service doesn't exist, fail gracefully
...
}
#### Why?
Breaking after the leading keyword resets indentation to the standard 2-space grid, which helps avoid fighting Xcode's ^ + I indentation behavior.
// WRONG
if let galaxy,
galaxy.name == "Milky Way" // Indenting by two spaces fights Xcode's ^+I indentation behavior
{ ... }
// WRONG
guard let galaxy,
galaxy.name == "Milky Way" // Variable width indentation (6 spaces)
else { ... }
// WRONG
guard let earth = universe.find(
.planet,
named: "Earth"),
earth.isHabitable // Blends in with previous condition's method arguments
else { ... }
// RIGHT
if
let galaxy,
galaxy.name == "Milky Way"
{ ... }
// RIGHT
guard
let galaxy,
galaxy.name == "Milky Way"
else { ... }
// RIGHT
guard
let earth = universe.find(
.planet,
named: "Earth"),
earth.isHabitable
else { ... }
// RIGHT
if let galaxy {
...
}
// RIGHT
guard let galaxy else {
...
}
=) 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.#### Why?
This makes it so that if and switch expressions always have the same "shape" as standard if and switch statements, where:
if / switch keyword is always the left-most token on a dedicated line of code.if / switch keyword.if / switch keywords are used for control flow, and thus makes it easier to recognize that the code is using an if or switch expression at a glance.
// WRONG. Should have a line break after the first `=`.
let planetLocation = if let star = planet.star {
"The \(star.name) system"
} else {
"Rogue planet"
}
// WRONG. The first `=` should be on the line of the variable being assigned.
let planetLocation
= if let star = planet.star {
"The \(star.name) system"
} else {
"Rogue planet"
}
// WRONG. `switch` expression should be indented.
let planetLocation =
switch planet {
case .mercury, .venus, .earth, .mars:
.terrestrial
case .jupiter, .saturn, .uranus, .neptune:
.gasGiant
}
// RIGHT
let planetLocation =
if let star = planet.star {
"The \(star.name) system"
} else {
"Rogue planet"
}
// RIGHT
let planetType: PlanetType =
switch planet {
case .mercury, .venus, .earth, .mars:
.terrestrial
case .jupiter, .saturn, .uranus, .neptune:
.gasGiant
}
// ALSO RIGHT. A line break is not required because the declaration fits on a single line.
let moonName = if let moon = planet.moon { moon.name } else { "none" }
// ALSO RIGHT. A line break is permitted if it helps with readability.
let moonName =
if let moon = planet.moon { moon.name } else { "none" }
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.#### Why?
There are several benefits to using an if/switch expression over simply performing assignment on each branch of the following conditional statement:
var is unnecessary.// BEFORE
// 1. An explicit type annotation is required for the uninitialized property.
// 2. `var` is unnecessary here because `planetLocation` is never modified after being initialized, but the compiler doesn't diagnose.
// 3. The `planetLocation` property name is written on each branch so is redundant and visually noisy.
var planetLocation: String
if let star = planet.star {
planetLocation = "The \(star.name) system"
} else {
planetLocation = "Rogue planet"
}
print(planetLocation)
// AFTER
// 1. No need to write an explicit `: String` type annotation.
// 2. The compiler correctly diagnoses that the `var` is unnecessary and emits a warning suggesting to use `let` instead.
// 3. Each conditional branch is simply the value being assigned.
var planetLocation =
if let star = planet.star {
"The \(star.name) system"
} else {
"Rogue planet"
}
print(planetLocation)
#### Examples
// WRONG
let planetLocation: String
if let star = planet.star {
planetLocation = "The \(star.name) system"
} else {
planetLocation = "Rogue planet"
}
let planetType: PlanetType
switch planet {
case .mercury, .venus, .earth, .mars:
planetType = .terrestrial
case .jupiter, .saturn, .uranus, .neptune:
planetType = .gasGiant
}
let canBeTerraformed: Bool
if
let star = planet.star,
!planet.isHabitable,
planet.isInHabitableZone(of: star)
{
canBeTerraformed = true
} else {
canBeTerraformed = false
}
// RIGHT
let planetLocation =
if let star = planet.star {
"The \(star.name) system"
} else {
"Rogue planet"
}
let planetType: PlanetType =
switch planet {
case .mercury, .venus, .earth, .mars:
.terrestrial
case .jupiter, .saturn, .uranus, .neptune:
.gasGiant
}
let canBeTerraformed =
if
let star = planet.star,
!planet.isHabitable,
planet.isInHabitableZone(of: star)
{
true
} else {
false
}
// ALSO RIGHT. This example cannot be converted to an if/switch expression
// because one of the branches is more than just a single expression.
let planetLocation: String
if let star = planet.star {
planetLocation = "The \(star.name) system"
} else {
let actualLocaton = galaxy.name ?? "the universe"
planetLocation = "Rogue planet somewhere in \(actualLocation)"
}
#### Why?
Like with declarations in a file, inserting a blank line between scopes makes them easier to visually differentiate.
Complex switch statements are visually busy without blank lines between the cases, making it more difficult to read the code and harder to distinguish between individual cases at a glance. Blank lines between the individual cases make complex switch statements easier to read.
#### Examples
// WRONG. These switch cases should be followed by a blank line.
func handle(_ action: SpaceshipAction) {
switch action {
case .engageWarpDrive:
navigationComputer.destination = targetedDestination
warpDrive.spinUp()
warpDrive.activate()
case .enableArtificialGravity:
artificialGravityEngine.enable(strength: .oneG)
case .scanPlanet(let planet):
scanner.target = planet
scanner.scanAtmosphere()
scanner.scanBiosphere()
scanner.scanForArtificialLife()
case .handleIncomingEnergyBlast:
energyShields.engage()
}
}
// WRONG. While the `.enableArtificialGravity` case isn't multi-line, the other cases are.
// For consistency, it should also include a trailing blank line.
func handle(_ action: SpaceshipAction) {
switch action {
case .engageWarpDrive:
navigationComputer.destination = targetedDestination
warpDrive.spinUp()
warpDrive.activate()
case .enableArtificialGravity:
artificialGravityEngine.enable(strength: .oneG)
case .scanPlanet(let planet):
scanner.target = planet
scanner.scanAtmosphere()
scanner.scanBiosphere()
scanner.scanForArtificialLife()
case .handleIncomingEnergyBlast:
energyShields.engage()
}
}
// RIGHT. All of the cases have a trailing blank line.
func handle(_ action: SpaceshipAction) {
switch action {
case .engageWarpDrive:
navigationComputer.destination = targetedDestination
warpDrive.spinUp()
warpDrive.activate()
case .enableArtificialGravity:
artificialGravityEngine.enable(strength: .oneG)
case .scanPlanet(let planet):
scanner.target = planet
scanner.scanAtmosphere()
scanner.scanBiosphere()
scanner.scanForArtificialLife()
case .handleIncomingEnergyBlast:
energyShields.engage()
}
}
// RIGHT. Since none of the cases are multi-line, blank lines are not required.
func handle(_ action: SpaceshipAction) {
switch action {
case .engageWarpDrive:
warpDrive.engage()
case .enableArtificialGravity:
artificialGravityEngine.enable(strength: .oneG)
case .scanPlanet(let planet):
scanner.scan(planet)
case .handleIncomingEnergyBlast:
energyShields.engage()
}
}
// ALSO RIGHT. Blank lines are still permitted after single-line switch cases if it helps with readability.
func handle(_ action: SpaceshipAction) {
switch action {
case .engageWarpDrive:
warpDrive.engage()
case .enableArtificialGravity:
artificialGravityEngine.enable(strength: .oneG)
case .scanPlanet(let planet):
scanner.scan(planet)
case .handleIncomingEnergyBlast:
energyShields.engage()
}
}
// WRONG. While it's fine to use blank lines to separate cases, spacing within a single switch statement should be consistent.
func handle(_ action: SpaceshipAction) {
switch action {
case .engageWarpDrive:
warpDrive.engage()
case .enableArtificialGravity:
artificialGravityEngine.enable(strength: .oneG)
case .scanPlanet(let planet):
scanner.scan(planet)
case .handleIncomingEnergyBlast:
energyShields.engage()
}
}
break statements in switch cases.#### Why?
Swift automatically breaks out of a switch case after executing its code, so explicit break statements are usually unnecessary and add visual clutter.
// WRONG
switch spaceship.warpDriveState {
case .engaged:
navigator.engageWarpDrive()
break
case .disengaged:
navigator.disengageWarpDrive()
break
}
// RIGHT
switch spaceship.warpDriveState {
case .engaged:
navigator.engageWarpDrive()
case .disengaged:
navigator.disengageWarpDrive()
}
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.
// WRONG (else should be on its own line for multi-line guard statements)
guard
let galaxy,
galaxy.name == "Milky Way" else
{ ... }
// WRONG (else should be on the same line for single-line guard statements)
guard let galaxy
else { ... }
// RIGHT
guard
let galaxy,
galaxy.name == "Milky Way"
else { ... }
// RIGHT
guard let galaxy else {
...
}
// WRONG
var spaceQuote = """
βSpace,β it says, βis big. Really big. You just wonβt believe how vastly, hugely, mindbogglingly big it is.
I mean, you may think itβs a long way down the road to the chemistβs, but thatβs just peanuts to space.β
"""
// RIGHT
var spaceQuote = """
βSpace,β it says, βis big. Really big. You just wonβt believe how vastly, hugely, mindbogglingly big it is.
I mean, you may think itβs a long way down the road to the chemistβs, but thatβs just peanuts to space.β
"""
// WRONG
var universeQuote: String {
"""
In the beginning the Universe was created.
This has made a lot of people very angry and been widely regarded as a bad move.
"""
}
// RIGHT
var universeQuote: String {
"""
In the beginning the Universe was created.
This has made a lot of people very angry and been widely regarded as a bad move.
"""
}
Optional, Array, Dictionary), prefer using the shorthand form over the full generic form.
// WRONG
let optional: Optional<String> = nil
let array: Array<String> = []
let dictionary: Dictionary<String, Any> = [:]
// RIGHT
let optional: String? = nil
let array: [String] = []
let dictionary: [String: Any] = [:]
.init when not required.
// WRONG
if !planet.isHabitable
{
planet.terraform()
}
class Planet
{
func terraform()
{
generateAtmosphere()
generateOceans()
}
}
// RIGHT
if !planet.isHabitable {
planet.terraform()
}
class Planet {
func terraform() {
generateAtmosphere()
generateOceans()
}
}
// WRONG
if
let star = planet.nearestStar(),
planet.isInHabitableZone(of: star) {
planet.terraform()
}
// RIGHT
if
let star = planet.nearestStar(),
planet.isInHabitableZone(of: star)
{
planet.terraform()
}
// WRONG
struct Planet{
...
}
// WRONG
if condition{
...
}else{
...
}
// RIGHT
struct Planet {
...
}
// RIGHT
if condition {
...
} else {
...
}
// WRONG
func install ( _ engine: Engine ) { }
install ( AntimatterDrive( ) )
// RIGHT
func install(_ engine: Engine) { }
install(AntimatterDrive())
// for code comments and /// for documentation comments), rather than multi-line comments (/* ... */ and /** ... */).
// WRONG
/**
* A planet that exists somewhere in the universe.
*
* Planets have many properties. For example, the best planets
* have atmospheres and bodies of water to support life.
*/
class Planet {
/**
Terraforms the planet, by adding an atmosphere and ocean that is hospitable for life.
*/
func terraform() {
/*
Generate the atmosphere first, before generating the ocean.
Otherwise, the water will just boil off immediately.
*/
generateAtmosphere()
/* Now that we have an atmosphere, it's safe to generate the ocean */
generateOceans()
}
}
// RIGHT
/// A planet that exists somewhere in the universe.
///
/// Planets have many properties. For example, the best planets
/// have atmospheres and bodies of water to support life.
class Planet {
/// Terraforms the planet, by adding an atmosphere and ocean that is hospitable for life.
func terraform() {
// Generate the atmosphere first, before generating the ocean.
// Otherwise, the water will just boil off immediately.
generateAtmosphere()
// Now that we have an atmosphere, it's safe to generate the ocean
generateOceans()
}
}
///) instead of regular comments (//) before declarations within type bodies or at the top level.
// WRONG
// A planet that exists somewhere in the universe.
class Planet {
// Data about the composition and density of the planet's atmosphere if present.
var atmosphere: Atmosphere?
// Data about the size, location, and composition of large bodies of water on the planet's surface.
var oceans: [Ocean]
// Terraforms the planet, by adding an atmosphere and ocean that is hospitable for life.
func terraform() {
// This gas composition has a pretty good track record so far!
let composition = AtmosphereComposition(nitrogen: 0.78, oxygen: 0.22)
// Generate the atmosphere first, then the oceans. Otherwise, the water will just boil off immediately.
generateAtmosphere(using: composition)
generateOceans()
}
}
// RIGHT
/// A planet that exists somewhere in the universe.
class Planet {
/// Data about the composition and density of the planet's atmosphere if present.
var atmosphere: Atmosphere?
/// Data about the size, location, and composition of large bodies of water on the planet's surface.
var oceans: [Ocean]
/// Terraforms the planet, by adding an atmosphere and ocean that is hospitable for life.
func terraform() {
// This gas composition has a pretty good track record so far!
let composition = AtmosphereComposition(nitrogen: 0.78, oxygen: 0.22)
// Generate the atmosphere first, then the oceans. Otherwise, the water will just boil off immediately.
generateAtmosphere(using: composition)
generateOceans()
}
}
// ALSO RIGHT:
func terraform() {
/// This gas composition has a pretty good track record so far!
/// - Doc comments are not required before local declarations in function scopes, but are permitted.
let composition = AtmosphereComposition(nitrogen: 0.78, oxygen: 0.22)
/// Generate the `atmosphere` first, **then** the `oceans`. Otherwise, the water will just boil off immediately.
/// - Comments not preceding declarations can use doc comments, and will not be autocorrected into regular comments.
/// This can be useful because Xcode applies markdown styling to doc comments but not regular comments.
generateAtmosphere(using: composition)
generateOceans()
}
Regular comments are permitted before declarations in some cases.
For example, comment directives like // swiftformat:, // swiftlint:, // sourcery:, // MARK: and // TODO: are typically required to use regular comments and don't work correctly with doc comments:
// RIGHT
// swiftformat:sort
enum FeatureFlags {
case allowFasterThanLightTravel
case disableGravity
case enableDarkEnergy
case enableDarkMatter
}
// TODO: There are no more production consumers of this legacy model, so we
// should detangle the remaining code dependencies and clean it up.
struct LegacyGeocentricUniverseModel {
...
}
Regular comments are also allowed before a grouped block of declarations, since it's possible that the comment refers to the block as a whole rather than just the following declaration:
// RIGHT
enum Planet {
// The inner planets
case mercury
case venus
case earth
case mars
// The outer planets
case jupiter
case saturn
case uranus
case neptune
}
// ALSO RIGHT
enum Planet {
/// The smallest planet
case mercury
case venus
case earth
case mars
/// The largest planet
case jupiter
case saturn
case uranus
case neptune
}
// WRONG
@MainActor
/// A spacecraft with everything you need to explore the universe.
struct Spaceship { ... }
public
/// A spacecraft with everything you need to explore the universe.
struct Spaceship { ... }
// RIGHT
/// A spacecraft with everything you need to explore the universe.
@MainActor
struct Spaceship { ... }
/// A spacecraft with everything you need to explore the universe.
public struct Spaceship { ... }
//, ///, /*, and */)
// WRONG
///A spacecraft with incredible performance characteristics
struct Spaceship {
func travelFasterThanLight() {/*unimplemented*/}
func travelBackInTime() { }//TODO: research whether or not this is possible
}
// RIGHT
/// A spacecraft with incredible performance characteristics
struct Spaceship {
func travelFasterThanLight() { /* unimplemented */ }
func travelBackInTime() { } // TODO: research whether or not this is possible
}
{ }).
// WRONG
extension Spaceship: Trackable {}
extension SpaceshipView {
var accessibilityIdentifier: String {
get { spaceship.name }
set {}
}
}
// RIGHT
extension Spaceship: Trackable { }
extension SpaceshipView {
var accessibilityIdentifier: String {
get { spaceship.name }
set { }
}
}
for loops over the functional forEach(β¦) method, unless using forEach(β¦) as the last element in a functional chain.#### 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).
// 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() }
Void return types from function definitions.)).
class Universe {
// WRONG
func generateStars(at location: Point, count: Int, color: StarColor, withAverageDistance averageDistance: Float) -> String {
// This is too long and will probably auto-wrap in a weird way
}
// WRONG
func generateStars(at location: Point,
count: Int,
color: StarColor,
withAverageDistance averageDistance: Float) -> String
{
// Xcode indents all the arguments
}
// WRONG
func generateStars(
at location: Point,
count: Int,
color: StarColor,
withAverageDistance averageDistance: Float) -> String {
populateUniverse() // this line blends in with the argument list
}
// WRONG
func generateStars(
at location: Point,
count: Int,
color: StarColor,
withAverageDistance averageDistance: Float) throws
-> String {
populateUniverse() // this line blends in with the argument list
}
// WRONG
func generateStars(
at location: Point,
count: Int,
color: StarColor,
withAverageDistance averageDistance: Float) async throws // these effects are easy to miss since they're visually associated with the last parameter
-> String
{
populateUniverse()
}
// RIGHT
func generateStars(
at location: Point,
count: Int,
color: StarColor,
withAverageDistance averageDistance: Float
) -> String {
populateUniverse()
}
// RIGHT
func generateStars(
at location: Point,
count: Int,
color: StarColor,
withAverageDistance averageDistance: Float
) async throws -> String {
populateUniverse()
}
}
// WRONG
var galaxy: String { "Milky Way" }
func launchRocket() { print("Launching") }
init() { self.value = 0 }
subscript(index: Int) -> Int { array[index] }
// RIGHT
var galaxy: String {
"Milky Way"
}
func launchRocket() {
print("Launching")
}
init() {
self.value = 0
}
subscript(index: Int) -> Int {
array[index]
}
Exception: This rule does not affect protocol definitions.
// RIGHT
protocol SpaceshipEngine {
func engage() -> Bool
var fuelLevel: Double { get }
}
// WRONG
universe.generateStars(at: location, count: 5, color: starColor, withAverageDistance: 4)
// WRONG
universe.generateStars(at: location,
count: 5,
color: starColor,
withAverageDistance: 4)
// WRONG
universe.generateStars(
at: location,
count: 5,
color: starColor,
withAverageDistance: 4)
// WRONG
universe.generate(5,
.stars,
at: location)
// RIGHT
universe.generateStars(
at: location,
count: 5,
color: starColor,
withAverageDistance: 4
)
// RIGHT
universe.generate(
5,
.stars,
at: location
)
_).#### 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.
// WRONG
// 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
)
}
// RIGHT
// 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
)
}
#### Why?
Improves readability and maintainability, making it easier to see the sequence of functions that are applied to the object.
// WRONG
var innerPlanetNames: [String] {
planets
.filter { $0.isInnerPlanet }
.map { $0.name }
}
// WRONG
var innerPlanetNames: [String] {
planets
.filter { $0.isInnerPlanet }
// Gets the name of the inner planet
.map { $0.name }
}
// RIGHT
var innerPlanetNames: [String] {
planets
.filter { $0.isInnerPlanet }
.map { $0.name }
}
// RIGHT
var innerPlanetNames: [String] {
planets
.filter { $0.isInnerPlanet }
// Gets the name of the inner planet
.map { $0.name }
}
throws annotations from function definitions.#### 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.
// WRONG
func doSomething() throws(Never) -> Int {
return 0
}
func doSomethingElse() throws(Error) -> Int {
throw MyError.failed
}
// RIGHT
func doSomething() -> Int {
return 0
}
func doSomethingElse() throws -> Int {
throw MyError.failed
}
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.
// WRONG
func method(completion: () -> ()) {
...
}
// RIGHT
func method(completion: () -> Void) {
...
}
_).#### 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.
// WRONG
someAsyncThing() { argument1, argument2, argument3 in
print(argument3)
}
// RIGHT
someAsyncThing() { _, _, argument3 in
print(argument3)
}
// WRONG
let evenSquares = numbers.filter{$0.isMultiple(of: 2)}.map{ $0 * $0 }
// RIGHT
let evenSquares = numbers.filter { $0.isMultiple(of: 2) }.map { $0 * $0 }
// WRONG
let evenSquares = numbers.filter( { $0.isMultiple(of: 2) } ).map( { $0 * $0 } )
// RIGHT
let evenSquares = numbers.filter({ $0.isMultiple(of: 2) }).map({ $0 * $0 })
// WRONG
let evenSquares = numbers
.filter{
$0.isMultiple(of: 2)
}
.map{
$0 * $0
}
// RIGHT
let evenSquares = numbers
.filter {
$0.isMultiple(of: 2)
}
.map {
$0 * $0
}
Void return types from closure expressions.
// WRONG
planets.map({ $0.name })
// RIGHT
planets.map { $0.name }
// ALSO RIGHT, since this closure has a parameter name
planets.first(where: { $0.isGasGiant })
// ALSO FINE. Trailing closure syntax is still permitted for closures
// with parameter names. However, consider using non-trailing syntax
// in cases where the parameter name is semantically meaningful.
planets.first { $0.isGasGiant }
unowned captures. Instead prefer safer alternatives like weak captures, or capturing variables directly. unowned captures are unsafe because they will cause the application to crash if the referenced object has been deallocated.
// WRONG: Crashes if `self` has been deallocated when closures are called.
final class SpaceshipNavigationService {
let spaceship: Spaceship
let planet: Planet
func colonizePlanet() {
spaceship.travel(to: planet, onArrival: { [unowned self] in
planet.colonize()
})
}
func exploreSystem() {
spaceship.travel(to: planet, nextDestination: { [unowned self] in
planet.moons?.first
})
}
}
weak captures are safer because they require the author to explicitly handle the case where the referenced object no longer exists.
// RIGHT: Uses a `weak self` capture and explicitly handles the case where `self` has been deallocated
final class SpaceshipNavigationService {
let spaceship: Spaceship
let planet: Planet
func colonizePlanet() {
spaceship.travel(to: planet, onArrival: { [weak self] in
guard let self else { return }
planet.colonize()
}
)
}
func exploreSystem() {
spaceship.travel(to: planet, nextDestination: { [weak self] in
guard let self else { return nil }
return planet.moons?.first
}
)
}
}
Alternatively, consider directly capturing the variables that are used in the closure. This lets you avoid having to handle the case where self is nil, since you don't even need to reference self:
// RIGHT: Explicitly captures `planet` instead of capturing `self`
final class SpaceshipNavigationService {
let spaceship: Spaceship
let planet: Planet
func colonizePlanet() {
spaceship.travel(to: planet, onArrival: { [planet] in
planet.colonize()
}
)
}
func exploreSystem() {
spaceship.travel(to: planet, nextDestination: { [planet] in
planet.moons?.first
}
)
}
}
1...3).
// WRONG
let capacity = 1+2
let capacity = currentCapacity??0
let capacity=newCapacity
let latitude = region.center.latitude-region.span.latitudeDelta/2.0
// RIGHT
let capacity = 1 + 2
let capacity = currentCapacity ?? 0
let capacity = newCapacity
let latitude = region.center.latitude - region.span.latitudeDelta / 2.0
// WRONG
static func == (_ lhs: MyView, _ rhs: MyView) -> Bool {
lhs.id == rhs.id
}
// RIGHT
static func ==(_ lhs: MyView, _ rhs: MyView) -> Bool {
lhs.id == rhs.id
}
? and before the :, putting each conditional branch on a separate line.
// WRONG (too long)
let destinationPlanet = solarSystem.hasPlanetsInHabitableZone ? solarSystem.planetsInHabitableZone.first : solarSystem.uninhabitablePlanets.first
// WRONG (naive wrapping)
let destinationPlanet = solarSystem.hasPlanetsInHabitableZone ? solarSystem.planetsInHabitableZone.first :
solarSystem.uninhabitablePlanets.first
// WRONG (unbalanced operators)
let destinationPlanet = solarSystem.hasPlanetsInHabitableZone ?
solarSystem.planetsInHabitableZone.first :
solarSystem.uninhabitablePlanets.first
// RIGHT
let destinationPlanet = solarSystem.hasPlanetsInHabitableZone
? solarSystem.planetsInHabitableZone.first
: solarSystem.uninhabitablePlanets.first
if, guard, while), separate boolean conditions using commas (,) instead of && operators.
// WRONG
if let star = planet.star, !planet.isHabitable && planet.isInHabitableZone(of: star) {
planet.terraform()
}
if
let star = planet.star,
!planet.isHabitable
&& planet.isInHabitableZone(of: star)
{
planet.terraform()
}
// RIGHT
if let star = planet.star, !planet.isHabitable, planet.isInHabitableZone(of: star) {
planet.terraform()
}
if
let star = planet.star,
!planet.isHabitable,
planet.isInHabitableZone(of: star)
{
planet.terraform()
}
extension Collection<Planet>), or sugared syntax for applicable standard library types (extension [Planet]) instead of generic type constraints.
// WRONG
extension Array where Element == Star { ... }
extension Optional where Wrapped == Spaceship { ... }
extension Dictionary where Key == Moon, Element == Planet { ... }
extension Collection where Element == Universe { ... }
extension StateStore where State == SpaceshipState, Action == SpaceshipAction { ... }
// RIGHT
extension [Star] { ... }
extension Spaceship? { ... }
extension [Moon: Planet] { ... }
extension Collection<Universe> { ... }
extension StateStore<SpaceshipState, SpaceshipAction> { ... }
// ALSO RIGHT. There are multiple types that could satisfy this constraint
// (e.g. [Planet], [Moon]), so this is not a "bound generic type" and isn't
// eligible for the generic bracket syntax.
extension Array where Element: PlanetaryBody { }
### Examples
// WRONG. Semicolons are not required and can be omitted.
let mercury = planets[0];
let venus = planets[1];
let earth = planets[2];
// WRONG. While you can use semicolons to place multiple statements on a single line,
// it is more common and preferred to separate them using newlines instead.
let mercury = planets[0]; let venus = planets[1]; let earth = planets[2];
// RIGHT
let mercury = planets[0]
let venus = planets[1]
let earth = planets[2]
// WRONG
guard let moon = planet.moon else { completion(nil); return }
// WRONG
guard let moon = planet.moon else {
completion(nil); return
}
// RIGHT
guard let moon = planet.moon else {
completion(nil)
return
}
init time whenever possible, rather than using implicitly unwrapped optionals. A notable exception is UIViewController's view property.
// WRONG
class MyClass {
init() {
super.init()
someValue = 5
}
var someValue: Int!
}
// RIGHT
class MyClass {
init() {
someValue = 0
super.init()
}
var someValue: Int
}
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.internal memberwise initializers for structs, so explicit internal initializers equivalent to the synthesized initializer should be omitted.#### Why?
Removing redundant memberwise initializers reduces boilerplate and makes it easier to add more properties in the future.
// WRONG
struct Planet {
let name: String
let mass: Double
init(name: String, mass: Double) {
self.name = name
self.mass = mass
}
}
// RIGHT
struct Planet {
let name: String
let mass: Double
}
// ALSO RIGHT: Custom logic in initializer makes it non-redundant
struct Planet {
let name: String
let mass: Double
init(name: String, mass: Double) {
self.name = name.capitalized
self.mass = max(0, mass)
}
}
// ALSO RIGHT: Public initializer is not redundant since compiler-synthesized
// memberwise initializers are always internal
public struct Planet {
public let name: String
public let mass: Double
public init(name: String, mass: Double) {
self.name = name
self.mass = mass
}
}
Parameters using result builders like @ViewBuilder can be replaced with the equivalent behavior by adding the result builder attribute to the stored property:
/// WRONG
struct DashboardView<Instrument: View>: View {
let instrument: Instrument
init(@ViewBuilder instrument: () -> Instrument) {
instrument = instrument()
}
var content: some View {
instrument
}
}
// RIGHT
struct DashboardView<Instrument: View>: View {
@ViewBuilder let instrument: Instrument
var content: some View {
instrument
}
}
oldValue explicit.
// WRONG
class TextField {
var text: String? {
didSet {
guard oldValue != text else {
return
}
// Do a bunch of text-related side-effects.
}
}
}
// RIGHT
class TextField {
var text: String? {
didSet { textDidUpdate(from: oldValue) }
}
private func textDidUpdate(from oldValue: String?) {
guard oldValue != text else {
return
}
// Do a bunch of text-related side-effects.
}
}
guard to unwrap self for the duration of the callback.
// WRONG
class MyClass {
func request(completion: () -> Void) {
API.request() { [weak self] response in
if let self {
// Processing and side effects
}
completion()
}
}
}
// RIGHT
class MyClass {
func request(completion: () -> Void) {
API.request() { [weak self] response in
guard let self else { return }
self.doSomething(with: self.property, response: response)
completion()
}
}
func doSomething(with nonOptionalParameter: SomeClass, response: SomeResponseClass) {
// Processing and side effects
}
}
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.
public and private come before other modifiers like final or static.public to open and private to fileprivate unless you need that behavior. Avoid using public in internal types.
// WRONG
public struct Spaceship {
// WRONG: `engine` is used in `extension Spaceship` below,
// but extensions in the same file can access `private` members.
fileprivate let engine: AntimatterEngine
// WRONG: `hull` is not used by any other type, so `fileprivate` is unnecessary.
fileprivate let hull: Hull
// RIGHT: `navigation` is used in `extension Pilot` below,
// so `fileprivate` is necessary here.
fileprivate let navigation: SpecialRelativityNavigationService
}
extension Spaceship {
public func blastOff() {
engine.start()
}
}
extension Pilot {
public func chartCourse() {
spaceship.navigation.course = .andromedaGalaxy
spaceship.blastOff()
}
}
// RIGHT
public struct Spaceship {
fileprivate let navigation: SpecialRelativityNavigationService
private let engine: AntimatterEngine
private let hull: Hull
}
extension Spaceship {
public func blastOff() {
engine.start()
}
}
extension Pilot {
public func chartCourse() {
spaceship.navigation.course = .andromedaGalaxy
spaceship.blastOff()
}
}
// WRONG: Public declarations in internal types are internal, not public.
class Spaceship {
public init() { ... }
public func travel(to planet: Planet) { ... }
}
// RIGHT
class Spaceship {
init() { ... }
func travel(to planet: Planet) { ... }
}
However, you can use internal access control instead of private access control to enable the use of the compiler-synthesized memberwise initializer.
// ALSO RIGHT: Using `internal` access control instead of `private`
// to enable the synthesized memberwise init.
struct PlanetView: View {
let planet: Planet
let star: Star
var body: some View {
...
}
}
internal keyword when defining types, properties, or functions with an internal access control level.
// WRONG
internal class Spaceship {
internal init() { ... }
internal func travel(to planet: Planet) { ... }
}
// RIGHT, because internal access control is implied if no other access control level is specified.
class Spaceship {
init() { ... }
func travel(to planet: Planet) { ... }
}
#### Why?
Specifying the access control on the declaration itself helps engineers more quickly determine the access control level of an individual declaration.
// WRONG
public extension Universe {
// This declaration doesn't have an explicit access control level.
// In all other scopes, this would be an internal function,
// but because this is in a public extension, it's actually a public function.
func generateGalaxy() { }
}
// WRONG
private extension Spaceship {
func enableHyperdrive() { }
}
// RIGHT
extension Universe {
// It is immediately obvious that this is a public function,
// even if the start of the `extension Universe` scope is off-screen.
public func generateGalaxy() { }
}
// RIGHT
extension Spaceship {
// Recall that a private extension actually has fileprivate semantics,
// so a declaration in a private extension is fileprivate by default.
fileprivate func enableHyperdrive() { }
}
// WRONG
func age(of person: Person, bornAt: TimeInterval) -> Int {
...
}
func jump(person: Person) {
...
}
// RIGHT
class Person {
var bornAt: TimeInterval
var age: Int {
...
}
func jump() {
...
}
}
enums for organizing public or internal constants and functions into namespaces.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. Caseless enums work well as namespaces because they cannot be instantiated, which matches their intent.
// WRONG
struct Environment {
static let earthGravity = 9.8
static let moonGravity = 1.6
}
// WRONG
struct Environment {
struct Earth {
static let gravity = 9.8
}
struct Moon {
static let gravity = 1.6
}
}
// RIGHT
enum Environment {
enum Earth {
static let gravity = 9.8
}
enum Moon {
static let gravity = 1.6
}
}
#### 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.
// WRONG
enum ErrorType: String {
case error = "error"
case warning = "warning"
}
// WRONG
enum UserType: String {
case owner
case manager
case member
}
// WRONG
enum Planet: Int {
case mercury = 0
case venus = 1
case earth = 2
case mars = 3
case jupiter = 4
case saturn = 5
case uranus = 6
case neptune = 7
}
// WRONG
enum ErrorCode: Int {
case notEnoughMemory
case invalidResource
case timeOut
}
// RIGHT
// Relying on Swift's automatic enum values
enum ErrorType: String {
case error
case warning
}
// RIGHT
/// These are written to a logging service. Explicit values ensure they're consistent across binaries.
// swiftformat:disable redundantRawValues
enum UserType: String {
case owner = "owner"
case manager = "manager"
case member = "member"
}
// swiftformat:enable redundantRawValues
// RIGHT
// Relying on Swift's automatic enum values
enum Planet: Int {
case mercury
case venus
case earth
case mars
case jupiter
case saturn
case uranus
case neptune
}
// RIGHT
/// These values come from the server, so we set them here explicitly to match those values.
enum ErrorCode: Int {
case notEnoughMemory = 0
case invalidResource = 1
case timeOut = 2
}
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.
// WRONG
var results = [SomeType]()
for element in input {
let result = transform(element)
results.append(result)
}
// RIGHT
let results = input.map { transform($0) }
// WRONG
var results = [SomeType]()
for element in input {
if let result = transformThatReturnsAnOptional(element) {
results.append(result)
}
}
// RIGHT
let results = input.compactMap { transformThatReturnsAnOptional($0) }
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.
// WRONG
enum Fonts {
static var title = UIFont(...)
}
// RIGHT
enum Fonts {
static let title = UIFont(...)
}
// WRONG
struct FeatureState {
var count: Int
static var initial = FeatureState(count: 0)
}
// RIGHT
struct FeatureState {
var count: Int
static var initial: FeatureState {
// Vend static properties that are cheap to compute
FeatureState(count: 0)
}
}
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.
func didSubmitText(_ text: String) {
// It's unclear how this was called with an empty string; our custom text field shouldn't allow this.
// This assert is useful for debugging but it's OK if we simply ignore this scenario in production.
guard !text.isEmpty else {
assertionFailure("Unexpected empty string")
return
}
...
}
func transformedItem(atIndex index: Int, from items: [Item]) -> Item {
precondition(index >= 0 && index < items.count)
// It's impossible to continue executing if the precondition has failed.
...
}
func makeImage(name: String) -> UIImage {
guard let image = UIImage(named: name, in: nil, compatibleWith: nil) else {
fatalError("Image named \(name) couldn't be loaded.")
// We want the error message so we know the name of the missing image.
}
return image
}
#### Why?
Inline generic constraints (<T: Protocol>) are more concise and idiomatic than where clauses (<T> 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.
// WRONG
struct SpaceshipDashboard<Left, Right>: View
where Left: View, Right: View
{
...
}
extension Spaceship {
func fly<Destination>(
to: Destination,
didArrive: (Destination) -> Void
) where Destination: PlanetaryBody {
...
}
}
// RIGHT
struct SpaceshipDashboard<Left: View, Right: View>: View {
...
}
extension Spaceship {
func fly<Destination: PlanetaryBody>(
to: Destination,
didArrive: (Destination) -> Void
) {
...
}
}
// ALSO RIGHT: Complex constraints remain in where clause
struct Galaxy<T: Collection> where T.Element == Star {}
static.#### Why?
If a method needs to be overridden, the author should opt into that functionality by using the class keyword instead.
// WRONG
class Fruit {
class func eatFruits(_ fruits: [Fruit]) { ... }
}
// RIGHT
class Fruit {
static func eatFruits(_ fruits: [Fruit]) { ... }
}
final.
// WRONG
public class SpacecraftEngine {
...
}
// RIGHT
public final class SpacecraftEngine {
...
}
// ALSO RIGHT: Marked as `open`, explicitly intended to be subclassed.
open class SpacecraftEngine {
...
}
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:
public, mark the class as open. open access control indicates that the class is allowed to be subclassed:open class SpacecraftEngine {
...
}
class BaseSpacecraftEngine {
...
}
/// Base class for various spacecraft engine varieties
class SpacecraftEngine {
...
}
class SpacecraftEngine {
...
}
#if DEBUG
class MockSpacecraftEngine: SpacecraftEngine {
...
}
#endif
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.
// NOT PREFERRED
switch trafficLight {
case .greenLight:
// Move your vehicle
default:
// Stop your vehicle
}
// PREFERRED
switch trafficLight {
case .greenLight:
// Move your vehicle
case .yellowLight, .redLight:
// Stop your vehicle
}
// COUNTEREXAMPLES
enum TaskState {
case pending
case running
case canceling
case success(Success)
case failure(Error)
// We expect that this property will remain valid if additional cases are added to the enumeration.
public var isRunning: Bool {
switch self {
case .running:
true
default:
false
}
}
}
extension TaskState: Equatable {
// Explicitly listing each state would be too burdensome. Ideally this function could be implemented with a well-tested macro.
public static func == (lhs: TaskState, rhs: TaskState) -> Bool {
switch (lhs, rhs) {
case (.pending, .pending):
true
case (.running, .running):
true
case (.canceling, .canceling):
true
case (.success(let lhs), .success(let rhs)):
lhs == rhs
case (.failure(let lhs), .failure(let rhs)):
lhs == rhs
default:
false
}
}
}
#### Why?
Checking for nil makes it immediately clear what the intent of the statement is. Optional binding is less explicit.
var thing: Thing?
// WRONG
if let _ = thing {
doThing()
}
// RIGHT
if thing != nil {
doThing()
}
return keyword when not required by the language.
// WRONG
["1", "2", "3"].compactMap { return Int($0) }
var size: CGSize {
return CGSize(
width: 100.0,
height: 100.0
)
}
func makeInfoAlert(message: String) -> UIAlertController {
return UIAlertController(
title: "βΉοΈ Info",
message: message,
preferredStyle: .alert
)
}
var alertTitle: String {
if issue.severity == .critical {
return "π₯ Critical Error"
} else {
return "βΉοΈ Info"
}
}
func type(of planet: Planet) -> PlanetType {
switch planet {
case .mercury, .venus, .earth, .mars:
return .terrestrial
case .jupiter, .saturn, .uranus, .neptune:
return .gasGiant
}
}
// RIGHT
["1", "2", "3"].compactMap { Int($0) }
var size: CGSize {
CGSize(
width: 100.0,
height: 100.0
)
}
func makeInfoAlert(message: String) -> UIAlertController {
UIAlertController(
title: "βΉοΈ Info",
message: message,
preferredStyle: .alert
)
}
var alertTitle: String {
if issue.severity == .critical {
"π₯ Critical Error"
} else {
"βΉοΈ Info"
}
}
func type(of planet: Planet) -> PlanetType {
switch planet {
case .mercury, .venus, .earth, .mars:
.terrestrial
case .jupiter, .saturn, .uranus, .neptune:
.gasGiant
}
}
AnyObject instead of class in protocol definitions.#### Why?
SE-0156, which introduced support for using the AnyObject keyword as a protocol constraint, recommends preferring AnyObject over class:
> This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.
// WRONG
protocol Foo: class { }
// RIGHT
protocol Foo: AnyObject { }
os_log or 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).
#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 Evolution Proposal: Concise magic file names
#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.
// WRONG
lazy var universe: Universe = {
Universe()
}()
lazy var stars = {
universe.generateStars(
at: location,
count: 5,
color: starColor,
withAverageDistance: 4
)
}()
// RIGHT
lazy var universe = Universe()
lazy var stars = universe.generateStars(
at: location,
count: 5,
color: starColor,
withAverageDistance: 4
)
get clause from a computed property declaration that doesn't also have a set, willSet, or didSet clause.
// WRONG
var universe: Universe {
get {
Universe()
}
}
// RIGHT
var universe: Universe {
Universe()
}
// RIGHT
var universe: Universe {
get { multiverseService.current }
set { multiverseService.current = newValue }
}
some) over verbose named generic parameter syntax where possible.#### Why?
Opaque generic parameter syntax is significantly less verbose and thus more legible than the full named generic parameter syntax.
// WRONG
func spaceshipDashboard<WarpDriveView: View, CaptainsLogView: View>(
warpDrive: WarpDriveView,
captainsLog: CaptainsLogView
) -> some View {
...
}
func generate<Planets>(_ planets: Planets) where Planets: Collection, Planets.Element == Planet {
...
}
// RIGHT
func spaceshipDashboard(
warpDrive: some View,
captainsLog: some View
) -> some View {
...
}
func generate(_ planets: some Collection<Planet>) {
...
}
// 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<Body: PlanetaryBody>(_ 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<Body: PlanetaryBody>(_ planetaryBody: Body) {
planetaryBody.generateAtmosphere(Body.idealAtmosphere)
}
#### some Any
Fully-unconstrained generic parameters are somewhat uncommon, but are equivalent to some Any. For example:
func assertFailure<Value>(
_ result: Result<Value, Error>,
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<some Any, Error>,
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.
@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.
There are typically other, safer methods for suppressing concurrency-related errors:
### 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:
// 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
}
// 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
}
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):
// 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
}
### 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.
/// Defined in `UniverseKit` module
class Planet: PlanetaryBody {
var star: Star
}
// WRONG: Unsafely marking a non-thread-safe class as Sendable only to suppress errors
import PlanetaryBody
extension PlanetaryBody: @unchecked Sendable { }
// RIGHT
@preconcurrency import PlanetaryBody
### 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.
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:
class PlanetaryBody {
let mass: Double
}
class Planet: PlanetaryBody {
let star: Star
}
// 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:
// BETTER: Compiler-enforced thread safety.
protocol PlanetaryBody: Sendable {
var mass: Double { get }
}
final class Planet: PlanetaryBody, Sendable {
let mass: Double
let star: Star
}
### 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.
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.
struct Atomic<Value> {
/// `value` is thread-safe because it is manually protected by a lock.
var value: Value { ... }
}
// 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 { }
// 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):
class PlanetaryBody {
let mass: Double
}
class Planet: PlanetaryBody {
let star: Star
}
// 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 { }
// 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 { }
### 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.
// WRONG
var spaceship: Spaceship {
let spaceship = spaceshipBuilder.build(warpDrive: warpDriveBuilder.build())
return spaceship
}
// 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
}
// RIGHT
var spaceship: Spaceship {
let warpDrive = warpDriveBuilder.build()
return spaceshipBuilder.build(warpDrive: warpDrive)
}
### 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.
/// 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
}
}
/// 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
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.
/// 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
}
}
/// 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
}
() as a type. Prefer Void.Void() as an instance of Void. Prefer ().
let completion: (Result<Void, Error>) -> Void
// WRONG
completion(.success(Void()))
// RIGHT
completion(.success(()))
count(where: { ... }) over filter { ... }.count. Swift 6.0 (finally!) added a count(where:) method to the standard library. Prefer using the count(where:) method over using the filter(_:) method followed by a count call.
// WRONG
let planetsWithMoons = planets.filter { !$0.moons.isEmpty }.count
// RIGHT
let planetsWithMoons = planets.count(where: { !$0.moons.isEmpty })
#URL(_:) macro instead of force-unwrapping URL(string:)! initializer.#### 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.
// WRONG
let url = URL(string: "https://example.com")!
// RIGHT
let url = #URL("https://example.com")
#### Why?
// WRONG
// Copyright Β© 2018 Airbnb. All rights reserved.
//
import DLSPrimitives
import Constellation
import Constellation
import Epoxy
import Foundation
// RIGHT
// Copyright Β© 2018 Airbnb. All rights reserved.
//
import Constellation
import DLSPrimitives
import Epoxy
import Foundation
Exception: @testable import should be grouped after the regular import and separated by an empty line.
// WRONG
// Copyright Β© 2018 Airbnb. All rights reserved.
//
import DLSPrimitives
@testable import Epoxy
import Foundation
import Nimble
import Quick
// RIGHT
// Copyright Β© 2018 Airbnb. All rights reserved.
//
import DLSPrimitives
import Foundation
import Nimble
import Quick
@testable import Epoxy
// WRONG
struct Planet {
let mass: Double
let hasAtmosphere: Bool
func distance(to: Planet) { }
}
// RIGHT
struct Planet {
let mass: Double
let hasAtmosphere: Bool
func distance(to: Planet) { }
}
#### Why?
Dividing scoped declarations from other declarations at the same scope visually separates them, making adjacent declarations easier to differentiate from the scoped declaration.
// WRONG
struct SolarSystem {
var numberOfPlanets: Int {
...
}
func distance(to: SolarSystem) -> AstronomicalUnit {
...
}
}
struct Galaxy {
func distance(to: Galaxy) -> AstronomicalUnit {
...
}
func contains(_ solarSystem: SolarSystem) -> Bool {
...
}
}
// RIGHT
struct SolarSystem {
var numberOfPlanets: Int {
...
}
func distance(to: SolarSystem) -> AstronomicalUnit {
...
}
}
struct Galaxy {
func distance(to: Galaxy) -> AstronomicalUnit {
...
}
func contains(_ solarSystem: SolarSystem) -> Bool {
...
}
}
// WRONG
class Planet {
func terraform() {
generateAtmosphere()
generateOceans()
}
}
// RIGHT
class Planet {
func terraform() {
generateAtmosphere()
generateOceans()
}
}
// Also fine!
class Planet {
func terraform() {
generateAtmosphere()
generateOceans()
}
}
MARK comment.// MARK: - TypeName comment.// MARK: - TypeName + ProtocolName comment.// MARK: ProtocolName.MARK comment can be omitted.MARK comment can be omitted.MARK with a descriptive comment.
// MARK: - GalaxyView
final class GalaxyView: UIView { ... }
// MARK: ContentConfigurableView
extension GalaxyView: ContentConfigurableView { ... }
// MARK: - Galaxy + SpaceThing, NamedObject
extension Galaxy: SpaceThing, NamedObject { ... }
// 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.// MARK:.internal properties), it is OK to omit the // MARK:s.// MARK:s, as it would hurt legibility.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.)
// WRONG
class PlanetView: UIView {
static var startOfTime { -CGFloat.greatestFiniteMagnitude / 0 }
var atmosphere: Atmosphere {
didSet {
print("oh my god, the atmosphere changed")
}
}
override class var layerClass: AnyClass {
PlanetLayer.self
}
var gravity: CGFloat
static let speedOfLight: CGFloat = 300_000
}
// RIGHT
class PlanetView: UIView {
static let speedOfLight: CGFloat = 300_000
static var startOfTime { -CGFloat.greatestFiniteMagnitude / 0 }
override class var layerClass: AnyClass {
PlanetLayer.self
}
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 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.
// WRONG
struct CustomSlider: View {
// MARK: Internal
var body: some View {
...
}
// MARK: Private
@Binding private var value: Value
private let range: ClosedRange<Double>
@Environment(\.sliderStyle) private var style
private let step: Double.Stride
@Environment(\.layoutDirection) private var layoutDirection
}
// RIGHT
struct CustomSlider: View {
// MARK: Internal
var body: some View {
...
}
// MARK: Private
@Environment(\.sliderStyle) private var style
@Environment(\.layoutDirection) private var layoutDirection
@Binding private var value: Value
private let range: ClosedRange<Double>
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:
// 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
private let range: ClosedRange<Double>
private let step: Double.Stride
}
// 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
private let range: ClosedRange<Double>
private let step: Double.Stride
}
// WRONG
static let gravityEarth: CGFloat = 9.8
static let gravityMoon: CGFloat = 1.6
var gravity: CGFloat
// RIGHT
static let gravityEarth: CGFloat = 9.8
static let gravityMoon: CGFloat = 1.6
var gravity: CGFloat
#### Why?
// WRONG
let mercury, venus: Planet
let earth = planets[2], mars = planets[3]
let (jupiter, saturn) = (planets[4], planets[5])
enum IceGiants {
case neptune, uranus
}
// RIGHT
let mercury: Planet
let venus: Planet
let earth = planets[2]
let mars = planets[3]
let jupiter = planets[4]
let saturn = planets[5]
enum IceGiants {
case neptune
case uranus
}
// ALSO RIGHT: Tuple destructing is fine for values like function call results.
let (ceres, pluto) = findAndClassifyDwarfPlanets()
#### Why?
Improves readability since the code has no effect and should be removed for clarity.
// WRONG: The first extension is empty and redundant.
extension Planet {}
extension Planet: Equatable {}
// RIGHT: Empty extensions that add a protocol conformance aren't redundant.
extension Planet: Equatable {}
#### Why?
Using internal properties allows the compiler to synthesize the default internal memberwise initializer, reducing boilerplate.
// WRONG
struct PlanetView: View {
// MARK: Lifecycle
init(planet: Planet, star: Star) {
self.planet = planet
self.star = star
}
// MARK: Internal
var body: some View {
...
}
// MARK: Private
private let planet: Planet
private let star: Star
}
// RIGHT
struct PlanetView: View {
let planet: Planet
let star: Star
var body: some View {
...
}
}
This doesn't apply to SwiftUI dynamic properties, which should always be left private.
// RIGHT
struct PlanetView: View {
// MARK: Internal
let planet: Planet
let star: Star
var body: some View {
...
}
// MARK: Private
@State private var isRotating = false
@Environment(\.dismiss) private var dismiss
}
@Entry macro to define properties inside EnvironmentValues. When adding properties to SwiftUI EnvironmentValues, prefer using the compiler-synthesized property implementation when possible.### 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.
/// 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 }
}
}
/// RIGHT: The `EnvironmentValues` property uses the @Entry macro
extension EnvironmentValues {
@Entry var isSelected: Bool = false
}
@ViewBuilder when it is not required. @ViewBuilder is implicit on View.body properties and ViewModifier.body(content:) functions, and is unnecessary on single-expression properties or functions.#### Why?
@ViewBuilder is automatically applied by the compiler to View.body and ViewModifier.body(content:), so adding it explicitly is redundant. Similarly, single-expression properties and functions don't need @ViewBuilder since there's only one view being returned.
// WRONG
struct PlanetView: View {
@ViewBuilder
var body: some View {
Text("Hello, World!")
}
@ViewBuilder
var subtitle: some View {
Text("Subtitle")
}
}
// RIGHT
struct PlanetView: View {
var body: some View {
Text("Hello, World!")
}
var subtitle: some View {
Text("Subtitle")
}
}
// ALSO RIGHT: @ViewBuilder is necessary for conditionals
struct ConditionalView: View {
var showDetails: Bool
var body: some View {
title
}
@ViewBuilder
var title: some View {
if showDetails {
Text("Details")
} else {
Text("Summary")
}
}
}
test".### Why?
Prefixing test case methods with "test" was necessary with XCTest, but is not necessary in Swift Testing. Idiomatic usage of Swift Testing excludes the "test" prefix.
import Testing
/// WRONG
struct SpaceshipTests {
@Test
func testWarpDriveEnablesFTLTravel() { ... }
@Test
func testArtificialGravityMatchesEarthGravity() { ... }
}
/// RIGHT
struct SpaceshipTests {
@Test
func warpDriveEnablesFTLTravel() { ... }
@Test
func artificialGravityMatchesEarthGravity() { ... }
}
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.
import XCTest
final class SomeTestCase: XCTestCase {
func test_something() throws {
// WRONG:
guard let value = optionalValue, value.matchesCondition else {
XCTFail()
return
}
// RIGHT:
let value = try XCTUnwrap(optionalValue)
XCTAssert(value.matchesCondition)
}
}
import Testing
struct SomeTests {
@Test
func something() throws {
// WRONG:
guard let value = optionalValue, value.matchesCondition {
return
}
// RIGHT:
let value = try #require(optionalValue)
#expect(value.matchesCondition)
}
}
try!. try! will crash your test suite like a force-unwrapped optional. XCTest and Swift Testing support throwing test methods, so use that instead.
import XCTest
final class SomeTestCase: XCTestCase {
// WRONG
func test_something() {
try! Something().doSomething()
}
// RIGHT
func test_something() throws {
try Something().doSomething()
}
}
import Testing
struct SomeTests {
// WRONG
@Test
func something() {
try! Something().doSomething()
}
// RIGHT
@Test
func something() throws {
try Something().doSomething()
}
}
internal, and helper methods and properties should be private.#### 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.
import Testing
// WRONG
struct SpaceshipTests {
let spaceship = Spaceship()
func launchSpaceship() {
spaceship.launch()
}
@Test
func spaceshipCanLaunch() {
launchSpaceship()
#expect(spaceship.hasLaunched)
}
}
// RIGHT
struct SpaceshipTests {
// MARK: Internal
@Test
func spaceshipCanLaunch() {
launchSpaceship()
#expect(spaceship.hasLaunched)
}
// MARK: Private
private let spaceship = Spaceship()
private func launchSpaceship() {
spaceship.launch()
}
}
import XCTest
// WRONG
final class SpaceshipTests: XCTestCase {
let spaceship = Spaceship()
func launchSpaceship() {
spaceship.launch()
}
func testSpaceshipCanLaunch() {
launchSpaceship()
XCTAssertTrue(spaceship.hasLaunched)
}
}
// RIGHT
final class SpaceshipTests: XCTestCase {
// MARK: Internal
func testSpaceshipCanLaunch() {
launchSpaceship()
XCTAssertTrue(spaceship.hasLaunched)
}
// MARK: Private
private let spaceship = Spaceship()
private func launchSpaceship() {
spaceship.launch()
}
}
!) will crash your test suite. Use safe alternatives like try XCTUnwrap or try #require, which will throw an error instead, or standard optional unwrapping (?).
import XCTest
final class SpaceshipTests: XCTestCase {
// WRONG
func testCanLaunchSpaceship() {
let spaceship = (dependencies!.shipyardService as! DefaultShipyardService).build()
spaceship.engine!.prepare()
spaceship.launch(to: nearestPlanet()!)
XCTAssertTrue(spaceship.hasLaunched)
XCTAssertEqual(spaceship.destination! as! Planet, nearestPlanet())
}
// RIGHT
func testCanLaunchSpaceship() throws {
let spaceship = try XCTUnwrap((dependencies?.shipyardService as? DefaultShipyardService)?.build())
spaceship.engine?.prepare()
spaceship.launch(to: try XCTUnwrap(nearestPlanet()))
XCTAssertTrue(spaceship.hasLaunched)
XCTAssertEqual(spaceship.destination as? Planet, nearestPlanet())
}
}
import Testing
struct SpaceshipTests {
// WRONG
@Test
func canLaunchSpaceship() {
let spaceship = (dependencies!.shipyardService as! DefaultShipyardService).build()
spaceship.engine!.prepare()
spaceship.launch(to: nearestPlanet()!)
#expect(spaceship.hasLaunched)
#expect((spaceship.destination! as! Planet) == nearestPlanet())
}
// RIGHT
@Test
func canLaunchSpaceship() throws {
let spaceship = try #require((dependencies?.shipyardService as? DefaultShipyardService)?.build())
spaceship.engine?.prepare()
spaceship.launch(to: try #require(nearestPlanet()))
#expect(spaceship.hasLaunched)
#expect((spaceship.destination as? Planet) == nearestPlanet())
}
}
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.
import XCTest
final class PlanetTests: XCTestCase {
// WRONG
func test_habitability() async throws {
XCTAssertTrue(earth.isHabitable)
XCTAssertFalse(mars.isHabitable)
}
// RIGHT
func test_habitability() {
XCTAssertTrue(earth.isHabitable)
XCTAssertFalse(mars.isHabitable)
}
}
import Testing
struct PlanetTests {
// WRONG
@Test
func habitability() async throws {
#expect(earth.isHabitable)
#expect(!mars.isHabitable)
}
// RIGHT
@Test
func habitability() {
#expect(earth.isHabitable)
#expect(!mars.isHabitable)
}
}
@objc on individual methods and variables as necessary rather than exposing all API on a class to Objective-C via @objcMembers.
class PriceBreakdownViewController {
private let acceptButton = UIButton()
private func setUpAcceptButton() {
acceptButton.addTarget(
self,
action: #selector(didTapAcceptButton),
forControlEvents: .touchUpInside
)
}
@objc
private func didTapAcceptButton() {
...
}
}
We encourage you to fork this guide and change the rules to fit your teamβs style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.