Hello and good afternoon from Cupertino. Welcome to the Swift Group Lab. I'm Angelica from Developer Relations and it is my privilege to be here with some great panelists today. We're also excited to hear all the questions you have for us. Now, there are quite a few of you who are joining us today, so we're going to be focusing on the most popular questions, as well as questions that will benefit the broader audience. Now, if we aren't able to because we could continue the conversation in the Swift forums at forums.swift.org. And if you have specific issues, bug reports, feature requests, you can, of course, submit something in Feedback Assistant, but you can also submit a GitHub issue in the projects directly. Without further ado, let's get to know our panelists, starting across the table with Holly, someone you may recognize from Platform State of the Union, in fact, I think wearing the exact same shirt. Yes. Hi, I'm Holly. I work on the Swift team. I have a particular focus on generics, type inference, compiler diagnostics, and concurrency. I also sit on the open source language steering group and the Swift core team. I'm Corey. I work on the Swift server networking team at Apple. And I have a particular focus on shuffling packets around reliably and quickly to keep everybody's services up and running. Hi, I'm Tony. I work on Foundation, the Swift Standard Library, and some of our Swift packages, like algorithms, collections, atomics, and so forth. I'm also on the Foundation work group. And hello, I'm Doug. I work on the Swift language team. I've been working on Swift since it started, so I've worked on a number of features. And now I sit on the language steering group. Well, thank you again for joining me today for this Q&A. Before we get started with developer questions, there are tons of updates in Swift 6.3 and 6.4. maybe name which one is your favorite. Holly, you're nodding over there. Yeah, so I mentioned one of my focus areas is compiler diagnostics, and I'm especially excited about the general diagnostic improvements that are there in Swift 6.4, but also there's a feature that gives you more precise control over warnings in your project. It's called the at diagnose attribute, and you can use it to do things like suppress deprecation warnings in specific parts of your code, but you can also use it to opt in to warnings that are off by default in specific areas of your code where that might be especially important, things like strict memory safety or strict concurrency, and that might provide some finer-grained ways for you to do migration to the Swift 6 language mode. So I'm excited about that. Great. Nice. For me, I think one of the common bugbears for the server community and the networking community is wanting to continue to push structured concurrency. We've got a bunch of great features in that area, particularly around resource cleanup, which is just one of the most challenging pieces to consistently get right. But the big win that structured concurrency gives you. So for me, I think async defer and cancellation shields are the two sort of really big winners for me. They're going to clean up a lot of code. It can remove some of the custom cancellation shields we've put in. I'm very excited to take advantage of some of those. That's great. Yeah, for me, do I have to say just one or can I do more than one? I mean, if we did more than one, we might be here all day. I'll try, but there's so many to pick from. One big one is we have a new subprocess package coming out. It's entirely open source. It's cross-platform. I think it really fits in with Swift's mission. We've done so many improvements to align with some of the performance goals of the overall release and data and URL and many other types. And new APIs that are exciting, at least to me, like Progress Manager to make some of the most mundane tasks just easy and accessible in Swift. And we make those APIs for you. That's awesome. And Doug? Fantastic. I'm excited. I mean, all the improvements we've been making to embedded Swift. So embedded Swift is about running Swift in tiny places, bare metal, firmware, small devices. And we've really made the language just much easier to use. So the Swift code you write everywhere else, you can now use an embedded Swift. And so it's really fun to see what cool things people can build with it now that you can target anything really easily. Yeah, highly recommend also checking out the sample projects and things like that in the GitHub repo because it's so much fun to just explore how embedded Swift can be used. All right. We've got some great questions from developers. First up, we have one from Syracusa. 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 or relinquishing its own ability to access the data. Holly, do you have any ideas on how you could approach this? Yeah, so 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. So 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 in parameters or return types, there is a keyword called sending that indicates that, you know, it's some non-sendable value that you're going to transfer off or return from an actor to be used somewhere else safely. And then if you need to store those values, that's something that you can only accomplish with some of the unsafe opt-outs today. But there's an active pitch on the forums. I think right now it's called disconnected, but it is a data type that's meant to preserve that property through actually storing those values if you need to store it, and then later on transfer that somewhere else. So that's an active area of language evolution. I was going to say, if you have any feedback for that, definitely join the conversation in forums because it's still being designed and discussed. All right. Following up, there's another concurrency question. So, Holly, this might be for you again. From Ashrafi, what is some advice you give when using Swift Structured Concurrency? Actually, this also might be a great Corey question. So what are some of the best practices or some pitfalls to keep in mind? Actually, Corey, I would love if... Sure, I mean, Structured Concurrency works best if you lean into it as sort of assertively as you can. Once you start adding in little escape patches from Structured Concurrency, you can get into trouble. So it actually really helps to find sort of smaller parts of your code base, which you can refactor as a single unit. The way to think about this is you should avoid at almost all costs creating unstructured tasks, task.detached or regular task init unstructured tasks, unless you are trying to send some work elsewhere to be done. But you should not expect to do that in the mainline flow. So task groups become your friend. And where you can, you want to be making sure you've got objects whose life cycles fit those task groups with that lexical scope. So create an object, use it in that lexical scope, stop using it again. You can help yourself do this. There are a lot of width style functions in Swift. It's got a bit of an idiom about using width style functions. And this can really help you with that because it gives you a nice sort of lexical spelling. But you don't have to. You can, for example, use dInit-based cleanup in places if that's going to work for you. You do want to be trying to focus on doing things that way. Once you've made those initial steps, the subsequent trick tends to be don't fan out too much. Write linear asynchronous code in your task groups. Each task shouldn't be trying to do too many things in parallel. It should be a recipe. It should be a series of steps, A, then B, then C, then D. And when you have things you need to do in parallel, then you can use your task groups. There are a bunch of patterns that are like this in different parts of the programming ecosystem. If you've ever thought about sort of fork join models or if you've considered the sort of fan out, fan in, scatter gather programming patterns, those are really useful for doing this kind of structured concurrency work when you've got a lot of sort of repetitive work you have to do in that way. I do think that there are probably some other suggestions on the panel, though, for other ways to try to take advantage of structured concurrency beyond just what I have. Although cancellation shields is the other one. This is the other thing that is important. So when I say we have this, you want to do the scoped resource gathering, one of Structured Concurrency's big wins is the automated cancellation propagation when you want to cancel the whole task. You want to say, oh, everything I'm doing in service of this is no longer necessary. Maybe the view's been dismissed or on the server side it's maybe this connection got torn down, the client went away. I don't need to finish this operation I'm doing. That's great. But you might have open a resource or a handle to something that needs to be cleaned up. And that cleanup itself is asynchronous. A really common version for this would be file I.O. You might have partway written a file. You need to flush the data out to a known good state. Or maybe you have a database transaction you'd like to cancel. These pieces of asynchronous cleanup can become problematic if you are not using cancellation shields because you're executing in a canceled context. Most Swift code will sort of refuse to execute in a canceled context. It assumes it should be trying to unwind as rapidly as possible. So in those cases, remembering to have your cleanup use cancellation shields is a really important pattern. And it's a great companion to async defer. You can sort of look at your async defer blocks and say, well, anything I put in here, is that actually going to correctly execute? Are there any await calls in here that I think really will suspend? Maybe I should put this in a cancellation shield as well. That can be a really good sort of one-two punch for getting your resources cleaned up really nicely. I feel like this is an area where non-sendable types can help you. Yeah, 100%. Because a non-sendable type can't cross concurrency domains. And so in a lot of structured concurrency, you have not really concurrency. You just have asynchronous code. And within that asynchronous control flow, a non-sendable type, you can use it freely, but you can't accidentally escape it out. And so when you're embracing structured concurrency, these non-sendable types help you reason about the fact that this thing is just going to be treated linearly. You can easily reason about it. There's no concurrency there. So that when you do go concurrent, you have a smaller set of things to think about. Yeah, I think that's actually a really interesting additional point about thinking about particularly sendability. I often talk to people who are constantly asking the question, how do I make my data types sendable? And I think it's often not considered enough that maybe you should be making your data types non-sendable. You can actually achieve a lot, especially for sort of ephemeral data types that are used as part of a computation. Making them explicitly non-sendable can actually help you fit your data model much more cleanly, making sure you've got a really clear delineation about what objects you are expecting to pass around and which ones you're not. This can also really help with performance because objects you pass around, you want to make sure that that act of passing them around is as cheap as it can be, which may not be what you need for your intermediate types. Just as a nice little bonus, if you did want to make that actually explicit, there's a nicer syntax for doing that in Swift 6.4. It's tilde sendable, similar to tilde copyable. Instead of previously, you always had to write like an unavailable conformance to sendable. So nice little. One more thing on that, too, that I've talked to some people who are really in a rush to adopt Swift 6 mode. And while we think that's a great idea, don't go so far that you use unchecked too much to make it so. Because then you're just depriving yourself of the benefit the compiler can give you to know that it's safe. So I think leaning on the compiler and using real logic to make sure that it's truly sendable is important as well. Yeah, absolutely agree. All right. Moving away from that, though, we did talk a little bit about, touched on Sendable here. The next question we have from a developer, SJK27. Is there an overhead cost to unused or unnecessary conformances, such as Sendable, Equatable, Hashable, Identifiable, Comparable, et cetera, to every struct just out of easier compiling habits? What actually happens under the hood when such is called upon? please advise on best practices and common pitfalls. Doug, this might be for you. Sure. So there is some cost to having extra conformances to things like equatable and hashable. So in these cases, you have the code that's associated with the equality function or the hashing function that's needed to support that conformance. And the conformance itself, even if you don't use it in your code directly, it could be found later. Like if you do an as question mark cast to any type, like in any equatable type or in any hashable type, it can then discover that code at runtime and that conformance. And so for that reason, the compiler will actually keep those around, even if you're not using them directly in your own code. Now, something like sendable is a little bit different. Sendable is essentially just a tag that says, hey, this type is sendable. It doesn't have a runtime representation, so it doesn't have a cost anywhere in the system. Can I ask an extension of this, Doug, because I've wondered about this a lot? Does this come up with some of the time-to-check expressions as well with some generic overloads on operators? I've seen plenty of people write generic overloads on operators that take a protocol object. They're generic on one side or the other. It seems to me like that could also increase your compilation times in those areas. It's possible that it could increase your compilation time. So it widens the set of types that can satisfy that particular overload. And so if you have many overloads around that can satisfy this particular type check problem, it can cause the type checking to get a little slower because it has to sort through which of those overloads actually make sense in the larger context and what is the best of those. I'd like to add one more thing, too, which is that from an API design point of view, I think it's important to make sure that there's a meaningful conformance to those protocols, right? Don't feel like you have to just conform it because it's there. If it's equatable, make sure it actually can be equated, and the same for the others as well. So I think that helps prevent mistakes later when maybe it's not actually meant to be equatable or sendable or whatever. All right. Thanks so much. We have a question from a developer about at MainActor. So applying at main actor often triggers, oh, one second. Yes. Applying at main actor often triggers a massive chain reaction of async refactoring across the code base. What is the cleanest architectural pattern to stop this concurrency contagion in legacy apps without sacrificing Swift 6 safety? Holly? Yeah. So I think what we're talking about here is when you start to add main actor to a particular type in your code, you can then only use that type from another main actor context. So anywhere that you're then using that type throughout your code base, you have to go out and propagate that main actor annotation or make that code async so that if that code does end up running off the main actor, it can switch back to the main actor to use the things that are safe. I think there are two general approaches you can use to kind of minimize the amount of propagation that you have to do. One is to, so actually, if everything in your module should be on the main actor, you can go ahead and switch on main actor by default mode. That's just gonna make everything on the main actor, and if you have parts of your code that are offloading work or you need to be able to use from off the main actor, those then are the ones that you can go and annotate explicitly. But otherwise, I would say start with the sort of like leaf types in your project and go outward from there. Similarly, it might be the case that only parts of that class that you're trying to make main actor actually need to use mutable state. And there might be other things within that type where you can go and selectively mark a certain method as non-isolated if it's not actually touching any of the class's mutable state. And that will make it much easier because then not all uses of that type. need to be on the main actor. And so you can be a lot more precise about which code actually needs to be on the main actor versus what doesn't. You should also take a look at, I've seen a lot of cases where like there's a static variable somewhere in the code and it was written as static var. Sometimes it's the case that it used to be a computed property that was then later changed to be stored and that var, it's actually never mutated anywhere in the code. So you should actually take that as an opportunity to make some of your state immutable If it's the case that it's not actually mutated anywhere, and then you don't have to mark that as main actor, and that will, you know, if it can stay non-isolated, then it's a bit easier. You can just use it from anywhere. Yeah, I think some of this is also covered in a session from 2024, I believe. Migrate your app to Swift 6. It's a code-along kind of video where this type of practice is done with a sample project, and definitely follow along there. All right. Moving on to the next question from our developer Ying Zhu. 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. Tony, do you have any answer for Ying Zhu here? Yes. I did open by saying that we've worked on performance. So, yes, absolutely. First of all, I think it's important to profile, right? We have great tools and instruments to actually show you exactly where your time is going. There's a really cool flame graph view now that shows you a breakdown of where everything is happening. And that is where you really should start with to guide the rest of your optimization so that you know you're optimizing the code that is actually costing you time and memory. We had a really great talk last year in WWC. I can't remember the exact name. It was about performance in Swift. So if you search for it, you can absolutely find it. where we took a sample app that did some image processing through a series of steps. And each one, we used instruments to show where the slowdown was. And then we applied some of the techniques from modern Swift to make that performance overhead sort of magically disappear. So that's definitely a huge part of it and something to check into. We've had a really big focus on performance in Swift, especially in embedded Swift this year, with a focus on span, on a unique array, which is a new type that is, I think, maybe just recently accepted into Swift. But it's also available in the Swift collections package, which has some early prototypes of this and many more pieces of work that are about efficient use of data structures. So I think that's... I want to call out as well, Nate was the speaker for that Swift performance using instruments session. New this year on top of being able to use the flame graph is actually top functions, so you could actually filter out to the ones that are costing more or spending more time, and you could actually use that to do the same practice that Nate does and filter out the flame graph a little bit easier. One more thing, actually, which is worth calling out, too. It's not all just about which APIs you use, but don't forget all of your basic computer science classes and algorithms and making sure that you're using the correct algorithms in general and looking at sort of the big O performance as well. Okay, great. Let's take a look at the next question. This is from M. Traversony. I really hope I pronounced that correctly. 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? Holly? Yeah, I love talking about stuff like this. I think it's completely fine to have annotations that are redundant in your code. Some people actually prefer to have to make things be more explicit in certain cases, especially if it's inferred in a way that might not be obvious or inferred in a way that could change with a later modification to the code. Actually have a fun anecdote that's not about concurrency at all. There used to be a compiler warning when you were writing generic code if you stated something like a conformance requirement on a type parameter that was implied by something else. We later fixed a bug that made that warning a lot more accurate so people were seeing it a lot more in code and people complained that, Actually, I like restating that conformance requirement because it's helpful for me, you know, in documentation or in, you know, reading the signature of the function to know explicitly and to not have to work through mentally this was implied, you know, by some other conformance requirements. So we later then just got rid of the warning. You know, it's fine to restate things that are redundant. So if it's something that, you know, is evident from elsewhere in the code, like, for example, if you have non-isolated on a bunch of methods in an extension, you used to not be able to write non-isolated on an extension itself. I think that was new in the Swift 6.0 or Swift 6.2 release. Now you can just write it directly on the extension and you can go ahead and remove those non-isolated modifiers if that's your preference. But they're not harmful. It's not harmful to have those explicit annotations there. Yeah. So don't necessarily rip it out. It's just a practice of ripping it out. If you want to stay explicit, you can. Sometimes they provide value. It means someone thought about this. This type absolutely should not be sendable, for example. That's intentional. Or this type absolutely must be. Or this type absolutely must be sendable. And that's documentation. So if someone goes and changes the type later in a way that breaks it, they understand, no, this was deliberate. I shouldn't change this fundamental property. Yeah, that's for sure true. Though I will add that any time you find yourself doing that is often a good idea to write a little comment above it why you thought that. Yes. Because sometimes you can come back to it and go, well, I clearly meant something by this, but I don't know what it was. Who knows? I'll add on top of that as well, when working on, particularly when you start working on very big projects, you will often end up finding you need to write yourself little helpers. And behaving as though these little helpers have a little API contract that you're going to hold yourself to, even though they don't, is a really good idea, defining what you think the surface of this should be. And to that point, it can really help to behave as though you have to apply a bunch of annotations. Actually, the compiler infers and those are there. Those can be extremely helpful. And sometimes you just get a little bit ADHD about it and want to write equatable and hashable twice. I sometimes do. I find it helps. All right. Next question, and this is about Sendable, from our developer Petar Belakonsky. Why is user defaults not sendable, given that the doc says it is thread-safe? Is this a preliminary step? Tony. Yeah, great question. So a few years ago when Swift introduced sendable in general, we did an audit of everything in Foundation and went through and said, these things are sendable for sure. These things are absolutely not sendable. And then we were left with this sort of third category of things where they're classes. So maybe the superclass is sendable, but a subclass is not. And an easy example to understand how this could happen is NSString is immutable and sendable, but NSMutableString is a subclass and obviously mutable and not sendable. And so for classes where it's generally not subclassed often, but it could be, we had this sort of middle state of, well, it's not safe. And again, like I said earlier, we didn't want to be in a rush to mark something as sendable if it actually wasn't true, because it defeats the whole purpose of the exercise. And so for some of those cases, we chose to mark them not sendable. Now, that said, I believe we have a new feature in Swift 6.4, right, Holly, to allow us to more accurately annotate those types. And so that's something that we're in the process of doing now. Yep, that's the tilde sendable feature I mentioned earlier. It's actually not quite the same thing as an unavailable conformance to sendable. that says this type and all of its subclasses are definitely not sendable. Whereas if you have tilde sendable, that's just a lack of a conformance to sendable. So subclasses could have that unavailable conformance to sendable if they have mutable state, but other subclasses, if they don't add any mutable state and they're perfectly thread safe, you can use those. Those can add a sendable conformance. So it allows more flexibility where subclasses can be either or instead of saying this is definitely not sendable and that's propagated throughout the entire class hierarchy. Yeah. And that said, I believe that the standard global user defaults will give you a sendable conformance, so that is also something to check into. Great. All right. Moving on to the next developer question, another one from sjk27. New, and it was mentioned in What's New and Swift this year. Would it be good practice to migrate to using borrow and mutate instead of get and set altogether? In what cases would you not recommend migrating to this? Doug? I can take that, sure. So borrow and mutate are part of the ownership model that we've been fleshing out in Swift. And so what borrow says is you're essentially getting a reference to some data that is held elsewhere, like, for example, in the struct where you put the property that is getting borrowed. Mutate is like a reference, but it's a mutable reference, so it can be used when you want to change that. That's a little bit different from what get and set are. Get is producing a new value, so it could be referencing just a value that's inside your struct, or it could have been computed on the fly. Similarly, set can go through any amount of code to go ahead and make that change because you're updating at that point. So borrow and mutate can be more efficient because you don't have to create copies, you're not running extra code, you're just referencing something that's there. However, it only works when there is something there, and especially in the case of mutate, where 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 that you are the only person modifying that data. And so it's a little more restrictive model to get that better performance. So I would say where you are performance sensitive and you're essentially sharing out data that you're already storing, borrow and mutate are better. For all the other cases, get set. Okay. Hope that answers the question, SJK27. Moving on to a great question from Parai Pan. Again, I hope I said that correctly. With a lot of additions like mutex, inline array, span, type throws, non-copyable types, et cetera, 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 very overwhelming. This is a great question. Anyone have any thoughts about this? I can start, too. So we've actually, in Foundation, done a lot of this work, too. And I think it's important to remember that, in general, Swift is designed to be a language around progressive disclosure, right? You shouldn't have to learn all of the features if you're just getting started. And I think all the people in these work groups and panels and everything have been trying really hard to keep these features with that in mind. And so going back to what I answered earlier about performance, it doesn't make sense to just jump into using non-copyable types if they don't make any sense for your app. So you should absolutely measure first and see if there's an issue. And Nate's talk from last year talks about non-copyable types a little bit and describes what patterns you will see in instruments that indicate that you may have a need for a non-copyable type. So I wouldn't start with that because, as noted here fairly, they do add a complexity to using the language that you may not need from the start. So I recommend absolutely measuring first and then moving into those things. That said, we're trying to make all of these things also work really well together. So unique types and spans and the sendable analysis, all of these things should complement each other and hopefully when you're in that space, they all can work together well and you don't feel like you're having to get into mixing and matching. Yeah, and the intent here is that when you encounter a problem, like a performance problem, there's a tool that can help you that is similar to the tool you've been using, but has these greater restrictions that allow us to compile to faster code or use less memory. You mentioned unique array, Tony. And so unique array is similar to array. So we use arrays everywhere. Under the hood, these have copy-on-write semantics, which are great for general optimization and very easy to use. If you do end up with a performance problem where you're seeing these extra copies, you're seeing retain-release traffic show up in your instruments trace, then you can replace your array with a unique array. That's going to take a little bit of work. The compiler is going to tell you, well, it's unique, but you're trying to modify it in two places. you're trying to share it in a few different places where you can't do that. But then the compiler will guide you into these tighter restrictions around the type that allow it to perform better at runtime. And so you shouldn't feel the need to go and learn every Swift feature that there is. Instead, look for where you're having trouble, and then there will be a tool there in Swift to help you in that part of your code go and improve performance or get some extra expressivity. And that's the time to learn about the tool. Yeah, a friend of mine once said to me that language features aren't collectibles. You don't get a prize if you've got one of all of them. And I think that that's a really useful takeaway. I also think it's important to remember that as an engineer, you've got a limited number of hours in the day and you've got things you need to get done. And it is easy to end up spending way too much time trying to get some of the more advanced performance features to work in a setting where they were never needed. So again, to Tony's point about profiling, it's useful to try to bear in mind what problem you're actually trying to solve. Have you already achieved the performance that is needed in your situation? If you have, then your time is better spent elsewhere, working on bug fixes or adding new features, rather than trying to sort of squeeze every nanosecond out of the system you're building. The counterpoint is sometimes that really is necessary, and usually it feels like a weird thing to say, but usually you will know these moments when you see them. You will spot them in profiling, and you will realize that's where you need to go. They tend to be more like isolated, too, right? So when you do adopt these features, don't say, well, I guess my whole project has to convert to unique array now. Absolutely not. Like the whole, you know, keep those things in areas that are on their own so that you don't have to embark on a giant refactoring project at the same time. Yeah, we've had great success introducing spans and a couple of unique arrays just along the hottest path. And doing that gives you almost all the performance gains for a fairly small change to your actual code that's then supported by the compiler. Great. Thanks so much. Let's take a look at a developer question from Jason Chung. 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? So these features can affect build performance in the extreme, Like generally it doesn't affect a project's build performance overall. Once in a while you will hit some particular expression that takes a long time. If it's specifically the module emission phase that's slow, I would actually expect that it's more related to all the other modules that are being imported. And so with explicit module builds, which is something that we've been rolling out in Xcode and Swift over the last couple of years, and also the build timeline that you can see in Xcode, you can start to look at that to see where the compiler is actually spending its time. And you may have excess dependencies that are causing these rebuilds or these large rebuilds that would be easier to prune when looking at that particular data. In a sense, it's like performance tuning your code, right? Performance tuning your build, you have to go and see what's going on to find the places to actually optimize it. If you want to learn more about explicit build modules, there is a session on it from, I believe, two years ago where you could explore a little bit more about that. It is on by default now, right? I believe so. Yes. Okay, great. Okay, let's move on to, let's take a break from performance questions. We have a question here back to concurrency. 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, certainly. that there was actually two changes throughout the evolution of Swift concurrency that I think if we could go back from the very start, we would just make the latest change that we made, and that's to the behavior of async functions. So particularly in where a non-isolated async function actually runs. So those of you who follow along in Swift evolution, there were two different proposals that made changes to the behavior of non-isolated async functions. One was to make them always switch to the global concurrent thread pool in order to run there. And then there was a later proposal in Swift 6.2 to actually make them stay on whatever context they were called from. So if you called it from the main actor, the function would stay there. And this was based on our real-world experience and looking at feedback from people using async functions in the context of real-world code. And in particular, people who had adopted Swift's early one concurrency flag, later complete concurrency checking in the context of apps and services and libraries found that they were actually passing a lot of non-sendable types back and forth between an actor isolated context and these non-isolated async functions. And that was causing a lot of data race safety errors because, you know, the original actor isolated context would still have access to those non-sendable values at the same time that that async function was running on the global concurrent thread pool. So we found that a much better default for that was actually keeping them running in the context where they are called from. That means there's no issue with having possible concurrent access to those non-sendable values. And that behavior is still useful, the behavior where you offload it to the global concurrent thread pool. But that's the one that should be explicit and opt in. So I held on to the belief for a very long time that, you know, those functions running on the global concurrent thread pool was the right model for the long term, you know, for the long term benefits. But I was convinced over time and seeing how, you know, issues that that was causing in real world projects. But, yeah, I think that's insight we would not have gotten without real-world adoption of those features. But, yeah, if we could go back, it would be nice to just have had that insight from the beginning and not have to have any kind of transition where, you know, you adjust the annotations in your code in order to adopt the new behavior. I think it's an interesting tradeoff because, you know, back when we had to make a decision of where do these things run. Do they stay on the current task or do they go concurrent? And if you look at it from a whole systems perspective, well, having more concurrency available lets you get more parallelism out of your system can be better for performance. But what it ended up doing when we really thought through and people actually tried using the full safety model is that it pushes a lot more types toward being sendable. And that's not the natural way to express all of these ideas. And so, you know, we ended up changing the direction here because of the feedback and came to a better solution. I think it's easier to start with, so it's a much more approachable model of concurrency. It's more like how things work when they're non-concurrent, and it's very explicit the points at which I need to go concurrent, I'm going to introduce concurrency and unlock the parallelism of my system. Yeah, it's like actually opting in or choosing when to introduce that complexity in the project. Yeah, absolutely. Okay, switching over, actually, to Swift Package Manager. We have a question from Mirko KG. Again, sorry if I totally butchered that. What are the notable Swift Package Manager improvements in the latest Xcode and 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? Anyone want to take this? Sure. I think the biggest change for Swift Package Manager in Swift 6.4 release is one that you might not notice because it's not something that you have to opt into. But previously, Swift Package Manager in Xcode and Swift Package Manager that you use in other IDEs like VS Code from the open source Swift.org tool chains used different build system implementations. But now both of them are unified using the Swift build package. And that brings a lot more consistency between these two build systems. There's a single point of maintenance for bug fixes and future improvements that then you will see in Xcode and anywhere else that you might be using Swift Package Manager. And there was a preview of that in the Swift 6.3 release, and in Swift 6.4, that's now on by default. So it's not something you have to opt into, but it's more like under-the-hood infrastructure improvements for Swift Package Manager. There's a number of performance optimizations. they're in the Swift build system that now come to your normal package builds because of that. We talked about explicit modules. That's something that was implemented in Swift build and is now available. There's also other improvements where the build system can be much better about breaking apart the build of a module into separate pieces so you get more parallelism out of your build from this unification. Great. All right. Now, another highly upvoted question we have from Florentine F. And this one's a great one. What's the one Swift feature most developers don't know exists but should know? All the thinking faces. I don't know if developers should know this exists. This is my favorite. This is the one I like to spring on people. And it's not one feature. It's a combination of two. And there are some annotations that you can apply to functions to control the visibility of the function body across modules and the optimizer's decisions about inlining. So for a long time, there's been the inlineable attribute. And there has been, for a long time, there was the at underscore inline, which I believe we recently stabilized under a different name. We have inlinealways now. Inlinealways. Excellent. Great. Because that's the opposite of the one I want. So Inlineable is incredibly powerful across module boundaries. Inlineable unlocks a huge range of optimization opportunities, but its name can slightly mislead you into thinking the only one that it unlocks is Inlineability. It does unlock that one, but it also unlocks things like generic specialization or effects propagation that can really help the optimizer figure out exactly what's going on in your code. Interestingly, you can combine inlineable with the inline never annotation, which says under no circumstances inline this. And that turns out to be an incredibly useful performance tool for generic code with cold paths. This doesn't happen much. I mean, in the entire sort of Swift networking portfolio I can think of, I think we've used it three times, where we have sort of copy functions that end up falling back to a very laborious and slow bytewise copy operation. And in those cases, it's very helpful to hint the compiler to say all of that is going to generate a lot of code. I do not want it inlined. I want the fast path inlined because we're going to hit that all the time. Inlineable plus inline never unlocks that benefit. And that is a very powerful trick to have in your back pocket for certain somewhat unusual performance problems when you just cannot get the compiler to reliably inline the thing you wanted. I've got a very simple one. It's one that I'm sure a lot of people know about, but I remember this took me a while to learn when I was, or to realize when I was learning Swift, but writing type annotations with as is super useful. And if there's that, like if I've written, you know, a large expression and I've gotten some like ambiguity error message from the compiler because there are many overloads, it didn't know which one I meant. And there's a specific type in there that like I knew what the type was and maybe I just need to like write a type annotation in that specific part of the to influence overload resolution. You can just stick in the as with the type that you expect. And then it could be the case that that influences overload resolution in the right way or it causes it to infer a type parameter with a concrete type that wasn't otherwise available in the expression. Or sometimes I'll just then get a more precise error message at that point because I was more explicit about what I meant in that part of the expression. But yeah, I didn't realize how useful just the as, like plain as was when I was first learning Swift a while later. I've got sort of a meta one, actually. I think I'll talk about Swift as an open source project. I think that's, you know, some people, I think, may have the impression that the open source part of Swift only matters for, you know, other platforms, not Darwin. But for all the Swift APIs we're talking about today that are part of the language, the standard library, foundation, our networking APIs, all of these things are discussed on the forums. And I think we get a lot of really valuable, we could get a lot more valuable feedback from people who maybe never think about Swift on server at all, but use Swift on iOS. That's a perspective that is super valuable and open to everybody. And they may think that it's not for that, but actually it's the same language and the same libraries everywhere. So I think the feature I would recommend is that the development is happening in the open. And even if you only target one platform, you can still have an impact on the project overall by participating in those discussions. There's all kinds of really interesting APIs that go through, especially when you get a little bit higher up the stack in testing or foundation that maybe don't get a lot of feedback on the forums today. But I know there's people out there that would find them really useful and interesting, and I encourage people to jump in and participate. Testing's got loads of cool tricks. Yeah, Swift testing. There's also a great session on Swift testing, migrating to Swift testing this year. I wanted to add to that as well. Another feature to add to that is you don't have to contribute like a PR to participate in this community. I think a lot of people think, oh, I don't know anything about the Swift compiler. I can't possibly participate in this. But there was a conversation, I think, last year where Seema talked about updating diagnostics, just like what is written in them. Or other projects with syntax is a great one to start with. or again, just participating in the conversation and forums. You don't have to necessarily be the one submitting something for the evolution proposals. Even for diagnostics, I think one of the best ways to contribute, it really is contributing and shape improvements, is to ask questions about a compiler error message and explain what you found confusing about it or what you tried based on that error message that didn't work out and that can lead to improvements to make those more actionable, for example. Yeah, that applies to APIs as well, I think on the API designer front. When you're designing APIs, the same as when you're writing error messages, you've got every piece of context about the problem in your head. And so things will be very obvious to the API designer that it turns out completely non-obvious in the API, no matter how much review you go through to try to polish out all the edges of that, gets in front of real users and they say, that makes no sense to me at all. And that is really valuable feedback. Absolutely. Because then we can go back and think about, you know, what is the appropriate way to explain this so that, you know, people that don't have the background that I have necessarily or only have the background of using related APIs nearby. Right. That we make sense for them. And often, you know, sometimes there's radical shifts in the way we want to present things. I do want to add to you, though, that even if you see an API proposal and it looks completely fine to you, feel free to jump in and say so. That's valuable feedback, too. Sure. And of course, our cross-platform Swift is also super important. I just want to call out specifically that I think we could actually use more involvement from iOS developers, too. Yeah, 100%. Can I add a trailing one other feature? I just want to steal one more. The integer overflow APIs seem to be a real surprise to people that they're there. Actually really helpful. If you are working with integers that you have a real suspicion they're going to end up big, fast, Swift has a bunch of APIs that will let you carry the overflow so that you can run your whole straight line computation and then at the end just figure out, well, did any of that go wrong? Very helpful. Very, very helpful. Easy to miss. I almost only ever remember them after I have overflowed the integer before. Makes sense. That's in the class of APIs where when you need them, you really need them. I'm glad they're there. I did have a feature. It's a language feature. Because it keeps coming up, and I don't think people notice that keypads exist sometimes. It's interesting. People come and say, well, I need to go ahead and abstract over these different properties. I don't know how to do it. And they'll build up big piles of closures or something. And I'll point out, you know, there's a key path here. You can just refer to a property in the abstract without the instance. And they say, oh, I remember hearing about that. And then they can go and build it. This keeps happening. And so it's there. It's been there for years. But somehow it gets missed. It's an interesting part of what Tony just mentioned as well, which is this is very one of the things that helps with sort of trying to mix the different engineering cultures in the community a bit more, which is that I feel like the server ecosystem, particularly around things like database drivers, lives and breathes keypads. They're all over the API surface 'cause they let you write these very elegant API constructions. And so it's very funny when people, someone, when I bump into someone who has never worked with them at all, I'm like, oh, I don't think you could have gone five minutes writing a web app without using keypads. - I actually have one as well. Swiftly. I think a lot of, again, if you're using something like VS Code, you might be more familiar with this or CLI, but you can actually do this from Xcode directly as well. So if you want to play around with a newer tool chain and try out some experimental features, like check out Swiftly. It's a really easy way to install those tool chains. Yeah. That was mine. All right. Next, let's move on to another question. Going back to Equatable, Hashable, Comparable, Pirza has a question on what's left in language evolution to get tuples to finally conform to equatable, hashable, comparable, etc. conditionally. Yep, so this is an evolution of parameter packs. So now we have the base concept in the language. What we need in order to support conditional conformances of tuples to equatable, hashable is the ability to write an extension over a tuple type where its element types are represented with a parameter pack because they each could be different, you know, concrete types. It doesn't matter what they are. We just need the ability to write a where clause that has that condition of each of those elements in the parameter pack. If you're familiar with parameter pack, the keyword is literally each to say, you know, where each T conforms to the protocol that you're looking to conform to. So we need, you know, a syntax to write a tuple with a parameter pack. Similarly for the extension syntax to write a parameterized extension where the extension itself has a parameter pack. And then that's it. So it's a fairly small evolution to parameter packs. I actually think there's an experimental implementation that lives in the compiler repository right now that's not completely there, but is almost all the way there. And so once that's done, just revisiting the older proposal that used more bespoke conformances for those three protocols specifically. But I think what we want is now that we have parameter packs offering that as a general feature. So you could add conformances to your own protocols to tuples where that makes sense as well. Great. Hopefully that answers the question. Pizza? Moving into the next question. This is on strict concurrency. So this might be for you, Corey. let's see Charlie Polly is asking 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 at observable data model on the main actor without blocking the UI this also might be good for you Tony I think it's a combo platter this one me Tony and Holly actually possibly everybody we can include Doug why not The question is going to boil down to what does high frequency mean in this context? Exactly how frequency is high. There are different domains where your answers will change. If high frequency is still substantially lower frequency than the rest of your UI updates, then you might actually have no work to do. This might be very, very straightforward indeed. But assuming it is really high, one of the things you're going to want to do is avoid context switching excessively. So you want to make these switches from your background actor to your observable data model on the UI as infrequently as possible. So there are sort of two paths we can go down to. One of them we can talk about debouncing. To leverage debouncing, one of the other things you're going to want to try to do is accumulate data. So rather than sending one update for every data change, can you coalesce these down into a smaller number of events and bring them all over? There's also a question here about data loss. How much data loss is acceptable? For your background sensor, for example, if that might be a, let's say it's a sort of a voltmeter, for example, we're measuring electrical voltage. Do you need an update every nanosecond rendered to the screen? The answer is probably no. So you can probably naturally debounce with data loss. It's fine if you don't print the intermediate values. Or you can maybe save the data elsewhere, but your UI may not need to actually show every update. SwiftUI, of course, an observable work to naturally coalesce updates as well for efficiency. So that's something you just get for free with SwiftUI. But I would really consider what the requirements of what the UI has to show are. And the question mentions blocking the UI. I think it is important that if there is a lot of work happening to make sure it is happening asynchronously in the first place and then keeping the UI updates just for what the user needs to see. That might be a slightly different problem than having to stream everything. So I would think about maybe splitting the problem in half. perhaps. Yeah, 100%. If you are doing, needing to do some debouncing, I think Swift Async Algorithms has a debounce implementation that you can fit on top of your sort of standard async sequence model, which is a great way to sort of start doing some of that as well. Great. Nothing to add. Yeah, Holly looked like a you got everything kind of face. All right. Let's move on to the next question and this is around performance. Why is, why, Why is tuple more expensive than struct when returned from a method? Or is it a custom case and there's actually no difference? This is a surprise to me. Yeah, news to me. Surprise, okay. So they are handled differently. Okay. And so this is getting down into the guts of the compiler. But generally when you're dealing with structs, you'll pass the whole struct as an entity, a single entity. when you have a tuple, we'll often break that apart. And so if you're passing a tuple into a function, it'll be as if you had sent each element to the tuple as a separate parameter. We call this exploding the tuple inside the compiler. It is plausible that if you had a very large tuple, that this would be less performant than just keeping it all together, as you might do with a large structure. Maybe that's what we're seeing, but I think we'd really have to go and look at the actual code and what the optimizer produces to understand why we're all puzzled by this. Would you like a GitHub issue, Doug? I would love a GitHub issue, yes. GitHub issue. Action, yes. GitHub issue with a sample project, please. Yes. All right. Moving on to the next question. What's your favorite quality of life or 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? This is a great question. I'm going to use sort of a forward-looking one. We mentioned in What's New in Swift this year, there's work on a new set of protocols called Iterable. And this ties in with some of the performance questions that have been going on, non-copyable, non-escapable. And the reason I find it so interesting is that part of the goal there is to take what might be a pretty complex topic, but make it feel like natural SWIFT. Like, I want to call 4-in, and I want a 4-in over this span. And so I think things like that feel like a really simple problem statement, but then when we get down to the details of how to actually implement that in a way that preserves SWIFT guarantees around safety, lifetime safety and memory safety, becomes a really challenging design problem. So this is an area, I think, to watch. And, you know, we've mentioned in the future directions of that proposal that this is, you know, something that we anticipate will start to build up, you know, a whole bunch of new container types in Swift and new ways to think about how your data is structured in Swift. And so maybe that's a different definition of fun than other people have. That's my idea of fun is being able to design some of these things from the beginning, but doing them in a way that preserves what I mentioned earlier about progressive disclosure and making Swift easy to use from the start. So I think there's going to be a lot of really interesting stuff happening there soon. I think this is sort of often almost taken for granted for people who have been writing Swift for a very long time. But when I first started learning Swift, what I was most amazed by is both the bidirectional type inference and how little type information I had to write explicitly, as well as the diagnostics you get from Swift's generics model of actually being able to diagnose mistake. If there's a mistake in the implementation of the generic code, that's where you see the error message instead of at the use site, which in some other languages that use generic specialization and they monomorphize all of the generics. You don't know that you have a mistake until you try to use that generic API with a type that doesn't work. But I think a lot of those things, especially by directional type inference, it's kind of taken as a given in Swift. and it's something that you don't realize until you go back to another, I mean, we write a lot of the compiler code in C++ and when going back and forth between Swift and C++, I have such a great appreciation for how much I don't have to write in my Swift code when I'm using just basic standard library APIs in my code. - I remember being really taken aback by the way the actual implementation of Swift generics worked as well, which is sort of unlike everything else, having come from, again, a services world with a lot of Java, which does sort of type erasure generics. And it's not that, but it's also not global monomorphization. It is its own special thing I thought was really interesting. For me, I'm going to say, I mean, fun to write. I have a different one. I have the thing that I appreciate the most. And like Holly, it's stuck with me the entire time I've been writing Swift because I think Swift got it right from the beginning and has just added to it, which is Swift's tools for handling, avoiding mutable shared state. Early on, this was the copy-on-write feature, which I still remember being taken aback when I discovered that copy-on-write was not a compiler trick. It was just a feature you could use to build your own types. Being able to build your own copy-on-write types is still great. I still love doing it. But even without that, once we've added in some of the ownership model and other pieces, you can still do all of these things in performance-sensitive code as well. And that's really great. I mean, the ability to write code and know confidently that there is no spooky action at a distance that will make this code behave unexpectedly. It is purely a function of its inputs and outputs. It makes writing complex code so much easier. You know what's really fun is taking some C code and replacing it with Swift. And just to your point, right, like, you know, we've been doing that in a lot of projects because we want Swift guarantees around safety, memory safety, et cetera. I don't know. I find it very satisfying to be able to write what I feel is a very, you know, fluent language for these kinds of things without all of the very sharp edges of writing something in C. Absolutely. Yeah. I'm partial to the generic system. To me, the idea that you can extend a protocol and add an operation on there, a method to do something, it is so wonderfully simplifying. So that didn't come with Swift 1.0, right? We But it was very interesting because generics are considered a hard feature in many languages. People are afraid of them. They don't want to learn them. And yet, when you extend a protocol, you're building a generic algorithm. But we've made it so natural and simple, like the checking that Holly talks about and just the syntax just sort of melts away. You're just building something on a collection or you're just building something on a collection of equatable elements. And it's just as easy as writing non-generic code. I think that leads us to better, more reusable code patterns throughout the language. Yeah, absolutely. 100%. What a great question to end our group lab on. That's all the time we unfortunately have today. Thank you so much to all our panelists, Holly, Corey, Tony, Doug, for all your insightful answers today. And thank you to the folks behind the scenes helping triage questions and keeping this running smoothly. Most of all, thank you to everyone who joined us today, whether you asked a question or just tuned in. If you haven't already, check out Swift.org for a ton of resources, documentation, and blog posts. There's also a new generative AI search experience in developer.apple.com to get answers about frameworks, design, accounts, everything related to development. It actually includes all the Swift documentation and resources from Swift.org. And if you didn't have your question answered today, let's continue the conversation in forums.swift.org. Lastly, check out the SwiftLang organization in GitHub. And like I mentioned earlier, you can file feedback, bug reports, and enhancement requests through GitHub issues. And speaking of feedback, you should receive an email with a survey link to let us know about your experience at WWDC. We would love to incorporate your feedback in future events. Thank you for joining us. Have a great WWDC.