Airbnb's Swift Style Guide.

Last update: May 20, 2022

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 ^ + I indentation behavior.
  • We strive to make every rule lintable:
    • If a rule changes the format of the code, it needs to be able to be reformatted automatically (either using SwiftLint autocorrect or SwiftFormat).
    • 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.

Table of Contents

  1. Xcode Formatting
  2. Naming
  3. Style
    1. Functions
    2. Closures
    3. Operators
  4. Patterns
  5. File Organization
  6. Objective-C Interoperability
  7. Contributors
  8. 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.

  • (link) Each line should have a maximum column width of 100 characters.

    Why?

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

  • (link) Use 2 spaces to indent lines. SwiftFormat: indent

  • (link) Trim trailing whitespace in all lines. SwiftFormat: trailingSpace

back to top

Naming

  • (link) Use PascalCase for type and protocol names, and lowerCamelCase for everything else. SwiftLint: type_name

    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 a property or method 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, session, parser, onSuccess, onFailure)
      }
    
      private let _executeRequest: (
        URLRequest,
        @escaping (ModelType, Bool) -> Void,
        @escaping (NSError) -> 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
      }
    }
  • (link) Name booleans like isSpaceship, hasSpacesuit, etc. This makes it clear that they are booleans and not other types.

  • (link) Acronyms in names (e.g. URL) should be all-caps except when it’s the start of a name that would otherwise be lowerCamelCase, in which case it should be uniformly lower-cased.

    // 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)
  • (link) Names should be written with their most general part first and their most specific part last. The meaning of "most general" depends on context, but should roughly mean "that which most helps you narrow down your search for the item you're looking for." Most importantly, be consistent with how you order the parts of your name.

    // 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
  • (link) Include a hint about type in a name if it would otherwise be ambiguous.

    // WRONG
    let title: String
    let cancel: UIButton
    
    // RIGHT
    let titleText: String
    let cancelButton: UIButton
  • (link) Event-handling functions should be named like past-tense sentences. The subject can be omitted if it's not needed for clarity.

    // WRONG
    class ExperiencesViewController {
    
      private func handleBookButtonTap() {
        // ...
      }
    
      private func modelChanged() {
        // ...
      }
    }
    
    // RIGHT
    class ExperiencesViewController {
    
      private func didTapBookButton() {
        // ...
      }
    
      private func modelDidChange() {
        // ...
      }
    }
  • (link) Avoid Objective-C-style acronym prefixes. This is no longer needed to avoid naming conflicts in Swift.

    // WRONG
    class AIRAccount {
      // ...
    }
    
    // RIGHT
    class Account {
      // ...
    }
  • (link) Avoid *Controller in names of classes that aren't view controllers.

    Why?

    Controller is an overloaded suffix that doesn't provide information about the responsibilities of the class.

back to top

