What's New in Swift 6

Jun 19, 2024#swift

Swift 6 is set to launch in 2024, alongside Xcode 16 in September, marking the first major update since Swift 5’s release five years ago. This update builds on the features introduced in the Swift 5.x series, such as async/await, existentials, and macros.

  1. SE-0220: count(where:)

Counting the number of objects that pass a test has a wide range of uses in many domains. While the behavior can currently be approximated with a filter and a count, this approach creates an intermediate array which it immediately discards. This is a bit wasteful.

[1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3

The proposed solution would avoid a performance trap and provide a simple interface for users to both read and write. Autocomplete should present it to them handily as well.

[1, 2, 3, -1, -2].count(where: { $0 > 0 }) // => 3
  1. SE-0270: Add Collection Operations on Noncontiguous Elements

There are varied uses for tracking multiple elements in a collection, such as maintaining the selection in a list of items, or refining a filter or search result set after getting more input from a user.

The Foundation data type most suited for this purpose, IndexSet, uses integers only, which limits its usefulness to arrays and other random-access collection types.

This proposal adds a RangeSet type for representing multiple, noncontiguous ranges, as well as a variety of collection operations for creating and working with range sets.

public struct RangeSet<Bound: Comparable>: Equatable, CustomStringConvertible { 
  /***/
}

Although a range set can represent a set of values of any Comparable type, the primary intended use case is to maintain a set of indices into a collection.

var numbers = [10, 12, -5, 14, -3, -9, 15]
let negativeSubranges = numbers.subranges(where: { $0 < 0 })
// numbers[negativeSubranges].count == 3
  1. SE-0405: String Initializers with Encoding Validation

This proposal adds a new String initializer that can fail, returning nil, when its input is found to be invalid according the encoding represented by a type parameter that conforms to Unicode.Encoding.

extension String {
  public init?<Encoding: Unicode.Encoding>(
    validating codeUnits: some Sequence<Encoding.CodeUnit>,
    as: Encoding.Type
  )
}

When processing data obtained from C, it is frequently the case that UTF-8 data is represented by Int8 (typically as CChar) rather than UInt8. This proposal will also provide a convenience initializer for this use case:

extension String {
  public init?<Encoding: Unicode.Encoding>(
    validating codeUnits: some Sequence<Int8>,
    as: Encoding.Type
  ) where Encoding.CodeUnit == UInt8
}

The following example calls this initializer with the contents of two different arrays—first with a well-formed UTF-8 code unit sequence and then with an ill-formed UTF-16 code unit sequence.

let validUTF8: [UInt8] = [67, 97, 0, 102, 195, 169]
let valid = String(validating: validUTF8, as: UTF8.self)
print(valid ?? "nil") //=> "Café"


let invalidUTF16: [UInt16] = [0x41, 0x42, 0xd801]
let invalid = String(validating: invalidUTF16, as: UTF16.self)
print(invalid ?? "nil") //=> "nil"
  1. SE-0408: Pack Iteration

This proposal allows iteration over value packs using for-in loops. This syntax makes iterating through parameter packs much cleaner and more readable compared to pre-Swift 6 methods.

func iterate<each Element>(over element: repeat each Element) {
  for element in repeat each element {
    /***/
  }
}
  1. SE-0409: Access-level modifiers on import declarations

The author of a library may have a different intent for each of the library dependencies; some are expected to be known to the library clients while others are for implementation details internal to the package, module, or source file.

The core of this proposal consists of extending the current access level logic to support declaring the existing modifiers (excluding open) on import declarations and applying the access level to the imported declarations.

internal import DatabaseAdapter

internal func internalFunc() -> DatabaseAdapter.Entry {...} // Ok
public func publicFunc() -> DatabaseAdapter.Entry {...} // error: function cannot be declared public because its result uses an internal type

You can now explicitly control the visibility of imported symbols using access level modifiers like public, internal, private, and fileprivate. The default import access level has become internal. This means imported symbols are only visible within the current module by default, preventing unintended leaks to public APIs.