What’s New in Swift 6.4?

Learn what’s new in Swift 6.4, including syntax and ergonomics improvements, concurrency updates like async defer and task cancellation shields, and updates to Swift’s libraries and build system. By Mani Ramezan.

Leave a rating/review
Download materials
Save for later
How this was made
Share

Apple shipped Swift 6.4 alongside Xcode 27, and it’s another one of those releases that’s easy to underestimate at a glance. Nothing here is as flashy as macros or strict concurrency were, but a handful of small changes make day-to-day Swift noticeably less annoying to write, plus some real concurrency ergonomics and a standardized build system, make the upgrade worth it.

Below is a rundown of the changes worth knowing about, with links to the release notes and proposals if you want to go deeper. A few topics — Swift Testing, detailed concurrency changes, and library changes — only get a quick tour here. Dedicated articles on those topics are coming later on Kodeco.

In this article, everything’s grouped into three buckets:

  • Ergonomics and Syntax Improvements includes updates to Swift syntax and compiler behavior that make the language easier to use and more expressive.
  • Concurrency focuses on improvements to Swift’s concurrency model and async lifecycle management.
  • Library Updates details updates to Swift’s standard library and other core libraries like Foundation, XCTest, and Swift Build.

Use the Download Materials button at the top or bottom of this article to download the sample project. The Starter folder includes the code before Swift 6.4, and the Final folder includes the code for Swift 6.4. Make sure to use Xcode 26 when using the Starter project, while using Xcode 27 for the Final project. This helps to see the differences in the code and how Swift 6.4 improves the language and its features. For each topic, the sample project includes a Swift file with code snippets that show the new features and improvements. You can run the code snippets in a playground, or preview them in the canvas to see how they work in practice.

Ergonomics and Syntax Improvements

anyAppleOS

@available and #available have let you gate code by platform since Swift 2.0. The annoying part has always been that “Apple platform” isn’t one keyword — it’s five. Last year, for example, Apple introduced a new design language called Liquid Glass, and it shipped on every Apple platform. As part of that, Apple added many new APIs to SwiftUI and UIKit to help you adopt this new design language. If you want to use it in your code, you need to gate it by platform version. Up until Swift 6.4, supporting it meant spelling out every platform by hand:

extension View {
  @available(iOS 26, macOS 26, tvOS 26, watchOS 26, visionOS 26, *)
  func glassCard() -> some View {
    padding()
      .glassEffect(.regular, in: .rect(cornerRadius: 16))
  }

  @ViewBuilder
  func adaptiveCard() -> some View {
    if #available(iOS 26, macOS 26, tvOS 26, watchOS 26, *) {
      glassCard()
    } else {
      padding().background(.ultraThinMaterial, in: .rect(cornerRadius: 16))
    }
  }
}

Swift 6.4 introduced a new anyAppleOS keyword that collapses all that into one:

extension View {
  @available(anyAppleOS 26, *)
  func glassCard() -> some View {
    padding()
      .glassEffect(.regular, in: .rect(cornerRadius: 16))
  }

  @ViewBuilder
  func adaptiveCard() -> some View {
    if #available(anyAppleOS 26, *) {
      glassCard()
    } else {
      padding().background(.ultraThinMaterial, in: .rect(cornerRadius: 16))
    }
  }
}

anyAppleOS means “any Apple platform” so you can now gate code for all Apple platforms with a single keyword. You can also combine it with existing @available to exclude a specific platform or customize the availability further:

// 1
@available(anyAppleOS 26, iOS 25, *)
func myAwesomeMethod() { ... }

// 2
@available(anyAppleOS 26, *)
@available(macOS, unavailable)
func myOtherAwesomeMethod() { ... }
  1. Here, myAwesomeMethod() is available on all platforms starting from version 26 except for iOS, where it is available from version 25.
  2. Here, myOtherAwesomeMethod() is available on all platforms starting from version 26, except for macOS, where it is unavailable.

As you can see, anyAppleOS is a small change that makes your code cleaner and easier to read. It also reduces the risk of accidentally forgetting to include a platform in your availability checks.

Smarter memberwise inits

Swift comes with many compiler-generated conveniences. One of the most useful has always been the memberwise initializer for structs. If you don’t write your own initializer, Swift automatically generates one for you that takes parameters for each stored property in the struct.

struct Session {
  var userID: String
  var token: String

  // generated memberwise initializer:
  // init(userID: String, token: String) {}
}

This is great, but you can run into trouble if you have a private property with a default value. Swift still generates a memberwise initializer that includes the private property, but this time, the initializer itself is private. This means you can’t call it from outside the struct, which can lead to confusion and frustration.

struct Session {
  var userID: String
  var token: String
  private var retryCount: Int = 0

  // generated memberwise initializer:
  // private init(userID: String, token: String, retryCount: Int) {}
}

In Swift 6.4, private properties with default values are now excluded from the generated initializer:

struct Session {
  var userID: String
  var token: String
  private var retryCount: Int = 0

  // generated memberwise initializer:
  // init(userID: String, token: String) {}
}

As shown above, the generated initializer excludes the private property retryCount, which makes the initializer’s access level internal.

Warning control through @diagnose

Up to now, warning control mostly meant reaching for compiler flags like -warnings-as-errors or -suppress-warnings. Those work, but they’re a blunt instrument — they hit the whole module, not the one function for which you’re trying to make an exception.

@diagnose fixes that by letting you dial in warning behavior right at the declaration that needs it. Let’s say you have the following code in your app — a simple credential manager that stores a token on disk:

// 1
class CredentialManager {
  // 2
  func storeToken(token: String) { 
    // Saves the token on a file
  }
}

// 3
func signinUser(username: String, password: String) {
  // sign in the user and fetch the token
  credentialManager.storeToken(token: token)
}
  1. CredentialManager is a simple class that manages user credentials.
  2. storeToken saves the token to a file, which isn’t secure and can be accessed by malicious actors.
  3. signinUser stores the token fetched from the backend upon successful sign-in.

Later, you learn about Keychain and decide to deprecate storeToken in favor of a more secure implementation:

class CredentialManager {
  @available(*, deprecated)
  func storeToken(token: String) { ... }

  func securelyStoreToken(token: String) {
    // Saves the token in Keychain
  }
}

Now if you try to compile with -warnings-as-errors, the call to storeToken in signinUser will fail the build.

Screenshot of an error message in Xcode showing that the call to storeToken is deprecated and shown as an error

You could fix it by changing the call to securelyStoreToken, but maybe you don’t have time to do that right now. You simply want an exception for this one function. Before Swift 6.4, you needed to remove -warnings-as-errors from the build settings, which would stop every warning in the module from being treated as an error. This is not ideal.

With @diagnose, now you can add the deprecation diagnostic for this one function:

@diagnose(DeprecatedDeclaration, as: ignored, reason: "Required more testing")
func signinUser(username: String, password: String) {
  // sign in the user and fetch the token
  credentialManager.storeToken(token: token)
}

Now the rest of the code still treats warnings as errors, and you can take your time to properly fix and test this code later. If you’d rather downgrade the diagnostic to a warning instead of dropping it, swap in as: warning instead of as: ignored.

Let’s now look at some of the concurrency improvements in Swift 6.4.

How this was made

Our AI policy
Words
Written by the author, AI-edited
Code
AI-assisted, author-verified
Media
  • Illustrations: Illustrator