Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2021 EssentialsSwift

WWDC21 · 33 min · Essentials / Swift

What‘s new in Swift

Join us for an update on Swift. Discover the latest language advancements that make your code easier to read and write. Explore the growing number of APIs available as Swift packages. And we’ll introduce you to Swift’s async/await syntax, structured concurrency, and actors.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 23 snippets

Deque swift · at 6:16 ↗
import Collections

var colors: Deque = ["red", "yellow", "blue"]

colors.prepend("green")
colors.append("orange")
// `colors` is now ["green", "red", "yellow", "blue", “orange"]

colors.popFirst() // "green"
colors.popLast() // "orange"
// `colors` is back to ["red", "yellow", "blue"]
Ordered set swift · at 6:25 ↗
import Collections

var buildingMaterials: OrderedSet = ["straw", "sticks", "bricks"]

for i in 0 ..< buildingMaterials.count {
    print("Little piggie #\(i) built a house of \(buildingMaterials[i])")
}
// Little piggie #0 built a house of straw
// Little piggie #1 built a house of sticks
// Little piggie #2 built a house of bricks

buildingMaterials.append("straw") // (inserted: false, index: 0)
Ordered dictionary swift · at 6:42 ↗
import Collections

var responses: OrderedDictionary = [200: "OK", 403: "Forbidden", 404: "Not Found"]

for (code, phrase) in responses {
    print("\(code) (\(phrase))")
}
// 200 (OK)
// 403 (Forbidden)
// 404 (Not Found)
Swift Algorithms swift · at 7:39 ↗
import Algorithms

let testAccounts = [ ... ]

for testGroup in testAccounts.uniquePermutations(ofCount: 0...) {
    try validate(testGroup)
}

let randomGroup = testAccounts.randomSample(count: 5)
Swift System swift · at 7:52 ↗
import System

let fd: FileDescriptor = try .open(
    "/tmp/a.txt", .writeOnly,
    options: [.create, .truncate], permissions: .ownerReadWrite)
try fd.closeAfter {
    try fd.writeAll("Hello, WWDC!\n".utf8)
}
FilePath manipulation APIs swift · at 8:06 ↗
import System

var path: FilePath = "/tmp/WWDC2021.txt"
print(path.lastComponent)         // "WWDC2021.txt"

print(path.extension)             // "txt"
path.extension = "pdf"            // path == "/tmp/WWDC2021.pdf"
path.extension = nil              // path == "/tmp/WWDC2021"
print(path.extension)             // nil

path.push("../foo/bar/./")        // path == "/tmp/wwdc2021/../foo/bar/."
path.lexicallyNormalize()         // path == "/tmp/foo/bar"
print(path.ends(with: "foo/bar")) // true!
Float16 support on Apple silicon Macs swift · at 9:01 ↗
import Numerics

let x: Float16 = 1.5
let y = Float16.exp(x)
Complex elementary functions swift · at 9:05 ↗
import Numerics

let z = Complex(0, Float16.pi) // πi
let w = Complex.exp(z)         // exp(πi) ≅ -1
AWS Lambda runtime now with async/await swift · at 11:07 ↗
import AWSLambdaRuntime
import AWSLambdaEvents

@main
struct HelloWorld: LambdaHandler {
    typealias In = APIGatewayV2Request
    typealias Out = APIGatewayV2Response
    func handle(event: In, context: Lambda.Context) async throws -> Out {
        .init(statusCode: .ok, body: "Hello World")
    }
}
Memory management swift · at 14:52 ↗
class Traveler {
    var destination: String
}

func test() {
    let traveler1 = Traveler(destination: "Unknown")
    // retain
    let traveler2 = traveler1
    // release
    traveler2.destination = "Big Sur"
    // release
    print("Done traveling")
}
Codable synthesis for enums with associated values: A two-case enum swift · at 17:04 ↗
enum Command {
    case load(key: String)
    case store(key: String, value: Int)
}
Codable synthesis for enums with associated values: Before swift · at 17:11 ↗
// You used to have to manually implement all of this boilerplate.

enum Command: Codable {
    case load(key: String)
    case store(key: String, value: Int)

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if container.allKeys.count != 1 {
            let context = DecodingError.Context(
                codingPath: container.codingPath,
                debugDescription: "Invalid number of keys found, expected one.")
            throw DecodingError.typeMismatch(Command.self, context)
        }

