├── .gitignore ├── README.md ├── Whats-New-In-Swift-6-0.playground ├── Pages │ ├── 128-bit Integer Types.xcplaygroundpage │ │ └── Contents.swift │ ├── Access-level modifiers on import declarations.xcplaygroundpage │ │ └── Contents.swift │ ├── Add Collection Operations on Noncontiguous Elements.xcplaygroundpage │ │ └── Contents.swift │ ├── BitwiseCopyable.xcplaygroundpage │ │ └── Contents.swift │ ├── Complete concurrency enabled by default.xcplaygroundpage │ │ └── Contents.swift │ ├── Introduction.xcplaygroundpage │ │ └── Contents.swift │ ├── Pack iteration.xcplaygroundpage │ │ └── Contents.swift │ ├── Typed throws.xcplaygroundpage │ │ └── Contents.swift │ ├── Upgrades for noncopyable types.xcplaygroundpage │ │ └── Contents.swift │ └── count(where).xcplaygroundpage │ │ └── Contents.swift └── contents.xcplayground └── playground-screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcshareddata/ 19 | xcuserdata/ 20 | 21 | ## Other 22 | .DS_Store 23 | *.moved-aside 24 | *.xccheckout 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | .build/ 43 | 44 | # CocoaPods 45 | # 46 | # We recommend against adding the Pods directory to your .gitignore. However 47 | # you should judge for yourself, the pros and cons are mentioned at: 48 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 49 | # 50 | # Pods/ 51 | 52 | # Carthage 53 | # 54 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 55 | # Carthage/Checkouts 56 | 57 | Carthage/Build 58 | 59 | # fastlane 60 | # 61 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 62 | # screenshots whenever they are needed. 63 | # For more information about the recommended setup visit: 64 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 65 | 66 | fastlane/report.xml 67 | fastlane/Preview.html 68 | fastlane/screenshots 69 | fastlane/test_output 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What’s new in Swift 6.0? 2 | 3 | This is an Xcode playground that demonstrates the new features introduced in Swift 6.0: 4 | 5 | * Complete concurrency enabled by default 6 | * count(where:) 7 | * Typed throws 8 | * Pack iteration 9 | * Add Collection Operations on Noncontiguous Elements 10 | * Access-level modifiers on import declarations 11 | * Upgrades for noncopyable types 12 | * 128-bit Integer Types 13 | * BitwiseCopyable 14 | 15 | This is designed to complement my existing article [What’s New in Swift 6.0](https://www.hackingwithswift.com/articles/269/whats-new-in-swift-6). 16 | 17 | Alternatively, here you can find a complete breakdown of all Swift changes from 1.0 through 6.0, including downloadable playgrounds: [what's new in Swift](https://www.hackingwithswift.com/swift). 18 | 19 | If you hit problems or have questions, you're welcome to tweet me [@twostraws](https://twitter.com/twostraws) or email . 20 | 21 | ![Screenshot of Xcode 15.4 running this playground.](playground-screenshot.png) 22 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/128-bit Integer Types.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # 128-bit Integer Types 8 | 9 | [SE-0425](https://github.com/apple/swift-evolution/blob/main/proposals/0425-int128.md) introduces `Int128` and `UInt128`. I literally have nothing more to say about these, because I think you already know exactly how they work – even the evolution proposal says, "the actual API of the types is uninteresting." 10 | 11 | Still, I'd feel guilty if I didn't at least give you a code sample, so here goes: 12 | */ 13 | let enoughForAnybody: Int128 = 170_141_183_460_469_231_731_687_303_715_884_105_727 14 | /*: 15 | 16 |   17 | 18 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 19 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/Access-level modifiers on import declarations.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Access-level modifiers on import declarations 8 | 9 | [SE-0409](https://github.com/apple/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md) adds the ability to mark import declarations with access control modifiers, such as `private import SomeLibrary`. 10 | 11 | There are various ways this will be useful, including the ability for library developers to avoid accidentally leaking their own dependencies. For example, a banking might be split into multiple parts: 12 | 13 | - The app itself, presenting the user interface. 14 | - A Banking library that handles all the functionality and core logic. 15 | - Several smaller, internal libraries that handle individual pieces of work that are lower level, such as a Transactions package, a Networking package, and so on. 16 | 17 | So, the app depends on the Banking library, and the Banking library in turn depends on Transactions, Networking, and other internal libraries. 18 | 19 | We can demonstrate that setup with some code that also demonstrates the problem being resolved here. First, we could say that the low-level Transactions package has a struct such as this one: 20 | */ 21 | public struct BankTransaction { 22 | // code here 23 | } 24 | /*: 25 | Up in the Banking library we might write a function to send money from one account number to another using that `BankTransaction`: 26 | */ 27 | public func sendMoney(from: Int, to: Int) -> BankTransaction { 28 | // handle sending money then send back the result 29 | return BankTransaction() 30 | } 31 | /*: 32 | And now in the main app we can call `sendMoney()` to do the work. 33 | 34 | That's all regular Swift code, but it can create a rather unpleasant problem: very often wrapper libraries don't want to reveal the inner workings of the libraries they rely on internally, which is exactly what happens here – our main app is given access to the `BankTransaction` struct from the Transactions library, when really it should only use APIs from the Banking library. 35 | 36 | From 6.0 onwards we can solve this problem by using access control on the import for Transactions: by using `internal import Transactions` or similar in the Banking library, Swift will refuse to build any code declared as public that exposes API from the Transactions library. 37 | 38 | This really helps to clear up code boundaries: the Banking framework can still go ahead and use all the libraries it wants internally, but it won't be allowed to send those back to clients – the app in this case – by accident. If we genuinely did want to expose the internal framework types, we would use `public import Transactions` to make that explicit. 39 | 40 | On a more fine-grained level, this also allows files inside the same module to add extra restrictions – one file could privately import a framework without wanting to accidentally expose the contents of that framework elsewhere. 41 | 42 | Although Swift 6 hasn't shipped yet, it's looking like the default for imports will be `internal` when running in Swift 6 mode, but `public` in Swift 5 mode to retain compatibility with existing code. 43 | 44 |   45 | 46 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 47 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/Add Collection Operations on Noncontiguous Elements.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Add Collection Operations on Noncontiguous Elements 8 | 9 | [SE-0270](https://github.com/apple/swift-evolution/blob/main/proposals/0270-rangeset-and-collection-operations.md) introduces various new methods to handle more complex operations on collections, such as moving or remove multiple items that aren't contiguous. 10 | 11 | This change is powered by a new type called `RangeSet`. If you've ever used `IndexSet` from Foundation, think of `RangeSet` as being `IndexSet` except for any kind of `Comparable` type rather than just integers. 12 | 13 | Lots of Swift API has been upgraded to `RangeSet`. To give us some example data to work with, we could create an array of students with exam results like this: 14 | */ 15 | struct ExamResult { 16 | var student: String 17 | var score: Int 18 | } 19 | 20 | let results = [ 21 | ExamResult(student: "Eric Effiong", score: 95), 22 | ExamResult(student: "Maeve Wiley", score: 70), 23 | ExamResult(student: "Otis Milburn", score: 100) 24 | ] 25 | /*: 26 | We can get a `RangeSet` containing the indices of all students who score 85% or higher like this: 27 | */ 28 | let topResults = results.indices { student in 29 | student.score >= 85 30 | } 31 | /*: 32 | And if we wanted to get access to those students, we can use a new `Collection` subscript: 33 | */ 34 | for result in results[topResults] { 35 | print("\(result.student) scored \(result.score)%") 36 | } 37 | /*: 38 | This subscript returns another new type called `DiscontiguousSlice`, which is similar to `Slice` in that for performance reasons it refers to elements stored in a different collection, except the indices are *discontiguous*, meaning that they aren't necessarily adjacent in the collection. 39 | 40 | The "set" part of the name is there because `RangeSet` supports a variety of functions that come from the `SetAlgebra` protocol, including `union()`, `intersection()`, and `isSuperset(of:)`. This also means that inserting one range into another will merge any overlapping ranges rather than creating duplicates. 41 | 42 |   43 | 44 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 45 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/BitwiseCopyable.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction) 7 | # BitwiseCopyable 8 | 9 | [SE-0426](https://github.com/apple/swift-evolution/blob/main/proposals/0426-bitwise-copyable.md) introduces a new `BitwiseCopyable` protocol, which has the sole purpose of allowing the compiler to create more optimized code for conforming types. 10 | 11 | *Most of the time you don't need to do anything to enable `BitwiseCopyable` support*. Swift will automatically apply it to most structs and enums you create as long as all the properties they contain are also bitwise copyable. That includes a huge collection of built-in types: all integers, all floating-point numbers, `Bool`, `Duration`, `StaticString`, and more. 12 | 13 | Where things take a little more thinking is when you're building a library – if Swift were to automatically apply a conformance to `BitwiseCopyable` it could cause problems if your type changed in the future in a way that made it *not* support the protocol. 14 | 15 | So, Swift disables the automatic inference for types you export with `public` or `package` visibility unless you explicitly mark those types with `@frozen`. 16 | 17 | If you specifically need to disable `BitwiseCopyable`, you can do that by adding `~BitwiseCopyable` to your type's inheritance list. For example, the standard library's `CommandLine` enum is both `public` and `@frozen`, so the Swift team explicitly opt out of it being bitwise copyable like this: 18 | */ 19 | @frozen 20 | public enum CommandLine : ~BitwiseCopyable { 21 | } 22 | /*: 23 | **Important:** Opting out of `BitwiseCopyable` must happen directly where your type is declared rather than in an extension. 24 | 25 |   26 | 27 | [< Previous](@previous)           [Home](Introduction) 28 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/Complete concurrency enabled by default.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Complete concurrency enabled by default 8 | 9 | Swift 6 contains another barrage of updates around concurrency, and the team ought to be proud of the extraordinary advances they have made to make this release possible. 10 | 11 | By far the biggest change is that complete concurrency checking is enabled by default. Unless you're very fortunate indeed, there's a very good chance your code will need some adjustment – it's no surprise the Swift team made it optional in earlier versions to give folks time to evaluate what's changing. 12 | 13 | Swift 6 improves concurrency checking further, and the Swift team say it "removes many false-positive data-race warnings" that were present in 5.10. It also introduces several targeted changes that will do wonders to make concurrency easier to adopt – if you tried with 5.10 and found things just too gnarly to figure out, hopefully some of the changes in Swift 6 will help. 14 | 15 | Easily the biggest is [SE-0414](https://github.com/apple/swift-evolution/blob/main/proposals/0414-region-based-isolation.md), defines isolation regions that allow the compiler to conclusively prove different parts of your code can run concurrently. 16 | 17 | At the core of this change lies the existing concept of *sendability*. A `Sendable` type is one that can be safely passed around in a concurrent environment, which can include value types such as structs, final classes with constant properties, actors that automatically protect their own mutable state, and more. 18 | 19 | Before Swift 6 the compiler was very strict: if you had a non-sendable value on one actor and tried to send it to another actor, you'd get concurrency checking warnings. For example, although SwiftUI view bodies run on the main actor, SwiftUI views themselves *don't*, which can easily cause all sorts of false positive warnings from the compiler – Swift thinks there's a potential race condition when really there isn't. 20 | 21 | You can see the problem with the following code: 22 | */ 23 | import SwiftUI 24 | 25 | class User { 26 | var name = "Anonymous" 27 | } 28 | 29 | struct ContentView: View { 30 | var body: some View { 31 | Text("Hello, world!") 32 | .task { 33 | let user = User() 34 | await loadData(for: user) 35 | } 36 | } 37 | 38 | func loadData(for user: User) async { 39 | print("Loading data for \(user.name)…") 40 | } 41 | } 42 | /*: 43 | Before Swift 6 the call to `loadData()` would throw up a warning: "passing argument of non-sendable type 'User' outside of main actor-isolated context may introduce data races." 44 | 45 | *After* Swift 6 this warning goes away: Swift now detects that the code doesn't actually present a problem because `user` isn't being accessed from two or more places at once, so it won't emit a warning – the compiler is able to analyze the program's flow and detect that it's safe. 46 | 47 | This change effectively means sendable objects are now either those that conform to `Sendable`, or those that don't need to conform to `Sendable` because the compiler can prove they are being used safely – it's a dramatic simplification of concurrency for developers, made possible by truly cutting-edge compiler development. 48 | 49 | But there are many other, smaller improvements, including: 50 | 51 | - [SE-430](https://github.com/apple/swift-evolution/blob/main/proposals/0430-transferring-parameters-and-results.md) adds a new `sending` keyword for when we need to send values between isolation regions. 52 | - [SE-0423](https://github.com/apple/swift-evolution/blob/main/proposals/0423-dynamic-actor-isolation.md) improves concurrency support when needing to operate with Objective-C frameworks. 53 | - [SE-0420](https://github.com/apple/swift-evolution/blob/main/proposals/0420-inheritance-of-actor-isolation.md) allows us to make `async` functions that are isolated to the same actor as their caller. 54 | 55 | Some other changes were present in earlier versions of Swift, but hidden behind feature flags. For example, [SE-0401](https://github.com/apple/swift-evolution/blob/main/proposals/0401-remove-property-wrapper-isolation.md) removes a feature that was introduced back in Swift 5.5: actor inference for property wrappers. 56 | 57 | Previously, any struct or class using a property wrapper with `@MainActor` for its wrapped value will automatically be `@MainActor`. This is what makes `@StateObject` and `@ObservedObject` convey "main-actor-ness" on SwiftUI views that use them – if you use either of those two property wrappers in a SwiftUI view, the whole view becomes `@MainActor` too. 58 | 59 | As an example, consider the view model below, marked with `@MainActor` as is good practice: 60 | */ 61 | @MainActor 62 | class ViewModel: ObservableObject { 63 | func authenticate() { 64 | print("Authenticating…") 65 | } 66 | } 67 | /*: 68 | If you want to use that from a SwiftUI view using `@StateObject`, you must *also* mark the view with `@MainActor` from Swift 6 and later, like this: 69 | */ 70 | @MainActor 71 | struct LogInView: View { 72 | @StateObject private var model = ViewModel() 73 | 74 | var body: some View { 75 | Button("Hello, world", action: startAuthentication) 76 | } 77 | 78 | func startAuthentication() { 79 | model.authenticate() 80 | } 81 | } 82 | /*: 83 | Before Swift 6, `@MainActor` would have been conferred on the whole view because of its `@StateObject` property. 84 | 85 | Another old changed that's now enable in Swift 6 is [SE-0412](https://github.com/apple/swift-evolution/blob/main/proposals/0412-strict-concurrency-for-global-variables.md) requires global variables to be safe in concurrent environments. 86 | 87 | This applies to loose variables you might have in your projects at global scope: 88 | */ 89 | var gigawatts = 1.21 90 | /*: 91 | But also to static variables stored in types: 92 | */ 93 | struct House { 94 | static var motto = "Winter is coming" 95 | } 96 | /*: 97 | This data can be accessed anywhere at any time, which makes it inherently unsafe. To resolve the problem you either need to convert the variable into a sendable constant, restrict it to a global actor, e.g. `@MainActor`, or, if you have no other option or know it's protected somewhere else, mark it nonisolated. 98 | 99 | For example, all of these are allowed: 100 | */ 101 | struct XWing { 102 | @MainActor 103 | static var sFoilsAttackPosition = true 104 | } 105 | 106 | struct WarpDrive { 107 | static let maximumSpeed = 9.975 108 | } 109 | 110 | @MainActor 111 | var idNumber = 24601 112 | 113 | // Not recommended unless you're certain it's safe 114 | nonisolated(unsafe) var britishCandy = ["Kit Kat", "Mars Bar", "Skittles", "Starburst", "Twix"] 115 | /*: 116 | Another feature present earlier but now enabled is [SE-0411](https://github.com/apple/swift-evolution/blob/main/proposals/0411-isolated-default-values.md), which changes function default values to have the same isolation as the function they are inside. 117 | 118 | For example, the code below is now allowed, when previously it would have triggered an error: 119 | */ 120 | @MainActor 121 | class Logger { 122 | 123 | } 124 | 125 | @MainActor 126 | class DataController { 127 | init(logger: Logger = Logger()) { 128 | 129 | } 130 | } 131 | /*: 132 | Because both `DataController` and `Logger` have been restricted to the main actor, Swift now considers the `Logger()` creation to also be restricted to the main actor, which makes perfect sense. 133 | 134 | Swift concurrency remains a bit of a moving target, but if you'd like to know more I highly recommend [Matt Massicotte's blog](https://www.massicotte.org) – I don't think anyone is doing more to educate Swift developers about effective adoption of Swift concurrency. 135 | 136 | And remember: if Swift 6 throws up concurrency warnings and errors about your code, those problems were there beforehand too – they just weren't being diagnosed automatically! 137 | 138 |   139 | 140 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 141 | */ 142 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/Introduction.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | # What’s new in Swift 6.0 3 | 4 | * Created by [Paul Hudson](https://twitter.com/twostraws) – [Hacking with Swift](https://www.hackingwithswift.com) 5 | 6 | This playground is designed to showcase new features introduced in Swift 6.0. If you hit problems or have questions, you're welcome to tweet me [@twostraws](https://twitter.com/twostraws) or email . 7 | 8 |   9 | * [Complete concurrency enabled by default](Complete%20concurrency%20enabled%20by%20default) 10 | * [count(where:)](count(where)) 11 | * [Typed throws](Typed%20throws) 12 | * [Pack iteration](Pack%20iteration) 13 | * [Add Collection Operations on Noncontiguous Elements](Add%20Collection%20Operations%20on%20Noncontiguous%20Elements) 14 | * [Access-level modifiers on import declarations](Access-level%20modifiers%20on%20import%20declarations) 15 | * [Upgrades for noncopyable types](Upgrades%20for%20noncopyable%20types) 16 | * [128-bit Integer Types](128-bit%20Integer%20Types) 17 | * [BitwiseCopyable](BitwiseCopyable) 18 | 19 |   20 | 21 | [Next >](@next) 22 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/Pack iteration.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Pack iteration 8 | 9 | [SE-0408](https://github.com/apple/swift-evolution/blob/main/proposals/0408-pack-iteration.md) introduces pack iteration, which adds the ability to loop over the parameter pack feature introduced in Swift 5.9. 10 | 11 | Although value packs remain one of the most complex features of Swift, the evolution proposal shows just how useful this feature is by adding tuple comparison for any arity in just a few lines of code: 12 | */ 13 | func == (lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool { 14 | for (left, right) in repeat (each lhs, each rhs) { 15 | guard left == right else { return false } 16 | } 17 | return true 18 | } 19 | /*: 20 | If that means nothing to you, the Simple English version is that [SE-0015](https://github.com/apple/swift-evolution/blob/main/proposals/0015-tuple-comparison-operators.md) added support for direct tuple comparison up to arity 6, meaning that two tuples with up to six items could be compared using ==. If you tried comparing tuples with seven items – e.g. `(1, 2, 3, 4, 5, 6, 7) == (1, 2, 3, 4, 5, 6, 7)` – Swift would throw up an error. SE-0408, along with the code above, removes that restriction. 21 | 22 | Tantalizingly, the [Future Directions section](https://github.com/apple/swift-evolution/blob/main/proposals/0408-pack-iteration.md#future-directions) of this evolution proposal suggest that in the future we might see a variant of Swift's `zip()` function that supports any number of sequences. 23 | 24 |   25 | 26 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 27 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/Typed throws.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Typed throws 8 | 9 | [SE-0413](https://github.com/apple/swift-evolution/blob/main/proposals/0413-typed-throws.md) introduced the ability to specify exactly what types of errors a function can throw, known as "typed throws". This resolves an annoyance with errors in Swift: we needed a general catch clause even when we had specifically caught all possible errors. 10 | 11 | As an example of typed throws, we could define a `CopierError` that can track when a photocopier runs out of paper: 12 | */ 13 | enum CopierError: Error { 14 | case outOfPaper 15 | } 16 | /*: 17 | We could then create a `Photocopier` struct that creates some number of copies of a page. This might throw errors if there isn't enough paper loaded for the requested operation, but rather than mark it simply as `throws` we'll use `throws(CopierError)` to be clear exactly what kind of errors can be thrown: 18 | */ 19 | struct Photocopier { 20 | var pagesRemaining: Int 21 | 22 | mutating func copy(count: Int) throws(CopierError) { 23 | guard count <= pagesRemaining else { 24 | throw CopierError.outOfPaper 25 | } 26 | 27 | pagesRemaining -= count 28 | } 29 | } 30 | /*: 31 | - important: With this change you can either use `throws` to specify any kind of error being thrown, or `throws(OneSpecificErrorType)` to signal that only that one type can be thrown. You cannot write `throws(A, B, C)` to throw one of several errors. 32 | 33 | Now we can write code to attempt photocopying, catching the single error that can possibly be thrown: 34 | */ 35 | do { 36 | var copier = Photocopier(pagesRemaining: 100) 37 | try copier.copy(count: 101) 38 | } catch CopierError.outOfPaper { 39 | print("Please refill the paper") 40 | } 41 | /*: 42 | That call site is the important change here: in earlier versions of Swift we'd need a so-called "Pokémon catch" at the end, because Swift couldn't be sure exactly error types could be thrown – you've "gotta catch 'em all." 43 | 44 | This comes with several other advantages: 45 | 46 | 1. Because Swift knows that `CopierError` is the only error type that can be thrown, we can write `throw .outOfPaper`. 47 | 2. If the code in a `do` block only throws one kind of error, the `error` value in a general `catch` block will automatically have the same error type rather than being any kind of error. 48 | 3. If we attempt to throw any other kind of error not listed in the `throws` clause, Swift will issue a compile error. 49 | 50 | Where this gets really clever is that `throws(any Error)` is equivalent to using just `throws` by itself, and `throws(Never)` is equivalent to a non-throwing function. That might sound obscure, but it means in many places `rethrows` can be expressed more clearly: the function throws whatever the function parameter throws. 51 | 52 | As an example, Swift 6's new `count(where:)` method accepts a closure used to evaluate how many items match whatever kind of filter you're running. That closure might throw errors, and if it does `count(where:)` will throw that same error type: 53 | */ 54 | public func count( 55 | where predicate: (Element) throws(E) -> Bool 56 | ) throws(E) -> Int { 57 | print("Code goes here") 58 | return 0 59 | } 60 | /*: 61 | If that closure *doesn't* throw an error, `throws(E)` is effectively `throws(Never)`, meaning that `count(where:)` will also not throw errors. 62 | 63 | Even though typed throws seem very appealing, they are aren't a great choice when the errors that can be thrown might change in the future. They are a particularly poor choice in library code, because they lock you into a contract you might not want to stick to in the future. 64 | 65 | In fact, here I'll just defer to the authors of the evolution proposal, who sum it up like this: **even with the addition of typed throws to Swift, untyped throws is better for most scenarios.** 66 | 67 | Where typed throws *are* particularly useful is in the increasingly important realm of embedded Swift, where performance and predictability is critical. 68 | 69 |   70 | 71 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 72 | */ 73 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/Upgrades for noncopyable types.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Upgrades for noncopyable types 8 | 9 | Noncopyable types were [introduced in Swift 5.9](/swift/5.9/noncopyable-structs-and-enums), but are getting several upgrades in Swift 6. 10 | 11 | As a reminder, noncopyable types allow us create types that have unique ownership, which we can pass around using borrowing or consuming as needed. 12 | 13 | One example of noncopyable types I previously used were the secret messages used in the Mission Impossible movies – they famously self-destruct after being read, which we can model with a noncopyable type that is consumed (i.e. destroyed) upon reading: 14 | */ 15 | struct Message: ~Copyable { 16 | var agent: String 17 | private var message: String 18 | 19 | init(agent: String, message: String) { 20 | self.agent = agent 21 | self.message = message 22 | } 23 | 24 | consuming func read() { 25 | print("\(agent): \(message)") 26 | } 27 | } 28 | 29 | func createMessage() { 30 | let message = Message(agent: "Ethan Hunt", message: "You need to abseil down a skyscraper for some reason.") 31 | message.read() 32 | } 33 | 34 | createMessage() 35 | /*: 36 | In that code, the compiler enforces that `message.read()` can only ever be called once, because it consumes the object. 37 | 38 | The first major improvement is [SE-0427](https://github.com/apple/swift-evolution/blob/main/proposals/0427-noncopyable-generics.md), which introduces a batch of improvements at once. The biggest of those is that every struct, class, enum, generic type parameter, and protocol in Swift 6 automatically conforms to a new `Copyable` protocol unless you explicitly opt out using `~Copyable`. 39 | 40 | This impacts on the other changes introduced with this proposal. For example, noncopyable types can now be used with generics, allowing things like *optional* noncopyable instances because Swift's `Optional` is implemented a generic enum. However, because generic type parameters automatically conform to `Copyable` we must explicitly opt out using `~Copyable`. 41 | 42 | Similarly, this change means noncopyable types can now conform to protocols, but only when those protocols are also marked `~Copyable` because otherwise they get automatically opted into `Copyable` as mentioned above. (In case you were curious, `Copyable` types can conform to noncopyable protocols just fine.) 43 | 44 | [SE-0429](https://github.com/apple/swift-evolution/blob/main/proposals/0429-partial-consumption.md) improves things further by adding partial consumption of noncopyable values. 45 | 46 | Previously it could be a problem when one noncopyable type incorporated another. For example, even fairly trivial code like the below was a problem before [SE-0429](https://github.com/apple/swift-evolution/blob/main/proposals/0429-partial-consumption.md): 47 | */ 48 | struct Package: ~Copyable { 49 | var from: String = "IMF" 50 | var message: Message 51 | 52 | consuming func read() { 53 | message.read() 54 | } 55 | } 56 | /*: 57 | That code is now valid Swift, as long as the types in question don't have deinitializers. 58 | 59 | A third major noncopyable improvement is [SE-0432](https://github.com/apple/swift-evolution/blob/main/proposals/0432-noncopyable-switch.md), which allows us to borrow noncopyable types while switching over them. Previously it was impossible to do pattern matching with `where` clauses that depended on noncopyable values, whereas thanks to SE-0432 this is now possible in Swift 6. 60 | 61 | Continuing our Mission Impossible example, we could say that one set of orders might be signed or anonymous, like this: 62 | */ 63 | enum ImpossibleOrder: ~Copyable { 64 | case signed(Package) 65 | case anonymous(Message) 66 | } 67 | /*: 68 | Because that enum has associated values that are noncopyable, it must itself be noncopyable. However, the associated values being noncopyable also means that pattern matching with `where` was tricky – if you wanted to perform one set of actions for one `Message` type, and a different set for another `Message` type, you were out of luck. 69 | 70 | With [SE-0432](https://github.com/apple/swift-evolution/blob/main/proposals/0432-noncopyable-switch.md) this is now resolved, meaning code like the below is now allowed: 71 | */ 72 | func issueOrders() { 73 | let message = Message(agent: "Ethan Hunt", message: "You need to abseil down a skyscraper for some reason.") 74 | let order = ImpossibleOrder.anonymous(message) 75 | 76 | switch consume order { 77 | case .signed(let package): 78 | package.read() 79 | case .anonymous(let message) where message.agent == "Ethan Hunt": 80 | print("Play dramatic music") 81 | message.read() 82 | case .anonymous(let message): 83 | message.read() 84 | } 85 | } 86 | /*: 87 | Put together, this collection of changes helps make noncopyable types work much more naturally in Swift. 88 | 89 |   90 | 91 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 92 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/Pages/count(where).xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # count(where:) 8 | 9 | [SE-0220](https://github.com/apple/swift-evolution/blob/master/proposals/0220-count-where.md) introduced a new `count(where:)` method that performs the equivalent of a `filter()` and count in a single pass. This saves the creation of a new array that gets immediately discarded, and provides a clear and concise solution to a common problem. 10 | 11 | This example creates an array of test results, and counts how many are greater or equal to 85: 12 | */ 13 | let scores = [100, 80, 85] 14 | let passCount = scores.count { $0 >= 85 } 15 | /*: 16 | And this counts how many names in an array start with “Terry”: 17 | */ 18 | let pythons = ["Eric Idle", "Graham Chapman", "John Cleese", "Michael Palin", "Terry Gilliam", "Terry Jones"] 19 | let terryCount = pythons.count { $0.hasPrefix("Terry") } 20 | /*: 21 | This method is available to all types that conform to `Sequence`, so you can use it on sets and dictionaries too. 22 | 23 | - important: `count(where:)` was originally planned for Swift 5.0 way back in 2019, but was withdrawn at the time for performance reasons. 24 | 25 |   26 | 27 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 28 | */ -------------------------------------------------------------------------------- /Whats-New-In-Swift-6-0.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /playground-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twostraws/whats-new-in-swift-6-0/84aa880ed403907d74f4f40c7fc9eed6760897f4/playground-screenshot.png --------------------------------------------------------------------------------