← All articles

What's New in Swift at WWDC26: An Indie iOS Developer's Guide

Swift 6.3 and 6.4 shipped together at WWDC26. Bidirectional Swift Testing migration, ownership in ordinary code, daily ergonomics, gRPC Swift, MLX Swift. Here is what matters for indie iOS developers, in priority order.

What's New in Swift at WWDC26: An Indie iOS Developer's Guide

Swift 6.3 and 6.4 shipped together at WWDC26. One release is aimed at daily ergonomics, the other at performance-sensitive code, and together they move ownership patterns, bidirectional test framework migration, and a wider language reach (server, ML, Android) into the language an indie iOS developer writes every day. This post walks through what changed, in the order that matters most for an indie team shipping a paid iOS app.

The framing matters because Swift 2026 is not one big feature. It is dozens of small ones that compound. The single largest practical shift for most teams is that the friction of writing new Swift Testing tests next to an existing XCTest suite is finally gone in both directions. The second largest is that ownership patterns that used to live in unsafe code now live in safe standard library types. The rest sits underneath those two.

On this page

Everyday Swift: ergonomics that touch every file

The release notes lead with performance and ownership, but the changes you will feel first are the smaller ones. They sit in code you already wrote and read every day.

Watch on Apple Developer ↗

any P? without parentheses

The previous wrapping requirement is gone. Writing an optional existential is now what you would expect:

// Before
let handler: (any RequestHandler)? = nil
// 6.3
let handler: any RequestHandler? = nil

anyAppleOS availability

Stacked @available lines for the same version across every Apple platform now collapse to one attribute. The result is shorter declarations and fewer typos when adding a new platform target:

