Swift 6.4 gives ownership and build tools clearer contracts
Swift 6.4 adds uniquely owned collections and borrowing iteration while changing the default package build engine. More precise debugger module tracking addresses a separate source of development friction.
Swift 6.4 was released on September 15, 2026. Its changes include new ownership-aware containers, borrowing iteration and Swift Build as the default engine for Swift Package Manager. The release also completes work on precise debugger module tracking. These changes interest me because they make previously implicit behavior easier to describe, both in the running program and in the tools used to build it.
I like Swift's standard collections partly because copy-on-write gives them useful value semantics. I also appreciate that the language does not require a tracing garbage collector. Those preferences do not mean I want every container to behave identically. Some values should have a single owner, and some operations need a more predictable relationship to allocation. This release provides additional choices without requiring ordinary application code to abandon Array.
Unique ownership changes what an array can contain
The motivation in SE-0527 explains why adding noncopyable elements to ordinary Array is difficult. Copy-on-write may need to duplicate shared storage during mutation. That cannot simply copy elements that prohibit copying. Making mutation conditionally handle element copyability would introduce further runtime questions into an already complicated performance model.
UniqueArray instead holds its storage uniquely and is itself noncopyable. Moving it into another variable consumes the original binding. The container can therefore own noncopyable elements without needing a copy-on-write path. Its insertion and access operations describe their relationship to ownership through consuming and borrowing behavior.
This is useful beyond the question of whether an element can be copied. The proposal describes copy-on-write mutation as a source of complexity spikes when storage happens to be shared. A uniquely held container removes that particular uncertainty. It still has dynamically growing storage, so uniqueness should not be mistaken for a guarantee that every append avoids allocation.
The distinction matters to how I would choose a container. I would start with Array when its ordinary value semantics fit the program. For a collection of uniquely owned resources, I would assess UniqueArray and the ownership expectations of its callers. A change should make a real requirement easier to express. I would not replace a working container merely to use a newer type.
The proposal places these specialized containers in a Containers module. It also notes that the growth policy for a dynamically resizing array is an implementation detail. That is a useful limit on what application code should assume. A program can rely on the documented ownership contract without treating one observed capacity sequence as a permanent interface.
Iteration can borrow the element
The release adds Iterable so code can traverse elements without requiring each value to be copied. It describes this as extending beyond what Sequence supports. That connects the new containers to the operations callers perform on them. Owning a noncopyable element is only useful if the program can also inspect it without violating ownership.
Borrow and mutate accessors provide another part of that design. The release associates them with reading or updating values such as Span and InlineArray through properties. Ref and MutableRef provide first-class wrappers for shared or exclusive access to one value. Together, these facilities let library interfaces state more precisely whether an operation reads, mutates or takes ownership.
I would review a container API by following an element through a complete operation. The insertion point should explain whether the caller gives up ownership. The traversal operation should explain whether it borrows. A removal should make clear who owns the returned value. This kind of review is more informative than inferring semantics from a familiar method name alone.
Swift 6.4 also gives optionals containing noncopyable values ways to inspect or update their contents without consuming the optional. That is a practical composition issue. Optionality describes whether a value exists, while ownership describes what operations may use it. A useful language model needs those properties to work together without forcing callers to extract and consume a value just to inspect it.
I find that direction consistent with the reasons I like optionals. Absence should remain explicit even when the contained resource has stricter ownership rules. I would still keep a public API as simple as its requirements allow. The extra capabilities are most useful when a library can preserve the constraints of its elements without making application authors understand its internal storage design.
The package build engine becomes a default
Swift Build appeared as a preview in the previous release and is now the default in Swift Package Manager. The release describes a common build platform across Linux, macOS and Windows. It also includes software bill of materials generation in SPDX or CycloneDX format. These are build and distribution capabilities, separate from the ownership rules of the program being compiled.
Build time remains the part of Swift development I enjoy least. I therefore want to evaluate a build change on a package I understand, with its inputs held constant. A common engine is useful for consistency, but the release announcement does not supply a speedup for my applications. I would compare clean and incremental behavior before claiming an improvement in either one.
For a package with custom build steps, I would inspect those steps as part of the upgrade. The relevant question is whether the default engine receives the same intended inputs and produces a usable artifact. I would record any required configuration in the project procedure. That would let a future toolchain evaluation begin from an understood build instead of rediscovering local assumptions.
Interoperability preserves the surrounding ecosystem
The release also bridges Swift Span with C++20 std::span. That makes a view over contiguous elements usable across the language boundary without handwritten conversion code. I would still review the ownership of the underlying storage. A more convenient conversion does not remove the need to know which component keeps the elements alive.
On Android, the SDK moves to NDK 30 and Swift Build support removes a post-install script. These are useful examples of platform support improving through build integration. For a package targeting Android, I would evaluate the complete native dependency set alongside the Swift toolchain and retain the tested SDK combination in its build instructions.
The debugger needs the exact module
The earlier module tracking explanation, published on September 11, describes the debugging mechanism in detail. LLDB embeds compilers so that it can compile and execute expressions while stopped in a program. Evaluating a computed property can therefore require importing the module that defines the relevant types. Looking up that module only by its base name can be ambiguous.
The ambiguity is possible even in an otherwise disciplined build. A project can have different variants of a module or both a private binary module and a public interface. The old lookup could also fall back to importing dependencies implicitly, with substantial compilation work when caches were cold. The new representation tracks explicit module dependencies and records the path to an object's own Swift module in debug information.
The Swift driver passes that module path to compilation jobs. Ordinary SwiftPM and Xcode users receive the behavior automatically. Authors of custom build systems that invoke the frontend directly need to supply the appropriate -debug-module-path. This is a concrete compatibility detail for build maintainers, and it deserves more attention than a general claim that debugging has improved.
For explicitly built modules, Swift 6.4 also allows build systems to remove -modulewrap actions on Linux and Windows and -add_ast_path linker options on Darwin. The earlier approach embedded binary Swift modules in build products or dSYM bundles. Removing them reduces those artifacts, but it does not make all debugging independent of the original module files.
The article distinguishes variable inspection from complex expression evaluation. Reflection metadata supports inspection of variable contents. Function calls and computed getters still require LLDB to find the binary modules at their original or remapped locations. For an archived debugging workflow, I would verify that distinction with the actual artifacts retained by the release process. A smaller dSYM is useful only when the remaining materials still support the diagnosis the application needs.
My Swift 6.4 evaluation would therefore include both runtime ownership tests and a representative debugging session. For a library using the new containers, I would check the intended moves and borrows, including compiler rejection of invalid reuse. For a custom build, I would check module paths and debugger expression evaluation. Those checks would give me evidence about the specific contracts the release changes.