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)
    }
}
相关推荐
无心水2 小时前
22、Java开发避坑指南:日期时间、Spring核心与接口设计的最佳实践
java·开发语言·后端·python·spring·java.time·java时间处理
Hello.Reader2 小时前
双卡 A100 + Ollama 最终落地手册一键部署脚本、配置文件、预热脚本与 Python 客户端完整打包
开发语言·网络·python
cch89182 小时前
汇编VS C++:底层控制与高效开发之争
java·开发语言
lifewange2 小时前
代码托管平台
开发语言
yangyanping201082 小时前
Go语言学习之配置管理库Viper
开发语言·学习·golang
橘子编程2 小时前
UniApp跨端开发终极指南
开发语言·vue.js·uni-app
冬至喵喵2 小时前
构建 CLI 的 Python 框架:Typer技术介绍
开发语言·chrome·python
AbandonForce3 小时前
STL list
开发语言·c++
前端老石人3 小时前
HTML 入门指南:从规范视角建立正确知识体系
开发语言·前端·html