Join us for an update on Swift. Discover the latest language advancements that make your code easier to read and write. Explore the growing number of APIs available as Swift packages. And we'll introduce you to Swift's async/await syntax, structured concurrency, and actors.
♪ ♪ Hi! I'm Nicole, and welcome to "What's new in Swift." Swift 5.5 is our best release yet! The Swift language continues to evolve at a rapid pace, with many new features, including Swift Concurrency, a new model for asynchronous and concurrent programming designed to make concurrent programming in Swift convenient, efficient, and safe. In addition, we've made it easier than ever to develop in Swift, thanks to advances in the package experience, new standard library packages, and features enhancing the developer experience.
As important as these advances are, the heart of the Swift project is not code, but a community of people working towards the goals of the Swift project. This is why it is important that we invest in our community, as well as our software. I'd like to start things off by talking about an important Swift community initiative. Diversity is a core value of the Swift community. Research shows that diverse open source projects are more productive and make better decisions. Including people from all backgrounds and with diverse perspectives helps a community thrive. We, and other members of the Swift community, saw the opportunity to encourage active engagement in the Swift ecosystem and community from a wider range of developers with our Diversity in Swift initiative. The mission of Diversity in Swift is to foster an inclusive Swift community by elevating a wide variety of voices and making it easier for developers to start learning or contributing to Swift, regardless of their background. As part of this initiative, we have expanded the swift.org blog to include posts that recognize and highlight the contributions from a wide range of developers within our community. We have also created community groups in the Swift forums for developers to connect with others who may have had similar experiences or faced similar barriers. To get involved with Diversity in Swift, or find out more, please visit swift.org/diversity. We also want to help developers already in the community grow and reach their goals with Swift. To make contributing to Swift open source projects more approachable, we recently announced the Swift mentorship program to help newcomers get direct support and guidance from regular contributors. This program isn't limited to Apple's projects. It also encompasses the entire ecosystem of Swift packages and open source tools. Next, I'd like to talk about packages. Packages are a fundamental building block for building software, allowing you to conveniently take advantage of an ever-growing array of open source code. Members of the Swift community have come up with some great solutions to help developers find packages, like the Swift Package Index. The Swift Package Index is a page created by the community that helps you find packages which support the Swift Package Manager. Now, in Swift 5.5 and Xcode 13, we are providing additional ways for you to find and access packages, by providing integrated Xcode tooling support to make it even faster and easier to use packages in your project as part of your development workflow.
This year, we are introducing Swift Package Collections, curated lists of Swift packages you can use both from the command line and from Xcode 13. With package collections, you no longer need to search for packages on the Internet, or copy and paste URLs to add them. You can now simply browse a collection and add packages from a new package search screen in Xcode. Package collections are simple JSON files you can publish anywhere. Package collections are meant to allow anyone to build curated lists of packages for different use cases. For example, an instructor for a computer science class can put together a set of packages that helps teach the concepts of that class, or someone can put together a collection of packages that are good for a particular domain or task, or that are used by their organization. We're excited to see all the different ways in which people will use collections. We have a great presentation on package collections that I recommend you check out to learn more. Package collections include some powerful tooling support in Xcode. You are now just an import away from using your favorite APIs. When you try to import a module that cannot be found, Xcode will check if any of the packages in the package collections that you have configured provide that module and provide you the opportunity to automatically start using that package. Once you choose to use a package, all configuration is handled for you based on the information in the package collection. Package collections are also searchable, making it easy to find packages to meet your use case. If you are interested in trying out package collections today, there are some already available, including a package collection of the Swift Packages that Apple publishes on GitHub. Xcode comes pre-wired to use the Apple collection.
If you are interested in learning more about package collections, including collections already available from the community, check out the new swift.org blog post on package collections. Speaking of your favorite APIs, Apple publishes a growing family of open source Swift packages. In addition to some big enhancements to your favorite packages, this year, we launched four more new packages. First, I'd like to tell you about Swift Collections.
Swift Collections is a new open source package of data structures that complements those available in the Swift Standard Library. The initial version of Swift Collections comes with implementations for three of the most frequently requested data structures: Deque, OrderedSet, and OrderedDictionary.
Deque is like an Array, except that it supports efficient insertion and removal at both ends. OrderedSet is a powerful hybrid of an Array and a Set. Like Array, OrderedSet maintains its elements in order and supports random access. Like a Set, OrderedSet ensures each element appears only once and provides efficient membership testing.
And last is OrderedDictionary, which is a useful alternative to Dictionary when order is important, or we need random access to elements. Next, let's talk about Swift Algorithms. Swift Algorithms is a new open source package of Sequence and Collection algorithms.
We've already added over 40 algorithms to Swift Algorithms for things like generating all the combinations or permutations of a collection of elements, or iterating the elements of a sequence by two or three or in groups determined by a predicate, or selecting the five smallest elements in a collection, the five largest, or just any five at random.
It takes a little investment to learn the vocabulary, but once you do, it can be striking to discover just how many algorithms are hiding in plain sight.
If you want to learn more, we have a session which describes how the new Swift Algorithms and Collections packages can help you make your code clearer, faster, and more correct. Now, let's talk about Swift System. Last fall, we open-sourced Swift System, a library providing idiomatic, low-level interfaces to system calls. System is available on Apple platforms, Linux, and even Windows. We recently added powerful new APIs to System's FilePath type for performing common path manipulation operations. These include the ability to query or set extensions, add and remove components, and perform path normalization. A path can be decomposed into its root and its relative components. FilePath's ComponentView is a collection of structured path components, meaning it comes with support for many of Swift's generic algorithms right out of the box.
And when targeting Windows, Windows paths, with their complex root components, are fully supported by all the new FilePath APIs. Next, let's talk about Swift Numerics. Swift Numerics received a number of big additions this year. We brought Float16 to iOS, tvOS, and watchOS last year. This year, we added Float16 support to Apple Silicon Macs and the ability to make Float16-based complex numbers. Another addition this year is complex number support for all the elementary functions, like log, sine, and cosine. Because these implementations are written in Swift, they are frequently more efficient than a traditional C library and allow for optimizations that would otherwise not be possible. Finally, let's talk about Swift ArgumentParser. We've continued to refine Swift ArgumentParser this year, with enhancements like the ability to generate code-completion scripts for the Fish shell, joined short options, and improved error messages. We also passed an important milestone this spring when ArgumentParser was adopted by the Swift Package Manager in Xcode 12.5. That's right! If you've used the Swift Package Manager command-line tool recently, you've used Swift ArgumentParser. Next, I'd like to talk about some work we've done to support Swift on server development. Last year, we added support for a number of platforms, including Amazon Linux. This year, we've followed up on that work by investing in performance and functionality for Swift server applications.
We started by enabling static linking on Linux, which improves application startup time, as well as simplifies the deployment of server applications, which can now be deployed as a single file. Also, in Swift 5.5, the JSON encoding and decoding used on Linux were reimplemented from scratch, resulting in performance gains for most common use cases. Finally, we've enhanced and optimized the performance of the AWS Lambda runtime library itself. All this work made Swift programs running on AWS Lambda start 33% faster, as well as 40% faster invocation time for a lambda routed via AWS API Gateway. In addition to optimizing the AWS runtime library performance, we refactored it to use our new async/await model instead of closures. Improving the Swift developer experience is another key focus in Swift 5.5. I'd like to start by talking about documentation. Providing great documentation is key for creating a smooth and enjoyable experience for users of a framework. This year, we are introducing DocC, a documentation compiler that's deeply integrated inside Xcode 13, to help you teach developers how to use your Swift framework or package. It's now easier than ever to write and share great documentation.
DocC is built from the ground up, using tools and technologies that you already know and love, such as markdown comments in your Swift source code, so that you can easily write and diff your documentation.
We have four fantastic sessions to show you every aspect of DocC documentation in Xcode. I highly recommend you check them out. And now, I'm thrilled to announce that Swift DocC will be open-sourced later this year. Open-sourcing Swift DocC will allow developers to more easily generate great documentation on all of Swift's supported platforms.
In Swift 5.5, we invested in quality and performance improvements in the type checker. One result of this is you will see fewer "expression too complex" errors when compiling your code. We also sped up the performance for type checking of array literals.
In this release, we're also enhancing developer productivity with three major improvements to speed up incremental builds. First of all, we now support incremental imports, meaning we now no longer rebuild every source file that imports a module when that module changes. In addition, we now compute the module dependency graph up front so that we can quickly start incremental builds of only what has changed. Finally, we've extended selective recompilation to work with extensions, meaning fewer recompilations when you change the body of an extension. Using the SwiftDriver open source project as an example, on the average, with incremental imports in Swift 5.5, we now recompile less than a tenth as many files when imported modules change, and the build time is decreased by about a third. The performance improvements from incremental imports means that you can now modularize your project and change an imported module without a large penalty in build performance. And by the way, some of these performance improvements were made possible by an important milestone for the Swift project, the first part of the compiler to be written in Swift. This is the Swift Driver, the program that coordinates the compilation of Swift source code. This project began in late 2019 and, as of Xcode 13, is now the default for Swift compilation. Finally, let's talk about improvements we've made to make memory management in Swift more efficient, so Swift programs reclaim memory more quickly. Swift class instances use Automatic Reference Counting, ARC, to track how many references there are to a particular object at any given time. In most cases, this means that memory management just works in Swift, and you don't need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed. To do this, the Swift compiler inserts a retain operation any time a new reference is created and a release operation whenever a new reference stops being used. This year, we introduced a new way to track references inside the compiler that allows the compiler to significantly reduce the number of retain and release operations. We've seen measurable performance and code size improvements from this change. We've added an Xcode setting, Optimize Object Lifetimes, that will allow you to see the effect of this new, more aggressive ARC optimization on your code. For more information on ARC, please check out the "ARC in Swift" session.
These are just some of the improvements brought to you by Swift 5.5. Next, Tim will talk to you about Swift Concurrency and other evolutions in the Swift language. Thanks, Nicole. We're all very excited about Swift Concurrency, but before I get to that, I want to discuss a number of other changes we've made to Swift to simplify and improve everyday programming.
Here's a list of the ergonomic improvements we've made this year. The SE numbers identify Swift Evolution proposals. Each of these proposals was written by a member of the community, discussed on the Swift Evolution forum, and approved by the Swift Core Team before being accepted into the language. All SE proposals, whether accepted, rejected, or still in discussion, can be found in the Swift Evolution repository on GitHub. Let's look at a few of these changes. First, Result Builders. When SwiftUI was first announced, it introduced a new syntax that could be used to quickly and easily describe complex object hierarchies. This past year, that syntax was standardized and refined through the Swift Evolution process so that it could be more easily used in a wide variety of contexts. If you'd like to take advantage of this powerful technology, we have a session specifically devoted to using Result Builders. The Codable protocol is a convenient way to serialize your data, but it's long suffered from a notable omission. Consider this two-case Enum. In order to make it conform to Codable, you used to have to manually implement all of this boilerplate. Now, you just need to declare the Codable conformance, and the compiler will do all of that work for you.
We also made some key improvements to Swift's type checker. As you know, type inference in Swift means you can omit redundant type information. Here, it lets you shorten Coffee.regular down to just .regular. But Enum-like structures are also represented in other ways. For example, you might have a collection of types that conform to a protocol and want to use instances of those types in your API. You can now refer to instances of those types using the same dot-notation that you use for Enums, by declaring a few static properties on your protocol. This is enabled by improvements to Swift's type checker that allows it to reason more generally about static properties in generic contexts, including chained property references such as the .large here. This allows library authors to build sophisticated generic data models with natural and easy-to-use Enum-like APIs.
Property wrappers were also improved this year.
Property wrappers are a convenient tool for applying common semantics to properties. Many of you have implemented your own property wrappers using the @propertyWrapper annotation on a struct. Here's an example that adds the requirement that the property not be empty. With the implementation of SE-0293, those same property wrappers can now be used on function and closure parameters.
Cumulatively, these and other changes to the language can simplify a lot of common coding problems. Let's look at them together in the context of a simple SwiftUI code sample. Here's a SwiftUI View that has a single property, holding an array of settings, and a body that presents a list of those settings, with a toggle next to each one. Let's review this code and see how the new Swift 5.5 features can simplify it. First, that Toggle() initializer is obviously duplicated. This duplication used to be necessary, but we've relaxed the use of #if to allow it to surround postfix expressions, such as the toggleStyle modifiers here, which allows us to factor out that redundancy. SwiftUI has also been updated to take advantage of the new type checker improvements I mentioned earlier. So you can use natural dot-notation in many more places. It's--it's a little awkward to specify indexes for the settings array and then index the array within the closure. We'd rather just step through the values. Now you can pass the projected binding directly into the List constructor, which can then iterate over the array values. The new support for property wrapper arguments lets us write the closure argument with a dollar sign, which will give us a bound setting in our closure. That, in turn, allows us to access both the wrapped value and the binding. And finally, the Swift compiler now transparently converts between CGFloat and Double, which allows you to eliminate many redundant numeric conversions when working with Apple platform APIs. As you see, our continuing effort to refine the core language is making the code you write every day simpler than ever and creating more opportunities for library authors to build rich and easy-to-use APIs. For more information about some of the many ways that SwiftUI programming has been improved this year, please watch the "What's new in SwiftUI" session. Of course, the highlight of Swift 5.5 is a set of interlocking features to support asynchronous and concurrent programming. I'll introduce these in a moment, but first, let me briefly explain what I mean by "asynchronous" and "concurrent." Software projects are composed out of blocks of code that execute in some order. In the simplest case, those blocks execute, one after the other in a simple sequence. But other structures are common as well. For example, networking APIs are often designed in an asynchronous style. In these APIs, after you've sent a request to the remote server, there may be a long delay until you receive a response and need to do more work. Ideally, your code would be suspended during this delay so it does not use any resources until you are able to act on the response. In contrast, concurrent code is when you have two or more blocks of code that you would like to have running at the same time. These are often independent but related operations. Processing several frames of a video, for instance, or running the next iteration of an ML classifier at the same time that you're updating the UI with the previous set of results. With those ideas in mind, let's look at a simple asynchronous programming example without using Swift's new features. If you've done much iOS or macOS programming, you may have written code similar to this many times. This uses Foundation's URLSession class to make a network call. The dataTask method is an asynchronous operation. You call it with a closure argument. When the result becomes available, your closure will be called with the results to process. Using closures in this way to express asynchronous code results in a somewhat awkward order of operations, though, as you can see by walking through this code. First, there's some initial setup, and the dataTask method gives us back a task handle. Then we resume the task handle to start the background operation. The fetchImage function actually returns at this point. Whoever called us must be ready to continue, even though we haven't actually done the work we were asked to do. Later, after the network operation finishes, this closure will have a chance to deal with the results. Hopefully, everything goes well, and we can call our completion handler with the final result. Besides the somewhat awkward order of execution, using completion handlers also prevents us from using try/catch error handling. Notice how dataTask provides an extra error parameter to its completion handler and how we have to invoke our completion handler with every possible error. To see how Swift 5.5 improves this, let's take a look at this one line of code. Notice how this call returns a task, an abstract handle that represents the background operation. That's not really what we want. We just want to get the data. So let's push that idea a bit and see where we end up. Since we're dealing with HTTP, we also need to capture some response metadata. So the function is really going to return a pair, with the actual data as the first item and additional information as the second. By structuring this as a function call that returns the data, we can now use try/catch error handling to eliminate a lot of the boilerplate from the previous example. We just need a bit of syntax to tell the compiler that our function can be suspended as soon as the data method begins and that we won't be able to finish the assignment until that operation has completed. Which is exactly what the new await keyword does. Let's look at that in context. Here's what our fetchImage function looks like now. As you see, this code is now much easier to follow. Control flows from top to bottom, we no longer need nested closures, and we can use try/catch error handling. The await keyword indicates a point where this fetchImage function can be suspended, set aside to not run until some event happens that allows it to continue.
In this case, as soon as URLSession initiates the request, our function will be suspended by the Swift runtime while the operation is carried out elsewhere. When the final result is ready, whether that's a successful response or a thrown error, only then will our function be resumed. If it's a successful response, we'll complete the initialization of the data and response variables. If it's a thrown error, we'll pass that back to whoever called us. An asynchronous function does not use any resources while it's suspended. In particular, it's not blocking a thread. This allows the Swift runtime to reuse the thread this function was running on for other work. This allows a very few threads to be shared among many asynchronous processes.
Syntactically, the async and await keywords are used similarly to throws and try. async decorates the function declaration to indicate that this function must be compiled to support suspension. Use the await keyword to mark any call to an async function, method, or closure. Of course, the full mechanism is a lot more interesting than what I've shown here. You can watch the "Meet async/await in Swift" and "Swift concurrency: Behind the scenes" sessions to learn more about how all of this works. Next, let's take a look at Swift's new concurrency support, which builds on the async/await concepts I just described.
Here's a function that renders three different images and then combines them. As written here, these operations are sequential. The background, foreground, and title images will be rendered one after the other, with each one starting only after the previous one has completed. We'd like for the rendering operations to occur in parallel. But just running them in different threads isn't enough: we also need the merge operation to be held, until we have all three results. This is similar, in some respects, to the asynchronous coding I was just talking about. So we mark this function "async" so that it will be able to suspend if it needs to wait for results that are being computed in other threads. Next, we use the async let syntax to run the first two operations in parallel. async let looks a lot like a variable initialization, and that's basically what it is. But this initialization will run in parallel with other code until you try to use the results. Because the background and foreground variables are being initialized with async let, Swift's runtime will, if necessary, suspend the merge operation until those values are ready. We mark the merge function with the await keyword to indicate this. The most important point about this code is that the background tasks cannot outlive this function. Put another way, this function cannot and will not return if either of the two background tasks is still running. If an error is thrown from anywhere inside this function, the Swift runtime will still wait for the background tasks to complete. Here, I've highlighted a try marker that indicates the computation of the title image may throw, but the same applies to all thrown errors, even if they occur in a separate thread. In order to keep things responsive, when there is a thrown error, the Swift runtime will signal unfinished tasks to give them a chance to complete early. Our session on structured concurrency provides more details, including a full discussion of this cancellation mechanism and more flexible alternatives to the async let syntax I've discussed here. In the previous section, I showed how Swift 5.5 makes it easy to run operations on multiple threads in a disciplined, structured fashion. Of course, that's not quite enough. Whenever two separate threads share data, you run the risk that the data will be inconsistent, or even corrupted. Swift's new actor construct helps protect your data against such problems. Again, let's start with an example of code you may have written yourself. Here's a class that collects statistics. It contains a counter, and various other code will call the increment method to update that counter whenever something interesting happens. Unfortunately, this code doesn't work well in a multi-threaded system. If two or more threads call the increment method at the same time, you can end up with a badly-corrupted count. Changing this class into a Swift actor protects against such corruption. Actors work by suspending any operation that might cause data corruption until it's safe to make that particular change. This means you generally need to use await when you call an actor method from outside of the actor. Actors also work seamlessly with async/await. Marking this publish method as async allows it to be suspended while waiting on network operations. While it's suspended, other methods can run on this actor without waiting for the network operation to complete and without risk of data corruption. Actors are reference types, like classes, but they obey a number of rules designed to ensure that actors are safe to use in a multi-threaded environment. By packaging your data into actors, you are clearly stating that you expect this data to be accessed concurrently and that you want the Swift compiler and runtime to coordinate access so that no corruption is possible. And, of course, we have a full session devoted specifically to Swift's new actor construct. There, you can find out how to take advantage of the full range of capabilities this brings. Before we wrap up, let's talk a little bit about the future of Swift. We believe the three key concepts we've introduced in Swift 5.5-- asynchronous functions, structured concurrency, and actors-- are a good basis for creating safe and high-performance code. For Swift 6, we're already researching ways for the compiler to catch more kinds of concurrency mistakes earlier in the development process and give you more detailed errors and guidance in fixing those issues. Our goal, quite simply, is to entirely eliminate the most common kinds of concurrency bugs, in order to make asynchronous and concurrent programming no more complex than any other kind of programming. And, of course, as we continue to improve the compiler's understanding of these concepts, we also expect code using these new constructs to become even more efficient than it is today.
Swift is an open, collaborative effort that welcomes your input. To help make Swift 6 even better, tell us about your experiences with Swift 5.5. How well are these new features working for you in real-world app development? Try one of the compiler snapshots that you can find on swift.org. We provide these snapshots so you can install them into Xcode and try out new features as we're developing them. By using these snapshots, you can help guide the next version of Swift. The Swift forums are the lifeblood of the project. Each of the features I've discussed started life by being pitched on the Swift Evolution forum, where a diverse group of people helped to refine it into a working proposal. We also have forums devoted to many other aspects of Swift, including a help area for new users and a place to exchange news of interest to the Swift community. There are many other ways you can make Swift better, and we are eager to engage even more people, such as through the new Mentorship Program that Nicole mentioned at the beginning of this session. The only requirement is a sincere desire to help improve Swift for everyone in our community. I look forward to hearing from you, and I hope you enjoy the conference. [upbeat music]
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.