Swift 提供了多个类,通过 Combine 的 Publisher 系统 扩展了响应式处理的能力。这种设计思路常见于系统框架,目的是让传统的功能模块能够以响应式的方式与 Combine 集成。以下是一些常见的例子:
1、Timer
Timer.publish 提供了一种更现代化和响应式的方法来使用定时器。与传统的 Timer.scheduledTimer 或 Timer 构造器不同,它不是直接触发回调,而是返回一个 Combine 的 Publisher。这种机制更加适合 SwiftUI 的声明式和响应式编程风格。
使用方式
let timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
@State private var counter = 0
Text("Hello, World!")
.onReceive(timer) { time in
if counter == 5 {
timer.upstream.connect().cancel()
} else {
print("The time is now \(time)")
}
counter += 1
}
2、NotificationCenter
NotificationCenter 是一个发布-订阅模式的中心,通常用于系统级和应用内部的事件通知。通过 Combine,可以将 NotificationCenter 的通知转化为 Publisher。
使用方式
let publisher = NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)
publisher.sink { notification in
print("App became active!")
}
核心特性
1)publisher(for:) 方法:将特定的通知转换为一个 Combine Publisher。
2)返回值类型:NotificationCenter.Publisher,输出值为 Notification。
常见用途:
监听系统通知(如键盘显示、应用状态变更)。
处理自定义的广播事件。
3、URLSession
URLSession 是用于网络请求的核心类。结合 Combine,可以将网络请求结果转化为 Publisher,避免传统的回调或闭包模式。
使用方式
let url = URL(string: "https://api.example.com/data")!
let publisher = URLSession.shared.dataTaskPublisher(for: url)
publisher
.map(\.data)
.decode(type: MyDataModel.self, decoder: JSONDecoder())
.sink(
receiveCompletion: { completion in
print("Completion: \(completion)")
},
receiveValue: { data in
print("Received data: \(data)")
}
)
核心特性
1)dataTaskPublisher(for:) 方法:返回 URLSession.DataTaskPublisher,用来处理网络请求。
2)返回值类型:Publisher,输出值为 (Data, URLResponse)。
优势:
支持链式数据处理(如解析 JSON)。
与错误处理(tryCatch 等)集成简洁。
4、CLLocationManager
CLLocationManager 是获取地理位置信息的核心类。通过 Combine 的扩展,可以将位置更新事件转化为 Publisher。
扩展实现 原生的 CLLocationManager 没有直接支持 Combine,但可以通过自定义扩展实现。例如:
import Combine
import CoreLocation
class LocationManager: NSObject, ObservableObject {
private let manager = CLLocationManager()
let publisher = PassthroughSubject<CLLocation, Never>()
override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
publisher.send(location)
}
}
}
结合 SwiftUI 使用
import SwiftUI
import CoreLocation
struct ContentView: View {
@StateObject private var locationManager = LocationManager()
@State private var location: CLLocation?
var body: some View {
Text(location != nil ? "Lat: \(location!.coordinate.latitude)" : "Fetching location...")
.onReceive(locationManager.publisher) { newLocation in
location = newLocation
}
}
}
5、FileManager
虽然 FileManager 本身不直接支持 Combine,但可以通过封装创建一个 Publisher 来监听文件变化。例如,监控目录内容的变化。
自定义 FileManager 的 Publisher
import Combine
extension FileManager {
func publisher(for directory: URL) -> AnyPublisher<[URL], Error> {
Future { promise in
do {
let contents = try self.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)
promise(.success(contents))
} catch {
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
}
结合使用
let directoryURL = URL(fileURLWithPath: "/path/to/directory")
FileManager.default.publisher(for: directoryURL)
.sink(
receiveCompletion: { completion in
print("Completion: \(completion)")
},
receiveValue: { files in
print("Files: \(files)")
}
)
6、AVPlayer
AVPlayer 是用于播放音视频的类。通过 Combine,可以监听播放器状态和时间进度。
时间进度 Publisher
let player = AVPlayer(url: URL(string: "https://example.com/video.mp4")!)
let timePublisher = player.publisher(for: \.currentTime)
timePublisher
.sink { time in
print("Current time: \(CMTimeGetSeconds(time))")
}
结合 KVO
Combine 提供了一种便捷的方法,通过 @Published 属性或 publisher(for:) 监听 KVO 属性。
let statusPublisher = player.publisher(for: \.status)
statusPublisher
.sink { status in
print("Player status: \(status)")
}
7、UserDefaults
UserDefaults 的变化也可以转化为 Publisher。
实现方式
通过 Combine 的 @Published 属性包装 UserDefaults 的值,或者通过 NotificationCenter 的 Combine Publisher 监听变化:
let defaults = UserDefaults.standard
let publisher = NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification)
publisher.sink { _ in
print("UserDefaults changed")
}
总结
以下是一些常见的支持 Publisher 响应式处理的类或场景:
1、Timer:publish(every:on:in:),用途为定时事件流;
2、NotificationCenter:publisher(for:) ,用途为系统或自定义通知;
3、URLSession:dataTaskPublisher(for:) ,用途为网络请求;
4、CLLocationManager:自定义 Publisher(Combine 扩展),用途为地理位置更新;
5、FileManager:自定义目录变化的 Publisher,用途为监听文件系统
6、AVPlayer:KVO 或 Combine 扩展,用途为音视频播放状态、时间更新;
7、UserDefaults:NotificationCenter 或 @Published,用途为偏好设置变化。
Combine 的设计理念是使一切皆可响应,因此许多系统类都通过 Publisher 进行扩展或者可以轻松集成到响应式流中。
相关文章
Swift和Foundation框架创建和管理定时任务的Timer类:https://fangjunyu.com/2024/12/14/swift%e5%92%8cfoundation%e6%a1%86%e6%9e%b6%e5%88%9b%e5%bb%ba%e5%92%8c%e7%ae%a1%e7%90%86%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1%e7%9a%84timer%e7%b1%bb/