WWDC26: Swift Group Lab - Q&A
Direct answers from Apple Engineers during WWDC26
Any WWDC is a wonderful time! You watch tons of videos, read long articles and docs, and can even join live sessions with Apple developers to ask them questions. This format has become one of the most anticipated parts of the past year. Many of us, including me, have loved it and were glad to join.
WWDC26 gives us an opportunity not only to talk about already shipped features and best practices, but also to ask about the freshly announced ones.
I joined most of the sessions and, as usual, prepared a transcript: grouped, polished, and enriched with time codes. And there are plenty of sessions still to go! So I decided to start a series, probably a long one, with gathered Q&A from those Labs.
As usual, the goal is simple: make the questions easier to scan, easier to revisit, and easier to connect with real app development problems.
I tried to preserve the original wording and combine related answers where appropriate. However, some inaccuracies or mismatches are still possible.
Enjoy! And subscribe so you don’t miss the next Lab.
What’s the best way to transfer “ownership” of data from one isolation domain to another. For example, an actor that generates a large amount of non-Sendable data and then wants to pass this data off to another actor without making a copy, relinquishing its own ability to access the data.
The Swift concurrency model has this concept called region-based isolation, where you are allowed to transfer non-Sendable data from one actor to another as long as the original actor can no longer access that non-Sendable data after that point of transfer. There are some cases where the compiler can just know that this is safe because of your use of those values.
If you want to deliberately annotate parameters or return types, there is a keyword called sending that indicates that it is some non-Sendable value that you are going to transfer off or return from an actor to be used somewhere else safely. If you need to store those values, that is something you can only accomplish with some of the unsafe opt-outs today, but there is an active pitch on the forums. Right now, I think it is called disconnected, and it is a data type that is meant to preserve that property through actually storing those values if you need to store it and then later transfer that somewhere else. So that is an active area of language evolution.
If you have feedback for that, definitely join the conversation in the forums because it is still being designed and discussed.
What is some advice you give when using Swift structured concurrency. Some best practices / some pitfalls.
Structured concurrency works best if you lean into it as assertively as you can. Once you start adding little escape hatches from structured concurrency, you can get into trouble. It helps to find smaller parts of your codebase that you can refactor as a single unit.
You should avoid, at almost all costs, creating unstructured tasks, such as Task.detached or regular task initializer unstructured tasks, unless you are trying to send some work elsewhere to be done. You should not expect to do that in the mainline flow. Task groups become your friend, and where you can, you want to make sure you have objects whose lifecycles fit those task groups with that lexical scope. Create an object, use it in that lexical scope, and stop using it again.
There are a lot of with-style functions in Swift. Swift has a bit of an idiom around using with-style functions, and this can help because it gives you a nice lexical spelling. You do not have to use that everywhere; for example, you can use deinit-based cleanup in places if that works for you. But you do want to focus on doing things that way.
Once you have made those initial steps, the next trick tends to be: do not fan out too much. Write linear asynchronous code in your task groups. Each task should not try to do too many things in parallel. It should be a recipe: A, then B, then C, then D. When you have things that need to happen in parallel, then you can use your task groups. Patterns like fork-join, fan-out/fan-in, or scatter-gather are useful for this kind of structured concurrency work when you have a lot of repetitive work to do.
Cancellation shields are also important. One of structured concurrency’s big wins is automated cancellation propagation. When you want to cancel the whole task, you can say that everything being done in service of this is no longer necessary. Maybe the view was dismissed, or on the server side, maybe the connection was torn down and the client went away. But you might have opened a resource or a handle to something that needs to be cleaned up, and that cleanup itself may be asynchronous.
A common example is file I/O. You might have partly written a file and need to flush the data into a known good state. Or you might have a database transaction that you want to cancel. These pieces of asynchronous cleanup can become problematic if you are not using cancellation shields, because you are executing in a cancelled context. Most Swift code will try to unwind as rapidly as possible in a cancelled context. In those cases, remembering to have your cleanup use cancellation shields is an important pattern, and it is a great companion to async defer.
You can look at your async defer blocks and ask whether anything inside will actually execute correctly. If there are awaitcalls in there that you think really will suspend, maybe you should put that in a cancellation shield as well. That can be a really good one-two punch for getting resources cleaned up nicely.
Non-Sendable types can also help. A non-Sendable type cannot cross concurrency domains, and in a lot of structured concurrency you do not really have concurrency; you just have asynchronous code. Within that asynchronous control flow, you can use a non-Sendable type freely, but you cannot accidentally escape it out. When you embrace structured concurrency, non-Sendable types help you reason about the fact that something is going to be treated linearly. There is no concurrency there. So when you do go concurrent, you have a smaller set of things to think about.
People often ask how to make their data type Sendable, but maybe you should be making some data types non-Sendable. For ephemeral data types used as part of a computation, making them explicitly non-Sendable can help your data model fit much more cleanly. It gives you a clear delineation between objects you expect to pass around and objects you do not.
This can also help with performance, because objects you pass around should be cheap to pass around, which might not be what you need for intermediate types. In Swift 6.4, there is a nicer syntax for explicitly making a type non-Sendable: ~Sendable, similar to ~Copyable. Previously, you had to write an unavailable conformance to Sendable.
One more point: do not rush so far into adopting Swift 6 mode that you use unchecked too much. If you do that, you deprive yourself of the benefit the compiler can give you to know that the code is safe. Leaning on the compiler and using real logic to make sure something is truly Sendable is important.
Is there an overhead cost to unused/unnecessary conformances such as Sendable, Equatable, Hashable, Identifiable, Comparable etc... to every struct just out of easier compiling habits? What does really happen under the hood when such is called upon? Please advise on best practices & common pitfalls.
There is some cost to having extra conformances to things like Equatable and Hashable. In those cases, you have the code associated with the equality function or the hashing function that is needed to support that conformance, and the conformance itself exists even if you do not use it directly in your code.
It could be found later, for example if you do an as? cast to an existential type like any Equatable or any Hashable. It can discover that code and that conformance at runtime, and for that reason the compiler will keep those around even if you are not using them directly in your own code.
Something like Sendable is different. Sendable is essentially just a tag that says the type is sendable. It does not have a runtime representation, so it does not have a cost anywhere in the system.
It is also possible that extra conformances or generic overloads can affect compilation times. If you have many overloads that can satisfy a particular type-checking problem, it can cause type checking to get a little slower because the compiler has to sort through which overloads make sense in the larger context and which one is the best.
From an API design point of view, it is important to make sure there is a meaningful conformance to those protocols. Do not feel like you have to conform just because the protocol is there. If something is Equatable, make sure it actually can be equated, and the same applies to the other protocols as well. That helps prevent mistakes later when maybe the type was not actually meant to be Equatable, Sendable, or something else.
Applying @MainActor often triggers a massive chain reaction of async refactoring across the codebase. What is the cleanest architectural pattern to stop this “concurrency contagion” in legacy apps without sacrificing Swift 6 safety?
When you start to add MainActor to a particular type in your code, you can then only use that type from another MainActorcontext. Anywhere you use that type throughout your codebase, you have to propagate the MainActor annotation or make that code async so that, if the code ends up running off MainActor, it can switch back to MainActor to use the safe things.
There are two general approaches you can use to minimize the amount of propagation you have to do. If everything in your module should be on MainActor, you can switch on MainActor-by-default mode. That makes everything on MainActor, and if you have parts of your code that are offloading work or need to be used from off MainActor, those are the ones you can annotate explicitly.
Otherwise, start with the leaf types in your project and go outward from there. It might also be the case that only parts of the class you are trying to make MainActor actually need to use mutable state. There might be other things within that type where you can selectively mark a method as nonisolated if it is not touching any of the class’s mutable state. That makes it easier because not all uses of that type need to be on MainActor. You can be more precise about which code actually needs to be on MainActor and which code does not.
It is also worth looking for static variables in your code. Sometimes something was written as static var, and sometimes it used to be a computed property that was later changed to be stored, but the var is never actually mutated anywhere in the code. Take that as an opportunity to make some of your state immutable. If it is not mutated anywhere, you do not have to mark it as MainActor, and if it can stay nonisolated, it is easier because you can use it from anywhere.
Some of this is also covered in the “Migrate your app to Swift 6” session from 2024. It is a code-along video where this kind of practice is done with a sample project.
What are the most essential modern Swift features or official resources you recommend I adopt first to ensure high efficiency and great performance? Thank you all so much.
First of all, it is important to profile. We have great tools in Instruments that show you exactly where your time is going. There is a flame graph view that shows you a breakdown of where everything is happening, and that is what you should start with to guide the rest of your optimization. That way, you know you are optimizing the code that is actually costing you time and memory.
There was a great WWDC talk last year about performance and Swift. It used a sample app that did image processing through a series of steps. In each step, Instruments was used to show where the slowdown was, and then modern Swift techniques were applied to make that performance overhead disappear.
There has also been a big focus on performance in Swift, especially in embedded Swift this year, with a focus on Span, UniqueArray, and related work. UniqueArray is a new type that I think was recently accepted into Swift, and it is also available in the Swift Collections package, which has early prototypes and more work around efficient use of data structures.
On top of the flame graph, there is also a Top Functions view. You can filter down to the functions that cost more or spend more time, and use that to do the same kind of practice, filtering the flame graph a little more easily.
One more thing worth calling out is that it is not just about which APIs you use. Do not forget the basics from computer science classes: algorithms, choosing the correct algorithms, and looking at big-O performance as well.
For teams that did the full Swift 6 strict-concurrency migration and annotated everything by hand, what’s the recommended path now that the isolation model reduces the required annotations? Should we be tearing out annotations that are now redundant, or leave them and let them become no-ops?
It is completely fine to have annotations that are redundant in your code. Some people prefer to make things more explicit in certain cases, especially if something is inferred in a way that might not be obvious or might change with a later modification to the code.
There used to be a compiler warning when you were writing generic code and stated a conformance requirement on a type parameter that was implied by something else. Later, a bug was fixed that made that warning much more accurate, so people saw it more often. People complained that they actually liked restating that conformance requirement because it was helpful as documentation and made the function signature easier to read without mentally working through what was implied by another conformance requirement. So the warning was removed. It is fine to restate things that are redundant.
If something is evident from elsewhere in the code, you can remove the redundant annotations if that is your preference. For example, if you have nonisolated on a bunch of methods in an extension, you used to not be able to write nonisolated on the extension itself. Now you can write it directly on the extension and remove those nonisolated modifiers from the individual methods if you prefer. But they are not harmful.
Sometimes explicit annotations provide value. They mean someone thought about this and said that the type absolutely must be Sendable, or absolutely should not be Sendable. That is documentation. If someone later changes the type in a way that breaks it, they understand that this was deliberate and should not change that fundamental property.
That said, whenever you do that, it is often a good idea to write a small comment above it explaining why. Otherwise, you may come back later and know you meant something by it, but not remember what.
When working on very large projects, you often end up writing little helpers. It is a good idea to behave as though those helpers have a little API contract that you are going to hold yourself to, even if they are internal. Defining what you think the surface should be can help. Applying annotations that the compiler may already infer can still be extremely helpful.
Why is UserDefaults not Sendable given that the doc says it is thread-safe? Is this a preliminary step?
A few years ago, when Swift introduced Sendable, Foundation did an audit of everything and looked at which things were definitely Sendable, which things were absolutely not Sendable, and then there was a third category of things that were classes. Maybe the superclass is Sendable, but a subclass is not.
An easy example is NSString: it is immutable and Sendable, but NSMutableString is a subclass and is obviously mutable and not Sendable. For classes where the type is generally not subclassed often but could be subclassed, Foundation had this middle state of “it is not safe.” We did not want to rush to mark something as Sendable if it was not actually true, because that defeats the purpose of the exercise. For some of those cases, we chose to mark them as not Sendable.
There is a new feature in Swift 6.4 that allows those types to be annotated more accurately. That is the ~Sendable feature. It is not quite the same thing as an unavailable conformance to Sendable. An unavailable conformance says that this type and all of its subclasses are definitely not Sendable. With ~Sendable, it is just a lack of conformance to Sendable. Subclasses could have that unavailable conformance to Sendable if they have mutable state, but other subclasses, if they do not add mutable state and are perfectly thread safe, can add a Sendable conformance. It allows more flexibility where subclasses can be either Sendable or not, instead of saying the whole class hierarchy is definitely not Sendable.
The standard global UserDefaults should give you a Sendable conformance, so that is also something to check into.
Would it be good practice to migrate to using borrow/mutate instead of get/set altogether? In what cases would you not recommend migrating to?
borrow and mutate are part of the ownership model that has been fleshed out in Swift. borrow says that you are essentially getting a reference to some data that is held elsewhere, for example in the struct where the property being borrowed lives. mutate is like a reference, but a mutable reference, so it can be used when you want to change that data.
That is different from get and set. get produces a new value. It could be referencing a value inside your struct, or it could have been computed on the fly. Similarly, set can go through any amount of code to make that change, because you are updating at that point.
borrow and mutate can be more efficient because you do not have to create copies and you are not running extra code. You are just referencing something that is already there. However, it only works when there is something there. In the case of mutate, when you want to mutate something, you have to be the only person that can refer to that data, and the compiler will make sure you are the only person modifying that data. So it is a more restrictive model to get better performance.
Where you are performance-sensitive and essentially sharing out data that you are already storing, borrow and mutate are better. For all the other cases, use get and set.
With a lot of additions like Mutex, InlineArray, Span, typed throws, non-copyable types etc. How do we as app developers know what’s meant for us versus systems or embedded developers? And how do you recommend keeping up with the pace? Many developers find them overwhelming.
Swift is designed around progressive disclosure. You should not have to learn all of the features if you are just getting started. The people working on these features have been trying to keep that in mind.
Going back to performance, it does not make sense to jump into using non-copyable types if they do not make sense for your app. You should measure first and see whether there is an issue. Nate’s talk from last year talks about non-copyable types and describes the patterns you will see in Instruments that indicate you may need a non-copyable type. I would not start with that, because these features do add complexity that you may not need from the start.
That said, these features are meant to work well together. Unique types, spans, and sendability analysis should complement each other. When you are in that space, they should work together well so you do not feel like you are mixing and matching unrelated tools.
The intent is that when you encounter a problem, such as a performance problem, there is a tool that can help you. The tool should be similar to what you have already been using, but with greater restrictions that allow Swift to compile faster code or use less memory.
For example, UniqueArray is similar to Array. We use arrays everywhere, and they have copy-on-write semantics, which are great for general optimization and easy to use. If you end up with a performance problem where you see extra copies or retain/release traffic in Instruments, then you can replace your array with a UniqueArray. That will take some work. The compiler will tell you where you are trying to modify it in two places or share it in places where you cannot. But then the compiler guides you into tighter restrictions around the type that allow it to perform better at runtime.
You should not feel the need to learn every Swift feature. Instead, look for where you are having trouble, and there will be a tool in Swift to help that part of your code improve performance or gain extra expressivity. That is the time to learn the tool.
Language features are not collectibles. You do not get a prize for having one of each. You have a limited number of hours in the day and things you need to get done. It is easy to spend too much time trying to make advanced performance features work in a setting where they were never needed. If you have already achieved the performance you need, your time is probably better spent on bug fixes or new features rather than squeezing every nanosecond out of the system.
Sometimes that really is necessary, and usually you will know those moments when you see them. You will spot them in profiling and realize that is where you need to go. Those areas tend to be isolated, too. When you adopt these features, do not decide that your whole project has to convert to UniqueArray. Keep them in areas that are on their own so you do not create a giant refactoring project. Introducing spans or UniqueArray along the hottest path can give you almost all of the performance gains for a fairly small code change supported by the compiler.
Our project has very slow incremental builds, with Swift Emit Module often taking minutes. Splitting into smaller modules didn’t help much. Can Swift features like type inference, generics, associated types affect incremental build performance? How would you diagnose the root cause?
These features can affect build performance in the extreme, but generally they do not affect a project’s build performance overall. Once in a while, you will hit a particular expression that takes a long time.
If it is specifically the module emission phase that is slow, I would expect it to be more related to all the other modules being imported. With explicit module builds, which have been rolling out in Xcode and Swift over the last couple of years, and with the build timeline in Xcode, you can start looking at where the compiler is actually spending its time.
You may have excess dependencies that are causing rebuilds or large rebuilds, and those may be easier to prune when looking at that data. In a sense, it is like performance-tuning your code: you are performance-tuning your build. You have to go and see what is actually happening to find the places to optimize.
If you want to learn more about explicit module builds, there is a session from a couple of years ago that explores that topic. Explicit module builds are on by default now.
Now that Swift 6 concurrency has been out long enough to see how developers actually adopted it, is there anything the team would approach differently if designing it today?
Yes. There are two changes throughout the evolution of Swift concurrency where, if we could go back to the very start, we would probably just make the latest change we made. That is the behavior of async functions, particularly where a nonisolated async function actually runs.
There were two different proposals that made changes to the behavior of nonisolated async functions. One made them always switch to the global concurrent thread pool to run there. A later proposal in Swift 6.2 made them stay on whatever context they were called from. So if you called it from MainActor, the function would stay there.
This was based on real-world experience and feedback from people using async functions in real-world code. People who had adopted Swift’s early concurrency warnings, later complete concurrency checking, in apps, services, and libraries found that they were passing a lot of non-Sendable types back and forth between an actor-isolated context and these nonisolated async functions. That caused a lot of data-safety errors because the original actor-isolated context would still have access to those non-Sendable values at the same time that the async function was running on the global concurrent thread pool.
A much better default was to keep them running in the context where they are called from. That means there is no issue with possible concurrent access to those non-Sendable values. The behavior where you offload to the global concurrent thread pool is still useful, but that should be explicit and opt-in.
I held on for a long time to the belief that those functions running on the global concurrent thread pool was the right long-term model, but I was convinced over time by the issues that caused in real-world projects. That insight would not have been possible without real-world adoption. If we could go back, it would be nice to have had that insight from the beginning and not require a transition where people adjust annotations to adopt the new behavior.
It is an interesting trade-off. From a whole-systems perspective, having more concurrency available lets you get more parallelism out of your system and can be better for performance. But once people tried using the full safety model, it pushed many more types toward being Sendable, and that was not the natural way to express all of these ideas. The newer model is more approachable, more like how things work when they are not concurrent, and more explicit about the points where you introduce concurrency to unlock parallelism.
What are the notable Swift Package Manager improvements in the latest Xcode/Swift release - especially around build and dependency-resolution performance? Are there changes I should adopt to speed up builds in a large multi-package project?
The biggest change for Swift Package Manager in the Swift 6.4 release is one you might not notice because it is not something you have to opt into. Previously, Swift Package Manager in Xcode and Swift Package Manager used in other IDEs, like VS Code with the open-source swift.org toolchains, used different build system implementations. Now both are unified using the Swift Build package.
That brings much more consistency between the two build systems. There is a single point of maintenance for bug fixes and future improvements, and you will see those improvements in Xcode and anywhere else you use Swift Package Manager. There was a preview of this in Swift 6.3, and in Swift 6.4 it is on by default. So it is not something you need to opt into; it is more of an under-the-hood infrastructure improvement.
There are several performance optimizations that were in the Swift Build system and now come to normal package builds because of that. Explicit modules are one example. Swift Build can also be better about breaking apart the build of a module into separate pieces, so you get more parallelism out of your build from this unification.
What’s the one Swift feature most developers don’t know exists but should?
One useful combination is @inlinable with @inline(never). @inlinable is incredibly powerful across module boundaries. It unlocks many optimization opportunities, not only inlining. It can also unlock things like generic specialization or effects propagation that help the optimizer understand exactly what is going on in your code.
You can combine @inlinable with @inline(never), which says that under no circumstances should this function be inlined. That turns out to be a useful performance tool for generic code with cold paths. For example, if you have copy functions that fall back to a laborious and slow byte-wise copy operation, it can be helpful to tell the compiler not to inline that part. You want the fast path inlined because it is hit all the time, but not the slow path that would generate a lot of code. It is a powerful trick for unusual performance problems where you cannot get the compiler to reliably inline the thing you want.
A simpler one is writing type annotations with as. If you have written a large expression and got an ambiguity error from the compiler because there are many overloads, and you know what a specific type should be, you can write a type annotation in that part of the expression to influence overload resolution. Sometimes that guides overload resolution correctly, sometimes it causes a type parameter to be inferred with a concrete type that was not otherwise available, and sometimes it simply gives you a more precise error message because you were more explicit.
Another important “feature” is that Swift is an open-source project. The open-source part of Swift is not only for non-Darwin platforms. The Swift language, standard library, Foundation, networking APIs, and other APIs discussed here are developed and discussed on the forums. Feedback from iOS developers is valuable, even if they never think about Swift on the server. You do not have to contribute a pull request to participate. Asking questions about compiler diagnostics, explaining what confused you, or saying that an API proposal looks good are all valuable forms of feedback.
There are also integer overflow APIs. They can be easy to miss, but if you are working with integers that may get large quickly, Swift has APIs that let you carry the overflow flag around so you can run a straight-line computation and then check at the end whether anything went wrong.
Key paths are another feature that people sometimes forget about. They let you refer to a property in the abstract without an instance, which can avoid building large piles of closures when you need to abstract over different properties.
Finally, Swiftly is worth checking out if you want to install and try newer toolchains, including experimental features. It is familiar if you use VSCode or the CLI, but you can also use it from Xcode.
What’s left in language evolution to get tuples to finally conform to Equatable, Hashable, Comparable, etc. conditionally?
This is an evolution of parameter packs. We now have the base concept in the language. What is needed to support conditional conformances of tuples to Equatable, Hashable, and similar protocols is the ability to write an extension over a tuple type where its element types are represented with a parameter pack, because each element could have a different concrete type.
It does not matter what those concrete types are. We just need the ability to write a where clause with the condition that each element in the parameter pack conforms to the protocol you are looking to conform to. If you are familiar with parameter packs, the keyword is literally each: something like saying where each T conforms to the protocol.
So we need syntax to write a tuple with a parameter pack, and similarly, extension syntax to write a parameterized extension where the extension itself has a parameter pack. That is basically it. It is a fairly small evolution to parameter packs.
There is an experimental implementation in the compiler repository right now. It is not completely there yet, but it is almost all the way there. Once that is done, the older proposal can be revisited. The older proposal used more bespoke conformances for those three protocols specifically, but now that we have parameter packs, the goal is to offer this as a general feature. That way, you could add conformances to your own protocols for tuples where that makes sense as well.
With Swift 6 strict concurrency enforcements, what is the recommended pattern to ingest high-frequency sensor data on a background Actor and safely stream those updates to an @Observable data model on the MainActor without blocking the UI?
The question depends on what “high frequency” means in this context, and exactly how high that frequency is. There are different domains where the answer will change. If high frequency is still substantially lower than the rest of your UI updates, you might have no work to do. It may be straightforward.
Assuming it really is high frequency, one of the things you want to do is avoid context switching excessively. You want to make the switches from your background actor to your observable data model and UI as infrequent as possible.
There are a couple of paths here. One is debouncing. Another is accumulating data. Rather than sending one update for every data change, can you coalesce those changes into a smaller number of events and bring them over together?
There is also a question about data loss. How much data loss is acceptable for your background sensor? For example, if you are measuring electrical voltage, do you need an update every nanosecond rendered to the screen? Probably not. You can likely debounce with data loss, and it is fine if you do not print every intermediate value.
You can also save the data elsewhere while the UI only shows what it actually needs to show. SwiftUI and @Observable naturally coalesce updates for efficiency, which you get for free. Consider the requirements of what the UI really has to show.
If there is a lot of work happening, it is important to make sure that work is happening asynchronously in the first place, and to keep the UI updates only for what the user needs to see. That might be a different problem from having to stream everything, so it may help to split the problem in half.
If you need to do debouncing, Swift Async Algorithms has a debounce implementation that you can fit on top of a standard async sequence model, which is a good way to start.
Performance question: Why tuple is more expensive than struct when returned from a method? 5-10 msec vs 1-3 msec. Or it’s a custom case, and no difference: call stack size, access to tuple items/struct fields etc.?
That was surprising to the panel, but tuples and structs are handled differently. This gets down into the guts of the compiler.
Generally, when you are dealing with structs, the whole struct is passed as a single entity. When you have a tuple, the compiler will often break it apart. If you are passing a tuple into a function, it can be as if you had sent each element of the tuple as a separate parameter. Inside the compiler, this is called exploding the tuple.
It is plausible that if you had a very large tuple, this would be less performant than keeping everything together as you might do with a large structure. Maybe that is what is happening here, but the team would need to look at the actual code and what the optimizer produces to understand why.
The recommendation was to file a GitHub issue with a sample project.
What’s your favorite quality of live / quality of code feature in Swift? Not the obvious ones like structured concurrency, but neat, lesser known things that make Swift especially fun to write.
One forward-looking answer is the new set of protocols called Iterable. This ties in with non-copyable and non-escapable performance work. The interesting part is taking what might be a complex topic and making it feel like natural Swift. For example, you want to use for-in over a Span. That sounds like a simple problem statement, but implementing it in a way that preserves safety, lifetime safety, and memory safety becomes a challenging design problem.
This is an area to watch. It is expected to help build up new container types in Swift and new ways to think about how data is structured, while preserving progressive disclosure and keeping Swift easy to use from the start.
Another favorite quality-of-life feature is the general ability in Swift to build nice APIs that feel natural while still preserving strong guarantees. Some of the features mentioned earlier, such as key paths, integer overflow APIs, type annotations with as, Swift Testing, and the open Swift Evolution process, also fit into that category of smaller things that can make Swift especially fun and effective to write.
🏆Acknowledgments
A huge thank-you to everyone who joined and shared thoughtful, insightful, and engaging questions throughout the session. Your curiosity and input made the discussion truly rich and collaborative.
Question acknowledgments: siracusa, ashrafi, sjk_27, CaiqueMP, YingZHU, mtraversonyy, petarbelokonski, paraipan, Jason-Chung, florentinf, mirkokg, pyrtsa, charly_polley, seriyvolk83, Yoorbo.
Finally, a heartfelt thank-you to the Apple employees for leading the session, sharing expert guidance, and offering clear explanations of Swift and concurrency topics. Your contributions made this an outstanding learning experience for everyone involved.

