Streaming is available in most browsers,
and in the Developer app.
-
What’s new in Swift
Join us for an update on Swift. We'll talk about workflow improvements that make you more productive, and new and modernized library APIs for fundamental programming tasks. We'll show examples of Swift adoption throughout more layers of the software stack. Finally, we'll explore new language features for both improving approachability of concurrency, and achieving peak performance when you need it.
Chapters
- 0:00 - Introduction & Agenda
- 0:48 - swiftlang updates
- 3:06 - Development workflow: Writing code
- 4:40 - Development workflow: Building
- 7:36 - Development workflow: Debugging
- 9:14 - Libraries: Subprocess
- 10:45 - Libraries: Foundation
- 12:31 - Libraries: Observation
- 14:13 - Libraries: Testing
- 16:08 - Swift throughout the stack: Embedded Swift
- 18:00 - Swift throughout the stack: Security
- 19:37 - Swift throughout the stack: Server
- 23:23 - Swift throughout the stack: Platforms
- 26:11 - Language evolution: Performance
- 30:28 - Language evolution: Concurrency
- 37:15 - Wrap up
Resources
Related Videos
WWDC25
- Code-along: Elevate an app with Swift concurrency
- Embracing Swift concurrency
- Explore Swift and Java interoperability
- Improve memory usage and performance with Swift
- Meet Containerization
- Safely mix C, C++, and Swift
WWDC24
-
Search this video…
Hello, and welcome to What’s New in Swift. I'm Holly. And I’m Allan. And today we’ll show you the new features and improvements in Swift 6.2.
These improvements aim to make you a more productive Swift programmer, no matter where you choose to write code or what kind of code you’re writing. First, we’ll talk about workflow improvements for writing, building, and debugging code. Then we’ll cover new library APIs for fundamental programming tasks. We’ll also show you how Swift can be adopted at every layer of the software stack. Finally, we’ll explore new language features that make concurrency more approachable and help you achieve peak performance when you need it. All of the changes we’ll cover today were developed together in open source.
The swiftlang organization on GitHub has grown to over 50 projects, including the compiler, the Swift.org website, the foundation and testing libraries, and other key components underpinning the broader ecosystem. Beyond migrating projects into swiftlang, we’ve open sourced Xcode’s build system called Swift Build.
Swift Build also supports the build process for Apple’s operating systems. There’s an active open source effort to adopt Swift Build as the Swift Package Manager’s low-level build system. This will unify the build engine between Xcode and the Swift.org toolchains.
You can read more about Swift Build in the blog post on “The Next Chapter in Swift Build Technologies”. One of the newest members of the swiftlang project is a version manager called swiftly. Swiftly was originally developed by the open source community for streamlining Swift toolchain management on Linux. Swiftly now supports macOS, and the 1.0 release is available on swift.org. With just one command, you can install the latest Swift toolchain. Or you can install a nightly snapshot from a GitHub branch to try out in-development language features. In Xcode, toolchains installed by swiftly are available in the Toolchains menu. Remember that if you’re building an app, you still need to use the Xcode toolchain to submit your app to the App Store. In VS Code, you can select toolchains installed by swiftly from the Toolchain command. You can learn more about swiftly 1.0 on the Swift blog.
The improvements to getting up and running with Swift extend beyond installing the tools.
The Swift.org homepage has a new look and a refreshed focus on helping you dive into different areas, from apps to cloud services to embedded systems. This work is in collaboration with the Swift Website Workgroup.
Getting started with Swift has never been easier. Allan will show you how Swift 6.2 brings more flexibility to create a development environment that works for you. Thanks, Holly.
This year’s Swift release is full of improvements to development workflows, and you’ll benefit from them no matter how you choose to write Swift code. These enhancements cover a wide range of workflows, including writing code, building, and debugging. Let’s start by taking a look at what’s new for you this year if you develop in VS Code.
The Swift extension in the VS Code marketplace is now officially verified and distributed by Swift.org. It also gained a number of new features over the past year. The first is background indexing. In Swift 6.1, we enabled background indexing by default for Swift PM projects in VS Code and other IDEs. This allows editor features, like jump to definition, to stay up to date as you make changes to your project. Code completion results in VS Code are now more relevant, thanks to improvements made to SourceKit-LSP. Debugging has also been simplified. LLDB support is now included automatically when you install the Swift extension.
The latest version of the extension also includes a new project panel, which makes it easy to find your package’s dependencies, targets, and tasks. Finally, the VS Code Swift extension also has a new way to view your project’s documentation. You can bring up a DocC preview side-by-side with your code, and it will update live as you type. The VS Code Swift extension is developed in open source, and you can find it on GitHub in the swiftlang organization.
Now, let’s talk about some improvements to building Swift code that are available to you everywhere. One of the most important aspects of your productivity is the performance of your tools.
Swift 6.2 significantly improves clean build times for projects that use macro-based APIs. To see how, let’s say you have a project that uses a macro from an imaginary package called Stringify.
Previously, in order to build your projects, Swift PM first had to fetch the sources of swift-syntax, which is a library that powers macros. Then it would build swift-syntax, build the Stringify macro plugin, and finally build your code. While the build of swift-syntax could be cached, it still lengthened the time needed to perform a clean build. This can be especially noticeable in a continuous integration environment. To speed up your build, the latest releases of Swift PM and Xcode support pre-built swift-syntax dependencies. This completely eliminates an expensive build step, and for some projects, it reduces clean build times by minutes.
If you own a package that provides macros, your clients will be able to take advantage of this optimization as long as the package depends on a tagged release of swift-syntax. Your productivity isn’t always just about build performance, though. Sometimes trying to resolve issues found by the compiler is what slows you down. For example, when you write concurrent code in the Swift 6 language mode, the compiler may detect potential data races that you need to prevent. This error is identifying something important, but that doesn’t mean it’s obvious how to solve it.
That’s why in Swift 6.2, we’ve begun expanding the documentation available for many categories of diagnostics.
These extended explanations of common warnings and errors help you understand the problem and give you concrete solutions. You can access the documentation from your IDE and online at swift.org. Having control over how diagnostics behave can also be important. For example, to keep code free of warnings, you might use the “warnings as errors” setting. This works well for warnings that are simple to address, but some warnings, like those for deprecated APIs, you may not want to be forced to resolve immediately. Swift 6.2 gives you the flexibility to decide which warnings to treat as errors. For example, you could decide to treat all warnings as errors by default, but make an exception for deprecated declarations. On the other hand, if you’re only interested in preventing some categories of warnings, you can just promote those warnings to errors instead. Adding controls for compiler warnings was a great example of a community-driven effort to improve Swift for all developers. Thank you to the proposal authors and everyone who participated in the discussions on the forums.
Finally, let's take a look at a few great improvements to debugging in Swift 6.2.
In this release, we’ve greatly improved the experience of debugging asynchronous code. As you step through code running in a Swift task, LLDB now follows execution into asynchronous functions, even if that requires switching between threads. The debugger's understanding of Swift’s task-centric model allows it to show you which task is executing. You can even name a task to make it easier to identify. Task names will also appear in Instruments profiles that are recorded with the Swift Concurrency template. You can also use new commands like swift task info to get more information about the currently executing task, such as its priority and child tasks. Lastly, your debugging sessions will be more responsive this year, thanks to explicitly built modules, which is a build system feature that enables parallelism and reuse when building module dependencies. Prior to explicitly built modules, the Xcode build and debugger would use completely separate module graphs. Now the debugger is able to reuse the modules from the build. That means that the first time you run p or po in the debugger, the command will evaluate much faster because type information is immediately available. Explicitly built modules are enabled by default in Xcode 26. Those are just some of the tooling improvements that we’ve made to Swift this year. Next, I’ll take you through some important updates we’ve made to the core Swift libraries that you use every day.
Libraries help you with all kinds of tasks, and Swift’s core libraries provide essential building blocks for writing Swift code. Swift 6.2 modernizes more of Foundation’s API surface, and it provides new APIs to streamline everyday programming tasks.
To improve the experience of using Swift for scripting tasks, the Foundation Workgroup introduced a new package with APIs for launching a subprocess.
You start by adding and importing the new Subprocess package. Then you launch a subprocess by calling the run function and awaiting the result.
If you provide a string with the process name, the run method will look up the executable based on the $PATH environment variable.
In most cases, you’ll launch a subprocess using a FilePath to specify the full path to an executable. When the subprocess terminates, you can inspect the exit status, the standard output, and other information about the process execution. The subprocess library has many more capabilities that give you fine grained control over process execution, platform specific configuration options, and more. You can explore the full API surface in the swift-subprocess repository. The package is in version 0.1, and feedback from your adoption will inform the API that is released in version 1.0.
The Foundation Workgroup also improved everyday APIs used most commonly in app projects.
Responding to notifications from UIKit is very common in iOS apps, but the code to observe a notification is prone to mistakes. First, you have to be careful to register for a notification name that the object supports posting. If you make a mistake, your notification callback will never run. Information about the notification is stored in an untyped dictionary, which requires you to manually subscript using the right key and dynamic cast the result to the correct type. And even when the notification is guaranteed to be posted on the main thread, you’d still get concurrency errors when accessing main actor APIs. Notification names and payloads now support concrete types. Concrete types allow the compiler to check that the object supports the notification kind you’re registering for. They also eliminate boilerplate when working with the notification payload. The notification type specifies where the notification is posted. You can freely access main actor APIs if the notification is posted on the main actor. A conformance to MainActorMessage guarantees that the notification is always posted synchronously on the main thread. A conformance to AsyncMessage means the notification is posted asynchronously on an arbitrary thread. Concrete notification types are available in the SDK for the notifications posted by frameworks like UIKit and Foundation. You can also add concrete notification types for your own notifications.
Broadcasting system notifications to registered observers is based on a more general observer pattern. The Observation Library provides general-purpose APIs for automatically tracking state changes in an object graph.
You can opt a class into observation tracking using the @Observable macro. Swift 6.2 introduces a way to stream state changes with an AsyncSequence from an observable type.
You start by creating an instance of the new Observations type with a closure. In the closure, you compute the value that you want to observe changes to. Here, I want to observe the string describing the player’s current state, and I want a new value each time the score is updated or the player gets a new item. You’ll get an updated value based on the observable properties that are used in the closure. Updates happen transactionally.
The tracking for an update begins when any of the observable properties in the closure have their willSet called. And it ends at the next await where the code suspends. The updated value will include all synchronous changes to other properties between these two points of the code. This ensures that synchronous updates to multiple properties don’t lead to observation updates with the object in an inconsistent state.
If I synchronously update both the score and the item, I’ll only get one updated value that contains both changes.
The observations type conforms to AsyncSequence, so you can iterate over updated values using a for-await loop.
As developers, we write tests every day.
Swift Testing is a cross platform library that provides macros for describing and organizing your tests, like the @Test attribute for declaring a test function. You use the expect and require macros to validate your assumptions, and Swift Testing gives you actionable information when an expectation or requirement fails.
But sometimes, test failures are difficult to triage, especially if they’re only failing in remote environments like CI. Understanding a test failure might require more context about the data used by the test.
Swift 6.2 introduces custom attachments to help you diagnose test failures. You can add an attachment to a test by calling the Attachment.record method. You can attach library types like Data and String, and you can implement attachment support for your own types by conforming to the attachable protocol.
Swift 6.2 also supports exit tests, allowing you to test code that you expect to terminate under certain conditions.
For example, if you write a function that validates assumptions about input parameters through preconditions, you can write a test case that will succeed when the precondition fails.
You write an exit test by passing the processExitsWith argument to #expect or #require. When you write an exit test, Swift Testing will launch a new process to run your test and you can validate that the process exits successfully with a specific exit code or signal or with any failure status.
These general purpose libraries allow you to write and test portable Swift code throughout your project, even beyond your application code. At Apple, we use Swift throughout our entire software stack.
We use it for firmware, apps and frameworks, and large-scale web services. You can take advantage of improvements in Swift 6.2 when writing code for embedded devices, security critical components, and servers. You’ll also be able to target a few new platforms. Let’s start at the bottom of the stack with Embedded Swift.
Embedded Swift is a subset of Swift that allows you to write code targeting the most constrained environments, like firmware for embedded devices and operating system kernels. It’s a compilation mode that supports core Swift features like value and reference types, closures, optionals, error handling, generics, and more. At Apple, we’re using Embedded Swift in some of the lowest-level software running on the iPhone. For example, in iOS 26, Embedded Swift runs on the coprocessor that manages access to the shared memory pages between the CPU and GPU. If you want to try out Embedded Swift, you can learn more by watching Go small with Embedded Swift. Swift 6.2 expands Embedded Swift with new capabilities. It now covers Swift's full string APIs, including string interpolation. Swift’s any types, which can represent values of types conforming to a protocol, are also now available whenever the protocol is constrained to class types. The Swift standard library is also gaining InlineArray and Span, which are two new APIs for efficiently working with regions of memory. These types, which Holly will cover in more detail later, are perfect for the kind of performance-sensitive code you often find in embedded programs. As Embedded Swift has become increasingly capable, the community has been busy creating examples that you can use to get started on your own embedded projects. Check them out on GitHub in the swift-embedded-examples repository.
One of the benefits of Embedded Swift is that it allows you to write low-level software in a language that is memory safe by default. Memory safe code is more secure, and we’ve been working on making Swift even better for code with the strongest security requirements.
Sometimes you need to write code to accomplish something that is fundamentally unsafe. When integrating with a dependency written in C, for example, it’s common to use an API that takes pointers. In security critical contexts, unsafe code should be avoided whenever possible. If it can’t be avoided, though, it should be easy to identify. That’s why we’ve introduced a new *opt-in* feature in Swift 6.2 called strict-memory-safety. This mode requires that all uses of unsafe APIs be acknowledged explicitly in source code. The annotations the mode requires help you identify which parts of your code need extra attention from a security perspective. Along with this new mode, Swift 6.2 also supports new annotations for C and C++ headers that cause APIs to be imported into Swift using safe, ergonomic types like Span. To learn more about the new interoperability features, check out Safely mix C, C++, and Swift.
In Apple’s operating systems, we’re adopting strict memory safety in two security critical components: WebKit and a subsystem of the Messages app that parses incoming messages and attachments. Both of these components handle untrusted input, which makes it especially important to limit the use of unsafe APIs.
Now, let’s check in on how Swift is being used in the server ecosystem.
Swift plays a big role in our services at Apple. Every second, millions of requests are processed by backend services that are powered by Swift. One of those services is responsible for alerting users about passwords that are compromised because they’ve been found in data breaches.
Previously built with Java, this service was recently rewritten in Swift, and the benefits were dramatic. Thanks to Swift’s efficient native code and deterministic memory management, the throughput of the service increased by 40%, while its hardware requirements were cut in half.
Many of you are benefiting from adopting Swift on the server. For example, the engineers at Cultured Code reimplemented the backend for the popular Things app, called Things Cloud, in Swift and wrote about their experience on swift.org.
For Things Cloud, using Swift led to a 3 times reduction in compute costs, while improving average response times by 400%.
Aside from improved performance, another benefit of adopting Swift was the opportunity to use the same language and tools for both client and server development.
A key part of what allows teams to build server backends with Swift is its libraries. Swift has a growing package ecosystem that covers a wide range of essential use cases. There are libraries for networking, database drivers, observability, message streaming, and more. One notable update this year was the release of gRPC Swift version 2.
gRPC is a modern, high-performance package for building service APIs. Some of the highlights from version 2 are modernized APIs that leverage Swift concurrency, idiomatic generated Swift code, and a pluggable, high-performance transport layer. The maintainers of gRPC Swift wrote a blog post on swift.org about the update. Check it out to learn more.
Interoperability with C, Objective-C, and C++ is one of Swift’s core strengths. You can gradually improve existing code bases written in these languages by adopting Swift one file at a time.
In the server ecosystem, many existing applications are written in Java, and we think there’s a great opportunity to offer incremental Swift adoption in those codebases too. That’s why last year we announced an exciting new open source project called swift-java, which aims to make the languages interoperate seamlessly.
With swift-java, you can create bindings that allow Swift code to call Java code and vice versa. These bindings are designed to wrap the native representations of values from each language without excessive overhead. Java bindings for Swift code can leverage Java’s next-generation foreign interface technology, or fall back to JNI if necessary. The swift-java project is currently experimental and rapidly evolving. You can learn more about how it works by watching Explore Swift and Java interoperability.
When working on apps that have both client and server components, you need a workflow that can test your code in both environments, locally. Apple is releasing a new open source containerization library that you can use to build tools based on Linux containers that run on your Mac. The library is fully implemented in Swift and designed with a focus on security, privacy, and performance. This is a great example of using Swift for systems-level development. To learn more about this library, watch Meet Containerization.
And visit the containerization repository on GitHub where you can find command line binaries for running containers.
Finally, here are some updates on the platforms that Swift supports.
Swift 6.2 adds official support for FreeBSD, an operating system that is popular on servers and embedded platforms. Additionally, Swift is gaining support for WebAssembly, also known as Wasm. WebAssembly is a virtual machine platform focused on portability, security, and high performance. Developers can build both client and server applications for Wasm and then deploy them to the browser or other runtimes.
WebAssembly support started out as a community project.
Earlier this year, that project reached an important milestone with a vision document for official WebAssembly support in Swift.
Let’s check out Wasm support in action. I have an app written in Swift that loads a 3D model from a file and then renders it in the browser. Here's what the app looks like right now.
The app is running on the Wasm virtual machine, and the Swift logo is rendered using an experimental web standard called WebGPU.
The app uses JavaScriptKit, which is an open source package developed by the SwiftWasm organization.
I can use this library to interact with the Javascript runtime through native Swift APIs. To make the final binary as compact as possible, I’m compiling the app using Embedded Swift, but for the most part, this code is just normal Swift code.
For example, I’ve added some geometry utilities that leverage generics.
And my parser for Wavefront object files uses Swift’s native String APIs.
Let's add some animation. I’ll jump to the update method of our renderer.
To give the model some spin, the logo’s rotation needs to be updated on each frame.
Let's test it out.
To re-run the application, I’ve triggered a task from the Project Panel.
The task is configured to invoke Swift Build to compile the Wasm app.
It then makes the binary as small as possible by running a few minimization tools on the build output. Finally, it starts a web server to host the content.
That looks great.
The heart of Swift being a great choice throughout the software stack is that it’s a great language. Swift prioritizes safety and ease-of-use by default, while still providing tools for advanced programmers to write complex complex code and tune it for maximum performance.
And you can learn about language features as you need to. So you can start small and dive deeper into advanced features when you’re ready.
Swift 6.2 evolves the language to make it easier to use for both beginners and experts. We’ve provided a more approachable path to introducing concurrency in a project. And we’ve made the language more capable for experts writing performance-critical code. Let's start with performance updates.
The Array type is an ordered list of elements, and it’s one of the most commonly used data types across Swift projects.
Features of Array like dynamic resizing make it easy to use and suitable for so many tasks. But this flexibility comes at a cost.
To support dynamic resizing, an array stores a reference to a heap-allocated buffer.
When more elements are added and the array is at capacity, new memory has to be allocated dynamically and the existing elements are copied over.
If you’re profiling performance-critical code that uses Array in a hot path, you might notice memory allocation or reference counting for the array buffer that you need to eliminate. If the size of an array never changes, there’s no need to pay the cost of heap allocation. InlineArray is a new fixed-size array type with inline storage for its elements. The size is part of the type, written in angle brackets before the element type.
Instead of storing a reference to a buffer containing the elements, the elements of an InlineArray are stored directly. This means the elements can be stored on the stack, or directly within other types without additional heap allocation.
InlineArrays can store copyable and non-copyable types.
When storing copyable types, the InlineArray itself can be copied, and the element values are copied eagerly. Writing the size as part of the type is achieved using a new generics feature to allow integers as type parameters.
Just like the element type, the size of an InlineArray can be inferred from an array literal. If you write an InlineArray of 1, 2, and 3, the type is inferred as an InlineArray of 3 ints. Knowing the size of an InlineArray at compile time also enables more optimizations, like eliminating bounds checking when the index is less than the size.
A container type often needs to provide direct access to its underlying storage.
This is useful for performing local processing over contiguous memory without needing to know the layout of the original container.
For example, a function might want to allow operating directly over the contiguous storage of an Array or an InlineArray.
Before Swift 6.2, the common solution was to drop down to unsafe pointers, which are efficient, but are fundamentally unsafe and prone to mistakes.
The new Span type is an abstraction that provides fast, direct access to contiguous memory without compromising memory safety.
The standard library provides a span property for all container types that have contiguous storage for their elements, including Array, ArraySlice, InlineArray, and more.
Span maintains memory safety by ensuring that the contiguous memory remains valid while you’re using the Span. These guarantees define away the memory safety problems inherent to pointers, including use-after-free and overlapping modification.
They’re checked at compile time with no runtime overhead.
For example, modifying the original container will prevent access to the span afterward.
After the modification, you can’t access the span variable again.
A span also can’t outlive the original container. This is called a lifetime dependency, and it prevents the underlying storage from being destroyed while it can still be accessed through the span.
To learn new ways of controlling performance with InlineArray and Span, watch Improve memory usage and performance with Swift. To learn more about understanding the performance characteristics of your Swift code, check out Explore Swift performance.
Another aspect of performance is responsiveness, when a program needs to perform multiple tasks at the same time.
Concurrent programming is hard because sharing memory between multiple tasks is prone to mistakes that lead to unpredictable behavior. Data-race safety in Swift 6 prevents these mistakes at compile time, so you can write concurrent code without fear of introducing hard-to-debug runtime bugs.
But in many cases, the most natural code to write is prone to data races, leading to compiler errors that you have to address.
A class with mutable state, like this PhotoProcessor class, is safe as long as you don’t access it concurrently. It has an async method to extract a Sticker by computing the subject of the given image data.
But if you try to call extractSticker from UI code on the main actor, you’ll get an error that the call risks causing data races. This is because there are several places in the language that offload work to the background implicitly, even if you never needed code to run in parallel.
Swift 6.2 changes this philosophy to stay single threaded by default until you choose to introduce concurrency.
The language changes in Swift 6.2 make the most natural code to write data race free by default. This provides a more approachable path to introducing concurrency in a project.
When you choose to introduce concurrency because you want to run code in parallel, data-race safety will protect you.
First, we've made it easier to call async functions on types with mutable state.
Instead of eagerly offloading async functions that aren't tied to a specific actor, the function will continue to run on the actor it was called from.
This eliminates data races because the values passed into the async function are never sent outside the actor. Async functions can still offload work in their implementation, but clients don’t have to worry about their mutable state.
Next, we’ve made it easier to implement conformances on main actor types. Here I have a protocol called Exportable, and I’m trying to implement a conformance for my main actor StickerModel class.
The export requirement doesn’t have actor isolation, so the language assumed that it could be called from off the main actor, and prevented StickerModel from using main actor state in its implementation.
Swift 6.2 supports these conformances. A conformance that needs main actor state is called an *isolated* conformance. This is safe because the compiler ensures a main actor conformance is only used on the main actor. I can create an ImageExporter type that adds a StickerModel to an array of any Exportable items as long as it stays on the main actor.
But if I allow ImageExporter to be used from anywhere, the compiler prevents adding StickerModel to the array because it isn’t safe to call export on StickerModel from outside the main actor.
With isolated conformances, you only have to solve data race safety issues when the code indicates that it uses the conformance concurrently.
Global and static variables are prone to data races because they allow mutable state to be accessed from anywhere.
The most common way to protect global state is with the main actor. And it’s common to annotate an entire class with the main actor to protect all of its mutable state, especially in a project that doesn’t have a lot of concurrent tasks.
You can model a program that's entirely single-threaded by writing @MainActor on everything in your project.
To make it easier to model single-threaded code, we’ve introduced a mode to infer main actor by default. This eliminates data-race safety errors about unsafe global and static variables, calls to other main actor functions like ones from the SDK, and more, because the main actor protects all mutable state by default.
It also reduces concurrency annotations in code that’s mostly single-threaded.
This mode is great for projects that do most of the work on the main actor, and concurrent code is encapsulated within specific types or files. It’s opt-in and it’s recommended for apps, scripts, and other executable targets.
Offloading work to the background is still important for performance, such as keeping apps responsive when performing CPU-intensive tasks.
Let’s look at the implementation of the extractSticker method on PhotoProcessor.
It first checks whether it already extracted a sticker for an image, so it can return the cached sticker immediately.
If the sticker hasn’t been cached, it extracts the subject from the image data and creates a new sticker. The extractSubject method performs expensive image processing that I don’t want to block the main actor or any other actor. I can offload this work using the @concurrent attribute. @concurrent ensures that a function always runs on the concurrent thread pool, freeing up the actor to run other tasks at the same time.
These language changes work together to make concurrency more approachable.
You start by writing code that runs on the main actor by default, where there’s no risk of data races. When you start to use async functions, those functions run wherever they’re called from. There’s still no risk of data races because all of your code still runs on the main actor. When you’re ready to embrace concurrency to improve performance, it’s easy to offload specific code to the background to run in parallel.
If you’re interested in learning more about using concurrency in practice, check out Elevate an app with Swift concurrency.
To learn more about the fundamental concurrency concepts, watch Embracing Swift concurrency.
Some of these language changes are opt-in because they require changes in your project to adopt. You can find and enable all of the approachable concurrency language changes under the Swift Compiler - Concurrency section of Xcode build settings. You can also enable these features in a Swift package manifest file using the SwiftSettings API. Swift 6.2 includes migration tooling to help you make the necessary code changes automatically. You can learn more about migration tooling at swift.org/migration.
These concurrency improvements were shaped by your feedback on adopting data array safety in your projects. Whether you reported a bug, asked a question about a concurrency error you didn’t understand, reviewed an evolution proposal on the forums, or authored language changes yourself, your input made a difference.
Thank you for helping make concurrency more approachable for everybody.
If you want to get involved in improving Swift, join us on the forums at Swift.org. Everyone is welcome to participate in pushing the language and the ecosystem forward, and we’d love to hear about your own projects in the community showcase section. You can also use the forums to stay up to date with everything happening in Swift, whether it’s conferences, meetups, social media, and more. Thanks for joining us today, and we’ll see you on the forums.
-
-
9:44 - Subprocess: Call `run` with string
import Subprocess let result = try await run( .name("pwd") )
-
10:04 - Subprocess: Call `run` with file path
import Subprocess let swiftPath = FilePath("/usr/bin/swift") let result = try await run( .path(swiftPath), arguments: ["--version"] )
-
10:05 - Subprocess: Accessing standard output
import Subprocess let swiftPath = FilePath("/usr/bin/swift") let result = try await run( .path(swiftPath), arguments: ["--version"] ) let swiftVersion = result.standardOutput
-
10:51 - NotificationCenter: Dynamic types
import UIKit @MainActor class KeyboardObserver { func registerObserver(screen: UIScreen) { let center = NotificationCenter.default let token = center.addObserver( forName: UIResponder.keyboardWillShowNotification, object: screen, queue: .main ) { notification in guard let userInfo = notification.userInfo else { return } let startFrame = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect let endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect guard let startFrame, let endFrame else { return } self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame) } } func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {} }
-
11:34 - NotificationCenter: Concrete types
import UIKit @MainActor class KeyboardObserver { func registerObserver(screen: UIScreen) { let center = NotificationCenter.default let token = center.addObserver( of: screen, for: .keyboardWillShow ) { keyboardState in let startFrame = keyboardState.startFrame let endFrame = keyboardState.endFrame self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame) } } func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {} }
-
12:01 - NotificationCenter: Conformances
extension UIResponder { public struct KeyboardWillShowMessage: NotificationCenter.MainActorMessage } extension HTTPCookieStorage { public struct CookiesChangedMessage: NotificationCenter.AsyncMessage }
-
12:48 - Observation: The @Observable macro
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } }
-
12:58 - Observation: The Observations type
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" }
-
13:56 - Observation: Transactional updates
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" } player.score += 2 player.item = .banana
-
14:05 - Observation: AsyncSequence
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" } player.score += 2 player.item = .banana for await value in values { print(value) }
-
14:17 - Swift Testing
import Testing import Foundation import EvolutionMetadataModel @Test func validateProposalID() async throws { let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL) let jsonDecoder = JSONDecoder() let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data) for proposal in metadata.proposals { #expect(proposal.id.starts(with: "SE")) } }
-
14:54 - Swift Testing: Attachments
import Testing import Foundation import EvolutionMetadataModel @Test func validateProposalID() async throws { let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL) Attachment.record(data, named: "evolution-metadata.json") let jsonDecoder = JSONDecoder() let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data) for proposal in metadata.proposals { #expect(proposal.id.starts(with: "SE")) } }
-
15:23 - Exit Tests: Preconditions
extension Proposal { public var number: Int { let components = id.split(separator: "-") precondition( components.count == 2 && components[1].allSatisfy(\.isNumber), "Invalid proposal ID format \(id); expected SE-<Number>" ) return Int(components[1])! } }
-
15:34 - Exit Tests: processExitsWith argument
import Testing import EvolutionMetadataModel @Test func invalidProposalPrefix() async throws { await #expect(processExitsWith: .failure) { let proposal = Proposal(id: "SE-NNNN") _ = proposal.number } }
-
31:06 - Concurrency: Async function error message
class PhotoProcessor { func extractSticker(data: Data, with id: String?) async -> Sticker? { } } @MainActor final class StickerModel { let photoProcessor = PhotoProcessor() func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? { guard let data = try await item.loadTransferable(type: Data.self) else { return nil } return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier) } }
-
32:06 - Concurrency: Run async functions on the caller's actor
// Run async functions on the caller's actor class PhotoProcessor { func extractSticker(data: Data, with id: String?) async -> Sticker? {} } @MainActor final class StickerModel { let photoProcessor = PhotoProcessor() func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? { guard let data = try await item.loadTransferable(type: Data.self) else { return nil } return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier) } }
-
32:36 - Concurrency: Conformance error
protocol Exportable { func export() } extension StickerModel: Exportable { // error: Conformance of 'StickerModel' to protocol 'Exportable' crosses into main actor-isolated code and can cause data races func export() { photoProcessor.exportAsPNG() } }
-
33:04 - Concurrency: Isolated conformances
// Isolated conformances protocol Exportable { func export() } extension StickerModel: @MainActor Exportable { func export() { photoProcessor.exportAsPNG() } }
-
33:20 - Concurrency: Isolated conformance use
// Isolated conformances @MainActor struct ImageExporter { var items: [any Exportable] mutating func add(_ item: StickerModel) { items.append(item) } func exportAll() { for item in items { item.export() } } }
-
33:31 - Concurrency: Isolated conformance error
// Isolated conformances nonisolated struct ImageExporter { var items: [any Exportable] mutating func add(_ item: StickerModel) { items.append(item) // error: Main actor-isolated conformance of 'StickerModel' to 'Exportable' cannot be used in nonisolated context } func exportAll() { for item in items { item.export() } } }
-
33:51 - Concurrency: Unsafe static variable
final class StickerLibrary { static let shared: StickerLibrary = .init() // error: Static property 'shared' is not concurrency-safe because non-'Sendable' type 'StickerLibrary' may have shared mutable state }
-
34:01 - Concurrency: Protecting static variables
final class StickerLibrary { @MainActor static let shared: StickerLibrary = .init() }
-
34:05 - Concurrency: Protecting classes
@MainActor final class StickerLibrary { static let shared: StickerLibrary = .init() }
-
34:15 - Concurrency: A single-threaded program
@MainActor final class StickerLibrary { static let shared: StickerLibrary = .init() } @MainActor final class StickerModel { let photoProcessor: PhotoProcessor var selection: [PhotosPickerItem] } extension StickerModel: @MainActor Exportable { func export() { photoProcessor.exportAsPNG() } }
-
34:22 - Concurrency: Mode to infer main actor by default
// Mode to infer main actor by default final class StickerLibrary { static let shared: StickerLibrary = .init() } final class StickerModel { let photoProcessor: PhotoProcessor var selection: [PhotosPickerItem] } extension StickerModel: Exportable { func export() { photoProcessor.exportAsPNG() } }
-
35:06 - Concurrency: Explicitly offloading async work
// Explicitly offloading async work class PhotoProcessor { var cachedStickers: [String: Sticker] func extractSticker(data: Data, with id: String) async -> Sticker { if let sticker = cachedStickers[id] { return sticker } let sticker = await Self.extractSubject(from: data) cachedStickers[id] = sticker return sticker } @concurrent static func extractSubject(from data: Data) async -> Sticker {} }
-
-
- 0:00 - Introduction & Agenda
Learn what's new in Swift 6.2. Highlights include: workflow enhancements, new library APIs, expanded adoption across the software stack, and improved concurrency language features for more productive coding.
- 0:48 - swiftlang updates
The swiftlang organization on GitHub has grown significantly, encompassing over 50 projects, including the Swift compiler, Xcode’s build system Swift Build, and the Swift.org website. Swift Build, now open source, supports the build process for Apple operating systems, and is adopted by the Swift Package Manager. A new version manager, swiftly, originally developed for Linux, now supports macOS and simplifies Swift toolchain management. The redesigned Swift.org homepage can also help you dive into different areas.
- 3:06 - Development workflow: Writing code
The latest Swift release enhances development workflows across all platforms, with specific improvements for VS Code Swift extension users including official verification and distribution of the Swift extension by Swift.org. Now, there's also background indexing for real-time editor features, enhanced code completion, simplified debugging with automatic LLDB support, and new project panel and live DocC previews.
- 4:40 - Development workflow: Building
Swift 6.2 introduces several enhancements to boost your productivity. Clean build times for projects utilizing macro-based APIs are significantly improved by eliminating the need to build the swift-syntax library during clean builds. This optimization, supported by Swift PM and Xcode, can reduce build times by minutes. This release also enhances compiler diagnostics documentation by providing more detailed explanations of common warnings and errors, making them easier to understand and resolve. Additionally, you now have greater control over compiler warnings; you can specify which warnings to treat as errors, a feature driven by community feedback.
- 7:36 - Development workflow: Debugging
Debugging is also enhanced in Swift 6.2, particularly for asynchronous code, with improved LLDB functionality, task naming and visibility in Instruments profiles. It also has faster debugger response times due to explicitly built modules enabled by default in Xcode 26.
- 9:14 - Libraries: Subprocess
This release introduces the new Subprocess package, with which you can launch and manage subprocesses directly from Swift code. The package gives you fine-grained control over process execution, so you can specify the executable path. When the subprocess terminates, you can inspect the exit status, standard output, and other information about the process execution, with version 0.1 now available and ready for feedback.
- 10:45 - Libraries: Foundation
The Foundation Workgroup enhanced iOS app development by introducing concrete types for notification names and payloads, streamlining code, eliminating errors, and improving thread safety for both framework and custom notifications. You can also add concrete notification types for your own notifications.
- 12:31 - Libraries: Observation
The Observation Library in Swift uses the observer pattern to track state changes in an object graph. The 'Observable' macro enables observation tracking. Swift 6.2 introduces the 'Observations' type, with which you can create an 'AsyncSequence' to stream state changes based on a closure that computes a value from observable properties. Updates are transactional, ensuring consistent state, and you can iterate over them using a for-await loop.
- 14:13 - Libraries: Testing
Using Swift Testing, a cross-platform library, you can write and organize tests by using macros. Swift 6.2 enhances this capability with custom attachments for better failure diagnosis, especially in remote environments, and exit tests to validate code that terminates under specific conditions. These features support testing portable Swift code, beyond just your application code.
- 16:08 - Swift throughout the stack: Embedded Swift
Swift 6.2 enhances Embedded Swift, enabling you to write code for embedded devices, servers, and security-critical components. It now includes full-string APIs, Swift's 'any' types for class-constrained protocols, and new APIs like 'InlineArray' and 'Span' for working efficiently with regions of memory. Apple uses Embedded Swift in some of the lowest-level software on iPhone, and the community created examples available on GitHub in the swift-embedded-examples repository.
- 18:00 - Swift throughout the stack: Security
Swift 6.2 introduces strict-memory-safety mode, an opt-in feature that requires explicit acknowledgment of unsafe API uses, aiding in identifying security-critical code sections. Apple is adopting this mode in WebKit and a Messages app subsystem, which both handle untrusted input.
- 19:37 - Swift throughout the stack: Server
Swift is widely used in the server ecosystem, particularly at Apple, where it powers backend services processing millions of requests per second. A notable example is a password alert service, previously built in Java, which saw a 40% increase in throughput and a 50% reduction in hardware requirements after being rewritten in Swift. Other companies, such as Cultured Code, also benefited significantly from adopting Swift. The Things Cloud backend, reimplemented in Swift, experienced a 3x reduction in compute costs and a 400% improvement in average response times. Swift's growing package ecosystem, interoperability with C, Objective-C, and C++, and new open source projects like swift-java and a containerization library, enable developers to build efficient, performant server backends, and seamlessly integrate Swift with existing codebases, particularly in Java.
- 23:23 - Swift throughout the stack: Platforms
Swift 6.2 now officially supports FreeBSD and WebAssembly (Wasm), enabling you to build client and server applications for browsers and other runtimes. The Wasm support, which started as a community project, allows you to compile Swift code and run it in the browser, as demonstrated by a 3D rendering app using WebGPU and JavaScriptKit. Swift's safety, ease of use, and performance make it an attractive choice throughout the software stack.
- 26:11 - Language evolution: Performance
The new release also introduces two new types, 'InlineArray' and 'Span', to enhance performance of critical code. 'InlineArray' is a fixed-size array that stores elements directly, eliminating the need for heap allocation. This improves performance, especially in hot paths, and enables more optimizations like eliminating bounds checking. The system specifies the size of an 'InlineArray' as part of the type, and can infer it from array literals. 'Span' provides fast, direct access to contiguous memory without compromising memory safety. It allows functions to operate over the underlying storage of various container types, such as 'Array' and 'InlineArray', safely and efficiently. 'Span' ensures memory validity through compile-time checks, preventing common memory safety issues inherent to pointers.
- 30:28 - Language evolution: Concurrency
Concurrency is a challenging aspect of programming due to the potential for data races when multiple tasks share memory. Swift 6 introduced data-race safety at compile time, but this often led to compiler errors when because the language would offload work to the background implicitly, even if you never needed the code to run in parallel. Swift 6.2 addresses this issue by changing the default philosophy to stay single-threaded until the you explicitly introduce concurrency. This capability, by default, makes the most natural code to write data race-free. The language now allows async functions that aren't tied to a particular actor to continue in the same actor it was called from, eliminating data races. The release also introduces isolated conformances, enabling main actor types to conform to protocols while ensuring the compiler protects main actor state. Additionally, there's now an opt-in mode to infer the main actor by default, reducing concurrency annotations in mostly single-threaded code. When you need concurrency for performance improvement, such as offloading CPU-intensive tasks to the background, Swift 6.2 provides tools to do so safely. The concurrent attribute ensures that specific functions run on the concurrent thread pool, freeing up the current actor to perform tasks simultaneously. These changes work together to make concurrency more approachable and easier to implement in Swift projects.
- 37:15 - Wrap up
User feedback led to Swift's concurrency improvements, making it more user friendly. The community is encouraged to participate in the forums at Swift.org to continue language and ecosystem development, share projects, and stay updated on Swift-related events.