---
title: "Swift中的模式匹配"
description: "Swift 作为一门现代语言，除去安全，快速等特性之外，还有个明显有别于其他语言的特性，就是巨量细致入微的语言特性。类似iOS API，初学者觉得繁杂，无从下手，但是熟悉之后，绝对能少写不少代码。"
url: "https://hicc.pro/p/swift/pattern-matching-in-swift"
author: "tyler wang"
published: "2022-02-05T07:43:31.000Z"
updated: "2022-08-09T11:16:15.000Z"
category: "Swift"
image: "https://image.hicc.pro/pro/20220206005904lqjVBIwGtv_PME1KF8bG509jJv61KiVjZRxKCVfEeN32L-nVg.jpg"
lang: "zh-CN"
site: "Hipo日志"
---

# Swift中的模式匹配

![iPhone 13 微距，水中花，一家人在青青世界](https://image.hicc.pro/pro/20220206005904lqjVBIwGtv_PME1KF8bG509jJv61KiVjZRxKCVfEeN32L-nVg.jpg)

*iPhone 13 微距，水中花，一家人在青青世界*

Swift 作为一门现代语言，除去安全，快速等特性之外，还有个明显有别于其他语言的特性，就是巨量细致入微的语言特性。类似iOS API，初学者觉得繁杂，无从下手，但是熟悉之后，绝对能少写不少代码。

其中强大的**模式匹配**绝对让你用的很爽。

主要整理自：[pattern-matching-in-swift](https://www.swiftbysundell.com/articles/pattern-matching-in-swift/#iterative-patterns)

#### 迭代器中

我们经常会在`for`循环中，使用`if`判断。

```swift
func deleteMarkedMessages() {
    for message in messages {
        if message.isMarked {
            database.delete(message)
        }
    }
}
```

上面绝对能工作，你可能会使用函数式的写法。

```swift
func deleteMarkedMessages() {
    messages.filter { $0.isMarked }
            .forEach(database.delete)
}
```

上述可能需要团队熟悉函数式编程，而且整个数组会经过两遍处理。

而Swift中`where`判断，则是此类情况的绝佳解决方案。

```swift
func deleteMarkedMessages() {
    for message in messages where message.isMarked {
        database.delete(message)
    }
}
```

而对于Swift Optional数据来说，例如类似的数据结构：

```swift
struct Match {
    var startDate: Date
    var players: [Player?]
}
```

假设我们需要循环`players`，且需要排除`nil`的值，我们可以使用传统的`if`判断，或者使用函数式的，`compactMap`函数。但是在Swift中有个更高阶的方法，使用`for case let`语法。

```swift
func makePlayerListView(for players: [Player?]) -> UIView {
    let view = PlayerListView()

    for case let player? in players {
        view.addEntryForPlayer(named: player.name,
                               image: player.image)
    }

    return view
}
```

这个乍看很奇怪，一般只会在`switch`和`enum`声明中才能看到case。但是实际上，swift中optional值底层是`Optional<Wrapped>`的枚举`enum`，而且swift的模式匹配不是只在`switch`下才能工作。

#### switch中的optional可选判断

例如如下的enum，

```swift
enum LoadingState {
    case loading
    case failed(Error)
}
```

在无状态的情况下，我们使用可选optional的`LoadingState?`，在`switch`匹配中，我们同样可以将`?` 使用在case的情况，以此来匹配有值的情况。

```swift
extension ContentViewController: ViewModelDelegate {
    func viewModel(_ viewModel: ViewModel,
                   loadingStateDidChangeTo state: LoadingState?) {
        switch state {
        case nil:
            removeLoadingSpinner()
            removeErrorView()
            renderContent()
        case .loading?:
            removeErrorView()
            showLoadingSpinner()
        case .failed(let error)?:
            removeLoadingSpinner()
            showErrorView(for: error)
        }
    }
}
```

#### 声明式的错误处理

错误处理，特别是http错误处理通常挺复杂的，一大堆if else代码。而在swift的强大的模式匹配下，我们可以写出声明式的代码。

```swift
func handle(_ error: Error) {
    switch error {
    // Matching against a group of offline-related errors:
    case URLError.notConnectedToInternet,
         URLError.networkConnectionLost,
         URLError.cannotLoadFromNetwork:
        showOfflineView()
    // Matching against a specific error:
    case let error as HTTPError where error == .unauthorized:
        logOut()
    // Matching against our networking error type:
    case is HTTPError:
        showNetworkErrorView()
    // Fallback for other kinds of errors:
    default:
        showGenericErrorView(for: error)
    }
}
```

#### 模式匹配底层逻辑，以及自定义模式匹配

Swift中模式匹配部分依赖变量相关语法(例如case let)， 这里值和模式匹配的真正逻辑并没有到编译那一步，甚至也不是语言语法，类似很多貌似“底层”的特性其实是在标准库中通过常规的Swift 代码来实现。

具体，Swift使用重载`～=`运算符号来实现模式匹配——这也就就给了我们自定义模式匹配的方法。

类似上面的判断错误，使用`～=`运算符号重载

```swift
func ~=<E: Error & Equatable>(rhs: E, lhs: Error) -> Bool {
    return (lhs as? E) == rhs
}
```

这样，上述的switch case写起来更简洁。

```swift
func handle(_ error: Error) {
    switch error {
    case URLError.notConnectedToInternet,
         URLError.networkConnectionLost,
         URLError.cannotLoadFromNetwork:
        showOfflineView()
    case HTTPError.unauthorized:
        logOut()
    case is HTTPError:
        showNetworkErrorView()
    default:
        showGenericErrorView(for: error)
    }
}
```

---

swift 模式匹配很强大，无疑会减少不少代码，不过里面高级的玩法确实看起来容易费解，这点可能就是swift 这门语言让人又爱又恨的地方。

从我角度来说，我喜欢这种复杂度，可玩性更高～^-^～。

---

我的博客即将同步至腾讯云开发者社区，邀请大家一同入驻：[https://cloud.tencent.com/developer/support-plan?invite\_code=s4vceg07lp5g](https://cloud.tencent.com/developer/support-plan?invite_code=s4vceg07lp5g)

---

原文 / Source: <https://hicc.pro/p/swift/pattern-matching-in-swift>
