├── .gitignore ├── README.md ├── Whats-New-In-Swift-5-0.playground ├── Pages │ ├── A standard Result type.xcplaygroundpage │ │ └── Contents.swift │ ├── Checking for integer multiples.xcplaygroundpage │ │ └── Contents.swift │ ├── Customizing string interpolation.xcplaygroundpage │ │ └── Contents.swift │ ├── Dynamically callable types.xcplaygroundpage │ │ └── Contents.swift │ ├── Flattening nested optionals resulting from try?.xcplaygroundpage │ │ └── Contents.swift │ ├── Handling future enum cases.xcplaygroundpage │ │ └── Contents.swift │ ├── Introduction.xcplaygroundpage │ │ └── Contents.swift │ ├── Raw strings.xcplaygroundpage │ │ └── Contents.swift │ └── Transforming and unwrapping dictionary values.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 5.0? 2 | 3 | This is an Xcode playground that demonstrates the new features introduced in Swift 5.0: 4 | 5 | * Raw strings 6 | * A standard `Result` type 7 | * Customizing string interpolation 8 | * Dynamically callable types 9 | * Handling future enum cases 10 | * Flattening nested optionals resulting from `try?` 11 | * Checking for integer multiples 12 | * Transforming and unwrapping dictionary values with `compactMapValues()` 13 | 14 | This is designed to complement my existing article [What’s New in Swift 5.0](https://www.hackingwithswift.com/articles/126/whats-new-in-swift-5-0). You might also want to read [What’s New in Swift 4.2](https://www.hackingwithswift.com/articles/77/whats-new-in-swift-4-2) and [What's New in Swift 4.1](https://www.hackingwithswift.com/articles/50/whats-new-in-swift-4-1). Alternatively, I have a whole website dedicated to tracking [what's new in Swift](https://www.whatsnewinswift.com) – you should check it out at . 15 | 16 | If you hit problems or have questions, you're welcome to tweet me [@twostraws](https://twitter.com/twostraws) or email . 17 | 18 | ![Screenshot of Xcode 10.2 running this playground.](playground-screenshot.png) 19 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/A standard Result type.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 3 | 4 | ## A standard Result type 5 | 6 | [SE-0235](https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md) introduces a `Result` type into the standard library, giving us a simpler, clearer way of handling errors in complex code such as asynchronous APIs. 7 | 8 | Swift’s `Result` type is implemented as an enum that has two cases: `success` and `failure`. Both are implemented using generics so they can have an associated value of your choosing, but `failure` must be something that conforms to Swift’s `Error` type. 9 | 10 | To demonstrate `Result`, we could write a function that connects to a server to figure out how many unread messages are waiting for the user. In this example code we’re going to have just one possible error, which is that the requested URL string isn’t a valid URL: 11 | */ 12 | enum NetworkError: Error { 13 | case badURL 14 | } 15 | /*: 16 | The fetching function will accept a URL string as its first parameter, and a completion handler as its second parameter. That completion handler will itself accept a `Result`, where the success case will store an integer, and the failure case will be some sort of `NetworkError`. We’re not actually going to connect to a server here, but using a completion handler at least lets us simulate asynchronous code. 17 | 18 | Here’s the code: 19 | */ 20 | import Foundation 21 | 22 | func fetchUnreadCount1(from urlString: String, completionHandler: @escaping (Result) -> Void) { 23 | guard let url = URL(string: urlString) else { 24 | completionHandler(.failure(.badURL)) 25 | return 26 | } 27 | 28 | // complicated networking code here 29 | print("Fetching \(url.absoluteString)...") 30 | completionHandler(.success(5)) 31 | } 32 | /*: 33 | To use that code we need to check the value inside our `Result` to see whether our call succeeded or failed, like this: 34 | */ 35 | fetchUnreadCount1(from: "https://www.hackingwithswift.com") { result in 36 | switch result { 37 | case .success(let count): 38 | print("\(count) unread messages.") 39 | case .failure(let error): 40 | print(error.localizedDescription) 41 | } 42 | } 43 | /*: 44 | There are three more things you ought to know before you start using `Result` in your own code. 45 | 46 | First, `Result` has a `get()` method that either returns the successful value if it exists, or throws its error otherwise. This allows you to convert `Result` into a regular throwing call, like this: 47 | */ 48 | fetchUnreadCount1(from: "https://www.hackingwithswift.com") { result in 49 | if let count = try? result.get() { 50 | print("\(count) unread messages.") 51 | } 52 | } 53 | /*: 54 | Second, `Result` has an initializer that accepts a throwing closure: if the closure returns a value successfully that gets used for the `success` case, otherwise the thrown error is placed into the `failure` case. 55 | 56 | For example: 57 | */ 58 | let result = Result { try String(contentsOfFile: someFile) } 59 | /*: 60 | Third, rather than using a specific error enum that you’ve created, you can also use the general `Error` protocol. In fact, the Swift Evolution proposal says “it's expected that most uses of Result will use `Swift.Error` as the `Error` type argument.” 61 | 62 | So, rather than using `Result` you could use `Result`. Although this means you lose the safety of typed throws, you gain the ability to throw a variety of different error enums – which you prefer really depends on your coding style. 63 | 64 | 65 | ## Transforming Result 66 | 67 | `Result` has four other methods that may prove useful: `map()`, `flatMap()`, `mapError()`, and `flatMapError()`. Each of these give you the ability to transform either the success or error somehow, and the first two work similarly to the methods of the same name on `Optional`. 68 | 69 | The `map()` method looks inside the `Result`, and transforms the success value into a different kind of value using a closure you specify. However, if it finds failure instead, it just uses that directly and ignores your transformation. 70 | 71 | To demonstrate this, we’re going to write some code that generates random numbers between 0 and a maximum then calculate the factors of that number. If the user requests a random number below zero, or if the number happens to be prime – i.e., it has no factors except itself and 1 – then we’ll consider those to be failures. 72 | 73 | We might start by writing code to model the two possible failure cases: the user has tried to generate a random number below 0, and the number that was generated was prime: 74 | */ 75 | enum FactorError: Error { 76 | case belowMinimum 77 | case isPrime 78 | } 79 | /*: 80 | Next, we’d write a function that accepts a maximum number, and returns either a random number or an error: 81 | */ 82 | func generateRandomNumber(maximum: Int) -> Result { 83 | if maximum < 0 { 84 | // creating a range below 0 will crash, so refuse 85 | return .failure(.belowMinimum) 86 | } else { 87 | let number = Int.random(in: 0...maximum) 88 | return .success(number) 89 | } 90 | } 91 | /* 92 | When that’s called, the `Result` we get back will either be an integer or an error, so we could use `map()` to transform it: 93 | */ 94 | let result1 = generateRandomNumber(maximum: 11) 95 | let stringNumber = result1.map { "The random number is: \($0)." } 96 | /*: 97 | As we’ve passed in a valid maximum number, `result` will be a success with a random number. So, using `map()` will take that random number, use it with our string interpolation, then return another `Result` type, this time of the type `Result`. 98 | 99 | However, if we had used `generateRandomNumber(maximum: -11)` then `result` would be set to the failure case with `FactorError.belowMinimum`. So, using `map()` would still return a `Result`, but it would have the same failure case and same `FactorError.belowMinimum` error. 100 | 101 | Now that you’ve seen how `map()` lets us transform the success type to another type, let’s continue: we have a random number, so the next step is to calculate the factors for it. To do this, we’ll write another function that accepts a number and calculates its factors. If it finds the number is prime it will send back a failure `Result` with the `isPrime` error, otherwise it will send back the number of factors. 102 | 103 | Here’s that in code: 104 | */ 105 | func calculateFactors(for number: Int) -> Result { 106 | let factors = (1...number).filter { number % $0 == 0 } 107 | 108 | if factors.count == 2 { 109 | return .failure(.isPrime) 110 | } else { 111 | return .success(factors.count) 112 | } 113 | } 114 | /*: 115 | If we wanted to use `map()` to transform the output of `generateRandomNumber()` using `calculateFactors()`, it would look like this: 116 | */ 117 | let result2 = generateRandomNumber(maximum: 10) 118 | let mapResult = result2.map { calculateFactors(for: $0) } 119 | /*: 120 | However, that make `mapResult` a rather ugly type: `Result, FactorError>`. It’s a `Result` inside another `Result`. 121 | 122 | Just like with optionals, this is where the `flatMap()` method comes in. If your transform closure returns a `Result`, `flatMap()` will return the new `Result` directly rather than wrapping it in another `Result`: 123 | */ 124 | let flatMapResult = result2.flatMap { calculateFactors(for: $0) } 125 | /*: 126 | So, where `mapResult` was a `Result, FactorError>`, `flatMapResult` is flattened down into `Result` – the first original success value (a random number) was transformed into a new success value (the number of factors). Just like `map()`, if either `Result` was a failure, `flatMapResult` will also be a failure. 127 | 128 | As for `mapError()` and `flatMapError()`, those do similar things except they transform the *error* value rather than the *success* value. 129 | 130 |   131 | 132 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 133 | */ 134 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Checking for integer multiples.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 3 | 4 | ## Checking for integer multiples 5 | 6 | [SE-0225](https://github.com/apple/swift-evolution/blob/master/proposals/0225-binaryinteger-iseven-isodd-ismultiple.md) adds an `isMultiple(of:)` method to integers, allowing us to check whether one number is a multiple of another in a much clearer way than using the division remainder operation, `%`. 7 | 8 | For example: 9 | */ 10 | let rowNumber = 4 11 | 12 | if rowNumber.isMultiple(of: 2) { 13 | print("Even") 14 | } else { 15 | print("Odd") 16 | } 17 | /*: 18 | Yes, we could write the same check using `if rowNumber % 2 == 0` but you have to admit that’s less clear – having `isMultiple(of:)` as a method means it can be listed in code completion options in Xcode, which aids discoverability. 19 | 20 |   21 | 22 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 23 | */ 24 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Customizing string interpolation.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 3 | 4 | ## Customizing string interpolation 5 | 6 | [SE-0228](https://github.com/apple/swift-evolution/blob/master/proposals/0228-fix-expressiblebystringinterpolation.md) dramatically revamped Swift’s string interpolation system so that it’s more efficient and more flexible, and it’s creating a whole new range of features that were previously impossible. 7 | 8 | In its most basic form, the new string interpolation system lets us control how objects appear in strings. Swift has default behavior for structs that is helpful for debugging, because it prints the struct name followed by all its properties. But if you were working with classes (that don’t have this behavior), or wanted to format that output so it could be user-facing, then you could use the new string interpolation system. 9 | 10 | For example, if we had a struct like this: 11 | */ 12 | struct User { 13 | var name: String 14 | var age: Int 15 | } 16 | /*: 17 | If we wanted to add a special string interpolation for that so that we printed users neatly, we would add an extension to `String.StringInterpolation` with a new `appendInterpolation()` method. Swift already has several of these built in, and uses the interpolation *type* – in this case `User` to figure out which method to call. 18 | 19 | In this case, we’re going to add an implementation that puts the user’s name and age into a single string, then calls one of the built-in `appendInterpolation()` methods to add that to our string, like this: 20 | */ 21 | extension String.StringInterpolation { 22 | mutating func appendInterpolation(_ value: User) { 23 | appendInterpolation("My name is \(value.name) and I'm \(value.age)") 24 | } 25 | } 26 | /*: 27 | Now we can create a user and print out their data: 28 | */ 29 | let user = User(name: "Guybrush Threepwood", age: 33) 30 | print("User details: \(user)") 31 | /*: 32 | That will print **User details: My name is Guybrush Threepwood and I'm 33**, whereas with the custom string interpolation it would have printed **User details: User(name: "Guybrush Threepwood", age: 33)**. Of course, that functionality is no different from just implementing the `CustomStringConvertible` protocol, so let’s move on to more advanced usages. 33 | 34 | Your custom interpolation method can take as many parameters as you need, labeled or unlabeled. For example, we could add an interpolation to print numbers using various styles, like this: 35 | */ 36 | import Foundation 37 | 38 | extension String.StringInterpolation { 39 | mutating func appendInterpolation(_ number: Int, style: NumberFormatter.Style) { 40 | let formatter = NumberFormatter() 41 | formatter.numberStyle = style 42 | 43 | if let result = formatter.string(from: number as NSNumber) { 44 | appendLiteral(result) 45 | } 46 | } 47 | } 48 | /*: 49 | The `NumberFormatter` class has a number of styles, including currency ($72.83), ordinal (1st, 12th), and spell out (five, forty-three). So, we could create a random number and have it spelled out into a string like this: 50 | */ 51 | let number = Int.random(in: 0...100) 52 | let lucky = "The lucky number this week is \(number, style: .spellOut)." 53 | print(lucky) 54 | /*: 55 | You can call `appendLiteral()` as many times as you need, or even not at all if necessary. For example, we could add a string interpolation to repeat a string multiple times, like this: 56 | */ 57 | extension String.StringInterpolation { 58 | mutating func appendInterpolation(repeat str: String, _ count: Int) { 59 | for _ in 0 ..< count { 60 | appendLiteral(str) 61 | } 62 | } 63 | } 64 | 65 | print("Baby shark \(repeat: "doo ", 6)") 66 | /*: 67 | And, as these are just regular methods, you can use Swift’s full range of functionality. For example, we might add an interpolation that joins an array of strings together, but if that array is empty execute a closure that returns a string instead: 68 | */ 69 | extension String.StringInterpolation { 70 | mutating func appendInterpolation(_ values: [String], empty defaultValue: @autoclosure () -> String) { 71 | if values.count == 0 { 72 | appendLiteral(defaultValue()) 73 | } else { 74 | appendLiteral(values.joined(separator: ", ")) 75 | } 76 | } 77 | } 78 | 79 | let names = ["Harry", "Ron", "Hermione"] 80 | print("List of students: \(names, empty: "No one").") 81 | /*: 82 | Using `@autoclosure` means that we can use simple values or call complex functions for the default value, but none of that work will be done unless `values.count` is zero. 83 | 84 | With a combination of the `ExpressibleByStringLiteral` and `ExpressibleByStringInterpolation` protocols it’s now possible to create whole types using string interpolation, and if we add `CustomStringConvertible` we can even make those types print as strings however we want. 85 | 86 | To make this work, we need to fulfill some specific criteria: 87 | 88 | - Whatever type we create should conform to `ExpressibleByStringLiteral`, `ExpressibleByStringInterpolation`, and `CustomStringConvertible`. The latter is only needed if you want to customize the way the type is printed. 89 | - *Inside* your type needs to be a nested struct called `StringInterpolation` that conforms to `StringInterpolationProtocol`. 90 | - The nested struct needs to have an initializer that accepts two integers telling us roughly how much data it can expect. 91 | - It also needs to implement an `appendLiteral()` method, as well as one or more `appendInterpolation()` methods. 92 | - Your main type needs to have two initializers that allow it to be created from string literals and string interpolations. 93 | 94 | We can put all that together into an example type that can construct HTML from various common elements. The “scratchpad” inside the nested `StringInterpolation` struct will be a string: each time a new literal or interpolation is added, we’ll append it to the string. To help you see exactly what’s going on, I’ve added some `print()` calls inside the various append methods. 95 | 96 | Here’s the code. 97 | */ 98 | struct HTMLComponent: ExpressibleByStringLiteral, ExpressibleByStringInterpolation, CustomStringConvertible { 99 | struct StringInterpolation: StringInterpolationProtocol { 100 | // start with an empty string 101 | var output = "" 102 | 103 | // allocate enough space to hold twice the amount of literal text 104 | init(literalCapacity: Int, interpolationCount: Int) { 105 | output.reserveCapacity(literalCapacity * 2) 106 | } 107 | 108 | // a hard-coded piece of text – just add it 109 | mutating func appendLiteral(_ literal: String) { 110 | print("Appending \(literal)") 111 | output.append(literal) 112 | } 113 | 114 | // a Twitter username – add it as a link 115 | mutating func appendInterpolation(twitter: String) { 116 | print("Appending \(twitter)") 117 | output.append("@\(twitter)") 118 | } 119 | 120 | // an email address – add it using mailto 121 | mutating func appendInterpolation(email: String) { 122 | print("Appending \(email)") 123 | output.append("\(email)") 124 | } 125 | } 126 | 127 | // the finished text for this whole component 128 | let description: String 129 | 130 | // create an instance from a literal string 131 | init(stringLiteral value: String) { 132 | description = value 133 | } 134 | 135 | // create an instance from an interpolated string 136 | init(stringInterpolation: StringInterpolation) { 137 | description = stringInterpolation.output 138 | } 139 | } 140 | /*: 141 | We can now create and use an instance of `HTMLComponent` using string interpolation like this: 142 | */ 143 | let text: HTMLComponent = "You should follow me on Twitter \(twitter: "twostraws"), or you can email me at \(email: "paul@hackingwithswift.com")." 144 | print(text) 145 | /*: 146 | Thanks to the `print()` calls that were scattered inside, you’ll see exactly how the string interpolation functionality works: you’ll see “Appending You should follow me on Twitter”, “Appending twostraws”, “Appending , or you can email me at “, “Appending paul@hackingwithswift.com”, and finally “Appending .” – each part triggers a method call, and is added to our string. 147 | 148 |   149 | 150 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 151 | */ 152 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Dynamically callable types.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 3 | 4 | ## Dynamically callable types 5 | 6 | [SE-0216](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md) adds a new `@dynamicCallable` attribute to Swift, which brings with it the ability to mark a type as being directly callable. It’s syntactic sugar rather than any sort of compiler magic, effectively `random(numberOfZeroes: 3)` into `random.dynamicallyCall(withKeywordArguments: ["numberOfZeroes": 3])`. 7 | 8 | `@dynamicCallable` is the natural extension of Swift 4.2's `@dynamicMemberLookup`, and serves the same purpose: to make it easier for Swift code to work alongside dynamic languages such as Python and JavaScript. 9 | 10 | To add this functionality to your own types, you need to add the `@dynamicCallable` attribute plus `func dynamicallyCall(withArguments args: [Int]) -> Double` and/or `func dynamicallyCall(withKeywordArguments args: KeyValuePairs) -> Double`. 11 | 12 | The first of those is used when you call the type without parameter labels (e.g. `a(b, c)`), and the second is used when you *do* provide labels (e.g. `a(b: cat, c: dog)`). 13 | 14 | `@dynamicCallable` is really flexible about which data types its methods accept and return, allowing you to benefit from all of Swift’s type safety while still having some wriggle room for advanced usage. So, for the first method (no parameter labels) you can use anything that conforms to `ExpressibleByArrayLiteral` such as arrays, array slices, and sets, and for the second method (with parameter labels) you can use anything that conforms to `ExpressibleByDictionaryLiteral` such as dictionaries and key value pairs. 15 | 16 | As well as accepting a variety of inputs, you can also provide multiple overloads for a variety of outputs – one might return a string, one an integer, and so on. As long as Swift is able to resolve which one is used, you can mix and match all you want. 17 | 18 | Let’s look at an example. Here’s a struct that generates numbers between 0 and a certain maximum, depending on what input was passed in: 19 | */ 20 | import Foundation 21 | 22 | @dynamicCallable 23 | struct RandomNumberGenerator1 { 24 | func dynamicallyCall(withKeywordArguments args: KeyValuePairs) -> Double { 25 | let numberOfZeroes = Double(args.first?.value ?? 0) 26 | let maximum = pow(10, numberOfZeroes) 27 | return Double.random(in: 0...maximum) 28 | } 29 | } 30 | /*: 31 | That method can be called with any number of parameters, or perhaps zero, so we read the first value carefully and use nil coalescing to make sure there’s a sensible default. 32 | 33 | We can now create an instance of `RandomNumberGenerator1` and call it like a function: 34 | */ 35 | let random1 = RandomNumberGenerator1() 36 | let result1 = random1(numberOfZeroes: 0) 37 | /*: 38 | If you had used `dynamicallyCall(withArguments:)` instead – or at the same time, because you can have them both a single type – then you’d write this: 39 | */ 40 | @dynamicCallable 41 | struct RandomNumberGenerator2 { 42 | func dynamicallyCall(withArguments args: [Int]) -> Double { 43 | let numberOfZeroes = Double(args[0]) 44 | let maximum = pow(10, numberOfZeroes) 45 | return Double.random(in: 0...maximum) 46 | } 47 | } 48 | 49 | let random2 = RandomNumberGenerator2() 50 | let result2 = random2(0) 51 | /*: 52 | There are some important rules to be aware of when using `@dynamicCallable`: 53 | 54 | - You can apply it to structs, enums, classes, and protocols. 55 | - If you implement `withKeywordArguments:` and don’t implement `withArguments:`, your type can still be called without parameter labels – you’ll just get empty strings for the keys. 56 | - If your implementations of `withKeywordArguments:` or `withArguments:` are marked as throwing, calling the type will also be throwing. 57 | - You can’t add `@dynamicCallable` to an extension, only the primary definition of a type. 58 | - You can still add other methods and properties to your type, and use them as normal. 59 | 60 | Perhaps more importantly, there is no support for method resolution, which means we must call the type directly (e.g. `random(numberOfZeroes: 5)`) rather than calling specific methods on the type (e.g. `random.generate(numberOfZeroes: 5)`). There is already some discussion on adding the latter using a method signature such as this: `func dynamicallyCallMethod(named: String, withKeywordArguments: KeyValuePairs)`. 61 | 62 | If that became possible in future Swift versions it might open up some very interesting possibilities for test mocking. In the meantime, `@dynamicCallable` is not likely to be widely popular, but it *is* hugely important for a small number of people who want interactivity with Python, JavaScript, and other languages. 63 | 64 |   65 | 66 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 67 | */ 68 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Flattening nested optionals resulting from try?.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 3 | 4 | ## Flattening nested optionals resulting from try? 5 | 6 | [SE-0230](https://github.com/apple/swift-evolution/blob/master/proposals/0230-flatten-optional-try.md) modifies the way `try?` works so that nested optionals are flattened to become regular optionals. This makes it work the same way as optional chaining and conditional typecasts, both of which flatten optionals in earlier Swift versions. 7 | 8 | Here’s a practical example that demonstrates the change: 9 | */ 10 | struct User { 11 | var id: Int 12 | 13 | init?(id: Int) { 14 | if id < 1 { 15 | return nil 16 | } 17 | 18 | self.id = id 19 | } 20 | 21 | func getMessages() throws -> String { 22 | // complicated code here 23 | return "No messages" 24 | } 25 | } 26 | 27 | let user = User(id: 1) 28 | let messages = try? user?.getMessages() 29 | /*: 30 | The `User` struct has a failable initializer, because we want to make sure folks create users with a valid ID. The `getMessages()` method would in theory contain some sort of complicated code to get a list of all the messages for the user, so it’s marked as `throws`; I’ve made it return a fixed string so the code compiles. 31 | 32 | The key line is the last one: because the user is optional it uses optional chaining, and because `getMessages()` can throw it uses `try?` to convert the throwing method into an optional, so we end up with a nested optional. In Swift 4.2 and earlier this would make `messages` a `String??` – an optional optional string – but in Swift 5.0 and later `try?` won’t wrap values in an optional if they are already optional, so `messages` will just be a `String?`. 33 | 34 | This new behavior matches the existing behavior of optional chaining and conditional typecasting. That is, you could use optional chaining a dozen times in a single line of code if you wanted, but you wouldn’t end up with 12 nested optionals. Similarly, if you used optional chaining with `as?`, you would still end up with only one level of optionality, because that’s usually what you want. 35 | 36 |   37 | 38 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 39 | */ 40 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Handling future enum cases.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 3 | 4 | ## Handling future enum cases 5 | 6 | [SE-0192](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md) adds the ability to distinguish between enums that are fixed and enums that might change in the future. 7 | 8 | One of Swift’s security features is that it requires all switch statements to be exhaustive – that they must cover all cases. While this works well from a safety perspective, it causes compatibility issues when new cases are added in the future: a system framework might send something different that you hadn’t catered for, or code you rely on might add a new case and cause your compile to break because your switch is no longer exhaustive. 9 | 10 | With the `@unknown` attribute we can now distinguish between two subtly different scenarios: “this default case should be run for all other cases because I don’t want to handle them individually,” and “I want to handle all cases individually, but if anything comes up in the future use this rather than causing an error.” 11 | 12 | Here’s an example enum: 13 | */ 14 | enum PasswordError: Error { 15 | case short 16 | case obvious 17 | case simple 18 | } 19 | /*: 20 | We could write code to handle each of those cases using a `switch` block: 21 | */ 22 | func showOld(error: PasswordError) { 23 | switch error { 24 | case .short: 25 | print("Your password was too short.") 26 | case .obvious: 27 | print("Your password was too obvious.") 28 | default: 29 | print("Your password was too simple.") 30 | } 31 | } 32 | /*: 33 | That uses two explicit cases for short and obvious passwords, but bundles the third case into a default block. 34 | 35 | Now, if in the future we added a new case to the enum called `old`, for passwords that had been used previously, our `default` case would automatically be called even though its message doesn’t really make sense – the password might not be too simple. 36 | 37 | Swift can’t warn us about this code because it’s technically correct (the best kind of correct), so this mistake would easily be missed. Fortunately, the new `@unknown` attribute fixes it perfectly – it can be used only on the `default` case, and is designed to be run when new cases come along in the future. 38 | 39 | For example: 40 | */ 41 | func showNew(error: PasswordError) { 42 | switch error { 43 | case .short: 44 | print("Your password was too short.") 45 | case .obvious: 46 | print("Your password was too obvious.") 47 | @unknown default: 48 | print("Your password wasn't suitable.") 49 | } 50 | } 51 | /*: 52 | That code will now issue warnings because the `switch` block is no longer exhaustive – Swift wants us to handle each case explicitly. Helpfully this is only a *warning*, which is what makes this attribute so useful: if a framework adds a new case in the future you’ll be warned about it, but it won’t break your source code. 53 | 54 |   55 | 56 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 57 | */ 58 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Introduction.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | # What’s new in Swift 5.0 3 | 4 | * Created by [Paul Hudson](https://twitter.com/twostraws) – [Hacking with Swift](https://www.hackingwithswift.com) 5 | * Last update: March 27th 2019 6 | 7 | This playground is designed to showcase new features introduced with Swift 5.0. I already [wrote an article on it](https://www.hackingwithswift.com/articles/126/whats-new-in-swift-5-0), but it's a lot more fun to see things in action and experiment yourself. 8 | 9 | If you hit problems or have questions, you're welcome to tweet me [@twostraws](https://twitter.com/twostraws) or email . 10 | 11 |   12 | 13 | ## Contents 14 | 15 | * [Raw strings](Raw%20strings) 16 | * [A standard Result type](A%20standard%20Result%20type) 17 | * [Customizing string interpolation](Customizing%20string%20interpolation) 18 | * [Dynamically callable types](Dynamically%20callable%20types) 19 | * [Handling future enum cases](Handling%20future%20enum%20cases) 20 | * [Flattening nested optionals resulting from try?](Flattening%20nested%20optionals%20resulting%20from%20try%3F) 21 | * [Checking for integer multiples](Checking%20for%20integer%20multiples) 22 | * [Transforming and unwrapping dictionary values with compactMapValues()](Transforming%20and%20unwrapping%20dictionary%20values) 23 | 24 |   25 | 26 | [Next >](@next) 27 | */ 28 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Raw strings.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 3 | 4 | ## Raw strings 5 | 6 | [SE-0200](https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md) added the ability to create raw strings, where backslashes and quote marks are interpreted as those literal symbols rather than escapes characters or string terminators. This makes a number of use cases more easy, but regular expressions in particular will benefit. 7 | 8 | To use raw strings, place one or more `#` symbols before your strings, like this: 9 | */ 10 | let rain = #"The "rain" in "Spain" falls mainly on the Spaniards."# 11 | /*: 12 | The `#` symbols at the start and end of the string become part of the string delimiter, so Swift understands that the standalone quote marks around “rain” and “Spain” should be treated as literal quote marks rather than ending the string. 13 | 14 | Raw strings allow you to use backslashes too: 15 | */ 16 | let keypaths = #"Swift keypaths such as \Person.name hold uninvoked references to properties."# 17 | /*: 18 | That treats the backslash as being a literal character in the string, rather than an escape character. This in turn means that string interpolation works differently: 19 | */ 20 | let answer = 42 21 | let dontpanic = #"The answer to life, the universe, and everything is \#(answer)."# 22 | /*: 23 | Notice how I’ve used `\#(answer)` to use string interpolation – a regular `\(answer)` will be interpreted as characters in the string, so when you want string interpolation to happen in a raw string you must add the extra `#`. 24 | 25 | One of the interesting features of Swift’s raw strings is the use of hash symbols at the start and end, because you can use more than one in the unlikely event you’ll need to. It’s hard to provide a good example here because it really ought to be extremely rare, but consider this string: **My dog said "woof"#gooddog**. Because there’s no space before the hash, Swift will see `"#` and immediately interpret it as the string terminator. In this situation we need to change our delimiter from `#"` to `##"`, like this: 26 | */ 27 | let str = ##"My dog said "woof"#gooddog"## 28 | /*: 29 | Notice how the number of hashes at the end must match the number at the start. 30 | 31 | Raw strings are fully compatible with Swift’s multi-line string system – just use `#"""` to start, then `"""#` to end, like this: 32 | */ 33 | let multiline = #""" 34 | The answer to life, 35 | the universe, 36 | and everything is \#(answer). 37 | """# 38 | /*: 39 | Being able to do without lots of backslashes will prove particularly useful in regular expressions. For example, writing a simple regex to find keypaths such as `\Person.name` used to look like this: 40 | */ 41 | let regex1 = "\\\\[A-Z]+[A-Za-z]+\\.[a-z]+" 42 | /*: 43 | Thanks to raw strings we can write the same thing with half the number of backslashes: 44 | */ 45 | let regex2 = #"\\[A-Z]+[A-Za-z]+\.[a-z]+"# 46 | /*: 47 | We still need *some*, because regular expressions use them too. 48 | 49 |   50 | 51 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 52 | */ 53 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/Pages/Transforming and unwrapping dictionary values.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [< Previous](@previous)           [Home](Introduction) 3 | 4 | ## Transforming and unwrapping dictionary values with compactMapValues() 5 | 6 | [SE-0218](https://github.com/apple/swift-evolution/blob/master/proposals/0218-introduce-compact-map-values.md) adds a new `compactMapValues()` method to dictionaries, bringing together the `compactMap()` functionality from arrays (“transform my values, unwrap the results, then discard anything that’s nil”) with the `mapValues()` method from dictionaries (“leave my keys intact but transform my values”). 7 | 8 | As an example, here’s a dictionary of people in a race, along with the times they took to finish in seconds. One person did not finish, marked as “DNF”: 9 | */ 10 | let times = [ 11 | "Hudson": "38", 12 | "Clarke": "42", 13 | "Robinson": "35", 14 | "Hartis": "DNF" 15 | ] 16 | /*: 17 | We can use `compactMapValues()` to create a new dictionary with names and times as an integer, with the one DNF person removed: 18 | */ 19 | let finishers1 = times.compactMapValues { Int($0) } 20 | /*: 21 | Alternatively, you could just pass the `Int` initializer directly to `compactMapValues()`, like this: 22 | */ 23 | let finishers2 = times.compactMapValues(Int.init) 24 | /*: 25 | You can also use `compactMapValues()` to unwrap optionals and discard nil values without performing any sort of transformation, like this: 26 | */ 27 | let people = [ 28 | "Paul": 38, 29 | "Sophie": 8, 30 | "Charlotte": 5, 31 | "William": nil 32 | ] 33 | 34 | let knownAges = people.compactMapValues { $0 } 35 | /*: 36 | [< Previous](@previous)           [Home](Introduction) 37 | */ 38 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-0.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twostraws/whats-new-in-swift-5-0/86917017202eb47b10664d7b537cff09076db260/playground-screenshot.png --------------------------------------------------------------------------------