Style

  • (link) Don't include types where they can be easily inferred. SwiftFormat: redundantType

    // WRONG
    let host: Host = Host()
    
    // RIGHT
    let host = Host()
    enum Direction {
      case left
      case right
    }
    
    func someDirection() -> Direction {
      // WRONG
      return Direction.left
    
      // RIGHT
      return .left
    }
  • (link) Don't use self unless it's necessary for disambiguation or required by the language. SwiftFormat: redundantSelf

    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()
      }
    }
  • (link) Bind to self when upgrading from a weak reference. SwiftFormat: strongifiedSelf

    //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 = self else { return }
          // Do work
          completion()
        }
      }
    }
  • (link) Add a trailing comma on the last element of a multi-line array. SwiftFormat: trailingCommas

    // WRONG
    let rowContent = [
      listingUrgencyDatesRowContent(),
      listingUrgencyBookedRowContent(),
      listingUrgencyBookedShortRowContent()
    ]
    
    // RIGHT
    let rowContent = [
      listingUrgencyDatesRowContent(),
      listingUrgencyBookedRowContent(),
      listingUrgencyBookedShortRowContent(),
    ]
  • (link) Name members of tuples for extra clarity. Rule of thumb: if you've got more than 3 fields, you should probably be using a struct.

    // 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
  • (link) Place the colon immediately after an identifier, followed by a space. SwiftLint: colon

    // WRONG
    var something : Double = 0
    
    // RIGHT
    var something: Double = 0
    // WRONG
    class MyClass : SuperClass {
      // ...
    }
    
    // RIGHT
    class MyClass: SuperClass {
      // ...
    }
    // WRONG
    var dict = [KeyType:ValueType]()
    var dict = [KeyType : ValueType]()
    
    // RIGHT
    var dict = [KeyType: ValueType]()
  • (link) Place a space on either side of a return arrow for readability. SwiftLint: return_arrow_whitespace

    // WRONG
    func doSomething()->String {
      // ...
    }
    
    // RIGHT
    func doSomething() -> String {
      // ...
    }
    // WRONG
    func doSomething(completion: ()->Void) {
      // ...
    }
    
    // RIGHT
    func doSomething(completion: () -> Void) {
      // ...
    }
  • (link) Omit unnecessary parentheses. SwiftFormat: redundantParens

    // WRONG
    if (userCount > 0) { ... }
    switch (someValue) { ... }
    let evens = userCounts.filter { (number) in number % 2 == 0 }
    let squares = userCounts.map() { $0 * $0 }
    
    // RIGHT
    if userCount > 0 { ... }
    switch someValue { ... }
    let evens = userCounts.filter { number in number % 2 == 0 }
    let squares = userCounts.map { $0 * $0 }
  • (link) Omit enum associated values from case statements when all arguments are unlabeled. SwiftLint: empty_enum_arguments

    // WRONG
    if case .done(_) = result { ... }
    
    switch animal {
    case .dog(_, _, _):
      ...
    }
    
    // RIGHT
    if case .done = result { ... }
    
    switch animal {
    case .dog:
      ...
    }
  • (link) When destructuring an enum case or a tuple, place the let keyword inline, adjacent to each individual property assignment. SwiftFormat: hoistPatternLet

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

    1. Consistency: We should prefer to either always inline the let keyworkd 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).

    2. Clarity: Inlining the let keyword makes it more clear which identifiers are part of the conditional check and which identifiers are binding new variables, since the let keyword is always adjacent to the variable identifier.

    // `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 its less obvious that they represent new variable bindings
    case let .enumCaseWithSingleAssociatedValue(string):
    case let .enumCaseWithMultipleAssociatedValues(string, int):
    
  • (link) Place function/type attributes on the line above the declaration. SwiftFormat: wrapAttributes

    // WRONG
    @objc class Spaceship {
    
      @discardableResult func fly() -> Bool {
      }
    }
    
    // RIGHT
    
    @objc
    class Spaceship {
    
      @discardableResult
      func fly() -> Bool {
      }
    }
  • (link) Multi-line arrays should have each bracket on a separate line. Put the opening and closing brackets on separate lines from any of the elements of the array. Also add a trailing comma on the last element. SwiftFormat: wrapArguments

    // WRONG
    let rowContent = [listingUrgencyDatesRowContent(),
                      listingUrgencyBookedRowContent(),
                      listingUrgencyBookedShortRowContent()]
    
    let rowContent = [
      listingUrgencyDatesRowContent(),
      listingUrgencyBookedRowContent(),
      listingUrgencyBookedShortRowContent()
    ]
    
    // RIGHT
    let rowContent = [
      listingUrgencyDatesRowContent(),
      listingUrgencyBookedRowContent(),
      listingUrgencyBookedShortRowContent(),
    ]
  • (link) Multi-line conditional statements should break after the leading keyword. Indent each individual statement by 2 spaces. SwiftFormat: wrapArguments

    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,
      galaxy.name == "Milky Way" // Indenting by two spaces fights Xcode's ^+I indentation behavior
    {  }
    
    // WRONG
    guard let galaxy = galaxy,
          galaxy.name == "Milky Way" // Variable width indentation (6 spaces)
    else {  }
    
    // WRONG
    guard let earth = unvierse.find(
      .planet,
      named: "Earth"),
      earth.isHabitable // Blends in with previous condition's method arguments
    else {  }
    
    // RIGHT
    if
      let galaxy = galaxy,
      galaxy.name == "Milky Way"
    {  }
    
    // RIGHT
    guard
      let galaxy = galaxy,
      galaxy.name == "Milky Way"
    else {  }
    
    // RIGHT
    guard
      let earth = unvierse.find(
        .planet,
        named: "Earth"),
      earth.isHabitable
    else {  }
    
    // RIGHT
    if let galaxy = galaxy {
      
    }
    
    // RIGHT
    guard let galaxy = galaxy else {
      
    }
  • (link) Use constructors instead of Make() functions for NSRange and others. SwiftLint: legacy_constructor

    // WRONG
    let range = NSMakeRange(10, 5)
    
    // RIGHT
    let range = NSRange(location: 10, length: 5)
  • (link) For standard library types with a canonical shorthand form (Optional, Array, Dictionary), prefer using the shorthand form over the full generic form. SwiftFormat: typeSugar

    // 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] = [:]

Functions

  • (link) Omit Void return types from function definitions. SwiftLint: redundant_void_return

    // WRONG
    func doSomething() -> Void {
      ...
    }
    
    // RIGHT
    func doSomething() {
      ...
    }
  • (link) Separate long function declarations with line breaks before each argument label and before the return signature. Put the open curly brace on the next line so the first executable line doesn't look like it's another parameter. SwiftFormat: wrapArguments SwiftFormat: wrapMultilineStatementBraces

    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
      }
    
      // 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)
        throws -> String
      {
        populateUniverse()
      }
    }
  • (link) Long function invocations should also break on each argument. Put the closing parenthesis on the last line of the invocation. SwiftFormat: wrapArguments

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

