# Swift 6.3 and 6.4: the new features you will code every day

🤯 Some Swift releases arrive with fireworks, and others arrive with something far more valuable: less friction. The kind that makes that little annoyance you ran into every morning… simply disappear. And it turns out the language's two latest releases are packed with exactly that. So much so that this year Apple decided to make its canonical "What's New in Swift?" video for WWDC 2026 cover both versions and everything they bring.

🤔 Before we dive in, let's get our bearings, because there are two versions playing at once here. **Swift 6.3 has been available since March 2026** (we're on 6.3.2), it's stable, and you can ship it in production today. **Swift 6.4 was just unveiled at WWDC 2026 and is in beta**, inside the developer builds of Xcode 27. In other words: 6.3 is "now"; 6.4 is "almost now, but still in the oven." I'll flag it change by change, because leaning your build on one or the other tomorrow is not the same thing.

👨🏻‍💻 And here's an idea you'll see running through the whole article: almost none of these improvements ask you to write more code. Quite the opposite. Nearly all of them exist so you can say **exactly what you mean, without noise around it**. Which is, in the end, what understanding the code is all about.

## Goodbye to the parentheses that didn't belong

✨ *(Swift 6.4 — beta)* Let's start with the smallest one, the kind you'll only notice as an annoyance going away.

When you wanted to use `some` or `any` with an optional type, the compiler forced you to add parentheses. Not out of spite, but because it fell out of the operator precedence rules. Still, it was pedantic.

```swift
// Before
func delegate() -> (any MissionDelegate)?

// Now
func delegate() -> any MissionDelegate?
```

🫵🏻 What does it give you? Nothing spectacular — and that's precisely why it's good: the compiler now understands the only thing you could have possibly meant. One less pebble in your shoe.

## The error that used to slip out of a Task

🤯 *(Swift 6.4 — beta)* This one matters, because it's about real safety.

Until now, if you launched a `Task` with throwing code and didn't store it or handle the error, that error vanished in silence. Your code *looked* correct. It wasn't.

```swift
// This silently swallowed the error, no warning
Task {
    try lander.fly(to: moon)
}
```

From now on, the compiler warns you and points you to the two correct ways out: handle the error inside the task itself, or save the task to check its result later.

```swift
// Option 1: handle it inside
Task {
    do {
        try lander.fly(to: moon)
    } catch {
        lander.abort()
    }
}

// Option 2: save the task and check it later
let landingTask = Task {
    try lander.fly(to: moon)
}
```

👨🏻‍🏫 What does it give you? It stops the language from letting you fail in silence. An error you don't handle isn't an error that doesn't exist: it's a time bomb that goes off in production, far away from your Xcode. This surfaces it at compile time.

## `async` inside a `defer`

🔦 *(Swift 6.4 — beta)* There was an old, unintuitive restriction: you couldn't call `async` functions from a `defer` block. Which was odd, because `defer` is exactly where you want your cleanup to run no matter what — whether the function returns or throws.

```swift
// This used to fail to compile
func rendezvous() async throws {
    let landingTask = Task { try lander.fly(to: moon) }
    defer {
        await orbiter.rendezvous(with: lander) // ❌ not allowed
    }
    try await orbiter.justHangOut(waitingFor: landingTask)
}
```

🙏🏻 Now it's allowed. What does it give you? Your asynchronous cleanup logic lives where it belongs, guaranteed to run whether you exit cleanly or through an exception. Fewer artificial `do/catch` blocks written just to clean up.

## `weak let`, `~Sendable`, and the end of fighting `@unchecked`

🤖 *(Swift 6.4 — beta, with a caveat)* How many times have you ended up slapping `@unchecked Sendable` on a class just because it had a `weak var` property and the compiler wouldn't leave you alone? That `@unchecked` is a promise you make to the compiler: "trust me." And "trust me" is exactly what we don't want in serious code.

The catch is that `weak var` is mutable, and that's what breaks `Sendable` checking. But if you don't need to reassign that reference, you can now declare it `weak let`: it can still become `nil` if the object is deallocated, but it's immutable and no longer blocks `Sendable`.

```swift
final class Spacecraft: Sendable {
    weak let dockedAt: SpaceStation?   // immutable and Sendable-friendly
}
```

And if what you want is the opposite — to make it **explicit** that a type should NOT be `Sendable` — there's now the tilde syntax:

```swift
class Mission: ~Sendable { ... }

// As a bonus: this does NOT stop subclasses from being Sendable
class CrewedMission: Mission, @unchecked Sendable { ... }
```

