Swift 中的 Combine 框架完整指南(含示例代码 + 实战)

文章目录

📘 Combine 框架完整指南(含示例代码 + 实战)


一、核心概念

Combine 是 Apple 的响应式编程框架:

Publisher → Operator → Subscriber


二、基础示例

swift 复制代码
import Combine

let cancellable = Just(5)
    .map { $0 * 2 }
    .sink { print($0) } // 10

三、常用 Publisher

Just

swift 复制代码
Just("Hello")
    .sink { print($0) }

Future

swift 复制代码
Future<String, Error> { promise in
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        promise(.success("完成"))
    }
}
.sink(receiveCompletion: { _ in },
      receiveValue: { print($0) })

PassthroughSubject

swift 复制代码
let subject = PassthroughSubject<String, Never>()
subject.sink { print($0) }
subject.send("事件1")

CurrentValueSubject

swift 复制代码
let subject = CurrentValueSubject<Int, Never>(0)
subject.sink { print($0) }
subject.send(1)

四、操作符

swift 复制代码
Just(10)
    .map { $0 * 2 }
    .filter { $0 > 10 }
    .sink { print($0) }

五、输入优化(完整实战)

ViewModel

swift 复制代码
import Combine
import SwiftUI

class SearchViewModel: ObservableObject {
    @Published var keyword: String = ""

    private var cancellables = Set<AnyCancellable>()

    init() {
        $keyword
            .debounce(for: .milliseconds(500), scheduler: RunLoop.main)
            .removeDuplicates()
            .filter { !$0.isEmpty }
            .filter { $0.count > 2 }
            .sink { [weak self] value in
                self?.search(keyword: value)
            }
            .store(in: &cancellables)
    }

    func search(keyword: String) {
        print("发起搜索:", keyword)
    }
}

SwiftUI 页面

swift 复制代码
struct SearchView: View {
    @StateObject var vm = SearchViewModel()

    var body: some View {
        TextField("输入搜索", text: $vm.keyword)
            .textFieldStyle(.roundedBorder)
            .padding()
    }
}

六、按钮防重复点击(完整实战)

ViewModel

swift 复制代码
import Combine

class ButtonViewModel {
    let tapSubject = PassthroughSubject<Void, Never>()

    private var cancellables = Set<AnyCancellable>()

    init() {
        tapSubject
            .throttle(for: .seconds(1), scheduler: RunLoop.main, latest: false)
            .sink { [weak self] in
                self?.handleTap()
            }
            .store(in: &cancellables)
    }

    func handleTap() {
        print("按钮点击处理")
    }
}

SwiftUI 页面

swift 复制代码
struct ButtonView: View {
    let vm = ButtonViewModel()

    var body: some View {
        Button("点击") {
            vm.tapSubject.send(())
        }
    }
}

七、网络请求

swift 复制代码
struct User: Decodable {
    let name: String
}

func fetchUser() -> AnyPublisher<User, Error> {
    let url = URL(string: "https://api.example.com/user")!

    return URLSession.shared.dataTaskPublisher(for: url)
        .map(\.data)
        .decode(type: User.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

八、内存管理

swift 复制代码
var cancellables = Set<AnyCancellable>()

publisher
    .sink { _ in }
    .store(in: &cancellables)

九、总结

  • debounce → 输入优化
  • throttle → 防止重复点击
  • Future → 异步封装
  • AnyPublisher → 类型擦除

🚀 一句话

Combine = 用数据流方式写异步代码


十、输入框 + 网络请求 + 自动取消(高级🔥)

核心思路


ViewModel 实现

swift 复制代码
class AdvancedSearchViewModel: ObservableObject {
    
    @Published var keyword: String = ""
    @Published var results: [User] = []
    @Published var isLoading: Bool = false
    
    private let network = NetworkService()
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $keyword
            .debounce(for: .milliseconds(500), scheduler: RunLoop.main)
            .removeDuplicates()
            .filter { !$0.isEmpty }
            
            .map { [weak self] keyword -> AnyPublisher<[User], Never> in
                guard let self = self else {
                    return Just([]).eraseToAnyPublisher()
                }
                
                self.isLoading = true
                
                return self.network.searchUser(keyword: keyword)
                    .catch { _ in Just([]) }
                    .handleEvents(receiveCompletion: { _ in
                        self.isLoading = false
                    })
                    .eraseToAnyPublisher()
            }
            
            .switchToLatest()
            .receive(on: RunLoop.main)
            .sink { [weak self] users in
                self?.results = users
            }
            .store(in: &cancellables)
    }
}
相关推荐
c++之路4 分钟前
C++ 预处理器
开发语言·c++
CN-Dust18 分钟前
【C++专题】格式化输出与输入
开发语言·c++·算法
AI人工智能+电脑小能手22 分钟前
【大白话说Java面试题】【Java基础篇】第19题:HashMap的key如何减少发生哈希冲突
java·开发语言·后端·面试·哈希算法·hash-index·hash
im_AMBER29 分钟前
Leetcode 162 除了自身以外数组的乘积 | 接雨水
开发语言·javascript·数据结构·算法·leetcode
是个西兰花36 分钟前
C++:异常
开发语言·c++·异常
AI人工智能+电脑小能手41 分钟前
【大白话说Java面试题】【Java基础篇】第18题:HashMap底层是如何扩容的
java·开发语言·面试·散列表·hash-index·hash
AbandonForce43 分钟前
Map类:pair键值对|map的基本操作|operator[]
开发语言·c++·算法·leetcode
澈2071 小时前
C++核心:封装与static静态成员实战指南
开发语言·c++·算法
四眼蒙面侠1 小时前
深入 Open Agent SDK(五):会话持久化与安全防线
swift·claudecode·bmad·openagentsdk
wuyoula1 小时前
全新多平台电商代付商城源码
开发语言·c++·ui·小程序·php源码