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 briefly go through a history of Swift over the past decade, and show you how the community has grown through workgroups, expanded the package ecosystem, and increased platform support. We'll introduce you to a new language mode that achieves data-race safety by default, and a language subset that lets you run Swift on highly constrained systems. We'll also explore some language updates including noncopyable types, typed throws, and improved C++ interoperability.
Chapters
- 0:00 - Introduction
- 0:12 - Swift over the years
- 3:44 - Agenda
- 3:58 - Swift project update
- 4:08 - Community
- 4:59 - Packages
- 5:50 - Blogs
- 6:33 - Swift everywhere
- 7:37 - Cross compilation to Linux
- 11:27 - Foundation
- 13:06 - Swift Testing
- 14:34 - Improvements to builds
- 16:15 - Swift's new space
- 17:03 - Language updates
- 17:29 - Noncopyable types
- 19:55 - Embedded Swift
- 21:47 - C++ interoperability
- 23:34 - Typed throws
- 26:07 - Swift 6 language mode and data-race safety
- 28:43 - Low-level synchronization primitives
- 29:59 - Wrap up
Resources
- Forum: Programming Languages
- Install Swift
- Swift 6 Migration Guide
- Swift Blog
- Swift Community Overview
- Swift Forums
- The Swift Programming Language
Related Videos
WWDC24
- Consume noncopyable types in Swift
- Demystify explicitly built modules
- Explore Swift performance
- Go further with Swift Testing
- Go small with Embedded Swift
- Meet Swift Testing
- Migrate your app to Swift 6
WWDC23
-
Download
Hi! Welcome to “What’s new in Swift”. I’m Mishal and later, I will be joined by my colleague Meghana. This year marks a major milestone for the Swift project as it reaches 10 years since it was announced at WWDC2014.
The year after Swift was announced it was open sourced and also available on Linux. We also introduced a community driven evolution process for the language and the standard library.
In 2016, we introduced the Swift Package Manager along with Swift 3.
This was the first language version to use the new evolution process and included over 80 language proposals.
If you were with us during the migration from Swift 2 to 3, you might remember it was challenging one. We learned from that experience and in Swift 4 we figured out how to introduce a new language mode without requiring all of your Swift code to move to it at once. The same compiler can support multiple language modes, so you can freely mix modules compiled as Swift 3 and as Swift 4 in the same program. This new approach made it possible to incrementally adopt the new language modes throughout the ecosystem, so every developer could migrate when they are ready.
2018 brought improvements to the Generics system, like conditional conformances that we take for granted in Swift today.
This also set the stage for one of our most important releases: In Swift 5 we introduced the stable ABI on Apple Platforms. For app developers, this meant a smaller download size, because you no longer bundled a complete copy of the Swift standard library in your app. Instead, the Swift standard library was part of the OS itself, optimized for that OS and shared across all Swift apps and frameworks.
This represented a fundamental shift, because now we could use the full expressive power of Swift to build better safer APIs and frameworks. And we did, with the introduction of SwiftUI, which leverages Swift’s unique features to build UI apps better, faster.
By 2020, Swift arrived to more platforms, and the community port to Windows became officially available on swift.org.
We introduced a key feature, the concurrency model with Async/Await, Actors, and structured concurrency The next year, we introduced distributed actors, which made it easier to build network services. We also continued to refine the concurrency model.
The same year, the community released the Swift extension for VSCode, providing a cross-platform editing experience for Swift development.
Last year, we introduced bidirectional interoperability with C++. This made it easier to bring Swift’s safety and expressivity to improve large, cross-platform C++ code bases.
We also introduced Macros, which helped reduce repetitive boilerplates and enabled a new generation of even more expressive APIs like SwiftData.
That brings us to Swift 6. Swift 6 improves portability, performance, and the overall developer experience. It also introduces the Swift 6 language mode, providing data-race safety guarantees that help you write correct concurrent programs.
We’ll cover some of the key achievements and new investments in the Swift project. We’ll also look at some of the new places Swift is available. and we’ll talk about some of the new language features including the new language mode, Swift 6. The Swift programming language is part of a larger ecosystem which contains tools, packages, and a vibrant community including developers like you. Let’s talk about how the community has evolved. In the beginning, the Swift Core team was the only steering group and was responsible for the language evolution process and so much more.
As the community has grown, we have expanded the number of steering groups and workgroups over the last few years. This year, we introduced a Platform steering group to focus on bringing Swift to more places. Additionally, the Swift core team is also actively working on creating a new Ecosystem steering group to focus on the developer experience and the broader ecosystem. To continue the momentum for the low-level environments, the Swift Core team is also working on introducing a new Embedded workgroup. We welcome you to join the Swift community and explore the different steering groups and workgroups on swift.org/community. Swift.org is the home for the Swift Project. The website workgroup has made major improvements to the homepage and getting started guides. These guides now contain a variety of new tutorials including cross-platform library and web services to help you explore different technologies in Swift.
To explore Swift packages, you can now visit swift.org/packages which is integrated with swiftPackageIndex.com. The Swift Package Index builds all of the packages for multiple Swift versions and platforms to help you pick the right package for your application. Each month, the Swift community nominates packages for the Community Showcase. If you want to nominate your favorite packages, we would love to hear about them on forums.swift.org This year’s swift.org blog posts highlighted exciting developments across the Swift community. Here are a few examples.
The Browser Company was able to leverage Swift’s interoperability to build Windows app using native UI.
The blog post Writing GNOME Apps with Swift, describes how result builders enabled a declarative syntax for a native GNOME UI library. This blog post covers a new feature in Swift 6, Pack iteration. Pack iteration simplifies the process of interacting with the value parameter packs. For example, pack iteration makes it easy to implement the equality operator for tuples of any length.
Swift continues to grow as cross platform language, as it is being ported to more and more platforms. Swift is officially supported on Apple platforms, Linux, and Windows. In addition, there are a number of platforms being brought up by the Swift community, including WebAssembly.
This year, we are expanding the supported Linux platforms to include Fedora and Debian.
Xcode has been the primary IDE for Swift since the beginning and still is for developing apps on Apple platforms. But there are many different development environments, so we developed SourceKit LSP, a language server implementation for Swift that enables IDEs and editors to integrate Swift Support. It’s exciting to see the community has adopted sourcekit-lsp in VSCode, Neovim, Emacs and more.
This makes it much easier for you to develop on the editor and platform of your choice.
Cross compilation is a common development use case, and if you have been developing for Apple platforms you are already using cross compilation. With cross compilation, you can generate an executable on one environment and run it on a different environment. For example, you can build your app on macOS and run it on an iPad. Now we want to bring this capability to Linux. This means, you can develop on a familiar macOS environment and deploy the program to a Linux server or a container.
We are excited to introduce a fully static Linux SDK for Swift to help you cross compile from macOS to Linux. With static linking the libraries you will no longer need to install additional packages in order to run your program. Let’s see how this works in action. We are going to use a Swift package which has a simple REST API that returns one cat-face emoji at random. If you want to learn more about this package, check out “Meet Swift OpenAPI Generator”.
We will build this package on macOS, and deploy it on a Linux server using the static Linux SDK. I have three terminal windows open. The one on the left is where we'll build the swift package. This terminal is logged into my local machine, running macOS. The top right terminal is also logged into my local macOS machine.
Below that is a terminal with an open SSH connection to a Linux host. First, I'll build my service for macOS, which I can do by running swift build.
The resulting binary is built for running on macOS, which we can see by inspecting the build output.
Let's run this service on my local machine in the top right terminal.
Now we can make a request to the server listening on localhost, and see the local server logging the request.
Now let's cross-compile this service so we can run it on the Linux server. First, I’ll need to install the fully static Linux SDK for Swift.
While the SDK is being installed, let’s go over the flag required to cross compile.
The swift-sdk flag identifies which SDK to build against.
Here we're specifying that we'd like to compile for an ARM64 Linux environment and link against musl, to produce a statically linked binary which can run on any Linux machine, even one without a Swift runtime installed.
I will add the flag to the swift build command and start the build.
This time, the resulting binary is built for running on Linux, which we can see by inspecting the build output.
Let's copy this over to our Linux server and run it! And we can now make a request to the Linux server from our Mac.
It worked, we got a cat-face emoji! We first created the service on macOS for a macOS host. Then, we used the fully static Linux SDK for Swift to cross-compile from macOS to the Linux host.
This allowed us to generate a statically linked binary that can run on any Linux machine without needing an additional runtime to be installed. You can download the SDK preview at swift.org/install.
Now lets dive into some of the key libraries in Swift.
Foundation is an essential component of many applications, providing important APIs including JSON decoding, date and time formatting, file system operations, and more. It is also one of our longest-lived frameworks, with history dating back to the very beginning of macOS X.
When Swift was open sourced, we knew that this API would be useful on all platforms. So, we introduced the swift-corelibs-foundation project. Since then, the language has evolved, and allowed us to now use a single, unified implementation across all platforms. That is why we rewrote Foundation from legacy C and Objective-C into modern, portable Swift.
The result is more consistent, has a higher quality, and has better performance. New features and improvements come simultaneously to all supported platforms, just like Swift itself. This Swift implementation first shipped in iOS and macOS last fall. Even Objective-C applications gained the benefit of these improvements. Even better, swift-foundation is open source. We invite community contributions, and have an open evolution process for adding new API.
For example, Predicate is an API that is now available on all platforms in Swift 6 via swift-foundation.
This year, it also gained support for regular expressions.
We're excited about this new direction for Foundation, for both its new implementation and close interaction with the community.
Let’s talk about a new framework, Swift Testing. It’s approachable, expressive, flexible, and scalable. It takes advantage of modern Swift features such as macros and seamlessly integrates with concurrency. The package is developed in open source with cross platform in mind. It’s designed to be seamlessly integrated into multiple IDEs such as Xcode and VSCode.
The vision for the Swift Testing library is to become the ecosystem's official, default testing solution, and you can read about the full API direction in the Swift evolution vision document. Let’s explore some of the features of Swift Testing.
To declare a test, you add a Test attribute to your function. You can provide a custom display name, as an argument to the Test attribute. This can help make it easier to understand what the test is doing. You can verify the results are met by using expect which is a macro so you can write a simple or complex Swift expression.
Swift Testing allows you to use tags to organize and filter your tests. You can use arguments to avoid duplicating the same test for multiple inputs. This is highlighting just some of the features of Swift Testing. If you’d like to learn more, check out “Meet Swift Testing” and “Go further with Swift Testing”.
Before you can test your code, you need to build it, so we’ve made improvements to how Xcode builds your code.
When you build your Swift code, each module depends on other modules, often ones from the SDK.
When the Swift compiler encounters a module like SwiftUI, it may need to build a binary version of that module.
That happens implicitly, so you might not even notice it except for as a one-off slowdown. However, it’s hiding a lot of work, because it has to build every module that’s used, including ones written in Objective-C or C++, sequentially.
When another Swift module is built, it will re-use the same binary modules, but it might have to wait until they are ready, which means we aren’t getting as much parallelism out of the build as we want.
Additionally, the debugger might have to build its own versions of all of these module files. This could lead to long pauses when first printing variables in the debugger.
Explicitly built modules turn those implicit steps into explicit build steps.
This means that the module builds can be performed in parallel, and that they show up clearly in the build log.
The result leads to more predictable and reliable builds.
The debugger can now share binary modules with the build, resulting in faster debugging.
Explicit module builds can be enabled in the Xcode build settings. To learn more, please see “Demystify explicitly built modules”.
In the last 10 years, Swift has grown up a lot. It’s made new friends and has gone through some unique experiences and now, its time for it to move into its own space. Swift will be moving to a new organization on github.com/swiftlang. And will be managed by the Swift Project. This will include the Swift Compiler, Foundation, and many more Swift ecosystem packages. We will be starting the migration soon and details will be posted on swift.org. We are excited to see Swift grow in its new space.
Thank you for helping evolve the language and the ecosystem. This is just the beginning. Here is Meghana to tell you more about the great new language features in Swift 6. This has been an incredible year for Swift. Swift 6 introduces a new language mode that achieves data-race safety, expanding Swift’s safety guarantees to concurrent programs. We also have a new language subset called Embedded Swift which can run on highly constrained systems.
Along with this, Swift 6 has many new improvements, that make Swift even better. Let’s start by talking about noncopyable types. All Swift types, whether value types or reference types are copyable by default. Noncopyable types suppress this default copyability. Making them appropriate for scenarios where you want to express unique ownership. For example, a unique system resource such as a file can be represented as a noncopyable struct with a deinitializer that automatically closes it. This representation prevents runtime issues like multiple writers to the same file. It also prevents resource leaks which are easy to introduce without automatic cleanup.
However, this representation still isn’t ideal. The initializer takes in an open file descriptor. This is unintuitive and also unsafe.
You may introduce some code between opening the file and initialization, that terminates the program, the deinitializer never gets to run, resulting in a resource leak. We can fix this with a failable initializer that takes in the filename, opens the file, and finishes initialization if the filename was valid.
This initializer returns an Optional of the noncopyable file type; an Optional is a generic type in the standard library. Swift 5.10’s support for noncopyable types was limited only to concrete types.
Swift 6 introduces support for noncopyable types in all generic contexts, and in standard library’s crucial generic types like Optional. With this, you can now write failable initializers of noncopyable types like the file type.
The powerful ability to abstract over both copyable and noncopyable types generically, expands the usability of noncopyable types. To learn more, check out “Consume noncopyable types in Swift”. Along with expressing unique ownership, noncopyable types let you have fine-grained control over performance. In low level systems with severe resource constraints, it’s possible for copies to become cost prohibitive and noncopyable types are appropriate there as well.
Low-level systems are constrained on memory, storage, and runtime capabilities. Due to their low footprint, C and C++ have been the primary choice for programming on such systems. Now, you can use Swift.
Embedded Swift is a new language subset and compilation model of Swift, that can produce extremely small standalone binaries, suitable for highly constrained systems. It turns off certain language features that need runtime support like reflection and any types, and uses compiler techniques such as full generics specialization, and static linking to produce suitable binaries.
Despite turning off some language features, the Embedded Swift subset feels very close to “full” Swift, and makes it easy to continue writing idiomatic, easy-to-read Swift code.
Equipped with Embedded Swift, games written in Swift can now run on a compact gaming console like the Playdate. Games with binary sizes of just a few kbytes! But, Embedded Swift is not just fun and games It can be used on a wide variety of ARM and RISC-V microcontrollers popular for building industrial applications.
The Apple Secure Enclave Processor uses Embedded Swift. The Secure Enclave is an isolated subsystem separate from the main processor. Dedicated to keep sensitive data secure.
Security is crucial in such systems, and Embedded Swift brings Swift’s safety guarantees to them. Equipped with Swift’s interoperability with C and C++, you can incrementally adopt Swift in your embedded projects. To learn more, check out “Go small with Embedded Swift”.
Last year, we introduced bidirectional interoperability with C++. Swift can interoperate directly with C++ providing a seamless developer experience and zero bridging costs. We have continued to expand the interoperable language features, and with Swift 6, C++ virtual methods, default arguments, move-only types and crucial C++ standard library types can be directly imported into Swift. Interoperability involves mapping similar language concepts and making necessary adjustments to match semantics. C++ virtual methods and default arguments are mapped to their equivalent Swift versions. C++ move-only types like the Person type is mapped to a noncopyable type in Swift.
The Swift compiler inserts calls to the C++ move constructor when needed. And if you unintentionally try to copy the noncopyable value, the Swift compiler will diagnose it for you. C++ is a widely used programming language, but its lack of safety guarantees leave it open for security attacks. You can incrementally adopt Swift in your C++ projects and improve your security and productivity. On the Mac, the Finder now uses C++ interoperability. The Browser Company used it to bring up their ARC browser on Windows. And Swift Godot uses it to provide Swift bindings to the Godot game engine.
C++ is large language and interoperability with it is an evolving story, head to swift.org to stay updated.
To learn more on C++ interoperability, watch “Mix Swift and C++” from last year.
Swift has first-class error handling support for throwing, propagating, and catching errors when your code runs into exceptional conditions. Errors conform to the error protocol and you can write functions that throw with the throws keyword.
Thrown errors are type erased and appear with the any Error type in the catch handler. Since type erasure loses the error’s concrete type information, you may have to insert a dynamic type check, in the rare cases you want to handle the error exhaustively. Type erasure also involves boxing and unboxing. In highly constrained systems without runtime allocation capabilities, this presents a challenge. Swift 6 introduces typed throws to overcome this. Typed throws let you specify the error type along with the throws keyword. There’s no type erasure involved and the error appears in the catch block with its concrete type.
Typed throws is a generalization of the error handling system we have. Untyped throws are the same as typed throws with an any Error type. And a non-throwing function is the same as a function with a typed throw of the Never type. This formulation introduced by typed throws, lets you to handle error types in a generic way. For example, the map function, takes in a closure that may throw and maps the closure over its elements.
With typed throws, the map function can be written generically over the Failure type abstracting over both throwing and non-throwing cases. This avoids code duplication and specifies a precise API contract.
Since typed throws specifies the failure type in the API contract, it constrains the evolution of the API.
If you want to maintain the flexibility to change the thrown error type, continue using untyped throws.
Use typed throws when working with internal functions, or functions that propagate the error from its arguments, or when working in constrained environments, where untyped throws are cost prohibitive.
Along with typed throws and the other updates we talked about, the Swift 6 compiler has several other improvements to the language, and under the hood improvements to performance and robustness. And in addition to all of this, the Swift 6 compiler has a new Swift 6 language mode that achieves Data-race safety by default.
Data-races are a common programmatic error when writing concurrent programs. They can happen when multiple threads are sharing data and one of them tries to mutate it. Data-races can lead to unexpected runtime behavior, program crashes, and hard to reproduce issues. Since its inception, data-race safety has been a primary goal of Swift concurrency and we have been making incremental progress towards it.
Swift concurrency was designed around mechanisms to achieve data isolation, actors for protecting mutable state, and Sendable for safe data sharing. Swift 5.10 achieved data-race safety under the complete concurrency checking flag.
The newly introduced Swift 6 language mode achieves data-race safety by default. Turning all the data-race issues in your app into compile time errors. Massively improving the security of your app and reducing crunch-time debugging adventures.
Since you may need to make adjustments to your code with the new language mode, you can adopt it when you are ready. And you can adopt it module-by-module and even interoperate with dependencies that may or may not have migrated to the Swift 6 language mode. Data-race safety is the only update governed by enabling the Swift 6 language mode. All the other updates are available by default, when you update to the Swift 6 compiler.
We have also made significant improvements to data-race checking. To ensure safety, complete concurrency checking in Swift 5.10 banned passing all non-Sendable values across actor isolation boundaries. Swift 6 can recognize that it is safe to pass non-Sendable values, in scenarios where they can no longer be referenced from their original isolation boundary. For example, passing the non-Sendable client reference from the MainActor to the ClientStore actor would result in a compiler warning with Swift5.10’s complete concurrency checking.
However, the client reference is no longer referenced on the MainActor after it is sent to the ClientStore actor. Since there is no sharing of the client’s state between the MainActor and the ClientStore actor, there can’t be a data-race. With the improvements to data-race checking in Swift 6, this compiles successfully.
And, if you introduce a client reference after passing it to the ClientStoreActor, the compiler will raise an error to indicate a data-race.
In addition to the high-level model of synchronization provided by actors, Swift 6 includes some new low-level primitives.
The Synchronization module introduces Atomics. They are generic over any type that provides an efficient lock-free implementation on the platform. Atomic values should always be stored in “let” properties for safe concurrent access.
All operations on atomics are explicit, with memory-ordering arguments similar to the C and C++ memory model.
The Synchronization module also introduces Mutex. Like atomics, a mutex should be stored in a let property, and can be safely accessed concurrently.
All accesses to the storage protected by the mutex is via the closure passed to the withLock method, which ensures mutually exclusive access.
With infrastructure for incremental migration, improved data-race checking, and new low-level primitives for synchronization, we have built all the necessary tools for your road to data-race safety. For best practices on migration, follow along a hands-on tutorial in “Migrate your app to Swift 6”.
From new and improved language features, valuable safety guarantees, and new avenues for Swift, we have covered a lot of ground today. Join us on swift.org to shape the next decade of Swift. Thank you for watching and happy coding!
-
-
9:15 - Swift Build
swift build
-
9:20 - Inspecting the build output
file .build/debug/CatService
-
9:24 - Run the REST API service
.build/debug/CatService
-
9:30 - Make a request to REST API service
curl localhost:8080/api/emoji
-
9:45 - Install the Fully static Linux SDK for Swift
swift sdk install ~/preview-static-swift-linux-0.0.1.tar.gz
-
10:18 - Swift build command flag to cross compile
swift build --swift-sdk aarch64-swift-linux-musl
-
10:30 - Inspecting the build output
file .build/debug/CatService
-
10:35 - Copy the service over to our Linux server and run it
scp .build/debug/CatService demo-linux-host:~/CatService ./CatService
-
10:45 - Make a request to REST API service from macOS to Linux
curl demo-linux-host:8080/api/emoji
-
13:50 - Swift Testing - Declare a test function
// Swift Testing import Testing @Test func rating() { let video = Video(id: 2, name: "Mystery Creek") #expect(video.rating == "⭐️⭐️⭐️⭐️") }
-
13:55 - Swift Testing - Customize a test’s name
// Swift Testing import Testing @Test("Recognized rating") func rating() { let video = Video(id: 2, name: "Mystery Creek") #expect(video.rating == "⭐️⭐️⭐️⭐️") }
-
14:13 - Swift Testing - Organize test function with tags
// Swift Testing import Testing @Test("Recognized rating", .tags(.critical)) func rating() { let video = Video(id: 2, name: "Mystery Creek") #expect(video.rating == "⭐️⭐️⭐️⭐️") }
-
14:19 - Swift Testing - Parameterize test with arguments
// Swift Testing import Testing @Test("Recognized rating", .tags(.critical), arguments: [ (1, "A Beach", "⭐️⭐️⭐️⭐️⭐️"), (2, "Mystery Creek", "⭐️⭐️⭐️⭐️"), ]) func rating(videoId: Int, videoName: String, expectedRating: String) { let video = Video(id: videoId, name: videoName) #expect(video.rating == expectedRating) }
-
17:50 - Noncopyable types
struct File: ~Copyable { private let fd: CInt init(descriptor: CInt) { self.fd = descriptor } func write(buffer: [UInt8]) { // ... } deinit { close(fd) } }
-
18:12 - Noncopyable types
guard let fd = open(name) else { return } let file = File(descriptor: fd) file.write(buffer: data)
-
18:42 - Noncopyable types
struct File: ~Copyable { private let fd: CInt init?(name: String) { guard let fd = open(name) else { return nil } self.fd = fd } func write(buffer: [UInt8]) { // ... } deinit { close(fd) } }
-
22:29 - C++ Interoperability
struct Person { Person(const Person&) = delete; Person(Person &&) = default; // ... };
-
22:34 - C++ Interoperability
struct Developer: ~Copyable { let person: Person init(person: consuming Person) { self.person = person } } let person = Person() let developer = Developer(person: person)
-
22:40 - C++ Interoperability
struct Developer: ~Copyable { let person: Person init(person: consuming Person) { self.person = person } } let person = Person() let developer = Developer(person: person) person.printInfo()
-
23:43 - Untyped throws
enum IntegerParseError: Error { case nonDigitCharacter(String, index: String.Index) } func parse(string: String) throws -> Int { for index in string.indices { // ... throw IntegerParseError.nonDigitCharacter(string, index: index) } } do { let value = try parse(string: "1+234") } catch let error as IntegerParseError { // ... } catch { // error is 'any Error' }
-
24:19 - Typed throws
enum IntegerParseError: Error { case nonDigitCharacter(String, index: String.Index) } func parse(string: String) throws(IntegerParseError) -> Int { for index in string.indices { // ... throw IntegerParseError.nonDigitCharacter(string, index: index) } } do { let value = try parse(string: "1+234") } catch { // error is 'IntegerParseError' }
-
24:39 - Typed throws - any and Never error types
func parse(string: String) throws -> Int { //... } func parse(string: String) throws(any Error) -> Int { //... } func parse(string: String) -> Int { //... } func parse(string: String) throws(Never) -> Int { //... }
-
28:02 - Passing a NonSendable reference across actor isolation boundaries
class Client { init(name: String, balance: Double) {} } actor ClientStore { static let shared = ClientStore() private var clients: [Client] = [] func addClient(_ client: Client) { clients.append(client) } } @MainActor func openAccount(name: String, balance: Double) async { let client = Client(name: name, balance: balance) await ClientStore.shared.addClient(client) }
-
28:52 - Atomic
import Dispatch import Synchronization let counter = Atomic<Int>(0) DispatchQueue.concurrentPerform(iterations: 10) { _ in for _ in 0 ..< 1_000_000 { counter.wrappingAdd(1, ordering: .relaxed) } } print(counter.load(ordering: .relaxed))
-
29:21 - Mutex
import Synchronization final class LockingResourceManager: Sendable { let cache = Mutex<[String: Resource]>([:]) func save(_ resource: Resource, as key: String) { cache.withLock { $0[key] = resource } } }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.