What's New in Swift 5.8

Swift 5.8 includes a small change to documentation as sorting algorithm is guaranteed to be stable (already implemeneted before Swift 5) and following features:

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.

Swift 5.8 extends this 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.

Conditional compilation for attributes

Swift 5.8 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.

The compiler flag -enable-upcoming-feature X can now be used to enable a specific feature X that has been accepted by the evolution process, but whose introduction into the language is waiting for the next major version (e.g., version 6).

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]
    }
  }
}

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

Pointer Family Initialization Improvements and Better Buffer Slices

Swift 5.8 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.