Closures

  • (link) Favor Void return types over () in closure declarations. If you must specify a Void return type in a function declaration, use Void rather than () to improve readability. SwiftLint: void_return

    // WRONG
    func method(completion: () -> ()) {
      ...
    }
    
    // RIGHT
    func method(completion: () -> Void) {
      ...
    }
  • (link) Name unused closure parameters as underscores (_). SwiftLint: unused_closure_parameter

    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)
    }
  • (link) Single-line closures should have a space inside each brace. SwiftLint: closure_spacing

    // WRONG
    let evenSquares = numbers.filter {$0 % 2 == 0}.map {  $0 * $0  }
    
    // RIGHT
    let evenSquares = numbers.filter { $0 % 2 == 0 }.map { $0 * $0 }

Operators

  • (link) Infix operators should have a single space on either side. Prefer parenthesis to visually group statements with many operators rather than varying widths of whitespace. This rule does not apply to range operators (e.g. 1...3) and postfix or prefix operators (e.g. guest? or -1). SwiftLint: operator_usage_whitespace

    // WRONG
    let capacity = 1+2
    let capacity = currentCapacity   ?? 0
    let mask = (UIAccessibilityTraitButton|UIAccessibilityTraitSelected)
    let capacity=newCapacity
    let latitude = region.center.latitude - region.span.latitudeDelta/2.0
    
    // RIGHT
    let capacity = 1 + 2
    let capacity = currentCapacity ?? 0
    let mask = (UIAccessibilityTraitButton | UIAccessibilityTraitSelected)
    let capacity = newCapacity
    let latitude = region.center.latitude - (region.span.latitudeDelta / 2.0)

back to top

