What's New in Swift 5.8

Updated Jun 14, 2023#swift-versions#swift

Swift has a formal process for proposing and accepting changes to the language, known as the Swift Evolution Process which covers all language features, standard library, compiler configuration, and package manager.



There are 11 proposals implemented and assigned to Swift 5.8, released since Xcode 14.3, including many features like back deployed, collection downcasts in cast patterns, improved unsafe pointer family, usage of implicit self after unwrapped, etc.

  1. SE-0365 • Allow implicit self for weak self captures, after self is unwrapped

Explicit self has historically been required in closures, in order to help prevent users from inadvertently creating retain cycles. As of Swift 5.3, implicit self is permitted in closures when self is written explicitly in the capture list.

This feature extends support to weak self captures, and permit implicit self as long as self has been unwrapped.

class FooViewController {
  let button: Button
  func dismiss() {}
  func setup() {
    button.tapHandler = { [weak self] in
      guard let self else { return }
      dismiss()
    }
  }
}

Like with implicit self for strong and unowned captures, the compiler will synthesize an implicit self. for calls to properties / methods on self inside a closure that uses weak self.

  1. SE-0367 • Conditional compilation for attributes

This feature introduces two related changes to make it easier to adopt new attributes in existing code:

  • Allow #if checks to surround attributes on a declaration wherever they appear, eliminating the need to clone a declaration just to adopt a new attribute.
  • Add a conditional directive hasAttribute(AttributeName) that evalutes true when the compiler has support for the attribute with the name AttributeName in the current language mode.
#if hasAttribute(preconcurrency)
@preconcurrency
#endif
protocol P: Sendable {
  func f()
  func g()
}

Conditional directive hasAttribute only considers attributes that are part of the language, custom attributes (property wrappers, result builders, and global actors) will evaluate false.

A conditionally-compiled branch based on #if hasAttribute(UnknownAttributeName) can still be parsed by an existing compiler, even though it will not be applied to the declaration because it isn’t understood.

  1. SE-0369 • Add CustomDebugStringConvertible conformance to AnyKeyPath

Currently, passing a keypath to print(), yields the standard output for a Swift class. This is not very useful.

struct Theme {
  var backgroundColor: Color
  var foregroundColor: Color
  var overlay: Color {
    backgroundColor.withAlpha(0.8)
  }
}

print(\Theme.backgroundColor) would have an output of roughly Swift.KeyPath<Theme, Color>, which doesn’t allow foregroundColor to be distinguished from any other property on Theme.

Swift 5.8 takes advantage of whatever information is available in the binary to implement the debugDescription requirement of CustomDebugStringConvertible.

print(\Theme.backgroundColor) // outputs "\Theme.<offset 0 (Color)>"
print(\Theme.overlay) // outputs \Theme.<computed 0xABCDEFG (Color)>
  1. SE-0370 • Pointer Family Initialization Improvements and Better Buffer Slices

This feature rounds out initialization functionality for every relevant member of UnsafeMutablePointer family:

- UnsafeMutablePointer
- UnsafeMutableRawPointer
- UnsafeMutableBufferPointer
- UnsafeMutableRawBufferPointer
- Slice<UnsafeMutableBufferPointer>
- Slice<UnsafeMutableRawBufferPointer>

The functionality will allow managing initialization state in a much greater variety of situations, including easier handling of partially-initialized buffers.

  1. SE-0372 • Document sorting as stable

Swift’s sorting algorithm was changed to be stable before Swift 5. Since all current versions of the Swift runtime include a stable sort (which was introduced before ABI stability), this change can be made to the standard library documentation.

A stable sort is a sort that keeps the original relative order for any elements that compare as equal or unordered. For example, given this list of players that are already sorted by last name, a sort by first name preserves the original order of the two players named “Ashley”:

var roster = [
   Player(first: "Sam", last: "Coffey"),
   Player(first: "Ashley", last: "Hatch"),
   Player(first: "Kristie", last: "Mewis"),
   Player(first: "Ashley", last: "Sanchez"),
   Player(first: "Sophia", last: "Smith"),
]

