为了方便使用,Foundation 为 iOS 开发中的几个常见操作提供了直接获取 Publisher 的方式。
- URLSession Publisher
- Timer Publisher
- Notification Publisher
- KVO
- @Published
URLSession Publisher
这是 URLSession 新增的一种网络 API,通过这个 API 可以更加简单的完成网络请求数据转换等操作。
代码语言:javascript复制// 服务器返回的数据对应的Model
struct NewsModel: Codable {
var reason: String
var error_code: Int
var result:Result
}
struct Result: Codable {
var stat: String
var data:[DataItem]
}
// 实现Hashable,List中的数据必须实现
struct DataItem: Codable, Hashable {
var title: String
var date: String
var category: String
var author_name: String
var url: String
}
let url = URL(string: "http://v.juhe.cn/toutiao/index?type=top&key=d1287290b45a69656de361382bc56dcd")
let request = URLRequest(url: url!)
let session = URLSession.shared
let backgroundQueue = DispatchQueue.global()
let dataPublisher = session.dataTaskPublisher(for: request)
.retry(5)
.timeout(5, scheduler: backgroundQueue)
.map{$0.data}
.decode(type: NewsModel.self, decoder: JSONDecoder())
.subscribe(on: backgroundQueue)
.eraseToAnyPublisher()
let subscription = dataPublisher.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {_ in }) {
newsModel in
print(newsModel.result.data)
}
Timer Publisher
当收到 Subscriber 请求时,大部分的 Publisher 会立即提供数据, 如 Just。但有一种符合 ConnectablePublisher 协议的 Publisher,它需要某种机制机制来启动数据流。而 Timer.TimerPublisher 就是这种类型的 Publisher。ConnectablePublisher 不同于普通的 Publisher,需要明确地对其调用connect()
或者 autoconnect()
方法,它才会开始发送数据。
- autoconnect()
// every:间隔时间 on:在哪个线程 in:在哪个Runloop
let subscription = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect()
.sink { _ in
print("Hello")
}
// 可以取消
// subscription.cancel()
- connect()
// every:间隔时间 on:在哪个线程 in:在哪个Runloop
let timerPublisher = Timer.publish(every: 1, on: .main, in: .default)
let cancellablePublisher = timerPublisher
.sink { _ in
print("Hello")
}
let subscription = timerPublisher.connect()
// 可以取消
// subscription.cancel()
Notification Publisher
和 Timer 类似,Foundation 中的 NotificationCenter 也提供了创建 Publisher 的辅助 API。
- 系统通知
let subscription = NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
.sink(receiveValue: { _ in
print("App进入后台")
})
- 自定义通知
// 自定义通知名
extension Notification.Name{
static var myNotiName:Notification.Name {
return Notification.Name("YungFan")
}
}
// 订阅通知
let subscription = NotificationCenter.default.publisher(for: .myNotiName)
.sink(receiveValue: { notification in
print(notification.object as? String)
})
// 创建通知
let noti = Notification(name: .myNotiName, object: "some info", userInfo: nil)
// 发送通知
NotificationCenter.default.post(noti)
- SwiftUI 监听 App 进入后台和重返前台。
struct ContentView : View {
var body: some View {
Text("")
// onReceive捕获通知
.onReceive(NotificationCenter.default.publisher(for:
UIApplication.willResignActiveNotification)) { _ in
print("App进入后台")
}.onReceive(NotificationCenter.default.publisher(for:
UIApplication.willEnterForegroundNotification)) { _ in
print("App进入前台")
}.onAppear() {
print("第一次显示")
}
}
}
KVO
任何 NSObject 对象一旦被KVO监听,则可以成为一个 Publisher。
代码语言:javascript复制class Person: NSObject {
@objc dynamic var age: Int = 0
}
let person = Person()
let _ = person.publisher(for: .age)
.sink { newValue in
print("person的age改成了(newValue)")
}
person.age = 10 // 改变时会收到通知
@Published
可以将任何一个属性转换成 Publisher,广泛应用于 UIKit 与 SwiftUI 中,此部分内容请参考《SwiftUI 实用教程》。
代码语言:javascript复制class Student {
@Published var name: String = "zhangsan"
}
let stu = Student()
// 访问的时候要带上$
let subscription = stu.$name.sink {
print($0)
}
stu.name = "lisi"
/*输出
zhangsan
lisi
*/