Patterns

  • (link) Prefer initializing properties at init time whenever possible, rather than using implicitly unwrapped optionals. A notable exception is UIViewController's view property. SwiftLint: implicitly_unwrapped_optional

    // WRONG
    class MyClass {
    
      init() {
        super.init()
        someValue = 5
      }
    
      var someValue: Int!
    }
    
    // RIGHT
    class MyClass {
    
      init() {
        someValue = 0
        super.init()
      }
    
      var someValue: Int
    }
  • (link) Avoid performing any meaningful or time-intensive work in init(). Avoid doing things like opening database connections, making network requests, reading large amounts of data from disk, etc. Create something like a start() method if these things need to be done before an object is ready for use.

  • (link) Extract complex property observers into methods. This reduces nestedness, separates side-effects from property declarations, and makes the usage of implicitly-passed parameters like oldValue explicit.

    // 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.
      }
    }
  • (link) Extract complex callback blocks into methods. This limits the complexity introduced by weak-self in blocks and reduces nestedness. If you need to reference self in the method call, make use of guard to unwrap self for the duration of the callback.

    //WRONG
    class MyClass {
    
      func request(completion: () -> Void) {
        API.request() { [weak self] response in
          if let self = self {
            // Processing and side effects
          }
          completion()
        }
      }
    }
    
    // RIGHT
    class MyClass {
    
      func request(completion: () -> Void) {
        API.request() { [weak self] response in
          guard let self = self else { return }
          self.doSomething(with: self.property, response: response)
          completion()
        }
      }
    
      func doSomething(with nonOptionalParameter: SomeClass, response: SomeResponseClass) {
        // Processing and side effects
      }
    }
  • (link) Prefer using guard at the beginning of a scope.

    Why?

    It's easier to reason about a block of code when all guard statements are grouped together at the top rather than intermixed with business logic.

  • (link) Access control should be at the strictest level possible. Prefer public to open and private to fileprivate unless you need that behavior.

  • (link) Avoid global functions whenever possible. Prefer methods within type definitions.

    // WRONG
    func age(of person, bornAt timeInterval) -> Int {
      // ...
    }
    
    func jump(person: Person) {
      // ...
    }
    
    // RIGHT
    class Person {
      var bornAt: TimeInterval
    
      var age: Int {
        // ...
      }
    
      func jump() {
        // ...
      }
    }
  • (link) Use caseless enums for organizing public or internal constants and functions into namespaces.

    • Avoid creating non-namespaced global constants and functions.
    • Feel free to nest namespaces where it adds clarity.
    • private globals are permitted, since they are scoped to a single file and do not pollute the global namespace. Consider placing private globals in an enum namespace to match the guidelines for other declaration types.

    Why?

    Caseless enums work well as namespaces because they cannot be instantiated, which matches their intent.

    enum Environment {
    
      enum Earth {
        static let gravity = 9.8
      }
    
      enum Moon {
        static let gravity = 1.6
      }
    }
  • (link) Use Swift's automatic enum values unless they map to an external source. Add a comment explaining why explicit values are defined. SwiftLint: redundant_string_enum_value

    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 explicity, 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"
    }
    
    enum UserType: String {
      case owner
      case manager
      case member
    }
    
    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
    }
    
    enum ErrorCode: Int {
      case notEnoughMemory
      case invalidResource
      case timeOut
    }
    
    // RIGHT
    enum ErrorType: String {
      case error
      case warning
    }
    
    /// These are written to a logging service. Explicit values ensure they're consistent across binaries.
    // swiftlint:disable redundant_string_enum_value
    enum UserType: String {
      case owner = "owner"
      case manager = "manager"
      case member = "member"
    }
    // swiftlint:enable redundant_string_enum_value
    
    enum Planet: Int {
      case mercury
      case venus
      case earth
      case mars
      case jupiter
      case saturn
      case uranus
      case neptune
    }
    
    /// 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
    }
  • (link) Use optionals only when they have semantic meaning.

  • (link) Prefer immutable values whenever possible. Use map and compactMap instead of appending to a new collection. Use filter instead of removing elements from a mutable collection.

    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) }
  • (link) Prefer immutable or computed static properties over mutable ones whenever possible. Use stored static let properties or computed static var properties over stored static vars 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)
      }
    }
  • (link) Handle an unexpected but recoverable condition with an assert method combined with the appropriate logging in production. If the unexpected condition is not recoverable, prefer a precondition method or fatalError(). This strikes a balance between crashing and providing insight into unexpected conditions in the wild. Only prefer fatalError over a precondition method when the failure message is dynamic, since a precondition method won't report the message in the crash report. SwiftLint: fatal_error_message SwiftLint: force_cast SwiftLint: force_try SwiftLint: force_unwrapping

    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
    }
  • (link) Default type methods to static.

    Why?

    If a method needs to be overridden, the author should opt into that functionality by using the class keyword instead.

    // WRONG
    class Fruit {
      class func eatFruits(_ fruits: [Fruit]) { ... }
    }
    
    // RIGHT
    class Fruit {
      static func eatFruits(_ fruits: [Fruit]) { ... }
    }
  • (link) Default classes to final.

    Why?

    If a class needs to be overridden, the author should opt into that functionality by omitting the final keyword.

    // WRONG
    class SettingsRepository {
      // ...
    }
    
    // RIGHT
    final class SettingsRepository {
      // ...
    }
  • (link) Never use the default case when switching over an enum.

    Why?

    Enumerating every case requires developers and reviewers have to consider the correctness of every switch statement when new cases are added.

    // WRONG
    switch anEnum {
    case .a:
      // Do something
    default:
      // Do something else.
    }
    
    // RIGHT
    switch anEnum {
    case .a:
      // Do something
    case .b, .c:
      // Do something else.
    }
  • (link) Check for nil rather than using optional binding if you don't need to use the value. SwiftLint: unused_optional_binding

    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()
    }
  • (link) Omit the return keyword when not required by the language. SwiftFormat: redundantReturn

    // 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)
    }
    
    // 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)
    }
  • (link) Use AnyObject instead of class in protocol definitions. SwiftFormat: anyObjectProtocol

    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 {}
  • (link) Specify the access control for each declaration in an extension individually. SwiftFormat: extensionAccessControl

    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() { }
    }
  • (link) Prefer dedicated logging systems like 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).

back to top