roster.sort(by: { $0.first < $1.first })
// roster == [
//    Player(first: "Ashley", last: "Hatch"),
//    Player(first: "Ashley", last: "Sanchez"),
//    Player(first: "Kristie", last: "Mewis"),
//    Player(first: "Sam", last: "Coffey"),
//    Player(first: "Sophia", last: "Smith"),
// ]
  1. SE-0368 • StaticBigInt

Types outside of the standard library which conform to ExpressibleByIntegerLiteral are restricted in practice in how large of a literal value they can be built with, because the value passed to init(integerLiteral:) must be of a type supported by the standard library.

Swift 5.8 adds a new type to the standard library called StaticBigInt which is capable of expressing any integer value. This can be used as the associated type of an ExpressibleByIntegerLiteral conformance.

extension UInt256: ExpressibleByIntegerLiteral {
  public init(integerLiteral value: StaticBigInt) {
    precondition(
      value.signum() >= 0 && value.bitWidth <= Self.bitWidth + 1,
      "integer literal '\(value)' overflows when stored into '\(Self.self)'"
    )
    self.words = Words()
    for wordIndex in 0..<Words.count {
      self.words[wordIndex] = value[wordIndex]
    }
  }
}
  1. SE-0376 • Function back deployment

Add a @backDeployed(before: ...) attribute to Swift that can be used to indicate that a copy of the function should be emitted into the client to be used at runtime when executing on an OS prior to the version identified with the before: argument.

struct Foo {
  @available(iOS 12.0, *)
  @backDeployed(before: iOS 16.0)
  public func bar() {}
}

The @backDeployed attribute may apply to functions, methods, and subscripts. Properties may also have the attribute as long as the they do not have storage. The compiler detects applications of back deployed function and generates code to automatically handle the potentially runtime unavailability of the API.

  1. SE-0375 • Opening existential arguments to optional parameters

Swift 5.7 has a limitation that prevents the opening of an existential argument when the corresponding parameter is optional. This release changes that behavior, so that such a call will succeed when a (non-optional) existential argument is passed to a parameter of optional type:

func openOptional<T: P>(_ value: T?) { }

func testOpenToOptional(p: any P) {
  openOptional(p) // okay, opens 'p' and binds 'T' to its underlying type
}
  1. SE-0373 • Lift all limitations on variables in result builders

The implementation of the result builder transform, introduced in Swift 5.4, places a number of limitations on local variable declarations in the transformed function. Specifically, local variables need to have an initializer expression, they cannot be computed, they cannot have observers, and they cannot have attached property wrappers. This release lifts all those limitations.

import SwiftUI

struct ContentView: View {
  var body: some View {
    GeometryReader { proxy in
      @Clamped(10...100) var width = proxy.size.width
      Text("\(width)")
    }
  }
}
  1. SE-0274 • Concise magic file names

This release changes the string that magic identifier #file evaluates to — instead of evaluating to the full path, it will now have the format <module-name>/<file-name>.

For those applications which still need a full path, there will be a new magic identifier, #filePath. Both of these features will otherwise behave the same as the old #file, including capturing the call site location when used in default arguments. The standard library’s assertion and error functions will continue to use #file.

With this feature, a file at /Users/becca/Desktop/0274-magic-file.swift in a module named MagicFile with this content:

print(#file)
print(#filePath)
fatalError("Something bad happened!")

Would produce this output:

MagicFile/0274-magic-file.swift
/Users/becca/Desktop/0274-magic-file.swift
Fatal error: Something bad happened!: file MagicFile/0274-magic-file.swift, line 3
  1. SE-0362 • Piecemeal adoption of upcoming language improvements

This feature introduces a compiler flag -enable-upcoming-feature X, where X is a name for the feature to enable, with naming convention as UpperCamelCaseFeatureName. Each proposal will document what X is, so it’s clear how to enable that feature.

For example, SE-0274 could use ConciseMagicFile, so that -enable-upcoming-feature ConciseMagicFile will enable that change in semantics.

You can pass multiple -enable-upcoming-feature flags to the compiler to enable multiple features.