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)
    }
}
相关推荐
Highcharts.js8 小时前
倒置百分比堆叠面积图表示列详解|Highcharts大气成分图表代码
开发语言·信息可视化·highcharts·图表开发·面积图·图表示例·推叠图
csdn_aspnet8 小时前
C语言 Lomuto分区算法(Lomuto Partition Algorithm)
c语言·开发语言·算法
晨曦中的暮雨8 小时前
4.15腾讯 CSIG云服务产线 一面
java·开发语言
存在morning8 小时前
【GO语言开发实践】二 GO 并发快速上手
大数据·开发语言·golang
xiaoerbuyu123310 小时前
开源Java 邮箱 基于SpringBoot+Vue前后端分离的电子邮件
java·开发语言
sparEE11 小时前
c++值类别、右值引用和移动语义
开发语言·c++
zhangjw3411 小时前
第11篇:Java Map集合详解,HashMap底层原理、哈希冲突、JDK1.8优化、遍历方式彻底吃透
java·开发语言·哈希算法
benpaodeDD12 小时前
视频10,11,12,13——java程序的加载与执行,安装jdk
java·开发语言
一颗牙牙12 小时前
安装mmcv
开发语言·python·深度学习
大空大地202612 小时前
C#高级语法总结
开发语言·c#