File Organization

  • (link) Alphabetize and deduplicate module imports within a file. Place all imports at the top of the file below the header comments. Do not add additional line breaks between import statements. Add a single empty line before the first import and after the last import. SwiftFormat: sortedImports SwiftFormat: duplicateImports

    Why?

    • A standard organization method helps engineers more quickly determine which modules a file depends on.
    • Duplicated import statements have no effect and should be removed for clarity.
    // 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
  • (link) Limit consecutive whitespace to one blank line or space (excluding indentation). Favor the following formatting guidelines over whitespace of varying heights or widths. SwiftLint: vertical_whitespace SwiftFormat: consecutiveSpaces

    // 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) { }
    
    }
  • (link) Files should end in a newline. SwiftLint: trailing_newline

  • (link) Each type and extension which implements a conformance should be preceded by a MARK comment. SwiftFormat: markTypes

    • Types should be preceded by a // MARK: - TypeName comment.
    • Extensions that add a conformance should be preceded by a // MARK: - TypeName + ProtocolName comment.
    • Extensions that immediately follow the type being extended should omit that type's name and instead use // MARK: ProtocolName.
    • If there is only one type or extension in a file, the MARK comment can be omitted.
    • If the extension in question is empty (e.g. has no declarations in its body), the MARK comment can be omitted.
    • For extensions that do not add new conformances, consider adding a MARK with a descriptive comment.
    // MARK: - GalaxyView
    
    final class GalaxyView: UIView {  }
    
    // MARK: ContentConfigurableView
    
    extension GalaxyView: ContentConfigurableView {  }
    
    // MARK: - Galaxy + SpaceThing, NamedObject
    
    extension Galaxy: SpaceThing, NamedObject {  }
  • (link) Use // MARK: to separate the contents of type definitions and extensions into the sections listed below, in order. All type definitions and extensions should be divided up in this consistent way, allowing a reader of your code to easily jump to what they are interested in. SwiftFormat: organizeDeclarations

    • // MARK: Lifecycle for init and deinit methods.
    • // MARK: Open for open properties and methods.
    • // MARK: Public for public properties and methods.
    • // MARK: Internal for internal properties and methods.
    • // MARK: Fileprivate for fileprivate properties and methods.
    • // MARK: Private for private properties and methods.
    • If the type in question is an enum, its cases should go above the first // MARK:.
    • Do not subdivide each of these sections into subsections, as it makes the method dropdown more cluttered and therefore less useful. Instead, group methods by functionality and use smart naming to make clear which methods are related. If there are enough methods that sub-sections seem necessary, consider refactoring your code into multiple types.
    • If all of the type or extension's definitions belong to the same category (e.g. the type or extension only consists of internal properties), it is OK to omit the // MARK:s.
    • If the type in question is a simple value type (e.g. fewer than 20 lines), it is OK to omit the // MARK:s, as it would hurt legibility.
  • (link) Within each top-level section, place content in the following order. This allows a new reader of your code to more easily find what they are looking for. SwiftFormat: organizeDeclarations

    • Nested types and typealiases
    • Static properties
    • Class properties
    • Instance properties
    • Static methods
    • Class methods
    • Instance methods
  • (link) Add empty lines between property declarations of different kinds. (e.g. between static properties and instance properties.) SwiftFormat: organizeDeclarations

    // 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
  • (link) 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.) SwiftFormat: organizeDeclarations

    // WRONG
    var atmosphere: Atmosphere {
      didSet {
        print("oh my god, the atmosphere changed")
      }
    }
    var gravity: CGFloat
    
    // RIGHT
    var gravity: CGFloat
    var atmosphere: Atmosphere {
      didSet {
        print("oh my god, the atmosphere changed")
      }
    }

back to top

Objective-C Interoperability

  • (link) Prefer pure Swift classes over subclasses of NSObject. If your code needs to be used by some Objective-C code, wrap it to expose the desired functionality. Use @objc on individual methods and variables as necessary rather than exposing all API on a class to Objective-C via @objcMembers.

    class PriceBreakdownViewController {
    
      private let acceptButton = UIButton()
    
      private func setUpAcceptButton() {
        acceptButton.addTarget(
          self,
          action: #selector(didTapAcceptButton),
          forControlEvents: .touchUpInside)
      }
    
      @objc
      private func didTapAcceptButton() {
        // ...
      }
    }

back to top

Contributors

back to top

Amendments

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.

back to top

GitHub

