Swift Package Indexing

This week we had the opportunity to talk to Holly Borla who manages the Swift Compiler Team at Apple. We chat about upcoming Swift 6 changes and why they're a big deal, but also why you shouldn’t worry too much. Of course, all three of us pick packages, too!

Interview with Holly
Packages

Creators & Guests

Host
Dave Verwer
Independent iOS developer, technical writer, author of @iOSDevWeekly, and creator of @SwiftPackages. He/him.
Host
Sven A. Schmidt
Physicist & techie. CERN alumnus. Co-creator @SwiftPackages. Hummingbird app: https://t.co/2S9Y4ln53I

What is Swift Package Indexing?

Join Dave and Sven, the creators of the Swift Package Index open-source project, as they talk about progress on the project and discuss a new set of community package recommendations every episode.

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.