Combine-Foundation中的Publisher

2020-03-24 10:31:33 浏览数 (1)

为了方便使用,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()
代码语言:javascript复制
// every:间隔时间 on:在哪个线程 in:在哪个Runloop
let subscription = Timer.publish(every: 1, on: .main, in: .default)
    .autoconnect()
    .sink { _ in
        print("Hello")
}

// 可以取消
// subscription.cancel()
  • connect()
代码语言:javascript复制
// 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。

  • 系统通知
代码语言:javascript复制
let subscription = NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
    .sink(receiveValue: { _ in
        print("App进入后台")
    })
  • 自定义通知
代码语言:javascript复制
// 自定义通知名
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 进入后台和重返前台。
代码语言:javascript复制
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
 */

0 人点赞