SkillAgentSearch skills...

Swift

Airbnb's Swift Style Guide

Install / Use

/learn @airbnb/Swift
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Airbnb Swift Style Guide

Goals

Following this style guide should:

  • Make it easier to read and begin understanding unfamiliar code.
  • Make code easier to maintain.
  • Reduce simple programmer errors.
  • Reduce cognitive load while coding.
  • Keep discussions on diffs focused on the code's logic rather than its style.

Note that brevity is not a primary goal. Code should be made more concise only if other good code qualities (such as readability, simplicity, and clarity) remain equal or are improved.

Guiding Tenets

  • This guide is in addition to the official Swift API Design Guidelines. These rules should not contradict that document.
  • These rules should not fight Xcode's <kbd>^</kbd> + <kbd>I</kbd> indentation behavior.
  • We strive to make every rule lintable:
    • If a rule changes the format of the code, it needs to be able to be reformatted automatically (either using SwiftFormat or SwiftLint autocorrect).
    • For rules that don't directly change the format of the code, we should have a lint rule that throws a warning.
    • Exceptions to these rules should be rare and heavily justified.
  • Format rules should be non-destructive.

Swift Package Manager command plugin

We offer 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
<details> <summary>Usage guide</summary>
# Prompts for permission to write to the package directory.
$ swift package format

# When using a noninteractive shell, you can 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 6.2

The package plugin returns a non-zero exit code if there is a lint failure that requires attention.

  • In --lint mode, any lint failure from any tool will result in a non-zero exit code.
  • In standard autocorrect mode without --lint, only failures from SwiftLint lint-only rules will result in a non-zero exit code.
</details>

Table of Contents

  1. Xcode Formatting
  2. Naming
  3. Style
    1. Functions
    2. Closures
    3. Operators
  4. Patterns
  5. File Organization
  6. SwiftUI
  7. Testing
  8. Contributors
  9. Amendments

Xcode Formatting

You can enable the following settings in Xcode by running this script, e.g. as part of a "Run Script" build phase.

  • <a id='column-width'></a>(<a href='#column-width'>link</a>) Each line should have a maximum column width of 100 characters.

    <details>

    SwiftFormat: wrap

    Why?

    Due to larger screen sizes, we have opted to choose a page guide greater than 80.

    We currently only "strictly enforce" (lint / auto-format) a maximum column width of 130 characters to limit the cases where manual clean up is required for reformatted lines that fall slightly above the threshold.

    </details>
  • <a id='spaces-over-tabs'></a>(<a href='#spaces-over-tabs'>link</a>) Use 2 spaces to indent lines.

    <details>

    SwiftFormat: indent

    </details>
  • <a id='trailing-whitespace'></a>(<a href='#trailing-whitespace'>link</a>) Trim trailing whitespace in all lines.

    <details>

    SwiftFormat: trailingSpace

    </details>

⬆ back to top

Naming

  • <a id='use-camel-case'></a>(<a href='#use-camel-case'>link</a>) Use UpperCamelCase for type and protocol names, and lowerCamelCase for everything else.

    <details>
    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.

    • Type erasure
    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
    }
    
    • Backing a less specific type with a more specific type
    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
      }
    }
    
    </details>
  • <a id='bool-names'></a>(<a href='#bool-names'>link</a>) Name booleans like isSpaceship, hasSpacesuit, etc. This makes it clear that they are booleans and not other types.

  • <a id='capitalize-acronyms'></a>(<a href='#capitalize-acronyms'>link</a>) Acronyms in names (ID, URL, etc) 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.

    <details>
    // 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)
    
    </details>
  • <a id='past-tense-events'></a>(<a href='#past-tense-events'>link</a>) Event-handling functions should be named like past-tense sentences (e.g. didTap, not handleTap). The subject can be omitted if it's not needed for clarity.

    <details>
    // WRONG
    class ExperiencesViewController {
    
      private func handleBookButtonTap() {
        ...
      }
    
      private func modelChanged() {
        ...
      }
    }
    
    // RIGHT
    class ExperiencesViewController {
    
      private func didTapBookButton() {
        ...
      }
    
      private func modelDidChange() {
        ...
      }
    }
    
    </details>
  • <a id='avoid-class-prefixes'></a>(<a href='#avoid-class-prefixes'>link</a>) Avoid Objective-C-style acronym prefixes. This is not needed to avoid naming conflicts in Swift.

    <details>
    // WRONG
    class AIRAccount {
      ...
    }
    
    // RIGHT
    class Account {
      ...
    }
    
    </details>

⬆ back to top

Style

  • <a id='use-implicit-types'></a>(<a href='#use-implicit-types'>link</a>) Don't include types where they can be easily inferred.

    <details> <!-- ai-skill-include: not fully autocorrectable -->

    SwiftFormat: redundantType

    // 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
    le
    

Related Skills

View on GitHub
GitHub Stars2.7k
CategoryDevelopment
Updated3h ago
Forks345

Languages

Markdown

Security Score

100/100

Audited on Apr 11, 2026

No findings