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)
    }
}
相关推荐
wuyk5551 分钟前
59.嵌入式C语言高级宏定义实战:多行宏、字符串化与符号拼接
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网·51单片机
云泽8087 分钟前
Visual Studio 2026 新手保姆级教程:环境搭建、界面优化与编译器配置详解
开发语言·c++·c·开发工具·visual studio
2zcode1 小时前
基于MATLAB深度学习的乳腺钼靶影像乳腺癌智能诊断系统设计与实现
开发语言·深度学习·matlab·乳腺癌
MoonBit月兔1 小时前
错过大赛报名?MoonBit 开源大赛- 8 月黑客松正式开启
开发语言·ai-native·moonbit
weixin_538601971 小时前
智能体测开Day31
开发语言·python
geovindu1 小时前
java: Facade Pattern
java·开发语言·后端·外观模式·结构型模式
listening7771 小时前
HarmonyOS 6.1 元服务深度优化:从“秒开”到“常驻”的极致体验
java·开发语言·spring
布朗克1681 小时前
Go 入门到精通-29-测试与基准
开发语言·后端·golang·测试与基准
格林威2 小时前
工业相机Chunk功能全解析:图像嵌入时间戳、编码器元数据(附堡盟C#代码)
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
小大宇3 小时前
Python 集成 Conda
开发语言·python·conda