43: Now I’m worried our metrics aren’t correct! with special guest Holly Borla
So there's a member of the Swift community called Joe Heck, who has been around for a while and
certainly is in and around the Swift Package Index community quite a lot as well. And after our last
podcast, when we talked about having Holly Borla from Apple on the podcast, in our Discord server,
he was teasing us a little bit for saying that we were going to have Holly on, but not when we were
going to have Holly on. And we teased him a little bit by saying, "Well, it's soon, Joe, it's soon."
Well, it turns out, Joe, the time is now, actually, because we do have Holly with us today. Hi, Holly.
Hi, thanks so much. I'm excited to be here with both of you today.
Hello, Holly. Welcome.
It's great to have you here. Thanks.
Thank you.
Thanks for your time. Do you want to introduce yourself and tell us a little bit about yourself?
Yeah, absolutely. My name's Holly. I manage the Swift language team at Apple.
In the last couple months, I've been focusing a lot on the concurrency features of Swift and
specifically making progress toward the Swift 6 language mode. I've been working at Apple for about
almost seven years now and on the Swift team for five of those years, previously working on
the generic system type inference and other language features.
So you like to hit the hard problems, right?
I don't envy the working on the generic system. I struggle enough using generics, let alone
creating it.
Well, that's part of why I like to... That's part of what got me interested in Swift. First,
I wrote Swift code. And what brought me to become more interested in Swift evolution and how the
compiler works is I was really just trying to understand how my own code worked.
Right.
So yeah, that was what got me interested in Swift compiler development.
You took it a step further than most people, though.
I always want to understand how my code works, but I never end up trying to forget the compiler.
And I think what we're going to talk about today is, well, it's several things, but all around
the topic of Swift 6. And Swift 6 is several things. It is some new compiler features,
but there's one big feature with Swift 6, which is Swift 6 strict concurrency mode,
which you've probably heard something about already.
But better than me trying to introduce it, I'll let you, Holly, introduce what is Swift 6.
Yeah, sure. So Swift 6, like you said, has a lot of new capabilities.
Like most other Swift compiler versions, it has a lot of new additive features.
Two in particular that I think a lot of people will be excited about are typed throws,
as well as a lot of enhancements to non-copyable types and specifically their interaction with
the generic system. But those are just two, and there's a lot of other features as well.
If you're interested in learning more about those, you can head over to the Swift Evolution
dashboard on Swift.org. But Swift 6 is particularly exciting because it also brings a new language
mode that will diagnose the risk of concurrent access to shared memory, in other words, a data
race, across your project. Some of you might already be using these checks and might be
familiar with what they mean in Swift 5.10 or before, using the complete concurrency checking
option, which will diagnose those issues as warnings in Swift 5 mode. But when you use Swift
6, there's a new language mode that you can opt into that will diagnose these issues by default
as errors, so that it helps you eliminate data races across your project.
That's great. And there's something really a little bit subtle in what you've just said,
then, which is the difference there between Swift 6, the compiler, and Swift 6, the language mode.
Certainly, when I first started hearing about Swift 6, I didn't get the distinction between
those two things. But over the last weeks and months, as I've kind of dug into it a little bit
more, it's become really apparent that those two are very different things.
Yeah, there's a subtle distinction and they are related concepts, definitely. The Swift 6 compiler
is just the first compiler version to offer the Swift 6 language mode. But when you start using
a Swift 6 toolchain that has this new compiler, your existing code will continue to build in the
language mode it's already using. So that might be Swift 4, Swift 4.2, or for most of you, it's
probably Swift 5. And then when you decide to opt in, you can migrate your project to the Swift 6
language mode, either in your build settings or by changing your package manifest. And what that
will do is it will start passing 6 to the Swift-version flag, which is what configures
the language mode. So when you start using the Swift 6 compiler, if you don't already have the
complete concurrency checking flag enabled, you're not going to suddenly start seeing any data race
issues in your project. That will only come once you decide to migrate to the new language mode.
You won't see any data race issues, you'll only see data races.
Well, that's actually, that's really interesting. Yeah, I didn't actually
realize that it was off by default. I thought it was going to be on by default in Swift 6.
Yeah, I did too.
So it's on by default when you enable the language mode specifically.
Right, but that will be an opt-in thing. So, because I think there's been quite some
discussion of whether Swift 6 is going to break everybody's code. And obviously people are
reluctant to have a compiler that breaks all their code. And so I think one bit of good news is that
those potential issues that you're going to get when you do switch on
Swift 6 language mode, that's completely within your ability to control when that happens.
Yes, that's exactly right.
So when you pick up a project that's been compiling fine in Swift 5 and you switch to Swift 6,
if I understand correctly, there's actually, you wouldn't expect anything to break, right?
There shouldn't be any.
Yeah, that's right.
Any breakage, at least in terms of concurrency, or is there anything else that might have
source incompatibility? Because Swift 6 is potentially source breaking, right? There's,
at least it would be allowed normally due to the language modes, right?
So when you start using the Swift 6 compiler for the first time and you build an existing project
in the Swift 5 language mode, there will be no deliberate source breaks, at least. Of course,
there might be compiler bugs and those are always worth reporting an issue for.
But without enabling a new language mode, the only source compatibilities that you might see
when starting to use a new compiler version are any compiler bugs that were fixed.
Like if there's some invalid code that the compiler wasn't diagnosing in previous versions,
maybe that code led to a miscompile because it wasn't supposed to be valid code.
Those minor incompatibilities are typically the only ones that you'll see when you start to use
a new compiler version for the first time. And the source breaking changes will only come
under the language mode, which as you've both said, it's your choice when you want to take on
fixing those issues in your project by migrating to the new language mode.
Right. I'm asking because I don't actually recall ever using a language mode in the compiler. I'm
not sure, is that different from the Swift 4 to 5 transition? Because I don't recall doing
anything there. Yeah, that's interesting. So when you create a new package or a new project in Xcode,
for example, the language mode will be set for you based on whatever compiler version you created
that project with. So if you created your project with the Swift 4 compiler, your language mode was
set to Swift 4 at that time. And then there would have been a step to migrate to Swift 5 because
there were source incompatibilities with the Swift 5 language mode. And at that point, your compiler
version or your language version is switched to Swift 5 mode. And then from there, you don't need
to ever modify that again until you're ready to migrate, you know, well, until there is a next
language mode and then you're ready to migrate to it. That's right. Yeah.
And so just out of curiosity, in a package manifest environment, is that set by the tools version?
Yeah, that's right. If you don't, there is a way to explicitly set the language mode
with an API in the package manifest. But if you don't set that, the default is based on
your tools version. Oh, OK. Now I get it. Yeah. So I did pick up on that when you were introducing
the concept, you talked about like the Swift 4 language mode and Swift 5 language mode. And I
did pick up on the little kind of subtle thing that this is not the first time that's happened,
but it's the first time I've been aware of it happening. So that's kind of interesting, actually.
Yeah, I think the source changes in previous language modes, at least with, you know,
the 4 to 5 transition, the nature of those source changes was very different than the nature of the
5 to 6 source changes. So let's say you are ready to make that jump to the Swift 6 language mode
and you switch the feature on. What kind of problems are people going to see when they do
that with their apps? Yeah, so I think it depends on the nature of the app that you're migrating.
If you just have a very straightforward app where you're not leveraging concurrency much,
then the nature of the changes that you'll need to make will probably be adding main actor
annotations throughout your code. And in some places in your code, changing vars to lets is
another really common one that I've seen. So when you turn on the Swift 6 language mode,
the compiler's job is to identify all possible places in your code where you might have shared
memory that's accessed across concurrent contexts. One very common example of that,
that I think nearly every app hits when they migrate to the Swift 6 language mode, is uses
of global and static variables. Because those are, you know, global state that's accessible
from anywhere in your code. And you need to protect that state in one way or another.
So one super common pattern of source change that I've seen when migrating code to Swift 6
is if you have a constant, like say it's just an enter, a double, or some other type that is
sendable, so the value itself is safe to access concurrently, a lot of people have declared those
with vars, even though those constants never change across the code. So as soon as you change
that to a let, the compiler will -- that will resolve the compiler issues pointing to that
global shared mutable state. Similarly, if you have a constant that you only ever use from the
main actor because it's something that's used in your UI code, you can just write a main actor
annotation on that, and the compiler will then verify that you only ever touch that state
from the main actor. So I think a lot of apps will almost certainly need to make changes
of that nature. But then some other changes might be a little less trivial.
Like if you're actually creating an instance of a reference type from a background thread,
and then passing that off to the UI to render there, you'll need to make it obvious in your
code that the background context where you created that reference type no longer accesses the value
once it's handed off to the main actor. And Swift 6 comes with a variety of different tools to let
you express that in your code, and the specific way to do that will, of course, depend on the
circumstance. But those are the sorts of changes that people can expect when migrating to Swift 6.
In my experience, these are all fairly localized changes in code, except for the case where you're
going to do like a main actor audit across your code. But a lot of them are at least narrowly
sculpted in the code. Yeah, certainly when we switched it on for package index project,
we saw a lot of warning because they're currently... So in Swift 5.10, you can switch on
the strict concurrency features today, right now. And we did switch them on and you get a preview
of what Swift 6 language mode is going to be like when Swift 6 arrives. And a lot of our errors I
noticed were around global states. So I think that's going to be quite... Not that any of us
ever use global variables, right? I mean, none of us would ever do that. Yeah. Something else to
note is that the number of warnings or errors that you get is not always proportional to the number
of source changes that you'll need to make. So like in the example that I was just talking about,
where you have a global var, but you never actually mutate it, you're going to get a warning both at
the declaration of that static variable, as well as at every use site of the static variable,
when you can really just fix it with one tiny change to change the var to a let, as long as
the type of the variable is sendable. And that will resolve dozens of warnings across your project.
And this word sendable keeps coming up. And you can't read about Swift 6 concurrency without
reading the word sendable. Can you give us a description of what is sendable?
Yeah. So sendable is Swift's concept of a type that is thread safe. So the type itself either
doesn't have any shared mutable state, or it protects that shared mutable state somehow,
perhaps with a global actor or with a lock. And then once you have those things, you can conform
your type to the sendable protocol to communicate to the compiler that this type is actually safe
to share across concurrent contexts. There are a lot of cases where the compiler will infer
sendable for you. Like, for example, if you have value types across your code, value types,
in a lot of cases, as long as they're composed of other sendable types, are naturally safe from data
races because of value semantics. So for example, if you are in a background context, and you create
an instance of a value type, and you pass that off to the main actor, that makes a copy. So
their background context and your main actor have independent copies of that value. And that,
you know, that saves you from potential data races there. So for value types, specifically,
the compiler will infer sendable conformances. And I think, I mean, generally, over the years
of writing Swift, people, well, certainly we have, have come to rely on structs,
I think more than classes these days. I mean, easily more than classes, actually. And that's
not to say that we don't use classes. There are several places in the package index app that we
use classes. But certainly, structs are, I think, where most Swift developers start these days,
aren't they? Yeah. And I think Swift definitely encourages use of value types, both in popular
library APIs that you use, but also amongst the community, specifically because it gives you that
property of local reasoning, which just means that when you're using a value type in your code,
the effects on your app or on your program are easier to determine from just the context where
you're using that value. So for example, if you mutate a value type, the effects of the mutation,
you know, it can't affect anywhere else in your code. It only affects the value that you have in
that scope. And that local reasoning property is also really valuable with concurrent code.
That's great. So as well as everybody's apps needing some work to move over to the Swift6
language mode, obviously, and part of the reason you're here on the package index podcast is,
this is also going to be a problem for packages within the ecosystem. So actually, first of all,
one question that I have is, can you use a, if you have switched your app to Swift6
language mode, can you still use a package that's in Swift5 language mode?
Yes, you can definitely mix modules that are built in different language modes together.
That's the case today. And that will remain the case with Swift6.
Well, that's great news.
Yeah, yeah. So if you want to migrate your code to Swift6, it is not a problem if none of your
dependencies have migrated to Swift6. And then on the flip side, if one of your dependencies
migrates to Swift6, that does not mean you need to migrate, you don't need to make any changes
until you decide to migrate your code to the Swift6 language mode.
That's great. But obviously, what we would like to do is, is see how many packages and how quickly
packages are making that transition to Swift6. And so one project that we are undertaking
is to track the compatibility of Swift6 language mode within all of the packages on the package
index. Or should I say not quite all of them, because what we're going to do is we're going to
take a sample of any package that has been that has had any kind of code modification in the last
year. So we're going to presume that any package that is older than a year is either not going to
be updated, or maybe it will be but maybe it was a complete package and it will need the change to
Swift6 but isn't active in active development. But what we're going to do is we're going to
take a sample of those packages. And well, we've already started running the test actually. So I
think we've run is it two or three so far, Sven? We've had two runs so far.
Two runs so far. And those have been with nightly builds of the Swift6 compiler, right?
Yeah, so what we've done is we've taken a Swift6 preview toolchain and install that alongside with
Xcode 15.3. And then we've run that Swift6 preview across the whole package ecosystem and collected
the metrics sort of the the error count in the Swift6 language preview mode.
And the idea with this is that we're going to continually run these builds every two or three
weeks as the Swift compiler gets closer to being released. So that we can track how many packages
across that set of packages that we're going to measure against how many are increasingly
compatible with the Swift6 language mode. And this is not going to affect the compatibility you'll
see on an average Swift package index page yet. So we're doing it slightly off to one side of
that process. In fact, we're going to have a separate page on the site, which we're already
well underway with creating. In fact, we were just chatting before we started recording the podcast
and previewing some of the charts that we're creating to track the packages. They don't have
real data in there yet, but the charts do exist already. And we're going to track those stats
over the next several months while Swift6 gets close to release. And separate to that,
we're then going to at some point, as we would always do, when there is a kind of a beta of
Swift6 that ships with an Xcode version, that will head on to our standard compatibility matrix,
as we have done with every other version of Swift so far. So whenever Swift 5.7 or 5.8 came out,
we would install the beta to start to gather compatibility. And so we'll still have that.
So the compatibility matrix will start to update when we get kind of public betas of Xcode. But
in the background, we're also testing with these custom tools and will continue to test so that we
should get, hopefully, a nice increasing line going up throughout the summer.
Yeah, there's actually two aspects that will influence, hopefully influence that number. And
one is that developers will start updating their packages for Swift6, perhaps even seeing the
numbers that we publish as an incentive. And the other is that the Swift team might be making
changes to the compiler to maybe eliminate some warnings. Because I think, if I understand
correctly, there's still some proposals also still in flight that might help reduce the number of
warnings people are seeing. So that's also something that hopefully this number will encompass.
Yeah, and that's right. One of the major goals for Swift6 is to mitigate the false positive
data race safety errors that are coming from the compiler so that you're only confronted
with data race safety issues if there is a risk for a data race in your code. So that's something
that's being actively worked on through proposals in Swift Evolution, as well as just other minor
usability improvements to the checking in the compiler.
And yeah, so hopefully, hopefully what we'll see is the ecosystem moving towards Swift6 as well.
But as you said, it's not the end of the world. Like there's nothing,
there's nothing disastrous going to happen here if you're using a dependency that hasn't yet
made that switch, or if a dependency you're using has made that switch and you haven't.
Like there's no, it's all quite within your control, I think is the main point of this.
Yeah, exactly. If there's one thing that people take away from listening to this episode,
it's that it is fully within your control when you decide when to tackle eliminating
data races from your code. One of the questions I had was around
concurrent programming in Swift is not a new thing. We've been doing concurrent programming
in Swift for a very long time, for at least as long as Swift has been around.
And over the years, even back into Objective-C, there are lots of ways within Foundation and
within the Apple frameworks to do concurrency. So there's dispatch and there's operation queues,
and there's even threads and things like that if you go right way back.
Is there something we should take away? Obviously, async/await, Swift concurrency is
the new thing, and it's not even that new anymore, I suppose. But it's certainly the modern way to
do concurrency. But is there any need to kind of touch those other bits of concurrent code?
Is that going to be affected by this change? Yeah. So the way that I've been encouraging
people to think about migrating their apps for concurrency is that you should think about it in
two different steps. The first step is migrating to the Swift 6 language mode,
which is addressing those data race safety errors that you get in your code with whatever library
APIs you're currently using for concurrency, whether that's dispatch or whether you're
primarily using the concurrency library that comes with Swift. Migrating to the Swift 6
language mode should not be a major refactoring task in your code base. You don't need to go
replace all of your dispatch queues with actors, and you don't need to make major changes to the
logic in your code unless that logic is prone to data races, of course. And then separately,
once you have eliminated data races from your code and you're getting the validation from the
compiler, then you can actually go in and strategically, if you want to, replace uses of
other concurrency primitives with the built-in language features like async/await and actors.
So I definitely view those as two different steps as part of modernizing your app for data
race safety and for concurrency. So there's no need to do it, but you will be missing the
additional safety that these checks bring. Yeah, not necessarily safety. I actually think
dispatch is an interesting example because dispatch does also fully validate that you
don't have data races in your code. Some of you may have noticed if you have the complete
concurrency checking on, but when you call async on a dispatch queue, that actually takes a
sendable closure because that's something that's going to be evaluated in potentially on some
other thread. The way that you can think of it in the Swift concurrency context is like it's being
evaluated in some other isolation domain. And I think that dispatch and Swift concurrency
work well together and dispatch lends itself really nicely to incrementally
migrating to a more native Swift concurrency approach.
Dispatch serial queue even conforms to serial executor from the concurrency library. And this
means that you can use a dispatch serial queue to back an actor. If that didn't mean much to some of
you, this provides you with a migration path from a class that's backed by a dispatch queue
over to actors while still allowing you to interoperate with APIs that require the dispatch
queue. So you can use the actor from your own code and you can still provide the dispatch
queue if you're using any frameworks that are still expressed in terms of dispatch.
Oh, interesting. Okay, that's clever. Yeah.
Yeah. And I think this incremental migration both to the Swift 6 language mode and to
using native Swift concurrency language features is a really critical property of the Swift 6.
It's almost like you thought about it. Yeah.
So yes. Well, I think we also have previous experiences where that wasn't the case with
the migration to a new language mode. And I think the infrastructure around incremental migration
and source compatibility guarantees has come a really long way since the introduction of Swift.
So let's say I... Let's pretend I'm a developer. And if you can stretch your mind that much.
And I've been completely ignoring async/await. I'm happy. And actually, this is not a
million miles from the truth. I really, really enjoyed the API of operation queue. That was where
I felt like I completely understood operation queues. I really liked operation queues.
But let's say I'm that developer and I've been completely ignoring async/await so far.
What's the best way for me to start learning Swift concurrency?
Yeah, that's a great question. I think there are a variety of different resources out there.
One that I always recommend that people start with is the WBDC videos covering the concurrency
features. If you're new to the features as a whole, there are different sessions covering
async/await, structured concurrency, and actors. And then beyond that, there are a variety of talks
covering how to use those features together as a more comprehensive overview. There's also,
you know, the Swift programming language. There's a concurrency chapter there. But I also recommend
following other members of the community who are actively talking about concurrency. I've even
learned a lot over the last couple of months, just from participating in community discussions
and building a shared understanding amongst the community. The other thing to keep in mind is
you're certainly not the only one who's learning this for the first time right now.
And I think that's going to continue to apply for years to come. And it's really helpful
to start that learning journey with other members of the community.
I do really enjoy using async/await. And it's certainly, it is, even for someone who does love
the old operation queues, it is without question a huge step forward for Swift. And the way that
you write, or the way that you think about concurrent code is much more simple with
Swift concurrency, I find. Yeah, I agree as well. And I think it dramatically simplifies the
structure of your concurrent code. I mean, a lot of people reference the big pyramid of
Doom with nested completion handlers and with async/await. That's straight line code. You use
normal control flow. You use normal error handling. So I'm really excited to see, in a couple of
years from now, when there's a new generation of programmers learning concurrent programming for
the first time with Swift concurrency. Because I think right now we're all in a state where all of
us learned concurrency using some other primitives. Or maybe we learned with dispatch. Maybe we
learned with lower level primitives, like working with threads directly. But I think that with Swift
concurrency, once people are starting to learn concurrency for the first time, I think this will
make learning the topic, which it is an advanced topic. It's like learning generics for the first
time or learning about ownership for the first time. I think Swift concurrency and the way that
it simplifies the code that you have to write, as well as diagnosing mistakes that lead to
data races at compile time, will lead to a much easier learning curve, even though it is still an
advanced topic. Even just having something to tell you when you've made a mistake before you try to
run and debug your code will be a huge step forward for learners. I definitely agree. So
converting our project over the servers with package index server over to async/await was
really great. It helped streamline the code, make it more readable. The one thing I wonder,
though, because we've talked about async/await now, and earlier you mentioned, Holly, that you
can do this bit by bit, like if a dependency updates, you don't need to update and vice versa.
With async/await, I wonder how true that is. Because if you make that API change at the same
time, async/await is a bit viral, right? That if you change a function over to async, it trickles
up. Now, in the server project that we have with the event loop futures, there's actually a
mechanism to stop that in its tracks. So you can actually migrate piecemeal because event loop
futures, you can wrap those and then you can sort of take a little bit in the middle that you convert
and you have conversions on both ends from event loop futures and the other way. Does the same
thing exist in UI land, where you don't have event loop futures, but dispatch queues and
operation queues and that sort of thing? Yeah, so I think in UI land, a lot of the
current API surface, where something is happening concurrently is expressed in terms of completion
handlers. And the way to incrementally migrate that to async code is to introduce an overload
and then express one in terms of the other using continuations. And there's some standard boiler
plate that you can do that you can add to your code to offer both the completion handler version
and the async version. And that will let existing call sites of your completion handler code continue
to work while you're working on integrating your new async version of your API into different parts
of your code base. And I think there are strategies for taking that incremental approach
with other things as well. A lot of people have noticed that the same is true of main actor
annotations. As soon as you annotate something with main actor, the compiler will emit errors
in cases where you're calling that main actor API from off of the main actor.
One tool that you can use to stage those in as warnings so that your code can continue to build
until you've addressed all of the use site issues is the pre-concurrency annotation. So if you have
some function, it needs to be marked as main actor, but either it's in a library and you don't
want to break your clients, or you just have a large code base with a lot of uses of that API,
you can write @preconcurrency @mainactor so that the compiler will start diagnosing
those issues as warnings to allow your code to continue to build.
Nice. So obviously Swift 6 is on its way. Is there anything still
kind of in flight for Swift 6 that's going to affect Swift concurrency or strict concurrency
checks before the arrival of Swift 6? Yeah, there are a number of different proposals
that are either accepted and implemented for Swift 6 or they are in review now that are related to
concurrency. Some of them are solving some pretty common problems that people have faced when using
Swift concurrency and others are specifically to lift some of the limitations of the strict
checking in Swift 5.10. So the first one that I'd like to mention is called region-based isolation.
The number is SEO 414. And this proposal is specifically to lift some of the limitations
around passing non-sendable values over isolation boundaries. So if you have an instance of,
you know, a reference type and you want to pass that from a non-isolated context over to the main
actor or between two different actors, this proposal provides you with tools for expressing that.
And in a lot of cases, the fact that that's safe is just inferred by the compiler through
control flow analysis based on how the value is used. So for a really simple example, if all you
do is create an instance of a class, you populate some of its state, you pass it across an isolation
boundary, and then you don't use it again after the point of concurrency, the compiler will just
allow you to do that without doing anything additional in your code because it knows that
the value is not used again. And therefore, you can't have any concurrent access. So it's a really
interesting proposal. And what I like most about this proposal is unless you've done something in
your code that specifically opens up the risk for concurrent access, you don't even know that this
proposal is there. So I think it strikes a really nice balance between eliminating the annotation
burden in your code. So a lot of really straightforward cases will just work.
And in the cases where it doesn't, the compiler will tell you that you have the potential for
concurrent access and then point out the specific places in your code where that risk,
that caused that risk. So that's a really interesting one. There's some other proposals
around improving the usability of global actor isolated types, as well as some more sendable
inference for key paths and function references. Those proposals are interesting. And then one
that I think that people will be pretty excited about, it's called dynamically isolated function
types, SEO 431. That sounds really technical and complicated, but it solves a really specific
problem that a lot of people have been struggling with. If you've used Swift concurrency a lot,
you've probably run into the problem where every task that you create always starts on the global
concurrent pool. So even if you create a task that's isolated to the main actor, that task will
start running on the global concurrent pool. And the first thing it will do is go and queue itself
on the main actor. So this has the really unfortunate consequence that if you create
two different tasks that are isolated to the main actor, like synchronously one after the other,
the order that those tasks start in is non-deterministic because they start on the
global concurrent pool. So what this proposal does is it adds this new language feature called
dynamically isolated function types and adopts it in all of the task creation APIs in the concurrency
library so that when you create a task, it is synchronously enqueued on the actor that it's
isolated to. So that eliminates that ordering issue with two different actor isolated tasks
that you create. Because they are synchronously enqueued, they will be enqueued in the order that
you create them. And then the ordering of when the tasks start will be guaranteed. So I know
that's something that a lot of people have struggled with when using tasks in Swift
concurrency land. And that problem is fixed by this proposal.
And we'll be sure to include links to both of those proposals in the show notes so that if
you haven't come across those yet, you can take a look at the detailed proposals, examples and
stuff like that. I always like the proposals that include a bit of example code.
Yes.
As we as we move towards getting public betas of Swift 6, we'll see more people dip their toes
into the Swift 6 language mode. As that happens, what can the community do to help with this
transition? Is there anything we can do?
Yeah, I think there's a lot of different things that people who are interested in
diving into some of the concurrency checks now, there's a lot of different ways that you can do
that. So one is to try out the nightly development snapshots of the Swift 6.0 compiler. You can find
those on swift.org/download. There's a section where you can download the nightly development
snapshots. And that's a place where you can actually try out all of those proposals that
we just discussed that are accepted and implemented for Swift 6. I think another thing that you can
do is add your packages to the Swift package index so that you your package can participate
in the ready for Swift 6. So I think, I think that's like a new, you know, new incentive to
add your package to Swift package index. But I think one of the most important things that
that you can do is to share what you learn and share what you're confused by. As you're trying
out the the data rate safety checks, either in 5.10 with the complete concurrency checking flag,
or using the Swift 6.0 nightly tool chains. You can share what you learn on, you know,
on your social media, you can share it on the Swift forums, you can blog about it,
there are a lot of different ways that you can communicate to other people what you tried,
what you learned, and what problems you faced. I think there's some really important discourse
going on in the community right now, where a lot of different people are trying these things out
in their own code. And and posting questions to get help from other people, when they encounter
something that they're confused by. And I think that's important for two different reasons.
One is just to spread knowledge amongst the community and to help the community build a
shared understanding because this is, Dave, I think you mentioned earlier, this, this will
transform the way you write concurrent code. And it changes the point where you're thinking about
concurrent code to prevent data rate safety issues, you know, from, from actually being
shipped in your code in production. So, so it is a shift for people to start thinking about data
rate safety in this way at compile time. So spreading knowledge is really important. But
it's also important feedback for the people who are working on the documentation and the compiler
and the tools surrounding it. I love reading this feedback on mastodon or on the Swift forums about
even just compiler error messages that people are confused by. And I think that's opportunity
to improve the documentation and improve the compiler error message to be more clear about
what the problem is in the code using terms that programmers understand. One other thing I'm
collecting this feedback for is I'm actively working on a migration guide to help you migrate
your code to Swift 6. And I'm working on that in collaboration with other members of the community,
as well as the Swift website workgroup. And that guide is intended to provide both the
concepts that you'll need to understand when migrating your code to Swift 6, but also to show
like a catalog of examples of common data rate safety errors and different strategies for resolving
them in your code based on the situation. So any and all feedback that you have about trying out
strict concurrency checking in Swift 5.10 or in Swift 6 will be valuable to incorporate into that
migration guide. I mean, that's going to be just a huge help. Through what medium is that going to
be made available? Yeah, that'll be available on Swift.org. Swift.org. Okay. That's great. I'll
be sure to link to that in iOS Dev Weekly when the time comes as well. Perfect. Thank you.
Yeah, a huge shout out to you and other members of the community, Holly, because just following you
and the others on Mastodon and seeing code snippets that are being discussed is hugely helpful to
better understand what's coming here. Because I think a lot of people are nervous. And that helps
calm the nerves sort of to see example code that is being transformed in the context of a
Mastodon thread. And it takes a bit of the edge out of the whole thing, I think.
Yeah, thank you. And I completely agree. I think that once people see the nature of the changes
that are required to migrate to Swift 6, it becomes a little more approachable. Because
like I said earlier, this is not a massive refactoring task in your code. And in a lot
of cases, these errors can be fixed by fairly narrow changes in the code. So I do agree that
seeing examples of errors and then strategies for fixing them is really valuable. And I've found
other discussions about this, just that I've read on my own feed, really illuminating as well.
There's a bit of another thing that frequently comes up on social media. And we've talked a lot
about new Swift features coming and many of them come with their own keywords, right? There's new
main actor annotation, there's sendable annotation, protocol, there's async/await if you haven't
adopted that yet, macros, all that. And there's the sentiment Swift is getting too complicated.
And I wonder, what do you think? Is it really? Is it just because we've used the language for
quite a while now that we don't see the complication anymore? Or is that overblown?
Is it just people being nervous and it sort of becoming viral, like an async keyword and
infecting others, becoming nervous? Is that sort of a self-fulfilling prophecy? Or
how would you counter that argument? It's getting too complicated.
Yeah, I think it's actually really important that Swift has these advanced features,
like the generic system, like the ownership system, and like concurrency.
One thing that prevents that complexity from just being pushed onto everybody using Swift is
the concept of progressive disclosure. I think that's critical to Swift's approachability.
Swift is a good language for learning how to code, specifically because you don't have to
confront any of these advanced concepts until you need to use them in your code. And I think that's
the key. That would be my response to the people who are saying Swift is getting too complex.
I think the complexity is good because when you're, for example, building an app for the first time,
you don't need to understand these things. You can just learn the basics with, you know,
structs and functions, and then you can get into generics with protocol conformances.
And then later on, when you need to actually implement some more performance-critical code
in your app, then you can tackle the concept of concurrency and maybe even ownership,
if that's something that can help with the performance in your code. So all of these
tools are there, and it makes Swift really applicable in a wide variety of use cases across
the software industry. But those tools are not used by everybody. And I think some of the
discomfort is that these new features are coming along and people aren't keeping up with them.
There are a lot of new features being discussed in Swift Evolution, but it's okay to not understand
those features. You don't need to understand every single feature of the language. Even,
I mean, I don't understand every single feature of the language. And that's okay. I learn the
features when I need to, either because I need to work on them in the compiler to fix a bug,
or because I need them in my own Swift code. So yeah, I think progressive disclosure is what
continues to make these advanced features really valuable, because they're there when you need them,
and if you don't need them, you don't need to learn about them.
>> Yeah, I get the sense that these are also features that people are happy to discuss,
right? This is the bleeding edge, so people talk about it. And then other people are sort of,
maybe not really put under pressure, but they put themselves under pressure. Well,
should I be following this? Should I know what this is? And one way I sort of think about it,
every two or three weeks we do this, I look at new packages, and what I often do is I spin them
up in a playground. And that, to me, drives home how little Swift you actually need to do things,
because you import a package, and even bring up a Swift UI view, it's so little code.
And you see that also in example code, right? It's often, it's not trimmed down to make it
presentable on the page. Often, that's actually all the code you need. And I think people need
to be aware, you have lots of bells and whistles that you can add to make it more performant or
whatnot. But you can go a long way without ever touching that sort of thing. And I think that's
something people really need to be maybe a bit more aware of and be less concerned about, you
know, knowing every little corner of the language. >> Yeah, yeah, I completely agree.
>> I'm gonna grab on to the segue that you started down there, Sven, and then veer off to one side,
which was checking out new packages each week, because I think we should check out some packages.
So... >> Can I stop you, though?
>> Of course you can. >> Because we spoke about Ready for Swift
6 earlier, and it's quiz time. >> Oh, okay.
>> This is where you put in the chime, Dave. >> Oh, okay.
>> Holly, you may not know, but it's custom on the show that from time to time, I surprise Dave
with a quiz around the Swift package index and related things. And
today it's even better, because I've got two candidates who can fight it out now.
>> Oh, no. >> Yes. So what I'm going to do is I'm going
to read out a code snippet, and you have to tell me if it'll compile in Swift 6.
And that's the stunned silence I was going for.
No, what I'm actually going to do is I'm going to ask you...
>> Did you test the Swift? >> No.
The actual question is, so we mentioned, we talked about earlier that we're running
Swift 6 previews, and we were counting errors in the packages. And I wonder if you know how
many packages in the Swift package index had no Swift 6 errors in our second preview run.
>> So is this across the entire gamut of however many 7,400 packages? Right, okay.
>> Yeah, exactly. So the maximum is 7,400, and we haven't done any of the thing you mentioned
earlier of trimming the input. So this is all packages running with the Swift 6 preview.
What do you think? >> There are going to be a lot of packages
that don't touch concurrency. And so I think that's...
>> Just to set the terms for this, so we're going to let Dave go first, because then Holly
has an easier time, and whoever's closest wins a compiler flag of their choosing.
>> I will step in front of this bus, yeah. So yeah, I think there's a certain number of
packages, probably quite a significant number, that don't even attempt to cover... Like anything
that imports a UI framework is unlikely to touch concurrency, because it's generally going to be
some kind of UI package or something like that. So it's probably not a tiny amount. I'm going to say
3,000. 3,000. >> Ooh, 3,000, just...
>> Dead, yeah. >> Plain 3,000. No, okay. Holly,
what do you think? Higher or lower? >> I'm going to go lower. I'm going to guess
50. >> 50 is very pessimistic.
>> I completely agree with you that a lot of library APIs don't contain any concurrency
and are less likely to have issues. But I, yeah, I...
>> Well, Holly would know. Holly would know. So...
>> Now I'm really worried whether we measured the right thing.
>> For example, like some of the... I've never seen a macro implementation that has
a concurrency issue unless it contains some kind of global state, but I think that's not
super common to have mutable global state. So I definitely think there are categories of packages
that are not going to have any necessary source changes when migrating to Swift 6. I just don't
have any context for how many of those packages are on the Swift package index.
>> Right. So put us out of our misery. >> So the reveal is it's 3,868 packages
have no Swift 6 errors. >> That's amazing.
>> That's 53%. >> Wow.
>> That's incredible. >> I'm now worried whether we've got a bug
where we're undercounting errors because you were so pessimistic, Holly, but maybe we should
just double check. >> We should, yeah.
I think that's... It's a little surprising, but... Because I thought I was maybe going a little high,
but that's fantastic news. Our graph starts at 50%.
>> Yeah, exactly. Can only go up from there. >> Okay, now can we do some package recommendations?
Well, not so much than ever recommendations, but interesting packages that we've come across
recently. >> Let's do it.
>> Holly, would you like to kick us off this week? >> Yeah, I have two packages that I think are
really interesting. Admittedly, what I'm impressed by with these packages is more about the maintainers
than the package itself. But both of these maintainers that I'm going to mention have
been doing exactly what I mentioned earlier, which is sharing their experience with concurrency with
the wider community. So the first package is GRDB, which is a library for integrating databases into
your apps. And the maintainer Gwendal Roué has been posting a lot of topics on the forums,
specifically to ask about how to integrate concurrency into library API design. So one
forum thread that comes to mind recently is he was going through his public API surface and adding
sendable conformances and sendable requirements to generic code and specifically wanted to ask
how that might impact clients. Is adding sendable a source breaking change in a library for clients?
The answer in that thread is generally no. But there was some really interesting discussion
around how to be thinking about concurrency when you're writing a library, even if that library
does not itself use concurrency. It's something that you should still think about in your API
design. And I think Gwendal has been doing a really good job of starting those discussion
topics amongst the wider community. >> Yeah, Gwendal is great. And
it'd be great if you could pass us across the URL of that thread so that we can stick that in the
show notes as well. >> Yes, I will.
>> Wonderful. My first package this week is by Matt Cox, and it's called pack. And it's a swift
package to serialize and deserialize data into a kind of a binary external representation. So a
little bit like codable, but not... In terms of using it, it's very like codable, but it stores
its output in very tightly packed... I guess that's the name of the package, right? Pack.
Tightly packed binary blobs of data. And I think that the usage of this package is not going to be
for everybody. I think codable is the kind of the default case. So that's what you should be using
if you just want to store some data. But this reminded me a little bit of when I first...
My very first job out of university, where we were doing programming with Delphi in Object Pascal.
And the way that we stored all of our data files was just to write out effectively a C struct
to the disk, like just binary write out the structs and read them back in again.
And this is a little bit more flexible than that, in that you do have the ability to modify
in what order you're bringing stuff back and what order you're putting stuff out into the file.
But it was a nice reminder of that era of programming. And it's the kind of thing that
if you are really trying to optimize every byte of something, maybe it's going over the network,
it needs to be very fast, or maybe it's on an embedded system where space is absolutely critical.
I thought this was quite an interesting package. And certainly to use it, the...
The interface to it is as familiar as you could ever imagine if you've ever used Codable.
Yeah, really nice. I actually had that on my shortlist as well. Isn't that a...
What's it called? A proper property builder? No, a result builder interface to assemble the data?
I don't think it's a result builder. No, I don't think it is. Maybe we're talking about
different packages. Yeah. That doesn't matter. I thought, okay. It might have been a different one.
But I liked it. And so it's... I thought it was worth talking about.
Right. My package pick is called Geo URI by Jeff Johnston. And this is interesting. This is a swift
implementation of the Geo URI scheme. And I didn't actually even know that that sort of thing existed.
But I guess it's kind of obvious that it does. So it's, as you might think, HTTP, it's geo colon.
And then it has WSG 84, which is like a geo location standard coordinates that you can attach
to it. And I thought I'd mention this because, you know, who knows, who knows, that might be
really useful when your favorite spatial computing platform becomes more widespread, maybe in the
future, and the objects permanence becomes a thing. So you can reference things in the real
world or something like that. You know, you left your car keys at geo colon something.
But yeah, it's a nice package that sort of takes care of passing out that data and
handling all the special cases that that URI scheme comes with. That's Geo URI by Jeff Johnston.
That's great. Holly, you have a final package for us?
Yeah, my second package that I think is really interesting is the concurrency recipes package
by Matt Massicotte. And again, I really like this package because the purpose of it is to
demonstrate different patterns in Swift concurrency, either to deal with specific
problems like task ordering or actor reentrancy, or to demonstrate data race safety issues and
how to resolve those in your code. Matt has been talking with a lot of different people on the
forums and on Mastodon to see what kinds of things they're struggling with with concurrency. And then
he's going back and taking those examples and putting them in the package as a reference for
other people to use. And again, I think sharing this kind of information is really important for
building a shared understanding of what the best practices for strict concurrency checking are.
I think that package has actually come up on the podcast before. I think Sven maybe
brought that up once before. It is a great resource. Yes.
Yes, it's great. Yeah, Matt is one of those people who's also been really active on Mastodon
together with you, Holly, and has tackled a lot of the really tricky questions around
concurrency stuff, hasn't he? Yeah, yeah. And he's even been a co-author
on some of the recent Swift Evolution proposals that are under review for improving some of the
usability of some of the concurrency features. That's great. So my final package for this
episode is going to be Generative AI Swift by Google Gemini. So it's an official Google
package written in Swift for accessing their Gemini APIs to get a kind of conversational LLM
API going inside your application. And the reason, we've talked about these several times in the
past, but the reason I thought this one was interesting is it's the first one that I saw
where the API allows you to also attach binary data. So you can start a session with the
generative model, pick the model name that you want, give it an API key, and then give it a
prompt just like you would normally give it a prompt. But you can also add images or other
pieces of data and then ask the LLM about the content of the images, for example. And this
technology has been around for a little while now, but it's the first time I've seen it used in
an API. And as you'd expect, the API fits beautifully into Swift and you can just give
it a UI image or actually I'm not sure whether it does support NS images, but the examples are a UI
image. So if you're using it from a UI kit or iPhone, iPad, tvOS app, then you're going to be
straight in there with Google Gemini and image processing, which I thought was interesting.
It's evolving so quickly and all of the consumer front ends move on quicker sometimes in the APIs,
and so I'm glad to see the API is keeping up there. And it's nice to see Google producing
Swift APIs for their services too. Yeah, that's really nice to see that they publish these APIs.
Really interesting. I think their documentation also, so that's...
You're right, they do. And the documentation is hosted on the Swift Package Index, which is great.
Right. My second and final pick is called Fit by Oleh Korchytskyi. And this is a nice Swift UI
package. I don't often pick Swift UI packages because I don't really do a whole lot with Swift
UI, but this really caught my eye because it does view layout sort of like text layout. So it's sort
of text layout for Swift UI views. I imagine you have like text fields that you bring up more and
more of with varying lengths and it'll line break them essentially. So you can have them left
justified, right justified, centered. And if that isn't clear how that actually works, there's a
really nice read me with an animated GIF showing how that actually works in a Xcode preview. So
that's really nice. And Holly, do you actually use Swift UI a lot? How does that work? Or is
compiler work so different that you don't actually get to use that a lot?
Yeah, it's a good question. I used to use Swift UI more than I do now.
But I do still use it from time to time either because I'm looking at a bug report where
someone is using Swift UI in their code and I have to get into their code to figure out what
compiler issue is at play or just to experiment to see what the new APIs are like and so on,
because I haven't written Swift myself in several years now.
Yeah, that was going to be my question is how much Swift do you use these days
as opposed to C++? Yeah.
Yeah, exactly.
Yeah, so I think I definitely still write more C++ than Swift, unfortunately. Hopefully that
continues to change in the future. But I do really enjoy contributing to the Swift parts of the Swift
compiler, even just writing the tests is so much easier in Swift than it is in C++.
You can fake a bad connection now, Holly, but I'm wondering, do you sometimes wish the compiler was
written in Swift as well?
Oh, absolutely. I dream of the day when the full type checker is written in Swift and I don't need
to write C++. I'm actually pretty fond of C++ because it was the first programming language
that I learned. But also because it was the first programming language that I learned, I didn't know
that there were more modern options out there and safer options out there until I started learning
Swift.
So I think with that, we should wrap it up. I think it's been a bumper episode, but of course,
it's been an absolute pleasure to have you on the podcast, Holly. And I know that we're all
looking forward to seeing the safer environment that Swift 6 language mode is going to leave us
with after the transition. So thank you so much for your time and for coming on and for talking to
us. And yeah, it's been wonderful having you.
Thank you so much for having me. I had a lot of fun in this discussion and I love talking about
the Swift 6 language mode and strict concurrency checking.
Thanks for joining us, Holly.
And we'll be back in a few weeks and we'll see you then.
See you next time. Bye bye.