🫵🏻 What does it give you? Two things I care about a lot. First, `weak let` takes an `@unchecked Sendable` off your shoulders, and every `@unchecked` you delete is one less lie in your codebase (note: `weak let` has existed since Swift 6.2; what's being sharpened now is how it fits with `Sendable`). Second, `~Sendable` lets you state your intent clearly, without tricks. Once again, the language rewards telling the truth.

## A second memberwise initializer

😎 *(Swift 6.4 — beta)* A `struct` mixing `internal` and `private` properties only gave you a `private` memberwise initializer, so you couldn't build it from another file in your own project without writing the `init` by hand.

```swift
struct Briefing {
    internal var topic: String
    internal var scheduledAt: Date
    private  var attendees: [Person] = []
}
```

Now Swift generates **an additional** second memberwise initializer that exposes only the `internal` properties, usable from the rest of your module:

```swift
// Generated automatically, accessible from other files in the project:
// init(topic: String, scheduledAt: Date)
let briefing = Briefing(topic: "Landing", scheduledAt: .now)
```

🙏🏻 What does it give you? Fewer hand-written `init`s just to work around visibility. The compiler does the boring work for you.

## `anyAppleOS`: the end of the `@available` tower

🤯 *(Swift 6.4 — beta)* You'll notice this one. A year ago Apple aligned the version numbers across all its systems (iOS, macOS, watchOS, tvOS, and visionOS now share numbering). Swift 6.4 takes the logical next step: condensing all those platform names into one.

```swift
// Before: the usual tower
@available(macOS 27, iOS 27, watchOS 27, tvOS 27, visionOS 27, *)
func showStatus() { ... }

// Now
@available(anyAppleOS 27, *)
func showStatus() { ... }
```

And when there are carve-outs, you use `anyAppleOS` as the baseline and add the exceptions on top:

```swift
@available(anyAppleOS 27, *)
@available(tvOS, unavailable)
func launch() { ... }
```

It even works in `#if os(anyAppleOS)` conditions to compile out entire blocks.

👩🏼‍💻 What does it give you? Your availability attributes stop taking up three lines and stop being a source of typos (did you write `watchOS` and forget `visionOS`?). More readable, less fragile.

## `@diagnose`: you decide what counts as a warning

🤯 *(Swift 6.4 — beta)* Here's one for those of us who, like me, get nervous over a single warning in the project. Until now you controlled warnings with a sledgehammer: at the project or package level. All or nothing.

The new `@diagnose` attribute lets you change the behavior of a diagnostic group **within a specific declaration**. Three uses, three real-world scenarios:

```swift
// 1. Silence a deprecated-API warning ONLY here, without touching the rest
@diagnose(DeprecatedDeclaration, as: ignored, reason: "New engine migration pending")
func makeLegacyMission() -> Mission { ... }

// 2. Turn on a warning that's off by default, in security-critical code
@diagnose(StrictMemorySafety, as: warning)
func uplinkCommand(from receiver: inout Receiver, to computer: inout Computer) { ... }

// 3. Promote to an error right now a warning that will be an error in the future
@diagnose(ErrorInFutureSwiftVersion, as: error)
func fetchPosition() -> (x: Double, y: Double, z: Double) { ... }
```

🫵🏻 What does it give you? Surgical control. You can temporarily silence that migration warning you've already got on your radar **without** turning it off across the whole project and without something else sneaking past underneath. And the other way around: you can harden a critical function to audit its use of unsafe APIs yourself. This is the difference between "I have zero warnings because I turned them all off" and "I have zero warnings because I know exactly what each one does."

## Module selectors: when two packages share a name

🔑 *(Swift 6.3 — stable)* Heads up — this one you already have. What happens when you import two modules that expose a type with the same name? Swift can't tell which one you mean and gives you an error. Until now you'd fix it with dot syntax, `Rocket.SaturnV`, but that has a hole: Swift prefers type names over module names. If the `Rocket` module also has a type called `Rocket`, it looks for the member inside the type and doesn't find it.

```swift
import Rocket
import GiftShopToys

let r1 = SaturnV()          // ❌ ambiguous: the real rocket or the toy one?
let r2 = Rocket.SaturnV()   // ⚠️ may be confused with the type Rocket
```

Swift 6.3's solution is elegant: swap the dot for a double colon (`::`). It's the **module selector**, and whatever is on the left is **always** treated as a module name.

```swift
let r3 = Rocket::SaturnV()  // ✅ unambiguous: the SaturnV from the Rocket module
```

It also works on methods and properties, handy when a type gets identically-named methods from extensions in two different modules:

```swift
launchPadTechnician.HumanResources::fire()  // fire from the job, not set on fire 🔥
```

😊 What does it give you? It saves the day when, say, both SwiftUI and a database package define a type called `View`. That said, Apple's advice is clear and I second it: use it to resolve conflicts between modules you don't control, not to design APIs that clash on purpose and then lean on this to tell them apart. Code compiling doesn't mean code reads well.

## The standard library sharpens up: dictionaries and paths

👨🏻‍💻 *(Swift 6.4 — beta)* Two small gems in the standard library.

The first, `mapKeyedValues`. Until now `mapValues` only passed the **value** into the closure. If you needed the **key** to compute the new value, you had to rebuild the dictionary by hand. A pain.

```swift
// Before: by hand, because mapValues doesn't give me the key
let names = [Mission: String](uniqueKeysWithValues:
    missions.lazy.map { mission, window in
        (mission, makeDisplayName(for: mission, in: window))
    }
)

// Now: key and value, straight away
let names = missions.mapKeyedValues { mission, window in
    makeDisplayName(for: mission, in: window)
}
```

The second, a new `FilePath` type in the standard library itself, based on the one from Swift System. Manipulating paths has subtle cross-platform differences that are easy to get wrong.

```swift
var path: FilePath = "/var/www/static"
path.components.append("WWDC")
print(path.components)   // ["var", "www", "static", "WWDC"]
```

🙏🏻 What do they give you? Less glue code and fewer silly mistakes when dealing with paths. Small things that add up.

## The task cancellation shield

🦾 *(Swift 6.4 — beta)* Checking whether a task was cancelled before starting expensive work is good practice. But there's a case where you want to finish the work **even if** it was cancelled: for example, finishing a write to disk so you don't corrupt a file.

That's what `withTaskCancellationShield` is for. Inside that block, cancellation checks always return `false`.

```swift
func sendSOS() {
    withTaskCancellationShield {
        radio.send(makeSOSPacket())   // this goes out no matter what
    }
}
```

🫵🏻 What does it give you? A precise tool for those critical moments. The advice: keep the region **short**, focused only on finishing or rolling back what you'd already started. It's not an excuse to ignore cancellation in general.

## Swift Testing keeps growing

🎯 Here the news is split across both versions, so watch the tags.

*(Swift 6.3 — stable)* You can now set the **severity** of an issue. An `Issue.record` with `severity: .warning` shows up in the results but does **not** mark the test as failed. Perfect for surfacing something suspicious that you don't want bringing down your CI.

```swift
if remaining < 0.10 {
    Issue.record("Fuel below the 10% reserve target", severity: .warning)
}
```

*(Swift 6.3 — stable)* And you can **cancel tests dynamically** with `Test.cancel()`. It shines with parameterized tests: you cancel the argument that shouldn't run instead of failing or running it to completion.

```swift
@Test(arguments: allRockets)
func testBurn(rocket: Rocket) throws {
    if rocket.engineType == .solid {
        try Test.cancel("\(rocket.name) uses solid fuel, not applicable")
    }
    // ...
}
```

*(Swift 6.4 — beta)* The headline feature: **two-way interoperability with XCTest**. XCTest assertion failures are reported as issues when called from Swift Testing, and Swift Testing macros like `#expect` work inside an `XCTestCase`. By default, those cross-framework cases are reported as warnings; you decide in Xcode's settings whether to promote them to failures.

🤔 What does it give you? Migrating from XCTest to Swift Testing stops being a leap of faith. You can coexist, move utilities over little by little, and not lose coverage along the way. As a bonus, the `swift test` command can now repeat a test until it passes (or until it fails), re-running only the ones that failed: the perfect tool against flaky tests.

## Progress done right, built for `async/await`

📅 *(Swift 6.4 — beta)* Foundation introduces `ProgressManager`, a progress-reporting type designed for modern concurrency. It cleanly separates progress **composition** from progress **reporting**, and lets you attach metadata in a type-safe way.

```swift
let manager = ProgressManager(totalCount: 100)
try await rocket.launch(manager.subprogress(assigningCount: 100))

extension Rocket {
    func launch(_ progress: consuming Subprogress? = nil) async throws {
        let stage = progress?.start(totalCount: 3)
        try await ignite();          stage?.complete(count: 1)
        try await liftoff();         stage?.complete(count: 1)
        try await stageSeparation(); stage?.complete(count: 1)
    }
}
```

👀 What does it give you? Progress reporting that genuinely fits the `async/await` world, without the old workarounds and with composition handled by the API itself.

## So, what do I take home?

🧠 Notice that none of these features asks you to learn a new paradigm. They all point the same way: getting the language out of your way so you can say what you already wanted to say. Errors that stop hiding, `@unchecked`s that disappear, availability attributes that shrink, warnings you control with a scalpel.

🤷🏻‍♂️ And here's the detail that separates whoever *uses* these things from whoever *understands* them: all these improvements only pay off if you **know why the annoyances existed in the first place**. Whoever didn't understand why `weak var` broke `Sendable` won't appreciate `weak let`. Whoever turned off every warning at once won't get any mileage out of `@diagnose`. The tool multiplies whoever understands; whoever doesn't, it leaves exactly where they were.

🫵🏻 So the question isn't "which version should I set?" It's: how many of these little workarounds were you living with without even noticing? Tell me. And tell me which one you'll try first.