https://github.com/airbnb/swift
Comments
  • 1. Add style for upgrading weak self to strong reference

    Summary

    Starting in Swift 4.2 (released September 2018), self is allowed to be upgraded from a weak reference to strong reference: Allow using optional binding to upgrade self from a weak to strong reference

    Reasoning

    I propose that we adopt this style for the same reasons it was accepted into Swift language.

    • Avoid having to deal with strongSelf, s, ss
    • Follows existing Swift convention of optional binding that reuses the name of the variable.

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by krunk4ever at 2019-08-06 05:22
  • 2. For long function declarations, add newline before return arrow as well

    Summary

    Function declarations sometimes have long unwieldy lines with the return type. This can fix that problem.

    This is the updated version of: https://github.com/airbnb/swift/pull/15

    Reasoning

    Stay within the 100 character limit. By doing this on ALL long function declarations, we keep some consistency with where to find the return type.

    Clarification: One-line functions with return signatures that are under the 100 character limit should still be in one line.

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by teradyl at 2018-09-10 20:14
  • 3. Change camel case acronym rules

    Summary

    This would update the style guide to treat acronyms as normal words within camel case.

    No matter what happens, I'll work on a linter rule so we can keep things consistent.

    Please read the reasoning below and mark with 👍/👎

    Reasoning

    Pros:

    • Easier transitioning between snake_case (from the server and from Codable models) and camelCase because every word is consistent without exception. This way we won't have to write special code or have an acronym whitelist to to convert variables with acronyms in them.
    • Potentially better for GraphQL variable conversion in the future.
    • Argument for readability: Easier to mentally separate the words with no all caps acronyms running into the next word. For examples: httpURLRequest vs httpUrlRequest
    • We won't be the only ones: Google's Java style guide does this https://google.github.io/styleguide/javaguide.html#s5.3-camel-case

    Cons:

    • This isn't how Apple treats acronyms in camelCase (https://swift.org/documentation/api-design-guidelines/#general-conventions)
    • Mental overhead of thinking about what name something should be depending on whether it's defined by Apple or Airbnb
    • Argument for readability: Acronyms in real English are in fact all caps

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by teradyl at 2019-01-10 19:19
  • 4. Add guiding tenet to make all rules lintable

    Summary

    Add guiding tenet to make all rules lintable

    Reasoning

    We've been discussing how to make this Style Guide scale with our team. We decided we should make all rules lintable and make all rules that reformat our code automatically reformatted.

    I'm introducing the usage of SwiftFormat as another option to make this happen since that project intention is to reformat Swift code while SwiftLint main purpose is to lint for it with the option of reformat it, so it seems appropriate.

    Plan if this is approved

    If this is approved I'll:

    • Create 1 issue for each rule that doesn't comply with this new tenet.
    • Create 1 PR removing each rule that doesn't comply with this new tenet pointing to the issue and merge it.
    • All of these rules will be automatically approved when/if someone puts a PR to SwiftFormat/SwiftLint to make them follow this new tenet, unless the discussion in the issue itself clearly points in the direction that we would not want to add this rule again (by using our usual criteria to approve/decline rules)
    • If an issue has been open for more than 1 year, we'll close it.

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by fdiaz at 2018-11-28 22:23
  • 5. Enable SwiftFormat wrap rule with an enforced max width of 130 characters

    Summary

    This PR enables the SwiftFormat wrap rule in airbnb.swiftformat. We would begin enforcing a maximum line width of 130 characters, and automatically wrap code to sit within the max line width. We would continue recommending (but not enforcing) a maximum line width of 100 characters.

    Reasoning

    We should begin enforcing our maximum column width rule with a linter ("We strive to make every rule lintable").

    SwiftFormat's wrap rule is not perfect, but it follows our style guide / existing informal style preferences a large majority of the time (in general, it is more successful at wrapping longer lines). 100 characters seems too strict to enforce with the SwiftFormat wrap rule at this time, given its current behavior. Enforcing a "more generous" maximum width of 130 characters results in a fewer cases of "weird" wrapping, and is better than the status quo (where we make a recommendation, but enforce nothing).

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by calda at 2021-08-25 15:40
  • 6. Remove closure_end_indentation rule

    Summary

    Remove the closure_end_indentation rule

    Reasoning

    It violates the second guiding tenet of the style guide:

    These rules should not fight Xcode's ^ + I indentation behavior.

    image

    I understand that many people dislike Xcode's default style of indenting closure end braces, but I think the guiding tenet is more important. This exception will negatively impact programmer experience and productivity by guaranteeing a locally inconsistent style and linting failure every time Xcode autocompletes a block or we use ^+I.

    The "don't fight Xcode" tenet is important to me because it, in turn, reflects a much broader tenet: "do no harm". It's an assurance that the goal of the style guide is to assist and supplement our other tools to improve the clarity and consistency of code -- not to impose rules even if there are negative side effects.

    P.S. SwiftLint is new to me so I don't know if there's a way to modify the rule to reflect Xcode's default indentation and leave the door open to change the details later (e.g. if Xcode ever makes this configurable).

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by rholmes at 2019-02-08 03:56
  • 7. Remove the need to indent code blocks (sample)

    Summary

    Pros:

    • We no longer have to indent code blocks.

    Cons:

    • We no longer have bold and non-bold sections in rule titles.

    Alternatives considered:

    • Instead of using a heading on the rules, use just standard bold. This causes the "details" link to hug the next rule and doesn't provide the spacing we'd like.

    Reasoning

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by swiftal64 at 2018-09-04 17:51
  • 8. Remove caseless enum rule

    Summary

    Remove caseless enum rule

    Reasoning

    1. Not lintable
    2. Too prescriptive and we're not really following in our codebase. This should be left to developer discretion.

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by fdiaz at 2018-11-08 22:42
  • 9. Remove Computed properties at end rule

    Summary

    Remove Computed properties at end rule

    Reasoning

    1. Not lintable
    2. Too prescriptive and we're not really following in our codebase. This should be left to developer discretion.

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by fdiaz at 2018-11-08 22:35
  • 10. 1:1 relationship between a style guide rule and SwiftLint rule

    Each of our style guide rules is associated with 0 or more SwiftLint rules.

    Take the example:

    Omit unnecessary parentheses. SwiftLint: control_statement, empty_parentheses_with_trailing_closure, unneeded_parentheses_in_closure_argument

    It's associated with 3 rules, but it's unclear if enabling all 3 of those captures all cases this rule encompasses.

    If instead there was a 1:1 mapping between our style guide rules and SwiftLint rules, it would be more clear.

    There are at least two solutions to achieve this goal:

    1. Split big rules into smaller ones (e.g. omit unnecessary parentheses in control statement, omit unnecessary parentheses in trailing closures, omit unnecessary parentheses in closure arguments).
    2. Submit PRs to SwiftLint that encapsulate our rule into one rule.

    Regardless of which solution we choose, it could also simplify the style guide by having a beginning section with all the rules that one should enable on SwiftLint, followed by another section containing those rules that can't be linted for.

    A few advantages:

    • Makes it easier for the developer to remember which styles they must remember and enforce manually vs. those that will automatically be caught.
    • Makes the style guide easier to parse since there will be less rules.
    • Avoids redundancy or mismatches between our style guide and SwiftLint rules.
    Reviewed by swiftal64 at 2018-09-14 21:35
  • 11. Fix capitalization of "Spacefleet" in README

    Summary

    Since space fleet is two words, the class name used in this example should have the correct casing to reflect that. Spacefleet -> SpaceFleet.

    Reasoning

    Our examples should follow Apple's guidelines of using UpperCamelCase for type names.

    Reviewers

    cc @airbnb/swift-styleguide-maintainers

    Please react with 👍/👎 if you agree or disagree with this proposal.

    Reviewed by bryankeller at 2019-02-15 19:33
  • 12. general-part-first contradicts hint-at-types examples

    Rule Names should be written with their most general part first and their most specific part last is not usual because ex. infoLabel, cancelButton are very common names in iOS which is also encouraged in the next rule Include a hint about type in a name if it would otherwise be ambiguous examples.

    https://github.com/airbnb/swift#general-part-first https://github.com/airbnb/swift#hint-at-types

    Reviewed by floprr at 2019-02-26 10:52
  • 13. Each protocol conformance implementation should occur in dedicated type extension within the same file as the type

    This rule is missing autocorrect. Following this PR.


    • (link) Each protocol conformance implementation should occur in dedicated type extension within the same file as the type. This extension should be marked with // MARK: ProtocolName, and should contain nothing more than the methods or properties required to conform to the protocol. As a result, no // MARK:s are needed for defining subsections.
    Reviewed by fdiaz at 2019-01-14 23:17
  • 14. Omit unnecessary type specifiers for closure parameters

    This rule is missing autocorrect. Following this PR.


    • (link) Omit unnecessary type specifiers for closure parameters.

      // WRONG
      someAsyncThing() { (argument: Bool, argument2: Bool) -> Void in
        ...
      }
      
      // RIGHT
      someAsyncThing() { argument, argument2 in
        ...
      }
      
    Reviewed by fdiaz at 2019-01-14 22:19
  • 15. Separate long function chains with line breaks before each dot

    This rule is missing autocorrect. Following this PR.


    • (link) Separate long function chains with line breaks before each dot. SwiftLint: multiline_function_chains

      Why?

      It's easier to follow control flow through long function chains when each call has the same indentation.

      /// WRONG
      
      match(pattern: pattern).compactMap { range in
          return Command(string: contents, range: range)
        }.compactMap { command in
          return command.expand()
      }
      
      /// RIGHT
      
      match(pattern: pattern)
        .compactMap { range in
          return Command(string: contents, range: range)
        }
        .compactMap { command in
          return command.expand()
      }
      
      // Short function chains can still be on one line:
      let evenSquares = [20, 17, 35, 4].filter { $0 % 2 == 0 }.map { $0 * $0 }
      
    Reviewed by fdiaz at 2019-01-14 21:40
  • 16. Indentation Rule Conflicts with Xcode Indentation Behavior

    For this rule:

    Closure end braces should have the same indentation as the line with their opening brace. This makes it easier to follow control flow through closures.

    https://github.com/airbnb/swift#closure-end-brace-indentation

    It's in conflict with this guiding tenet:

    These rules should not fight Xcode's ^ + I indentation behavior.

    Style guide example:

    // RIGHT
    
    match(pattern: pattern)
      .compactMap { range in
        return Command(string: contents, range: range)
      }
      .compactMap { command in
        return command.expand()
      }
    

    Xcode indentation:

    // RIGHT
    
    match(pattern: pattern)
      .compactMap { range in
        return Command(string: contents, range: range)
      }
      .compactMap { command in
        return command.expand()
    }
    
    Reviewed by brynbodayle at 2018-10-31 16:10