        switch container.allKeys.first.unsafelyUnwrapped {
        case .load:
            let nested = try container.nestedContainer(
                keyedBy: LoadCodingKeys.self,
                forKey: .load)
            self = .load(
                key: try nested.decode(String.self, forKey: .key))
        case .store:
            let nested = try container.nestedContainer(
                keyedBy: StoreCodingKeys.self,
                forKey: .store)
            self = .store(
                key: try nested.decode(String.self, forKey: .key),
                value: try nested.decode(Int.self, forKey: .value))
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case let .load(key):
            var nested = container.nestedContainer(keyedBy: LoadCodingKeys.self, 
                                                   forKey: .load)
            try nested.encode(key, forKey: .key)
        case let .store(key, value):
            var nested = container.nestedContainer(keyedBy: StoreCodingKeys.self, 
                                                   forKey: .store)
            try nested.encode(key, forKey: .key)
            try nested.encode(value, forKey: .value)
        }
    }

    /// Contains keys for all cases of the enum.
    enum CodingKeys: CodingKey {
        case load
        case store
    }

    /// Contains keys for all associated values of `case load`.
    enum LoadCodingKeys: CodingKey {
        case key
    }

    /// Contains keys for all associated values of `case store`.
    enum StoreCodingKeys: CodingKey {
        case key
        case value
    }
}
Codable synthesis for enums with associated values: After swift · at 17:15 ↗
enum Command: Codable {
    case load(key: String)
    case store(key: String, value: Int)
}
Static member lookup swift · at 17:26 ↗
enum Coffee {
    case regular
    case decaf
}

func brew(_ coffee: Coffee) { ... }

brew(.regular)
Flexible static member lookup swift · at 17:49 ↗
protocol Coffee { ... }
struct RegularCoffee: Coffee { }
struct Cappuccino: Coffee { }
extension Coffee where Self == Cappucino {
    static var cappucino: Cappucino { Cappucino() }
}

func brew<CoffeeType: Coffee>(_ coffee: CoffeeType) { ... }

brew(.cappucino.large)
Property wrappers on parameters swift · at 18:25 ↗
@propertyWrapper
struct NonEmpty<Value: Collection> {
    init(wrappedValue: Value) {
        precondition(!wrappedValue.isEmpty)
        self.wrappedValue = wrappedValue
    }

    var wrappedValue: Value {
        willSet { precondition(!newValue.isEmpty) }
    }
}

func logIn(@NonEmpty _ username: String) {
    print("Logging in: \(username)")
}
Ergonomic improvements in SwiftUI code: Before swift · at 19:02 ↗
// Instead of writing this...

import SwiftUI

struct SettingsView: View {
    @State var settings: [Setting]

    private let padding = 10.0

    var body: some View {
        List(0 ..< settings.count) { index in
            #if os(macOS)
            Toggle(settings[index].displayName, isOn: $settings[index].isOn)
                .toggleStyle(CheckboxToggleStyle())
            #else
            Toggle(settings[index].displayName, isOn: $settings[index].isOn)
                .toggleStyle(SwitchToggleStyle())
            #endif
        }
        .padding(CGFloat(padding))
    }
}
Ergonomic improvements in SwiftUI code: After swift · at 19:37 ↗
// You can now write this.

import SwiftUI

struct SettingsView: View {
    @State var settings: [Setting]

    private let padding = 10.0

    var body: some View {
        List($settings) { $setting in
            Toggle(setting.displayName, isOn: $setting.isOn)
              #if os(macOS)
              .toggleStyle(.checkbox)
              #else
              .toggleStyle(.switch)
              #endif
        }
        .padding(padding)
    }
}
Asynchronous programming with async/await: Before swift · at 22:20 ↗
// Instead of writing this...

func fetchImage(id: String, completion: (UIImage?, Error?) -> Void) {
    let request = self.imageURLRequest(for: id)
    let task = URLSession.shared.dataTask(with: request) {
        data, urlResponse, error in
        if let error = error {
            completion(nil, error)
        } else if let httpResponse = urlResponse as? HTTPURLResponse,
                httpResponse.statusCode != 200 {
            completion(nil, MyTransferError())
        } else if let data = data, let image = UIImage(data: data) {
            completion(image, nil)
        } else {
            completion(nil, MyOtherError())
        }
    }
   task.resume()
}
Asynchronous programming with async/await: URLSession.shared.data(for:) swift · at 23:58 ↗
let (data, response) = try await URLSession.shared.data(for: request)
Asynchronous programming with async/await: After swift · at 24:40 ↗
// You can now write this.

func fetchImage(id: String) async throws -> UIImage {
    let request = self.imageURLRequest(for: id)
    let (data, response) = try await URLSession.shared.data(for: request)
    if let httpResponse = response as? HTTPURLResponse,
       httpResponse.statusCode != 200 {
        throw TransferFailure()
    }
    guard let image = UIImage(data: data) else {
        throw ImageDecodingFailure()
    }
    return image
}
Structured concurrency swift · at 27:06 ↗
func titleImage() async throws -> Image {
    async let background = renderBackground()
    async let foreground = renderForeground()
    let title = try renderTitle()
    return try await merge(background,
                           foreground,
                           title)
}
Actors swift · at 29:26 ↗
actor Statistics {
    private var counter: Int = 0
    func increment() {
        counter += 1
    }
    func publish() async {
        await sendResults(counter)
    }
}

var statistics = Statistics()
await statistics.increment()

Resources