├── playground-screenshot.png ├── Whats-new-in-Swift-4.2.playground ├── playground.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Pages │ ├── Bool.toggle.xcplaygroundpage │ │ └── Contents.swift │ ├── MemoryLayout.offset.xcplaygroundpage │ │ └── Contents.swift │ ├── Immutable withUnsafePointer.xcplaygroundpage │ │ └── Contents.swift │ ├── #error and #warning.xcplaygroundpage │ │ └── Contents.swift │ ├── Implicitly unwrapped optionals.xcplaygroundpage │ │ └── Contents.swift │ ├── #if compiler version directive.xcplaygroundpage │ │ └── Contents.swift │ ├── Enumerating enum cases.xcplaygroundpage │ │ └── Contents.swift │ ├── Table of contents.xcplaygroundpage │ │ └── Contents.swift │ ├── guard let self = self.xcplaygroundpage │ │ └── Contents.swift │ ├── Ranges.xcplaygroundpage │ │ └── Contents.swift │ ├── inlinable.xcplaygroundpage │ │ └── Contents.swift │ ├── Conditional conformance.xcplaygroundpage │ │ └── Contents.swift │ ├── Sequence and Collection algorithms.xcplaygroundpage │ │ └── Contents.swift │ ├── Hashable redesign.xcplaygroundpage │ │ └── Contents.swift │ ├── Dynamic member lookup.xcplaygroundpage │ │ └── Contents.swift │ └── Random numbers.xcplaygroundpage │ │ └── Contents.swift └── contents.xcplayground ├── Whats-new-in-Swift-4.2.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── README.md /playground-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ole/whats-new-in-swift-4-2/HEAD/playground-screenshot.png -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What’s new in Swift 4.2 2 | 3 | An Xcode playground demonstrating the new features in Swift 4.2. 4 | 5 | Written by [Ole Begemann](https://oleb.net), June–September 2018. 6 | 7 | The playground requires Swift 4.2, which is part of Xcode 10. 8 | 9 | See also: my [What’s new in Swift 4.0 playground](https://github.com/ole/whats-new-in-swift-4) from 2017. 10 | 11 | ![Screenshot of the playground](playground-screenshot.png) 12 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Bool.toggle.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Bool.toggle 5 | 6 | [SE-0199](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md "Adding toggle to Bool") adds a mutating `toggle` method to `Bool`. 7 | 8 | This is especially useful if you need to toggle a boolean value deep inside a nested data structure because you don’t have to repeat the same expression on both sides of the assignment. 9 | */ 10 | struct Layer { 11 | var isHidden = false 12 | } 13 | 14 | struct View { 15 | var layer = Layer() 16 | } 17 | 18 | var view = View() 19 | 20 | // Before: 21 | view.layer.isHidden = !view.layer.isHidden 22 | view.layer.isHidden 23 | 24 | // Now: 25 | view.layer.isHidden.toggle() 26 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/MemoryLayout.offset.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # `MemoryLayout.offset(of:)` 5 | 6 | [SE-0210](https://github.com/apple/swift-evolution/blob/master/proposals/0210-key-path-offset.md "Add an offset(of:) method to MemoryLayout") adds an `offset(of:)` method to the `MemoryLayout` type, complementing the existing APIs for getting a type’s size, stride, and alignment. 7 | 8 | The `offset(of:)` method takes a key path to a type’s stored property and returns the property’s byte offset. An example where this is useful is passing an array of interleaved pixel values to a graphics API. 9 | */ 10 | struct Point { 11 | var x: Float 12 | var y: Float 13 | var z: Float 14 | } 15 | 16 | MemoryLayout.offset(of: \Point.z) 17 | 18 | /*: 19 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 20 | */ 21 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Immutable withUnsafePointer.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) 3 | 4 | # Calling `withUnsafePointer(to:_:)` and `withUnsafeBytes(of:_:)` with immutable values 5 | 6 | This is a small thing, but if you ever had to use the top-level functions `withUnsafePointer(to:_:)` and `withUnsafeBytes(of:_:)`, you may have noticed that they required their argument to be a mutable value because the parameter was `inout`. 7 | 8 | [SE-0205](https://github.com/apple/swift-evolution/blob/master/proposals/0205-withUnsafePointer-for-lets.md "withUnsafePointer(to:_:) and withUnsafeBytes(of:_:) for immutable values") adds overloads that work with immutable values. 9 | */ 10 | let x: UInt16 = 0xabcd 11 | let (firstByte, secondByte) = withUnsafeBytes(of: x) { ptr in 12 | (ptr[0], ptr[1]) 13 | } 14 | String(firstByte, radix: 16) 15 | String(secondByte, radix: 16) 16 | 17 | /*: 18 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) 19 | */ 20 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/#error and #warning.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # `#error` and `#warning` directives 5 | 6 | [SE-0196](https://github.com/apple/swift-evolution/blob/master/proposals/0196-diagnostic-directives.md "Compiler Diagnostic Directives") introduces `#error` and `#warning` directives for triggering a build error or warning in your source code. 7 | 8 | For example, use `#warning` to remember an important TODO before committing your code: 9 | */ 10 | func doSomethingImportant() { 11 | #warning("TODO: missing implementation") 12 | } 13 | doSomethingImportant() 14 | 15 | /*: 16 | `#error` can be helpful if your code doesn’t support certain environments: 17 | */ 18 | #if canImport(UIKit) 19 | // ... 20 | #elseif canImport(AppKit) 21 | // ... 22 | #else 23 | #error("This playground requires UIKit or AppKit") 24 | #endif 25 | 26 | /*: 27 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 28 | */ 29 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Implicitly unwrapped optionals.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Implicitly unwrapped optionals 5 | 6 | [SE-0054](https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md "Abolish ImplicitlyUnwrappedOptional type") was accepted already in March 2016, but it took until Swift 4.2 to implement it completely. 7 | 8 | In Swift 4.2, [implicitly unwrapped optionals](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID334) still exist — that is, you can annotate a type declaration with a `!` instead of a `?` to declare an optional value that will be unwrapped automatically. But there isn’t a separate `ImplicitlyUnwrappedOptional` type anymore. 9 | 10 | Instead, implicitly unwrapped optionals are just normal optionals (and have the type `Optional`) with a special annotation that tells the compiler to automatically add a force-unwrap when needed. 11 | 12 | There is a great article on the official Swift blog that goes into much more detail about the implications of this change: [Reimplementation of Implicitly Unwrapped Optionals](https://swift.org/blog/iuo/). 13 | */ 14 | 15 | /*: 16 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 17 | */ 18 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/#if compiler version directive.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # `#if compiler` version directive 5 | 6 | [SE-0212](https://github.com/apple/swift-evolution/blob/master/proposals/0212-compiler-version-directive.md "Compiler Version Directive") introduces a `compiler` directive for use in compile-time conditionals using `#if`. 7 | 8 | It has the same syntax as the existing `#if swift(>=4.2)` syntax, but `#if compiler` checks against the actual _compiler version_, regardless of which compatibility mode it’s running in, whereas `#if swift` is a _language version_ check. 9 | 10 | For example, `#if swift(>=4.2)` would be false when running Swift 4.2 in Swift-4.0 compatibility mode, but `#if compiler(>=4.2)` would be true in this case. The proposal has many more examples how complicated some `#if swift` checks can become and how the `compiler` directive simplifies them. 11 | */ 12 | 13 | #if compiler(>=4.2) 14 | print("Using the Swift 4.2 compiler or greater in any compatibility mode") 15 | #endif 16 | 17 | #if swift(>=4.2) 18 | print("Using the Swift 4.2 compiler or greater in Swift 4.2 or greater compatibility mode") 19 | #endif 20 | 21 | #if compiler(>=5.0) 22 | print("Using the Swift 5.0 compiler or greater in any compatibility mode") 23 | #endif 24 | 25 | /*: 26 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 27 | */ 28 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Enumerating enum cases.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Enumerating enum cases 5 | 6 | [SE-0194 — Derived Collection of Enum Cases](https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md "Derived Collection of Enum Cases"): The compiler can automatically generate an `allCases` property for enums, providing you with an always-up-to-date list of enum cases. All you have to do is conform your enum to the new `CaseIterable` protocol. 7 | */ 8 | enum Terrain: CaseIterable { 9 | case water 10 | case forest 11 | case desert 12 | case road 13 | } 14 | 15 | Terrain.allCases 16 | Terrain.allCases.count 17 | 18 | /*: 19 | Note that the automatic synthesis only works for enums without associated values — because associated values mean an enum can have a potentially infinite number of possible values. 20 | 21 | You can always implement the protocol manually if the list of all possible values is finite. As an example, here’s a conditional conformance for Optionals of types that are themselves `CaseIterable`: 22 | */ 23 | extension Optional: CaseIterable where Wrapped: CaseIterable { 24 | public typealias AllCases = [Wrapped?] 25 | public static var allCases: AllCases { 26 | return Wrapped.allCases.map { $0 } + [nil] 27 | } 28 | } 29 | 30 | // Note: this isn’t optional chaining! 31 | // We’re accessing a member of the Optional type. 32 | Terrain?.allCases 33 | Terrain?.allCases.count 34 | 35 | /*: 36 | (This is a fun experiment, but I doubt an implementation like this would be very useful in practice. Handle with care.) 37 | 38 | I wrote an article about `CaseIterable` that does into more detail: [Enumerating enum cases in Swift](https://oleb.net/blog/2018/06/enumerating-enum-cases/). 39 | */ 40 | /*: 41 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 42 | */ 43 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Table of contents.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | # What’s new in Swift 4.2 3 | 4 | By [Ole Begemann](https://oleb.net) • June–September 2018\ 5 | [Source on GitHub](https://github.com/ole/whats-new-in-swift-4-2) 6 | 7 | ## Contents 8 | 9 | 1. [`Bool.toggle`](Bool.toggle) 10 | 1. [Sequence and Collection algorithms](Sequence%20and%20Collection%20algorithms) 11 | 1. [Enumerating enum cases](Enumerating%20enum%20cases) 12 | 1. [Random numbers](Random%20numbers) 13 | 1. [Hashable redesign](Hashable%20redesign) 14 | 1. [Conditional conformance enhancements](Conditional%20conformance) 15 | 1. [Removal of `CountableRange` and `CountableClosedRange`](Ranges) 16 | 1. [Dynamic member lookup](Dynamic%20member%20lookup) 17 | 1. [Implicitly unwrapped optionals](Implicitly%20unwrapped%20optionals) 18 | 1. [guard let self = self](guard%20let%20self%20=%20self) 19 | 1. [`#error` and `#warning`](%23error%20and%20%23warning) 20 | 1. [`#if compiler` version directive](%23if%20compiler%20version%20directive) 21 | 1. [`MemoryLayout.offset(of:)`](MemoryLayout.offset) 22 | 1. [`@inlinable`](inlinable) 23 | 1. [Immutable `withUnsafePointer`](Immutable%20withUnsafePointer) 24 | 25 | 26 | ## Requirements 27 | 28 | This playground requires Swift 4.2, which ships with Xcode 10. Download Xcode 10 [from Apple’s Developer Site](https://developer.apple.com/download/) or [from the Mac App Store](https://itunes.apple.com/us/app/xcode/id497799835?mt=12), or download a Swift 4.2 snapshot [from swift.org](https://swift.org/download/#snapshots). 29 | 30 | ## Note 31 | 32 | This playground focuses on user-facing new features that can easily be demonstrated in a playground. Swift 4.2 ships with more changes, such as new Swift Package Manager features and many under-the-hood improvements. 33 | 34 | Check out [the full changelog](https://github.com/apple/swift/blob/master/CHANGELOG.md), as well as [the complete list of proposals implemented for Swift 4.2](https://apple.github.io/swift-evolution/#?version=4.2) (which lists some proposals not mentioned in the changelog). 35 | */ 36 | /*: 37 | [Next page](@next) 38 | */ 39 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/guard let self = self.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # `guard let self = self` 5 | 6 | [SE-0079](https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md "Allow using optional binding to upgrade self from a weak to strong reference") is another proposal that was originally accepted for Swift 3.0 but took a while to get implemented. 7 | 8 | It allows rebinding `self` from a weak (and optional) variable to a strong variable by using optional binding. That, is, you can now write `if let self = self { … }` or `guard let self = self else { … }` in a closure expression that has captured `self` weakly to conditionally rebind `self` to a strong variable within the scope of the `if let` or `guard let` statement. 9 | */ 10 | 11 | import Dispatch 12 | 13 | struct Book { 14 | var title: String 15 | var author: String 16 | } 17 | 18 | func loadBooks(completion: @escaping ([Book]) -> Void) { 19 | DispatchQueue.global().asyncAfter(deadline: .now() + 1) { 20 | DispatchQueue.main.async { 21 | completion([ 22 | Book(title: "Harry Potter and the Deathly Hallows", author: "JK Rowling"), 23 | Book(title: "Pippi Långstrump", author: "Astrid Lindgren")]) 24 | } 25 | } 26 | } 27 | 28 | class ViewController { 29 | var items: [Book] = [] 30 | 31 | func viewDidLoad() { 32 | loadBooks { [weak self] books in 33 | // This is now allowed 34 | guard let self = self else { 35 | return 36 | } 37 | self.items = books 38 | self.updateUI() 39 | } 40 | } 41 | 42 | func updateUI() { 43 | // ... 44 | } 45 | } 46 | 47 | /*: 48 | In previous Swift versions, it was possible to rebind `self` by wrapping the name in backticks, and many developers favored this over coming up with a new name, such as `strongSelf`. However, the fact that this worked was [a bug, not a feature](https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md#relying-on-a-compiler-bug). 49 | */ 50 | /*: 51 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 52 | */ 53 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Ranges.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Removal of `CountableRange` and `CountableClosedRange` 5 | 6 | The introduction of conditional protocol conformances ([SE-0143](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md "Conditional conformances")) in Swift 4.1 allowed the standard library to eliminate a ton of types that were formerly required but whose functionality can now be expressed as constrained extensions on a base type. 7 | 8 | For example, the functionality of `MutableSlice` is now incporporated by a “normal” `Slice`, together with an `extension Slice: MutableCollection where Base: MutableCollection`. 9 | 10 | Swift 4.2 introduces a similar consolidation for ranges. The formerly concrete types `CountableRange` and `CountableClosedRange` have been removed in favor of conditional conformances for `Range` and `ClosedRange`. 11 | 12 | The purpose of the countable range types was to allow a range to be a `Collection` if its underlying element type was _countable_ (i.e., it conformed to the `Strideable` protocol and used signed integers for striding). For example, range of integers can be collections, but ranges of floating-point numbers can't 13 | */ 14 | 15 | let integerRange: Range = 0..<5 16 | // We can map over a range of integers because it's a Collection 17 | let integerStrings = integerRange.map { String($0) } 18 | integerStrings 19 | 20 | let floatRange: Range = 0.0..<5.0 21 | // But this is an error because a range of Doubles is not a Collection 22 | //floatRange.map { String($0) } // error! 23 | 24 | /*: 25 | The names `CountableRange` and `CountableClosedRange` still exist; they have been converted to typealiases for source compatibility. You shouldn’t use them in new code anymore. 26 | 27 | The distinction between half-open `Range` and closed ranges `ClosedRange` still exists on the type system level because it cannot be eliminated so easily. A `ClosedRange` can never be empty and a `Range` can never contain the maximum value of its element type (e.g. `Int.max`). Moreover, the difference is important for non-countable types: it’s not trivial to rewrite a range like `0.0...5.0` into an equivalent half-open range. 28 | */ 29 | /*: 30 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 31 | */ 32 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/inlinable.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # `@inlinable` 5 | 6 | [SE-0193](https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md "Cross-module inlining and specialization") introduces two new attributes, `@inlinable` and `@usableFromInline`. 7 | 8 | These aren’t necessary for application code. Library authors can annotate some public functions as `@inlinable`. This gives the compiler the option to optimize generic code across module boundaries. 9 | 10 | For example, a library that provides a set of collection algorithms could mark those methods as `@inlinable` to allow the compiler to specialize client code that uses these algorithms with types that are unknown when the library is built. 11 | 12 | Example (adopted from the example given in SE-0193): 13 | */ 14 | // Inside CollectionAlgorithms module: 15 | extension Sequence where Element: Equatable { 16 | /// Returns `true` iff all elements in the sequence are equal. 17 | @inlinable 18 | public func allEqual() -> Bool { 19 | var iterator = makeIterator() 20 | guard let first = iterator.next() else { 21 | return true 22 | } 23 | while let next = iterator.next() { 24 | if first != next { 25 | return false 26 | } 27 | } 28 | return true 29 | } 30 | } 31 | 32 | [1,1,1,1,1].allEqual() 33 | Array(repeating: 42, count: 1000).allEqual() 34 | [1,1,2,1,1].allEqual() 35 | 36 | /*: 37 | Think carefully before you make a function inlinable. Using `@inlinable` effectively makes the body of the function part of your library’s public interface. If you later change the implementation (e.g. to fix a bug), binaries compiled against the old version might continue to use the old (inlined) code, or even a mix of old and new (because `@inlinable` is only a hint; the optimizer decides for each call site whether to inline the code or not). 38 | 39 | Because inlinable functions can be emitted into the client binary, they are not allowed to reference declarations that are not visible to the client binary. You can use the `@usableFromInline` annotation to make certain internal declarations in your library “ABI-public”, allowing their use in inlinable functions. 40 | */ 41 | /*: 42 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 43 | */ 44 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Conditional conformance.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Conditional conformance enhancements 5 | 6 | ## Dynamic casts 7 | 8 | Conditional protocol conformances ([SE-0143](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md "Conditional conformances")) were the headline feature of Swift 4.1. The final piece of the proposal, runtime querying of conditional conformances, has landed in Swift 4.2. This means a dynamic cast to a protocol type (using `is` or `as?`), where the value conditionally conforms to the protocol, will now succeed when the conditional requirements are met. 9 | 10 | Example: 11 | */ 12 | func isEncodable(_ value: Any) -> Bool { 13 | return value is Encodable 14 | } 15 | 16 | // This would return false in Swift 4.1 17 | let encodableArray = [1, 2, 3] 18 | isEncodable(encodableArray) 19 | 20 | // Verify that the dynamic check doesn't succeed when the conditional conformance criteria aren't met. 21 | struct NonEncodable {} 22 | let nonEncodableArray = [NonEncodable(), NonEncodable()] 23 | assert(isEncodable(nonEncodableArray) == false) 24 | 25 | /*: 26 | ## Synthesized conformances in extensions 27 | 28 | A small but important improvement to compiler synthesized protocol conformances, such as the automatic `Equatable` and `Hashable` conformances introduced in [SE-0185](https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md "Synthesizing Equatable and Hashable conformance"). 29 | 30 | Protocol conformances can now be synthesized in extensions and not only on the type definition (the extension must still be in the same file as the type definition). This is more than a cosmetic change because it allows automatic synthesis of conditional conformances to `Equatable`, `Hashable`, `Encodable`, and `Decodable`. 31 | 32 | This example is from the [What’s New in Swift session at WWDC 2018](https://developer.apple.com/videos/play/wwdc2018/401/). We can conditionally conform `Either` to `Equatable` and `Hashable`: 33 | */ 34 | enum Either { 35 | case left(Left) 36 | case right(Right) 37 | } 38 | 39 | // No code necessary 40 | extension Either: Equatable where Left: Equatable, Right: Equatable {} 41 | extension Either: Hashable where Left: Hashable, Right: Hashable {} 42 | 43 | Either.left(42) == Either.left(42) 44 | 45 | /*: 46 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 47 | */ 48 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Sequence and Collection algorithms.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Sequence and Collection algorithms 5 | 6 | ## `allSatisfy` 7 | 8 | [SE-0207](https://github.com/apple/swift-evolution/blob/master/proposals/0207-containsOnly.md "Add an allSatisfy algorithm to Sequence") adds an `allSatisfy` algorithm to `Sequence`. `allSatisfy` returns `true` if and only if all elements in the sequence satisfy the given predicate. This function is often just called `all` in functional languages. 9 | 10 | `allSatisfy` nicely complements `contains(where:)`, which can be used to test if any element (or none) satisfies a predicate. 11 | */ 12 | let digits = 0...9 13 | 14 | let areAllSmallerThanTen = digits.allSatisfy { $0 < 10 } 15 | areAllSmallerThanTen 16 | 17 | let areAllEven = digits.allSatisfy { $0 % 2 == 0 } 18 | areAllEven 19 | 20 | /*: 21 | ## `last(where:)`, `lastIndex(where:)`, and `lastIndex(of:)` 22 | 23 | [SE-0204](https://github.com/apple/swift-evolution/blob/master/proposals/0204-add-last-methods.md "Add last(where:) and lastIndex(where:) Methods") adds a `last(where:)` method to `Sequence`, and `lastIndex(where:)` and `lastIndex(of:)` methods to `Collection`. 24 | */ 25 | let lastEvenDigit = digits.last { $0 % 2 == 0 } 26 | lastEvenDigit 27 | 28 | let text = "Vamos a la playa" 29 | 30 | let lastWordBreak = text.lastIndex(where: { $0 == " " }) 31 | let lastWord = lastWordBreak.map { text[text.index(after: $0)...] } 32 | lastWord 33 | 34 | text.lastIndex(of: " ") == lastWordBreak 35 | 36 | /*: 37 | ### Rename `index(of:)` and `index(where:)` to `firstIndex(of:)` and `firstIndex(where:)` 38 | 39 | For consistency, SE-0204 also renames `index(of:)` and `index(where:)` to `firstIndex(of:)` and `firstIndex(where:)`. 40 | 41 | */ 42 | let firstWordBreak = text.firstIndex(where: { $0 == " " }) 43 | let firstWord = firstWordBreak.map { text[..<$0] } 44 | firstWord 45 | 46 | /*: 47 | ## `removeAll(where:)` 48 | 49 | [SE-0197](https://github.com/apple/swift-evolution/blob/master/proposals/0197-remove-where.md "Adding in-place removeAll(where:) to the Standard Library") adds a `removeAll(where:)` method to `RangeReplaceableCollection`, allowing you to remove all elements from a collection that match the given predicate. 50 | 51 | It’s essentially a mutating variant of `filter` with an inverted predicate — `filter` _keeps_ elements that match the predicate, whereas `removeAll(where:)` _removes_ them. 52 | */ 53 | 54 | var numbers = Array(1...10) 55 | numbers.removeAll(where: { $0 % 2 != 0 }) 56 | numbers 57 | 58 | /*: 59 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 60 | */ 61 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Hashable redesign.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # `Hashable` redesign 5 | 6 | Compiler-synthesized `Equatable` and `Hashable` conformances, introduced in Swift 4.1 ([SE-0185](https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md "Synthesizing Equatable and Hashable conformance")), dramatically reduce the number of manual `Hashable` implementations you have to write. 7 | 8 | But if you need to customize a type’s `Hashable` conformance, the redesign of the `Hashable` protocol ([SE-0206](https://github.com/apple/swift-evolution/blob/master/proposals/0206-hashable-enhancements.md "Hashable Enhancements")) makes this task much easier. 9 | 10 | In the new `Hashable` world, instead of implementing `hashValue`, you now implement the `hash(into:)` method. This method provides a `Hasher` object, and all you have to do in your implementation is feed it the values you want to include in your hash value by repeatedly calling `hasher.combine(_:)`. 11 | 12 | The advantage over the old way is that you don’t have to come up with your own algorithm for combining the hash values your type is composed of. The hash function provided by the standard library (in the form of `Hasher`) is almost certainly better and more secure than anything most of us would write. 13 | 14 | As an example, here is a type with one stored property that acts as a cache for an expensive computation. We should ignore the value of `distanceFromOrigin` in our `Equatable` and `Hashable` implementations: 15 | */ 16 | struct Point { 17 | var x: Int { didSet { recomputeDistance() } } 18 | var y: Int { didSet { recomputeDistance() } } 19 | 20 | /// Cached. Should be ignored by Equatable and Hashable. 21 | private(set) var distanceFromOrigin: Double 22 | 23 | init(x: Int, y: Int) { 24 | self.x = x 25 | self.y = y 26 | self.distanceFromOrigin = Point.distanceFromOrigin(x: x, y: y) 27 | } 28 | 29 | private mutating func recomputeDistance() { 30 | distanceFromOrigin = Point.distanceFromOrigin(x: x, y: y) 31 | } 32 | 33 | private static func distanceFromOrigin(x: Int, y: Int) -> Double { 34 | return Double(x * x + y * y).squareRoot() 35 | } 36 | } 37 | 38 | extension Point: Equatable { 39 | static func ==(lhs: Point, rhs: Point) -> Bool { 40 | // Ignore distanceFromOrigin for determining equality 41 | return lhs.x == rhs.x && lhs.y == rhs.y 42 | } 43 | } 44 | 45 | /*: 46 | In our `hash(into:)` implementation, all we need to do is feed the relevant properties to the hasher. 47 | 48 | This is easier (and more efficient) than coming up with your own hash combining function. For example, a naive implementation for `hashValue` might XOR the two coordinates: `return x ^ y`. This would be a less efficient hash function because `Point(3, 4)` and `Point(4, 3)` would end up with the same hash value. 49 | */ 50 | extension Point: Hashable { 51 | func hash(into hasher: inout Hasher) { 52 | // Ignore distanceFromOrigin for hashing 53 | hasher.combine(x) 54 | hasher.combine(y) 55 | } 56 | } 57 | 58 | let p1 = Point(x: 3, y: 4) 59 | p1.hashValue 60 | let p2 = Point(x: 4, y: 3) 61 | p2.hashValue 62 | assert(p1.hashValue != p2.hashValue) 63 | 64 | /*: 65 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 66 | */ 67 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Dynamic member lookup.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Dynamic member lookup 5 | 6 | [SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md "Introduce User-defined 'Dynamic Member Lookup' Types") introduces the `@dynamicMemberLookup` attribute for type declarations. 7 | 8 | A variable of a `@dynamicMemberLookup` type can be called with _any_ property-style accessor (using dot notation) — the compiler won’t check if a member with the given name exists or not. Instead, the compiler translates such accesses into calls of a subscript accessor that gets passed the member name as a string. 9 | 10 | The goal of this feature is to facilitate interoperability between Swift and dynamic languages such as Python. The [Swift for TensorFlow](https://github.com/tensorflow/swift) team at Google, who has driven this proposal, implemented a Python bridge that makes it possible to [call Python code from Swift](https://github.com/tensorflow/swift/blob/master/docs/PythonInteroperability.md). Pedro José Pereira Vieito packaged this up in a SwiftPM package called [PythonKit](https://github.com/pvieito/PythonKit). 11 | 12 | SE-0195 isn’t required to enable this interoperability, but it makes the resulting Swift syntax much nicer. It’s worth noting that SE-0195 only deals with property-style member lookup (i.e. simple getters and setters with no arguments). A complementary ["dynamic callable" proposal (SE-0216)](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md)) for a dynamic method call syntax has been accepted, but didn’t make the cut for Swift 4.2. It will be part of the next Swift release. 13 | 14 | Although Python has been the primary focus of the people who worked on the proposal, interop layers with other dynamic languages like Ruby or JavaScript will also be able to take advantage of it. 15 | 16 | And it’s not limited to this one use case, either. Any type that currently has a string-based subscript-style API could be converted to dynamic member lookup style. SE-0195 shows a `JSON` type as an example where you can drill down into nested dictionaries using dot notation. 17 | 18 | Here’s another example, courtesy of Doug Gregor: an `Environment` type that gives you property-style access to your process’s environment variables. Note that mutations also work. 19 | */ 20 | import Darwin 21 | 22 | /// The current process's environment. 23 | /// 24 | /// - Author: Doug Gregor, https://gist.github.com/DougGregor/68259dd47d9711b27cbbfde3e89604e8 25 | @dynamicMemberLookup 26 | struct Environment { 27 | subscript(dynamicMember name: String) -> String? { 28 | get { 29 | guard let value = getenv(name) else { return nil } 30 | return String(validatingUTF8: value) 31 | } 32 | nonmutating set { 33 | if let value = newValue { 34 | setenv(name, value, /*overwrite:*/ 1) 35 | } else { 36 | unsetenv(name) 37 | } 38 | } 39 | } 40 | } 41 | 42 | let environment = Environment() 43 | 44 | environment.USER 45 | environment.HOME 46 | environment.PATH 47 | 48 | // Mutations are allowed if the subscript has a setter 49 | environment.MY_VAR = "Hello world" 50 | environment.MY_VAR 51 | 52 | /*: 53 | This is a big feature that has the potential to change how Swift is used in fundamental ways if misused. By hiding a fundamentally “unsafe” string-based access behind a seemingly “safe” construct, you may give readers of your code the wrong impression that things have been checked by the compiler. 54 | 55 | Before you adopt this in your own code, ask yourself if `environment.USER` is really that much more readable than `environment["USER"]` to be worth the downsides. In most situations, I think the answer should be “no”. 56 | 57 | I wrote an article about `@dynamicMemberLookup` that goes into more detail: [Thoughts on @dynamicMemberLookup](https://oleb.net/blog/2018/06/dynamic-member-lookup/). 58 | */ 59 | /*: 60 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 61 | */ 62 | -------------------------------------------------------------------------------- /Whats-new-in-Swift-4.2.playground/Pages/Random numbers.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 3 | 4 | # Random numbers 5 | 6 | Working with random numbers used to be a little painful in Swift because (a) you had to call C APIs directly and (b) there wasn’t a good cross-platform random number API. 7 | 8 | [SE-0202](https://github.com/apple/swift-evolution/blob/master/proposals/0202-random-unification.md "Random Unification") adds random number generation to the standard library. 9 | 10 | ## Generating random numbers 11 | 12 | All number types have a `random(in:)` method that returns a random number in the given range (with a uniform distribution by default): 13 | */ 14 | Int.random(in: 1...1000) 15 | UInt8.random(in: .min ... .max) 16 | Double.random(in: 0..<1) 17 | 18 | /*: 19 | The API is designed to protect you from accidentally introducing [modulo bias](https://www.quora.com/What-is-modulo-bias), a common error when generating random numbers. 20 | 21 | `Bool.random` is also a thing: 22 | */ 23 | func coinToss(count tossCount: Int) -> (heads: Int, tails: Int) { 24 | var tally = (heads: 0, tails: 0) 25 | for _ in 0.. UInt64 { 71 | return base.next() 72 | } 73 | } 74 | 75 | var customRNG = MyRandomNumberGenerator() 76 | Int.random(in: 0...100, using: &customRNG) 77 | 78 | /*: 79 | ## Extending your own types 80 | 81 | You can provide a random data API for your own types by following the same pattern: 82 | */ 83 | enum Suit: String, CaseIterable { 84 | case diamonds = "♦" 85 | case clubs = "♣" 86 | case hearts = "♥" 87 | case spades = "♠" 88 | 89 | static func random(using generator: inout T) -> Suit { 90 | // Using CaseIterable for the implementation 91 | return allCases.randomElement(using: &generator)! 92 | 93 | } 94 | 95 | static func random() -> Suit { 96 | var rng = SystemRandomNumberGenerator() 97 | return Suit.random(using: &rng) 98 | } 99 | } 100 | 101 | let randomSuit = Suit.random() 102 | randomSuit.rawValue 103 | 104 | /*: 105 | I wrote an article about the random APIs that goes into more detail: [Random numbers in Swift](https://oleb.net/blog/2018/06/random-numbers-in-swift/). 106 | */ 107 | /*: 108 | [Table of contents](Table%20of%20contents) • [Previous page](@previous) • [Next page](@next) 109 | */ 110 | --------------------------------------------------------------------------------