LinkedIn's Official Swift Style Guide

Swift Style Guide Make sure to read Apple's API Design Guidelines. Specifics from these guidelines + additional remarks are mentioned below. This guid

May 22, 2022
The Official raywenderlich.com Swift Style Guide.
 The Official raywenderlich.com Swift Style Guide.

The Official raywenderlich.com Swift Style Guide. Updated for Swift 5 This style guide is different from others you may see, because the focus is cent

May 21, 2022
Sweet-swift - Make Swift Sweet by Gracefully Introducing Syntactic Sugar, Helper Functions and Common Utilities

Sweet Swift Make Swift Sweet by Gracefully Introducing Syntactic Sugar, Helper F

Feb 6, 2022
A self-taught project to learn Swift.
A self-taught project to learn Swift.

30 Days of Swift Hi Community I am Allen Wang, a product designer and currently learning Swift. This project was totally inspired by Sam Lu's 100 Days

May 16, 2022
Explanations and samples about the Swift programming language
Explanations and samples about the Swift programming language

About Swift Contents Explanations and samples about: Swift Programming Language Swift Standard Library Target audience Developers familiar with object

May 10, 2022
A collection useful tips for the Swift language
A collection useful tips for the Swift language

SwiftTips The following is a collection of tips I find to be useful when working with the Swift language. More content is available on my Twitter acco