// Before
@available(iOS 18.0, macOS 15.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
public func newAPI() { /* ... */ }
 
// 6.3
@available(anyAppleOS 2026, *)
public func newAPI() { /* ... */ }

It works in #if os(...) conditionals too, which removes a category of subtly broken cross-platform code where the developer forgot one platform.

@diagnose for per-declaration warning control

Per-declaration control over warning behavior. The two common uses are suppressing a deprecation warning during a deliberate migration window, and promoting a future-error warning to an immediate error for a specific call site.

@diagnose(.warning, deprecation: .suppress)
func legacySync() {
    // calls deprecated API during migration; the warning is suppressed
    // here without disabling the warning project-wide
}

The point of @diagnose is that it scopes the decision. Pre-2026, the same effect required project-wide compiler flags that hid useful warnings everywhere.

Module selectors with ::

Two modules exporting the same type name used to require import gymnastics. Module selectors disambiguate inline:

import Rocket
import LegoBricks
 
// Both modules export `SaturnV`. Pick the module explicitly.
let lift = Rocket::SaturnV.standardThrust
let kit  = LegoBricks::SaturnV.partsList

The left side is always a module name. Selectors work on methods and properties, not just types.

Smaller items worth knowing

  • async allowed in defer (SE-0493, Swift 6.4). Cleanup paths that need to await no longer require an explicit Task shim.
  • weak let qualifies for proper Sendable checking when the class would otherwise have qualified. The previous weak var requirement forced mutability into otherwise-immutable types.
  • ~Sendable as an explicit non-conformance marker. Subclasses can still opt into Sendable without the parent class blocking them.
  • @inline(always) plus final is the pairing for method inlining on classes; overridable methods may not inline.
  • @specialized generates a concrete specialization via where clause. Rarely needed: the optimizer is usually right. Reach for it only after measuring.

Standard library and Foundation updates

A few of these are genuinely useful in code you already maintain.

withTaskCancellationShield (SE-0504)

A scoped region inside an async function where cancellation checks return false. The motivating case is the async cleanup pattern: when a task is cancelled, you still want to flush a buffer or write a final state to disk before the task actually exits. Pre-shield, the cleanup itself often saw the cancellation and short-circuited.

func uploadAndFinalize() async throws {
    try await upload()
    try await withTaskCancellationShield {
        try await flushAuditLog()
    }
}

mapKeyedValues on Dictionary

A mapValues that also passes the key, so the transform can depend on which entry it is processing without dropping into a manual reduce:

let normalized = prices.mapKeyedValues { code, value in
    code.hasPrefix("USD") ? value : value * exchangeRates[code, default: 1]
}

File path type, Data modernization, URL unification

A new platform-agnostic FilePath type lands in Foundation, replacing the per-platform string juggling that used to surround anything filesystem-shaped. Data gets faster span access, equality, iteration, and mutation. NSURL and CFURL collapse into a single Swift implementation, retiring two Objective-C bridges that had subtly different behavior on different platforms.

ProgressManager and Subprocess 1.0

ProgressManager is an async/await-native progress reporting type, replacing patterns that used to involve KVO on NSProgress. The Subprocess package hit 1.0 with a simplified execution type, AsyncBufferSequence for streamed output, and a .strings() helper that respects grapheme cluster boundaries when reading line by line.

Ownership reaches ordinary code

The headline performance story in 2026 is that ownership patterns moved from unsafe code into safe, standard library types. You can now write code that the compiler verifies as copy-free without dropping to Unsafe anything.

Watch on Apple Developer ↗

Iterable and borrowing for loops

The classic for loop copied every element. The new Iterable protocol borrows them instead. Loops over Iterable collections work with noncopyable elements, skip reference counting, can throw mid-iteration, and enforce exclusivity (no mutating the collection while iterating). When Sequence is also available the optimizer picks the cheaper path.

borrow and mutate accessors

get and set copied the entire value. The new accessors do not.

struct UniqueBox<T: ~Copyable> {
    private var storage: InlineArray<256, T>
 
    var first: T {
        borrow { yield storage.first }
        mutate { yield &storage.first }
    }
}

For a 2 KB struct, this is the difference between copying the entire array twice (read then write) and modifying it in place. The optimizer used to be able to elide some of those copies; now you can write the intent directly.

New noncopyable standard library types

TypeWhat it is
UniqueArrayA noncopyable Array variant. Holds noncopyable elements and avoids reference-counting overhead, without a fixed-size commitment.
UniqueBoxA heap-allocated wrapper around a single value using the new accessors. Real standard library type, not an example.
RigidArrayFixed-capacity array. Available through swift-collections 1.3 pending standard library inclusion (SE-0527 was accepted in principle).
Ref / MutableRefSingle-value span containers. Construct MutableRef from a write access with the & prefix. Non-escapable, so the borrow ends when the variable leaves scope.
ContinuationCompile-time verification of exactly-one resume. Safer than CheckedContinuation and as efficient as UnsafeContinuation.

Practical rule: reach for these when you have measured a hot path. Reach for them on a normal code path only if the code already had the shape of "I am holding a unique value that I want to pass around without copies."

Equatable, Comparable, and Hashable now work with noncopyable types. Equatable and Comparable extend to non-escapable types. Associated types can be noncopyable or non-escapable. The result is that the protocols you already write for ordinary types also cover the new ones, instead of being a separate world.

Swift Testing: bidirectional XCTest migration

The single most practical 2026 change for teams maintaining a real XCTest suite. Pre-2026 you could call XCTest from Swift Testing in some ways but the reverse path was awkward. 6.3 makes both directions first-class.

Watch on Apple Developer ↗

The four interoperability modes

ModeDefault forCross-framework issues become
LimitedTest plans created before Xcode 27 and swift-tools-version: 6.3Warnings
CompleteNew Xcode 27 projects and swift-tools-version: 6.4+Errors
StrictOpt-inFatal errors that point to every XCTest API site needing replacement
NoneOpt-out (only temporarily)Silenced; not recommended, real bugs hide here

Set the mode via SWIFT_TESTING_XCTEST_INTEROP_MODE (mode name lowercase). XCTest issues calling into Swift Testing remain errors in every mode, so that direction is always strict.

What works across the boundary

#expect and #require can be called from inside an XCTestCase. XCTest assertions called from a Swift Testing test surface as test issues, preserving the failure coverage. Interoperability covers every XCTest assertion, the #expect/#require expectation macros, the known-issue API, and Test.cancel.

Migration patterns

  • XCTSkip becomes Test.cancel or one of the .enabled / .disabled traits.
  • continueAfterFailure = false becomes #require. #require throws on failure and halts the test, so the choice is per-expectation rather than per-test.
  • Touch one file, migrate it; leave the rest alone. The new modes mean incremental migration no longer breaks the build.

What stays XCTest-only

UI automation (XCUITest), performance testing, and Objective-C exception handling stay in XCTest. If your suite includes them, the boundary is the same as it was pre-2026, just better signposted. For the XCUITest side specifically, our App Store screenshot automation guide covers the patterns that make UI tests useful past green/red.

Parameterized tests and exit tests

Two Swift Testing additions worth knowing:

  • Parameterized tests: each argument becomes a separate test case and they run in parallel.
  • Exit tests: run code expected to crash in a child process. Supported on macOS, Linux, FreeBSD, and Windows. Useful for assert-on-precondition coverage.

Beyond apps: gRPC Swift and MLX Swift

Two ecosystem updates worth knowing about even if you are an iOS-first developer. Both move Swift further into territory that used to be dominated by other languages.

gRPC Swift: typed clients from .proto

The workflow is now a Swift Package Manager build step. Define your service in a .proto file, add grpc-swift-nio-transport and grpc-swift-protobuf as dependencies, attach the GRPCProtobufGenerator run-build-tool plugin, and recompile. You get a typed Swift client.

Watch on Apple Developer ↗

Four RPC shapes are supported:

// Unary: one request, one response
let user = try await client.getUser(.with { $0.id = "42" })
 
// Server-streaming: one request, many responses (live feed)
for try await event in client.subscribeEvents(.with { $0.topic = "subs" }) {
    handle(event)
}
 
// Client-streaming: many requests, one response
let summary = try await client.uploadEvents { writer in
    for event in pending { try await writer.write(event) }
}
 
// Bidirectional: AsyncSequence in, writer out
try await client.syncDocuments(requests: localChanges) { writer in
    for try await remote in serverChanges { try await writer.write(remote) }
}

Protobuf messages are roughly half the size of the equivalent JSON message on the wire, which is the case for using gRPC over REST when you control both ends. Apple itself uses gRPC in the open-source Containerization framework, and the same machinery underpins Private Cloud Compute, iCloud Keychain, iCloud Photos, and SharePlay file sharing.

For an iOS client, wrap the client in withGRPCClient, share it through the SwiftUI environment, and disconnect on scene background. For deployment, a multi-stage Containerfile that builds with swift:latest and runs on swift:slim lands cleanly on Google Cloud Run with HTTP/2.

MLX Swift: numerical computing that reads like math

MLX Swift sits alongside Accelerate (hand-tuned CPU), BNNS (neural networks), Metal Performance Shaders (direct GPU kernels), and Swift Numerics (Complex type), rather than replacing them. The central abstraction is N-dimensional arrays you can manipulate in NumPy-like syntax.

Watch on Apple Developer ↗
import MLX
 
// Mandelbrot set in two lines, GPU by default
let c = MLXArray.complexGrid(width: 1024, height: 768)
var z = MLXArray.zeros(like: c)
for _ in 0..<256 {
    z = z * z + c
    eval(z) // keep the compute graph small
}
let bounded = (abs(z) < 2).asInt32()

Two features matter most:

  • Lazy evaluation. Operations build a compute graph rather than executing immediately. Call eval() or read a value to materialize it. This is what enables both automatic GPU execution and the next item.
  • Automatic differentiation. The grad function transformation returns the exact gradient of a function with respect to a parameter, without you writing the derivative.

The ecosystem ships in three pieces: mlx-swift (core), mlx-swift-lm (language models), and mlx-swift-examples (runnable LLM, diffusion, and training samples). MIT licensed. The same engine sits behind the Python, C++, and C front-ends, so prototyping in Python and shipping in Swift is a real workflow.

Where to start, by team shape

The release surface is large. Pick the path that matches your project.

If you ship a paid iOS app daily

Start with ergonomics. They cost nothing to adopt and you will feel them on the first compile.

  • Collapse stacked @available lines into anyAppleOS where platforms align.
  • Use @diagnose for the deprecation warnings you are migrating away from, instead of project-wide compiler flags.
  • Reach for module selectors (::) the next time a third-party library collides with your own type names.
  • Move the codebase to swift-tools-version: 6.4 so new test files default to the complete interoperability mode.

If you maintain a large XCTest suite

Turn on interoperability and start writing new tests in Swift Testing. Move the test plan to complete mode so cross-framework issues become real build errors. Reuse the XCTest helpers you already have through the interop boundary. Migrate touched files incrementally; do not block on a big-bang sweep.

Keep UI automation, performance tests, and Objective-C exception handling in XCTest. Nothing about 2026 changes that boundary.

If your hot path actually matters

Audit the computed properties on your hot structs. If they hold large values and the call pattern is read-modify-write, switch from get/set to borrow/mutate. Replace UnsafePointer parameters with Ref and MutableRef. Where you were using UnsafeContinuation, switch to the new exactly-one-resume Continuation.

Measure before reaching for @inline(always) or @specialized. The optimizer is usually right. Forcing it can enlarge the binary and slow other call sites.

If you also write server or ML code

gRPC Swift gives you typed clients straight from .proto. MLX Swift gives you GPU-accelerated arrays and automatic differentiation. Both are real options now, not aspirational. The Xcode 27 translation agents workflow is the other 2026 piece that pairs with this: a localized Swift app served by a typed Swift backend is a real shape an indie team can ship.

FAQ

Do I have to adopt Swift 6.4 to use Swift Testing interoperability?

The interoperability features are part of 6.3 and 6.4, but the complete interop mode (the one where cross-framework issues become build errors) is the default for swift-tools-version: 6.4 and Xcode 27 new projects. On 6.3 the default is limited (warnings). You can opt into complete on 6.3 via SWIFT_TESTING_XCTEST_INTEROP_MODE=complete.

Will ownership types break my existing code?

No. Ownership types are opt-in. Your existing Array, Dictionary, and class-based types behave exactly as before. The new noncopyable types (UniqueArray, UniqueBox, Ref, MutableRef, the new Continuation) are additions, not replacements. The protocol updates (Equatable, Comparable, Hashable on noncopyable types) are extensions, not source-breaking changes.

Can I use anyAppleOS if I still support older Apple platform versions?

Yes, you fall back to the explicit per-platform list when the supported versions diverge. anyAppleOS is sugar for the case where the same version applies across every Apple platform. The compiler will tell you if you reach for it and one of your platforms supports an older version.

Does MLX Swift only run on Apple Silicon?

MLX Swift is optimized for Apple Silicon and the unified memory architecture, but the Swift API itself runs anywhere Swift runs. CPU fallback works on Intel Macs. The GPU acceleration that makes the demos compelling needs Apple Silicon.

Is gRPC Swift a replacement for URLSession?

No. URLSession remains the right choice for HTTP REST clients and basic networking. gRPC Swift is what you reach for when you control both ends of the connection and want typed methods, multiplexed streams over HTTP/2, and Protobuf's size advantage. The two cover different use cases.

The 2026 release is large enough that it would be easy to read through, file the highlights away as "I will look at that when I have time," and never come back. The single move that returns the most time per unit of effort, for an indie iOS team, is moving the project to swift-tools-version: 6.4 and writing the next test you would have written anyway in Swift Testing. Everything else, including the ownership work and the gRPC and MLX explorations, follows from there.


Spaceport generates production-ready SwiftUI Xcode projects with the boring-but-essential parts of a paid iOS app already wired up: RevenueCat subscriptions, onboarding, sign-in, analytics, an AI assistant scaffold, App Store Connect pricing across 25 markets, and Swift Skills context files (CLAUDE.md, .cursorrules, skill.json) that point coding agents at the project's conventions. The 2026 Swift changes described above land naturally on top of that foundation. From an indie iOS dev, for indie iOS devs.

Read more at spaceport.build

Community appsJoin Discord