---
title: "Swift 5.5 新特性"
description: "swift 5.5新特性全览，Async/await， concurrency， async let，Continuation function，actor，@Sendable， #if……"
url: "https://hicc.pro/p/swift/whats-new-in-swift-5-5"
author: "tyler wang"
published: "2021-07-25T06:56:36.000Z"
updated: "2022-02-05T16:58:09.000Z"
category: "Swift"
image: "https://image.hicc.pro/pro/20210725150053Ftl_0ozln2uIE2e3jy8ut5wHt99Quk4qVlM5KWIjSPc0iQSo9.jpg"
lang: "zh-CN"
site: "Hipo日志"
---

# Swift 5.5 新特性

![窗外.腾讯大厦](https://image.hicc.pro/pro/20210725150020Ftl_0ozln2uIE2e3jy8ut5wHt99QfhLWw0Ofd-TdVvtATlaEV.jpg)

*窗外.腾讯大厦*

WWDC21上发布了Swift 5.5，虽然是小版本，但是特性不少……

### Async/await

[SE-0296](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md)提案终于为开发者带来了期待已久的 `async`/`await`，语法基本上和javascript中的很像。

#### 老的方式

以前写异步函数像这样

```swift
func fetchWeatherHistory(completion: @escaping ([Double]) -> Void) {
    // Complex networking code here; we'll just send back 100,000 random temperatures
    DispatchQueue.global().async {
        let results = (1...100_000).map { _ in Double.random(in: -10...30) }
        completion(results)
    }
}

func calculateAverageTemperature(for records: [Double], completion: @escaping (Double) -> Void) {
    // Sum our array then divide by the array size
    DispatchQueue.global().async {
        let total = records.reduce(0, +)
        let average = total / Double(records.count)
        completion(average)
    }
}

func upload(result: Double, completion: @escaping (String) -> Void) {
    // More complex networking code; we'll just send back "OK"
    DispatchQueue.global().async {
        completion("OK")
    }
}
```

这样调用

```swift
fetchWeatherHistory { records in
    calculateAverageTemperature(for: records) { average in
        upload(result: average) { response in
            print("Server response: \(response)")
        }
    }
}
```

存在的问题是

- 回调函数很容易调用多次，或者忘记调用。
- 函数参数 `@escaping (String) -> Void` 看着也不直观
- “回调地狱”看起来也不美观
- 在Swift 5.0 增加了`Result` 类型之前，返回错误也困难。

#### Swift 5.5 Async/await方式

```swift
func fetchWeatherHistory() async -> [Double] {
    (1...100_000).map { _ in Double.random(in: -10...30) }
}

func calculateAverageTemperature(for records: [Double]) async -> Double {
    let total = records.reduce(0, +)
    let average = total / Double(records.count)
    return average
}

func upload(result: Double) async -> String {
    "OK"
}
```

使用也更简单

```swift
func processWeather() async {
    let records = await fetchWeatherHistory()
    let average = await calculateAverageTemperature(for: records)
    let response = await upload(result: average)
    print("Server response: \(response)")
}
```

#### Async / await 错误处理

swift 5.5 async 函数也可以像普通函数一样抛出错误 `async throws`。

```swift
enum UserError: Error {
    case invalidCount, dataTooLong
}

func fetchUsers(count: Int) async throws -> [String] {
    if count > 3 {
        // Don't attempt to fetch too many users
        throw UserError.invalidCount
    }

    // Complex networking code here; we'll just send back up to `count` users
    return Array(["Antoni", "Karamo", "Tan"].prefix(count))
}

func save(users: [String]) async throws -> String {
    let savedUsers = users.joined(separator: ",")

    if savedUsers.count > 32 {
        throw UserError.dataTooLong
    } else {
        // Actual saving code would go here
        return "Saved \(savedUsers)!"
    }
}
```

使用也是类似

```swift
func updateUsers() async {
    do {
        let users = try await fetchUsers(count: 3)
        let result = try await save(users: users)
        print(result)
    } catch {
        print("Oops!")
    }
}
```

> **函数加上`async` 并不会自动并行 `concurrency`**
> 除非特殊处理，函数还是顺序执行，并不会自动并行，更不会自动跑在在其他线程。

#### Xcode 13 playground中运行异步代码

现在(2021-7-25)之前，暂时还没有明显优雅的方式在playground中执行async / await 代码。参考[这里](https://github.com/kishikawakatsumi/swift-playground/discussions/101)，可以这样执行：

```swift
import Foundation

struct Main {
  static func main() async {
    await doSomething()
  }

  static func doSomething() async {
    print("doSomething")
  }
}

Task.detached {
  await Main.main()
  exit(EXIT_SUCCESS)
}
RunLoop.main.run()
```

### Async / await: sequences

[SE-0298](https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md)提案为swift 引入了`AsyncSequence` protocol，循环异步队列。

使用`AsyncSequence` 使用和`Sequence`几乎一样。需要遵循`AsyncSequence`和`AsyncIterator`，当然`next()`方法需要时异步`async`的，和`Sequence`一样，**迭代结束确认返回`nil`**。

```swift
struct DoubleGenerator: AsyncSequence {
    typealias Element = Int

    struct AsyncIterator: AsyncIteratorProtocol {
        var current = 1

        mutating func next() async -> Int? {
            defer { current &*= 2 }

            if current < 0 {
                return nil
            } else {
                return current
            }
        }
    }

    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator()
    }
}
```

运行也是常见的 `for await` 语法。

```swift
func printAllDoubles() async {
    for await number in DoubleGenerator() {
        print(number)
    }
}
```

更高级的是，`AsyncSequence`协议提供了一些常用的方法，像`map()`， `compactMap()`, `allSatisfy()`等

```swift
func containsExactNumber() async {
    let doubles = DoubleGenerator()
    let match = await doubles.contains(16_777_216)
    print(match)
}
```

### read-only 属性

SE-0310提案升级了swift的只读属性，让其支持了async和throws。

```swift
enum FileError: Error {
    case missing, unreadable
}

struct BundleFile {
    let filename: String

    var contents: String {
        get async throws {
            guard let url = Bundle.main.url(forResource: filename, withExtension: nil) else {
                throw FileError.missing
            }

            do {
                return try String(contentsOf: url)
            } catch {
                throw FileError.unreadable
            }
        }
    }
}
```

使用自然像如下

```swift
func printHighScores() async throws {
    let file = BundleFile(filename: "highscores")
    try await print(file.contents)
}
```

### Structured concurrency

[SE-0304](https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md)在async / await 和async sequence的基础上为swift 引入了一整套的并发的执行，取消，监控的方法。

为了更好说明，我们先假设这两个情况

```swift
enum LocationError: Error {
    case unknown
}

func getWeatherReadings(for location: String) async throws -> [Double] {
    switch location {
    case "London":
        return (1...100).map { _ in Double.random(in: 6...26) }
    case "Rome":
        return (1...100).map { _ in Double.random(in: 10...32) }
    case "San Francisco":
        return (1...100).map { _ in Double.random(in: 12...20) }
    default:
        throw LocationError.unknown
    }
}

func fibonacci(of number: Int) -> Int {
    var first = 0
    var second = 1

    for _ in 0..<number {
        let previous = first
        first = second
        second = previous + first
    }

    return first
}
```

在swift的项目中，可以这么执行(如果在playground文件中，可以使用上文的方法)

```swift
@main
struct Main {
    static func main() async throws {
        let readings = try await getWeatherReadings(for: "London")
        print("Readings are: \(readings)")
    }
}
```

结构化并发，实际上是引入了两个类型，`Task`和`TaskGroup`来，独立或者协同地运行并发代码。

简单来说，你只要将异步代码传入`Task`对象，就会立即在background 线程上运行，然后你用`await`等待结果就好。

```swift
func printFibonacciSequence() async {
    let task1 = Task { () -> [Int] in
        var numbers = [Int]()

        for i in 0..<50 {
            let result = fibonacci(of: i)
            numbers.append(result)
        }

        return numbers
    }

    let result1 = await task1.value
    print("The first 50 numbers in the Fibonacci sequence are: \(result1)")
}
```

需要注意，代码中明确指定了`Task { () -> [Int] in` 这样，Swift就会知道task需要返回，而如果你的异步代码比较简单，可以像下面这样写：

```swift
let task1 = Task {
    (0..<50).map(fibonacci)
}
```

再次，task会在创建之后立即运行，但是在斐波那契函数得到结果之后，，`` `printFibonacciSequence()` ``函数会继续运行在原来的线程。

> **`task`参数属于non-escaping 闭包**
> 主要到task的函数参数中并没有标注`@escape`，因为task会立即执行传入的函数，而不是存储然后之后执行。因此，如果你在`class`或者`struct`中使用`Task`，你不需要`self`来获取属性和方法。

上述函数中，通过`await task.value`来获取task的值，如果你不关心task 返回的值，你也不需要存储task。

对于会抛出错误的异步任务，从task的`value`取值，也会触发错误，因此仍然需要`try await`。

```swift
func runMultipleCalculations() async throws {
    let task1 = Task {
        (0..<50).map(fibonacci)
    }

    let task2 = Task {
        try await getWeatherReadings(for: "Rome")
    }

    let result1 = await task1.value
    let result2 = try await task2.value
    print("The first 50 numbers in the Fibonacci sequence are: \(result1)")
    print("Rome weather readings are: \(result2)")
}
```

Swift为task内置了几种优先级`high`， `default`，`low`和`background`。如果未指定，会默认设置为`default`，当然你可以显式指定Task(priority: .high)。当然如果你在Apple的平台上，你会使用很熟悉的优先级，`userInitiated`对应`high`，`utility`对应`low`，当然你不能使用`userInteractive`，它是为主线程保留的。

当然，Task也为我们提供了几个静态的方法。

- `Task.sleep()`会暂定当前任务一定时间(纳秒，也就是1\_000\_000\_000为1秒)
- `Task.checkCancellation()`会检查当前任务是否被cancel()取消，如果已经取消了，会抛出`CancellationError`错误。
- `Task.yield()`会暂停当前任务一定时间，让给其他等待的任务让出点时间，这点在循环做一些繁重的任务会很有用。

```swift
func cancelSleepingTask() async {
    let task = Task { () -> String in
        print("Starting")
        await Task.sleep(1_000_000_000)
        try Task.checkCancellation()
        return "Done"
    }

    // The task has started, but we'll cancel it while it sleeps
    task.cancel()

    do {
        let result = try await task.value
        print("Result: \(result)")
    } catch {
        print("Task was cancelled.")
    }
}
```

上述代码中，`` `Task.checkCancellation()`会检测到task已经被取消了，会立即跑出`CancellationError`的错误，当然只有在尝试`task.value`取值的时候，才会抛出。``

> **使用`task.result`来获取到`Result`类型的值**
> 你可以使用`task.result`来获取到`Result`类型的值，上述代码会返回`Result<String, Error>`。当然你也就不需要`try`来捕捉错误了。

对于复杂的任务，可以使用 task group来组织更多的task。

```swift
func printMessage() async {
    let string = await withTaskGroup(of: String.self) { group -> String in
        group.async { "Hello" }
        group.async { "From" }
        group.async { "A" }
        group.async { "Task" }
        group.async { "Group" }

        var collected = [String]()

        for await value in group {
            collected.append(value)
        }

        return collected.joined(separator: " ")
    }

    print(string)
}
```

不要将`withTaskGroup`里面的代码复制到外面，编辑不会报错，但是自然会有潜在的问题。

所有task group中的任务应该返回同样的数据类型。复杂情况，你可能需要一个有associated值的enum来准确取值，当然你还可以使用async let 绑定的替代方案。

task group的值需要所有的task都完成，但是每个task执行完顺序是不保证的。

你可以在task group中处理错误，或者你可以使用`withThrowingTaskGroup()`把错误抛出，这样也就需要`try`的方式来取值。

```swift
func printAllWeatherReadings() async {
    do {
        print("Calculating average weather…")

        let result = try await withThrowingTaskGroup(of: [Double].self) { group -> String in
            group.async {
                try await getWeatherReadings(for: "London")
            }

            group.async {
                try await getWeatherReadings(for: "Rome")
            }

            group.async {
                try await getWeatherReadings(for: "San Francisco")
            }

            // Convert our array of arrays into a single array of doubles
            let allValues = try await group.reduce([], +)

            // Calculate the mean average of all our doubles
            let average = allValues.reduce(0, +) / Double(allValues.count)
            return "Overall average temperature is \(average)"
        }

        print("Done! \(result)")
    } catch {
        print("Error calculating data.")
    }
}
```

上述每个 `async`任务，你可以简化使用`for location in ["London", "Rome", "San Francisco"] {`来调用。

task group 提供一个`cancelAll()`的方法来取消所有的task。之后你仍然可以给group添加异步任务。当然，你可以使用`asyncUnlessCancelled()`来跳过添加任务，如果group已经被取消—— 检查Boolean的返回值类判断group是否被取消。

### async let 绑定

[SE-0317](https://github.com/apple/swift-evolution/blob/main/proposals/0317-async-let.md)引入了一种更简易的语法`async let`来创建和等待子任务。这个可以作为task group的替代方法，特别是你需要使用不同数据类型的异步任务时候。

```swift
struct UserData {
    let username: String
    let friends: [String]
    let highScores: [Int]
}

func getUser() async -> String {
    "Taylor Swift"
}

func getHighScores() async -> [Int] {
    [42, 23, 16, 15, 8, 4]
}

func getFriends() async -> [String] {
    ["Eric", "Maeve", "Otis"]
}
```

可以使用如下简单并发取值。

```swift
func printUserDetails() async {
    async let username = getUser()
    async let scores = getHighScores()
    async let friends = getFriends()

    let user = await UserData(name: username, friends: friends, highScores: scores)
    print("Hello, my name is \(user.name), and I have \(user.friends.count) friends!")
}
```

> **你只能在`async`的上下文中，使用`async let`。**
> 你只能在`async`的上下文中，使用`async let`。而且如果你不去使用`await`取值，swift会在其作用于隐式等待。

绑定抛错的异步方法的时候，你也不需要使用`try`关键词。只需要取值时候`try await`。

更高级的是，我们可以递归的使用async let语法。

```swift
enum NumberError: Error {
    case outOfRange
}

func fibonacci(of number: Int) async throws -> Int {
    if number < 0 || number > 22 {
        throw NumberError.outOfRange
    }

    if number < 2 { return number }
    async let first = fibonacci(of: number - 2)
    async let second = fibonacci(of: number - 1)
    return try await first + second
}
```

### Continuation函数(转换回调异步为async函数)

[SE-0300](https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md)提供了把老的回调式异步函数转换为async函数的方法。

例如，有如下的回调函数

```swift
func fetchLatestNews(completion: @escaping ([String]) -> Void) {
    DispatchQueue.main.async {
        completion(["Swift 5.5 release", "Apple acquires Apollo"])
    }
}
```

swift 5.5之后，你不需要重写你的所有代码，你只需要使用`withCheckedContinuation()`函数包裹就好。

```swift
func fetchLatestNews() async -> [String] {
    await withCheckedContinuation { continuation in
        fetchLatestNews { items in
            continuation.resume(returning: items)
        }
    }
}
```

- `resume(returning:)`函数返回，你异步要返回的数据。
- 确保`resume(returning:)`函数只调用一次。在`withCheckedContinuation()`函数中，swift会告警甚至会崩溃代码，当然这会有性能损耗。
- 如果有更高性能要求，或者你确保你的代码不会有问题，你可以使用`` `withUnsafeContinuation()`。``

### Actors

[SE-0306](https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md)引入了`actor`，概念上和`class`很相似，但是swfit确保了，actor中的变量在任意时间段内只会被一个线程获取，这也就确保了actor在并发环境下的安全。

例如下面的代码

```swift
class RiskyCollector {
    var deck: Set<String>

    init(deck: Set<String>) {
        self.deck = deck
    }

    func send(card selected: String, to person: RiskyCollector) -> Bool {
        guard deck.contains(selected) else { return false }

        deck.remove(selected)
        person.transfer(card: selected)
        return true
    }

    func transfer(card: String) {
        deck.insert(card)
    }
}
```

在单线程的环境中都是OK的。但是在多线程的环境中，我们代码就有了潜在的资源竞争风险，这也就导致了，当代码并行运行时，代码的执行结果会可能不同。

假设我们调用`send(card:to:)` 在同一时间调用多次，

1. 第一个线程检查card是否在deck，存在，继续
2. 第二个线程也检查card是否在deck，存在，也继续
3. 第一个线程删除了deck中的card然后转移给了第二个人。
4. 第二个线程尝试删除deck中的card，但是实际上已经不存在了，但是它还是把card转移给了另一个人。

这样就导致给一个人转移了两个卡片。绝对的麻烦。

Actor通过actor isolation隔离的方式解决这个问题：

- **只能从外部异步地读取到actor的属性和方法，**
- **不能**从外部**写存储后的属性**

swift 内部通过队列的方式避免资源竞争，因此应能不会很好。

对于上述的例子，我们可以改写为：

```swift
actor SafeCollector {
    var deck: Set<String>

    init(deck: Set<String>) {
        self.deck = deck
    }

    func send(card selected: String, to person: SafeCollector) async -> Bool {
        guard deck.contains(selected) else { return false }

        deck.remove(selected)
        await person.transfer(card: selected)
        return true
    }

    func transfer(card: String) {
        deck.insert(card)
    }
}
```

1. 通过`actor`关键词来创建一个Actor，这个是swift 新加的类型。
2. `send()`方法被标为`async`，因为它需要一定时间来完成card转移。
3. `transfer(card:)` 并没有标准为`async`，但是我们仍然需要`await` 来调用，因为需要等待`SafeCollector` actor能够处理请求。

更细节来说，actor内部可以任意读写属性和方法，但是和另一个actor交互的时候就必须使用异步的方式。这样就保证了线程安全，而且更棒的事编译后保证了这一点。

actor和class很像

- 都是引用类型，因此它们可以被用来分享状态。
- 都有方法，属性，构造器，和下标方法。
- 可以遵循协议和泛型
- 两者静态属性和方法都是一样的，因为它们没有`self`，因此也就不需要隔离。

当然actor相比class有两个最大的不同：

1. Actor暂时不支持继承，因此它们的构造器initializer就简单多了——不需要convenience initializer，override，和`final` 关键词等。这个后续可能会改变。
2. 所有的actor隐式的遵循了Actor的协议，其他的类型不能使用这个协议。

> **最好的`actor`描述：“actors pass messages, not memory.”**
> actor不是直接访问别的acotr内存，或者调用它们的方法，而是发送消息，让swift runtime来安全处理数据。

### Global actors

[SE-0316](https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md)引入了全局actor来隔离全局状态避免数据竞争。

目前来说是引入了一个`@MainActor`来标柱装饰你的属性和方法，让其保证只在主线程运行。

对于app来说，UI更新就需要保证在主线程，以前的方式是使用`DispatchQueue.main`。swift 5.5之后，就简单了

```swift
class NewDataController {
    @MainActor func save() {
        print("Saving data…")
    }
}
```

> **`@MainActor`标柱之后，必须异步调用。**
> 就像上文，这里实际上是`actor`，因此，我们需要使用`await`，`async let` 等来调用`save()`

`@MainActor`底层是一个全局的`actor`底层是`MainActor`的结构体。其中有一个静态的`run()`方法来让我们代码在主线程中执行，而且也能够返回执行结果。

更多可以看，博主之前的文章：[使用@MainActor自动在主线程更新UI](https://hicc.pro/p/swift/the-main-actor-attribute)。

### Sendable协议和@Sendable闭包

[SE-0302](https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md)支持了“可传送”的数据，也就是可以安全的向另一个线程传送数据。也就是引入了新的`Sendable` protocol和装饰函数的`@Sendable`属性。

默认线程安全的有

- 所有Swift核心的值类型， `Bool`，`Int`，`String`等
- 包裹的值类型的可选值(Optional)。
- swift标准库值类型的集合，例如，`Array<String>`， `Dictionary<Int, String>`。
- 值类型的元组(Tuple)
- 元类型(Metatype),例如`String.self`

上述的值，都遵循了`Sendable`的协议。

而对于自定义的类型，如果满足下面的情况

- `Actor`自动遵循`Sendable`，因为actor内部异步的处理数据。
- 自定义的`struct`和`enum`，如果它们只包含遵循`Sendable`的值也会自动遵循`Sendable`，这点和`Codable`很像。
- `class`能够遵循`Sendable`，如果1. 继承自`NSObject`，或者2. 不继承，而且所有的属性是常量的且能够遵循`Sendable`，还有类得标注为final来防止将来的继承。

Swift让函数和闭包，标注@Sendable，来能够并发调用。例如，Task的初始化函数标注了@Sendable，下面代码能够并发执行，因为其内捕获的是常量。

```swift
func printScore() async { 
    let score = 1

    Task { print(score) }
    Task { print(score) }
}
```

> **如果`score`是变量，就不能在`task`内用了。**
> 因为`Task`会并发执行，如果是变量，就存在数据竞争了。

你可以在自己代码标注`@Sendable`，这样也会强制上述的规则(值捕获)。

```swift
func runLater(_ function: @escaping @Sendable () -> Void) -> Void {
    DispatchQueue.global().asyncAfter(deadline: .now() + 3, execute: function)
}
```

### 链式调用中支持#if语法

[SE-0308](https://github.com/apple/swift-evolution/blob/main/proposals/0308-postfix-if-config-expressions.md)中Swift支持了在链式调用( postfix member expression)使用`#if`的条件判断表达式。这个乍看有点费解，其实是为了解决SwiftUI中根据条件添加view的修饰器。

```swift
Text("Welcome")
#if os(iOS)
    .font(.largeTitle)
#else
    .font(.headline)
#endif
```

支持嵌套调用

```swift
#if os(iOS)
    .font(.largeTitle)
    #if DEBUG
        .foregroundColor(.red)
    #endif
#else
    .font(.headline)
#endif
```

当然，也不是非得在Swift UI中使用

```swift
let result = [1, 2, 3]
#if os(iOS)
    .count
#else
    .reduce(0, +)
#endif

print(result)
```

> **#if只能用在`.`操作**
> 只有`.`操作才算是postfix member expression，所以#if不能用在 `+`，`[]`等操作上。

### CGFloat 和 Double 隐式转换

[SE-0307](https://github.com/apple/swift-evolution/blob/main/proposals/0307-allow-interchangeable-use-of-double-cgfloat-types.md)改进为开发带来了巨大的便利：Swift 能够在大多数情况下隐式转换`CGFloat`和`Double`。

```swift
let first: CGFloat = 42
let second: Double = 19
let result = first + second
print(result)
```

Swift会有限使用`Double`，更棒的是，不需要重写原来的代码，列入，Swift UI中的`scaleEffect()`仍然可以使用`CGFloat`，swift 内部转换为`Double`。

### Codable支持enum 关联值

[SE-0295](https://github.com/apple/swift-evolution/blob/main/proposals/0295-codable-synthesis-for-enums-with-associated-values.md)升级了Swift `Codable`，让其能够支持枚举enum关联值。之前只有遵循`rawRepresentable`的enum才能使用Codable。

```swift
enum Weather: Codable {
    case sun
    case wind(speed: Int)
    case rain(amount: Int, chance: Int)
}
```

现在能使用JSONEncoder的

```swift
let forecast: [Weather] = [
    .sun,
    .wind(speed: 10),
    .sun,
    .rain(amount: 5, chance: 50)
]

do {
    let result = try JSONEncoder().encode(forecast)
    let jsonString = String(decoding: result, as: UTF8.self)
    print(jsonString)
} catch {
    print("Encoding error: \(error.localizedDescription)")
}

// [{"sun":{}},{"wind":{"speed":10}},{"sun":{}},{"rain":{"amount":5,"chance":50}}]
```

上面[json key 可以使用CodingKey来自定义](https://medium.com/%E5%BD%BC%E5%BE%97%E6%BD%98%E7%9A%84-swift-ios-app-%E9%96%8B%E7%99%BC%E5%95%8F%E9%A1%8C%E8%A7%A3%E7%AD%94%E9%9B%86/%E5%88%A9%E7%94%A8-enum-codingkeys-%E5%AE%A2%E8%A3%BD-json-%E5%B0%8D%E6%87%89%E7%9A%84-property-1b27f29c0c32)。

### 函数中支持lazy关键词

swift中`lazy`关键词能够让属性延迟求值，现在swift 5.5之后，函数中也能使用`lazy`关键词了。

```swift
func printGreeting(to: String) -> String {
    print("In printGreeting()")
    return "Hello, \(to)"
}

func lazyTest() {
    print("Before lazy")
    lazy var greeting = printGreeting(to: "Paul")
    print("After lazy")
    print(greeting)
}

lazyTest()
```

### property wrapper 可以装饰到 function 和 closure 参数

[SE-0293](https://github.com/apple/swift-evolution/blob/main/proposals/0293-extend-property-wrappers-to-function-and-closure-parameters.md)扩展了property wrapper让其能够装饰到函数和闭包参数。

例如原来的函数

```swift
func setScore1(to score: Int) {
    print("Setting score to \(score)")
}
setScore1(to: 50)
setScore1(to: -50)
setScore1(to: 500)
// Setting score to 50
// Setting score to -50
// Setting score to 500
```

使用property wrapper可以，用来固定score的参数。

```swift
@propertyWrapper
struct Clamped<T: Comparable> {
    let wrappedValue: T

    init(wrappedValue: T, range: ClosedRange<T>) {
        self.wrappedValue = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
}
```

现在使用的

```swift
func setScore2(@Clamped(range: 0...100) to score: Int) {
    print("Setting score to \(score)")
}

setScore2(to: 50)
setScore2(to: -50)
setScore2(to: 500)

// Setting score to 50
// Setting score to 0
// Setting score to 100
```

### 扩展泛型函数中协议静态成员的查找

[SE-0299](https://github.com/apple/swift-evolution/blob/main/proposals/0299-extend-generic-static-member-lookup.md) 提升了Swift对泛型函数中协议静态成员查找的能力。这点实际上也很能提升Swift UI的书写的便利。

以前需要这么写

```swift
Toggle("Example", isOn: .constant(true))
    .toggleStyle(SwitchToggleStyle())
```

现在这么写，就很方便

```swift
Toggle("Example", isOn: .constant(true))
    .toggleStyle(.switch)
```

跟具体来说，假设我们有如下的协议和结构体

```swift
protocol Theme { }
struct LightTheme: Theme { }
struct DarkTheme: Theme { }
struct RainbowTheme: Theme { }
```

我们再定义一个`Screen`协议，有一个theme的泛型函数，来设置主题。

```swift
protocol Screen { }

extension Screen {
    func theme<T: Theme>(_ style: T) -> Screen {
        print("Activating new theme!")
        return self
    }
}
```

现在我们有个 `Screen`的结构体

```swift
struct HomeScreen: Screen { }
```

我们可以指定screen的主题为

```swift
let lightScreen = HomeScreen().theme(LightTheme())
```

现在Swift 5.5之后，我们可以在`Theme`协议上加个静态的属性

```swift
extension Theme where Self == LightTheme {
    static var light: LightTheme { .init() }
}
```

现在Swift 5.5 设置主题就简单了

```swift
let lightTheme = HomeScreen().theme(.light)
```

---

[What's new in Swift 5.5?](https://www.hackingwithswift.com/articles/233/whats-new-in-swift-5-5): Async/await, actors, throwing properties, and more!

### Swift 3 到 Swift 5.4

- [Swift 5.2到5.4新特性](https://hicc.pro/p/swift/whats-new-in-swift-5-2-to-5-4)
- [Swift 5.1](https://hicc.pro/p/swift/whats-new-in-swift-5-1)
- [Swift 3到5.1新特性](https://hicc.pro/p/swift/whats-new-in-swift-3-to-5-1)

---

原文 / Source: <https://hicc.pro/p/swift/whats-new-in-swift-5-5>