May 3, 2022
Swift Featured Projects in brain Mapping
Swift Featured Projects in brain Mapping

Swift 开源精选   自 2014年 WWDC 发布 Swift 语言以来,本项目 一直致力于将主流 Swift 中文学习、开发资源汇集于此,并且尽力紧密地跟踪、甄选优秀 Swift 开源项目,以方便开发者快速获得并使用。考虑 Swift 已经正式发布超过四年半(更无力管理维护海量的 Swift

May 20, 2022
A collection of Swift tips & tricks that I've shared on Twitter

⚠️ This list is no longer being updated. For my latest Swift tips, checkout the "Tips" section on Swift by Sundell. Swift tips & tricks ⚡️ One of the

May 17, 2022
An Xcode playground showcasing the new features in Swift 4.0.
An Xcode playground showcasing the new features in Swift 4.0.

What’s new in Swift 4 An Xcode playground showcasing the new features in Swift 4.0. As seen in the What’s New in Swift session at WWDC 2017. Written b

May 16, 2022
📖 Design Patterns implemented in Swift 5.0

?? Design Patterns implemented in Swift 5.0

May 18, 2022
List of awesome iOS & Swift stuff!!
List of awesome iOS & Swift stuff!!

Awesome iOS Developer Feel free to fork this repository and pull requests!! ?? Content Coding Convention Swift Lint Design Pattern Adaptor Delegation

May 13, 2022
This repo shows how to set up and use GitHub Actions as a CI for Swift Packages
This repo shows how to set up and use GitHub Actions as a CI for Swift Packages

SwiftPackageWithGithubActionsAsCI This repo shows how to set up and use GitHub Actions as a CI for Swift Packages. Available environments on GitHib Li

Nov 3, 2021
The first HackingWithSwift project from the 100 Days of Swift series.

StormViwer A gallery to display images included in the app's internal assets folder. This is my first ever swift project and it is a part of the serie

Nov 9, 2021
Swift optional desugaring

Swift optional desugaring Setup Make sure you have installed Swift according to the instructions from you earlier assignment. Open a terminal / consol

Dec 4, 2021
Mini-application iOS native avec Xcode et Swift exploitant l'architecture MVVM et le framework Combine d'Apple pour la mise en place de la programmation réactive fonctionnelle, le tout avec UIKit.
Mini-application iOS native avec Xcode et Swift exploitant l'architecture MVVM et le framework Combine d'Apple pour la mise en place de la programmation réactive fonctionnelle, le tout avec UIKit.

iOS (Swift 5): Test MVVM avec Combine et UIKit L'architecture MVVM et la programmation réactive fonctionnelle sont très utlisées dans le développement

Nov 28, 2021
iOS native app demo with Xcode and Swift using MVVM architecture and Apple's Combine framework for functional reactive programming, all with UIKit
iOS native app demo with Xcode and Swift using MVVM architecture and Apple's Combine framework for functional reactive programming, all with UIKit

iOS (Swift 5): MVVM test with Combine and UIKit MVVM and functional reactive programming (FRP) are very used in iOS development inside companies. Here

Dec 31, 2021
A treelist ViewController that implemented with Uikit (swift).
A treelist ViewController that implemented with Uikit (swift).

TreeList ViewController A treelist viewcontroller that implemented with Uikit (swift). Features The sections could be expanded and folded by clicking

Dec 12, 2021
USC's ITP342 iOS Development Swift Final Project

READMEBlogs USC's ITP342 iOS Development Swift Final Project NOTE: You'll need to attach your own Firebase to the app LINK TO APP ZIP FILE: https://dr

Dec 8, 2021
USC's ITP342 iOS Development Swift Final Project

READMEBlogs USC's ITP342 iOS Development Swift Final Project NOTE: You'll need to attach your own Firebase to the app LINK TO APP ZIP FILE: https://dr

Dec 8, 2021