Combine 学习之 Publisher

12 阅读4分钟

Publisher用于声明一个能够随时间传递序列值的类型,它是响应式编程模型的 "事件生产者",负责发出数据、完成信号或错误信息。

protocol Publisher { 

         associatedtype Output  // 发布的值类型 

         associatedtype FailureError  // 可能抛出的错误类型 

         func receive<S>(subscriber: Swhere SSubscriberS.Input == OutputS.Failure == Failure 

}

Combine 提供多种开箱即用的 Publisher,覆盖常见异步场景:

表格

类型用途特点
Just发布单个值后立即完成不会产生错误,适用于已知单一结果场景
Future包装异步操作,产生单个结果异步执行,完成后发送值或错误
PassthroughSubject手动发送值 / 完成 / 错误无初始值,仅传递接收到的事件
CurrentValueSubject带初始值的 Subject保留最新值,新订阅者会立即收到当前值
Array.publisher发布数组元素序列按顺序发送所有元素后完成
URLSession.DataTaskPublisher网络请求处理 HTTP 请求,返回数据或错误
Timer.TimerPublisher定时发送值按指定时间间隔重复发送值
NotificationCenter.Publisher监听通知接收系统通知事件

Publisher 的基本使用流程

1. 创建发布者
  let stringPublisher = Just("Hello Combine!"// 发布单个字符串 
  let arrayPublisher = [1234].publisher  // 发布整数序列 
2. 存储订阅引用(防止提前释放) 
  var cancellables =Set<AnyCancellable>() 
3. 订阅发布者 
  stringPublisher.sink { completion in  // 处理完成/错误  
       switch completion {
            case .finished:
                  print("✅ 完成"case .failure(let error): 
                  print("❌ 错误: error)") 
       }
   } receiveValue: { value in  
       // 处理收到的值 print("📥 收到: value)");                
   }
   .store(in: &cancellables)  // 存储订阅
   // 输出: 📥 收到: Hello Combine! // ✅ 完成

操作符链式处理

[1, 2, 3, 4, 5].publisher
              .filter { $0 % 2 == 0 }  // 过滤偶数 
              .map { $0 * 2 }  // 乘以2 
              .sink { value in 
                  print("处理后: value)") 
              } 
              .store(in: &cancellables) // 输出: 处理后: 4 // 处理后: 8 

Just 可以发布所有 Swift 类型

是一个向订阅者发送单个值,然后立即发送完成事件的发布者

发布单个字符串
1. 创建 Just 发布者(发布单个字符串)      
   let justPublisher = Just("Hello Combine!")
2. 存储订阅(必须!否则订阅会被立即释放,无法收到值) 
   var cancellables = Set<AnyCancellable>() 
3. 订阅 Publisher 
   justPublisher.sink { completion in  
       // 处理完成/错误
   } receiveValue: { value in
       print("📥 收到值:\(value)") 
  }
  .store(in: &cancellables) // 保存订阅
  
发布数字
 Just(100).sink { 
   print("整数:\($0)") 
 }.store(in: &cancellables) 
 
发布数组
Just([1,2,3]).sinkprint("数组:\($0)") 
}.store(in: &cancellables) 
发布自定义对象
struct User:CustomStringConvertible { 
   let name: String 
   var description: String { name } 
} 
Just(User(name: "张三")).sink {
   print("用户:\($0)")
}.store(in: &cancellables)

Future 是一个异步执行闭包,最终通过 Promise 发布单个成功值单个错误,然后立即完成的 Publisher

用途:处理网络请求、延时任务、文件读写等异步单结果场景

1. 创建 Future:包装异步延时任务 
let futurePublisher = Future<IntError> { promise in 
// 异步逻辑:延时 1 秒后返回结果   
   DispatchQueue.global().asyncAfter(deadline: .now() + 1) { 
     let number = Int.random(in: 1...100// 模拟成功/失败(随机) 
     if number > 10 { 
      // ✅ 成功:发送单个值 promise(.success(number)) else { 
      // ❌ 失败:发送错误 
      promise(.failure(NSError(domain: "数值太小", code: 0))) 
     } 
   } 
} 
2. 存储订阅(必须) 
var cancellables =Set<AnyCancellable>() 
3.订阅 Future 
futurePublisher.sink { completion in 
    switch completion { 
      case .finished: 
          print("✅ 发布完成"case .failure(let error): 
          print("❌ 失败:\(error.localizedDescription)") 
    } 
} receiveValue: { value in 
    print("📥 异步收到值:\(value)") 
}.store(in:&cancellables)

PassthroughSubject 是一种既可以当发布者、又可以当订阅者的类型,手动发送事件不会保存任何历史值,新订阅者只能收到订阅之后发出的事件

UI交互、组件通信的首选

1.创建 PassthroughSubject // 发送 String 类型值,永不失败(Failure = Never) 
    let passthroughSubject =PassthroughSubject<StringNever>() 
2.存储订阅(必须!) 
    var cancellables = Set<AnyCancellable>() 
3.订Subject 
   passthroughSubject.sink { completion in 
      print("✅ 订阅结束:\(completion)") 
   } receiveValue: { value in 
      print("📥 收到实时值:\(value)") 
   }.store(in: &cancellables) 
4.手动发送事件(核心!) 
   passthroughSubject.send("第一条消息") 
   passthroughSubject.send("第二条消息")
   passthroughSubject.send("第三条消息"5.手动发送完成信号(结束数据流)
   passthroughSubject.send(completion: .finished) 
6.完成后再发值:无效! 
   passthroughSubject.send("这条消息收不到")

CurrentValueSubject 是一种包装单个值的 Subject,会永久保存最新的一个值,新订阅者会立即收到当前缓存值,之后再接收后续更新的事件

用途:状态管理、UI 绑定

1. 创建 CurrentValueSubject(必须传初始值:0) 
   let countSubject = CurrentValueSubject<Int, Never>(0// 存储订阅 
   var cancellables = Set<AnyCancellable>() 
2. 第一次订阅
   countSubject .sink { value in
      print("【订阅1】收到值:\(value)") 
   }.store(in: &cancellables) 
3. 更新值(两种方式效果完全一致)
   countSubject.send(1// 方式1:send() 发送 
   countSubject.value = 2  // 方式2:直接修改 .value 属性(最常用)  
4. 【重点】第二次订阅:立即收到当前最新值(2)    
   countSubject.sink { value in 
      print("【订阅2】收到值:\(value)") 
   }.store(in: &cancellables) 
  // 继续更新 
  countSubject.send(35.随时读取当前缓存值 
  print("当前最新值:\(countSubject.value)") 
6.结束数据流 countSubject.send(completion: .finished)

错误处理

使用`.catch { ... }`处理错误并返回新的 Publisher
使用`.replaceError(with:)`用默认值替换错误
使用`.assertNoFailure()`强制无错误 (开发调试用)