Swift • 1:01:17
Join us online for a deep dive into WWDC26 with Apple engineers and designers to ask questions, get advice, and follow the discussion about the week’s biggest Swift announcements. Conducted in English.
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it may have transcription errors.
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 you if we aren’t able to answer your question in the time we have, don’t fret because we could continue the conversation in the Swift [email protected]. 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 Cory, 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 I, 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.
That’s 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. And 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, 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 are 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.
So yeah, it’s awesome. Fantastic. I’m excited. I mean, all the improvements we’ve been making to embedded Swift. So embedded Swift is about, you know, 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 in 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, I 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 relinquishing its own ability to access the data.
This is 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-separable data from one actor to another as long as the original actor can no longer access that non-sensible 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, it’s some non-sensible value that you’re going to transfer off or return from an actor to, 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, you know, is a data type that’s meant to preserve that property through like actually storing those values if you need to store it, and then later on transfer that somewhere else. So that’s, you know, 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 on another, there’s another concurrency question. So Holly, this might be for you again from Ashraf. What is some advice you give when using swift structured concurrency? Actually this also might be a great query question. So what are the some 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 hatches from structured cards, 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. Tasks not detached or regular task in it. 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 with style functions in Swift. It’s got a bit of an idiom about using with 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 d init based cleanup in in places if that’s going to work for you. You do want to be trying to, to focus on, on doing things that way.
Once you’ve made those initial steps, you then the subsequent trick tends to be don’t fan out too much, right? 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 programing ecosystem. If you’ve ever thought about a sort of fork join models or if you’ve considered the sort of fan out fan in scatter gather programing 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. This is the other thing that is important. So when I say we have this, you want to do this scoped resource gathering.
One of structured concurrency is 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 has 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.
Awesome. I feel like this is an area where non-sensible types can help you. Yeah, 100%. Because, you know, a Non-sensible 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-sensible type, you can use it freely, but you can’t accidentally escape it out. And so once you’re, when you’re embracing structured concurrency, these non-sensible 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 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 sensibility. I often talk to people who are constantly asking the question, how do I make my data type 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 the round 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 Tilda Sendable. Similar to Tilda 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 using, you know, real using the leaning on the compiler and using real logic to make sure that it’s, it’s truly sendable is important as well. Yeah, absolutely. Yeah.
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, etc. to every struct just out of easier compiling habits.
What does 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 like 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.
If you do a as question mark cast to to an 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 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 on their 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 makes 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 like something. If it’s equatable, make sure it actually can be equated and, 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.
So all right. Thanks so much. We have a question from a developer about at Main actor. 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, you know, 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, you know, 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 two. 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 going to 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 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 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. We it’s a code along kind of a video where this type of practice is, is, is done with a sample project and definitely follow along there. All right, moving on to the next question from our developer, Ying Xu. 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 any any answer for Yang Xu here? Yeah. 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 what 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 WWE. I can’t remember the exact name. It was about performance and 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, to, 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 Unique Array, which is a new type that is, I think maybe just recently accepted into into Swift, but it’s also available in 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 performance as well.
Okay, great. Let’s take a look at the next question. This is from M Travers. I really hope I pronounced that correctly. For teams that did the full Swift six 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, 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.
I 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 in documentation or in reading the signature of the function to know explicitly and to not have to work through mentally. This was implied by some other conformance requirements.
So we later then just got rid of the warning. You know, it’s fine to 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 if you’re it’s just a practice of ripping it out. If you want to stay explicit, you can. Yeah. Sometimes they, they provide value. It means someone thought about this, right. This, this type absolutely should not be sendable, for example. That’s intentional.
Or this type absolutely must be. Or this, 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 anytime you find yourself doing that, it’s 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 knows are there. Those can be extremely helpful.
And sometimes you just get a little bit, you know, ADHD about it and want to write equatable and hashable twice. I sometimes do, I find it helps. It works. All right. Next question. And this is about Sendable from our developer, Peter Belkovsky. Why is Userdefaults not Sendable given that the doc says it’s it is thread safe. Is this a preliminary step?
Tony? Yeah. Great question. So a few years ago, when Swift introduced 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 there are classes. So maybe the superclass is Sendable, but a subclass is not an easy example to understand how this could happen is Nsstring is immutable and Sendable, but not mutable.
String 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, 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 for Holly to allow us to more accurately annotate those, those types. And so that’s something that we’re in the process of, 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 assignable 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’s it. 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. It was mentioned in what’s New in 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, the property that is getting borrowed mutate is like a reference, but it’s immutable 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 Penn. Again, I hope I said that correctly with a lot of additions like mutex, inline array span type 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 very overwhelming. This is a great question. Anyone have any thoughts about this?
I can start to. So maybe actually in Foundation done a lot of this work too. And I think it’s important to remember that 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 don’t, if you’re just getting started. And we, I think all the people on these workgroups 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 where 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, as noted here, fairly, they do add a complexity to using the language that you may not, you may not need from the start.
So I recommend absolutely measuring first and then moving into those things. That said, you know, we’re trying to make all of these things also work really well together. So unique types and spans and the sentiment 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 like 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 you know, has these greater restrictions that allow us to compile to, to faster code or use less memory. I 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 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 don’t you shouldn’t feel the need to go and learn every Swift feature that there is. Yeah. Instead, look for where you’re having trouble, you know, looking. 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, 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, right? 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 in 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. Yeah.
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. Ken 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 the 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 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, to find the places to actually optimize it.
If you want to learn more about explicit build modules, there are 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. 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 six 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. 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 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, of real world code and in particular.
People who had adopted Swift’s like early warn 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-sensible types back and forth between an actor, isolated context, and these non-isolated async functions. And that was causing a lot of data safety errors because the original actor isolated context would still have access to those non-sensible values at 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-sensible 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 that I held on to the belief for a very long time that 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 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 trade off because back when we had to make a decision of where do these things run? Do they 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 because of the feedback and came to a better solution, I think is it’s easier to start with. So it’s, 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 or 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 VSCode from the open source Swift org, Toolchains 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, you know, 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 that, you know, that there was a preview of that in the 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. And there’s a number of performance optimizations 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 it’s now available. There’s also other improvements where it can 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 Florentin 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. So there are some annotations that you can apply to functions to control the visibility of the function body across modules and the optimizers decisions about inlining.
So for a long time there’s been the inline able 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 inline always now inline always. Excellent. Great. Because that’s the opposite of the one I want. So inline able is incredibly powerful across module boundaries.
Inline unlocks a huge range of optimization opportunities, but its name can slightly mislead you into thinking the only one that it unlocks is inline ability and 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 inline 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 byte wise copy operation.
And in those cases, 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. Inline inline label 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 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 expression to influence overload resolution.
You can just stick in the, as you know, 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 as like plain as was when I was first learning Swift until a while later.
I’ve got sort of a meta one, actually. I think I want to 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 other platforms, not 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 we. 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 has got loads of cool tricks. Yeah. So if testing. I was going. To. Say it’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 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 Sema 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, you know, 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, because then we can go back and think about what is the appropriate way to explain this so that people that don’t have the background that I have necessarily or only have the background of using related APIs nearby, right? That we can make it 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. And of course, like 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, you know, iOS developers to 100%.
Can I add a trailing one other feature? I just want to steal one more. The integer overflow APIs I seems to seem to be a real surprise to people that they’re, they’re actually really helpful if you are working with integers, that you have a real suspicion you’re going to end up big fast.
Swift has a bunch of APIs that will let you carry the overflow flag around so that you can run your whole straight line computation. And then at the end, just figure out, oh, 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. Not before. Yeah. Makes sense.
That’s in the class of APIs, where when you need them, you really need them. Yeah, 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 the keypads exist sometimes. It’s interesting. It’s, you know, people come and say, well, I need to, you know, 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 go and build it. This keeps happening.
And so it’s, I. Think. That 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 key paths, they’re all over the API surface because they let you write these very elegant API constructions. And so it’s very funny when someone, when I bump into someone who’s 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 KeePass.
I actually have one as well, swiftly. I think I think a lot of again, if you’re using something like VSCode, you might be more familiar with this or CLI, but you could actually do this from Xcode directly as well. So if you want to play around with a newer toolchain and try out some experimental features like check out swiftly, it’s a really easy way to install those Toolchains. Yeah, yeah. That was mine. All right, next, let’s move on to another question going on. Going back to Equatable. Hashable comparable pure Piazza 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 where each t conforms to the protocol that you’re looking to conform to. So we need a, you know, a syntax to write a tuple with a parameter pack. Similarly, for the extension syntax to write a parametrized extension where the extension itself has a parameter pack.
And then that’s that’s that’s it. So it’s a fairly small evolution to 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 a 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, to tuples where that makes sense as well.
Great. Hopefully that answers the question. Piazza moving into the next question. This is on strict concurrency. So this might be for you Corey. Let’s see. Charlie Poli is asking with Swift 6 strict concurrency enforcements, what is the recommended pattern to ingest high frequency sensor data on a on a background actor and safely stream those updates to an observable data model on the main actor without blocking the UI.
This also might be good for you, Tony. Yeah, I think it’s a combo platter. This one might be me, Tony and Holly. Actually, possibly everybody. We can include Doug. Why not? The. 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. You know 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, yeah, 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.
Okay. Great. Yeah. You looked like you. Got everything kind of. 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 return from a method or 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 trucks, you’ll pass the sort of the whole struct as an entity, 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 of 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. Would you buy this? Would you like a GitHub issue? Doug. I would love a GitHub issue. Yes. GitHub issue. Yes.
Get 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 a 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 concurrent sorry, the performance questions we’ve 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 for in and I want to for 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 those 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 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.
It’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 generic 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 you know, in some other languages that use, you know, generic specialization and they monomorphic all of the generics you get, you don’t know that you have a mistake until you try to use that generic API with, with a type that doesn’t work.
So, but I think a lot of those things, especially bidirectional 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, you know, I have such a great appreciation for how much I don’t have to write in my Swift code when I’m using just like basic standard library APIs in my code.
So I remember being really taken aback by the way the 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 monomorphism.
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 has 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.
That was 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’s purely a function of its inputs and outputs. 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 and memory, safety, etc.. 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. Absolutely. So 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 that didn’t come with Swift 1.0. We added it later, 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, to better, more reusable code patterns like throughout the language. Yeah. 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 the 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, and 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.org. Lastly, check out the Swift Lang organization on 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 WW DC. We would love to incorporate your feedback in future events. Thank you for joining us. Have a great Wwdc.