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)
    }
}
相关推荐
不正经学生3 小时前
C语言预处理详解:编译器真正动手之前的那些事
c语言·开发语言·算法·面试·bug
门思科技3 小时前
LoRaWAN 设备类别:Class A、B、C 对比与选型指南
c语言·开发语言·php
恋恋西风6 小时前
C++ 理解 std::thread 在单核和多核上的行为差异
开发语言·c++
Brilliantwxx6 小时前
【Linux】 进程(9)程序与进程地址空间(基础+进阶+面试题)
linux·运维·服务器·开发语言·c++
BizzZ_7 小时前
C++(22)——类型转换和IO流
开发语言·c++
小范同学_7 小时前
JDK1.7 与 JDK1.8 HashMap 底层原理对比 + 数组并发扩容死循环详解
java·开发语言
geovindu7 小时前
CSharp: 万年历
开发语言·后端·c#·.net
我找到地球的支点啦8 小时前
Matlab系列(009) 一CRC循环冗余校验详解
开发语言·数据结构·算法·matlab·信息与通信
予昊8 小时前
从零实现“在线五子棋对战“:WebSocket 实时通信 + 段位匹配
java·开发语言·网络·websocket
weixin_461408588 小时前
Mybatis-flex小记
java·开发语言·mybatis