Publish
@objc
func startTimer() {
print("Start")
subscription = Timer
.publish(every: 1, on: .main, in: .common)
.autoconnect() // 连接???
.scan(0, { (count, _) in // 生成一个value, 这里跟reduce很像
return count + 1
})
.sink(receiveCompletion: { completion in // 使用每次订阅到的value或事件
switch completion {
case .failure(let error):
print("Stream has failed with error: \(error)")
case .finished:
print("Stream has completed")
}
}, receiveValue: { count in
print("Updating the label to the current value: \(count.format)")
self.countLbl.text = count.format
})
}
@objc
func stopTimer() {
print("Stop")
subscription?.cancel()
}
publisher(不是publish
), assing
final class LevelsManager {
var level: Int = 0 {
didSet {
print("User's current level \(level)")
}
}
}
let lvlsManager = LevelsManager()
let lvlsRange = (0...100)
let cancellable = lvlsRange.
publisher.
assign(to: \.level, on: lvlsManager)
event
// notification, 从post变成了publisher
let subscription = NotificationCenter.default
.publisher(for: .UIApplicationDidBecomeActive)
.sink { _ in
print("App is active")
}
NotificationCenter.default
.publisher(for: UITextField.textDidChangedNotification, object: inputTextField)
.compactMap { ($0.object as? UITextField)?.text }
.map { "The user entered \($0)" }
//.sink { print($0) }
// 上面是sink, 下面是assign
.assign(to: \.text, on: label)
.store(in: $subscriptions) // 这里是设了个set用来存储
// var subscriptions = Set<AnyCancellable>()
可见没有像
rxSwift
一样的把事件直接pulish的用法, 而用notification
来中转, 说明目前只扩展了notification
吧
CurrentValue Subject
// 假设有一个对象/结构体
struct Personl{
var firstName: String
var lastName: String
var occupation: string
}
extension Person{
var message: String
"\(firstName) \(lastName) is a \(occupation)"
}
var isValid: Bool{
firstName.isEmpty && IlastName.isEmpty && loccupation.isEmpty
}
}
// 你不能去new一个Person出来, 而是一个subject
private let person = CurrentValueSubject<Person, Error>(Person(firstName: "", LastName:"", occupation:""))
// 用.value来使用(current subject)
NotificationCenter
.default
.publisher(for: UITextField.textDidChangeNotification, object: firstNameTxtField)
.compactMap({ ($0.object as? UITextField)?.text })
.sink {[weak self] val in
self?.person.value.firstName = val // 这里
}
.store(in: &subscriptions)
// person也是可以被监听的
person.sink { _ in
} receiveValue: { p in
print(p)
}
.store(in: &subscriptions)
// 发停止监听信号, 如点击完成时:
func didClickConfirm() {
person.send(completion: .finished)
}
Passthrough Subjects
let subject = PassthroughSubject<String, Never>()
// passthrough subjects没有.value属性
// 意味着它只能通用sink来取值
subject.sink { value in
print(value)
}
subject.send("Hello")
subject.send("World")
Multithread
final class IMageDownloaderViewMOdel {
let image = PassthroughSubject<UIImage, Never>()
var subscriptions = Set<AnyCancellable>()
func downloadImage(url: String) {
URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
.subscribe(on: DispatchQueue.global(qos: .background))
.map { UIImage(data: $0.data) }
.receive(on: DispatchQueue.main)
.handleEvent(receiveSubscription: { _ in
print("Download started")
rint("Start subscription on the main thread: \(Thread.isMainThread)")
}, receiveOutput: { image in
print("Downloaded image")
}, receiveCompletion: { completion in
print("Download completed")
})
.sink(receiveCompletion: { completion in
print("Finished subscription on the main thread: \(Thread.isMainThread)")
switch completion {
case .failure(let error):
print("Stream has failed with error: \(error)")
case .finished:
print("Stream has completed")
}
}, receiveValue: { [weak self] image in
print("Recieved subscription on the main thread: \(Thread.isMainThread)")
self?.image.send(image)
self?.image.send(completion: .finished)
})
.store(in: &subscriptions)
}
}
// binding
imgDownlloaderViewModel.image.assign(to: \.image, on: contentImageView)
.store(in: &subscriptions)
// trigger
imgDownloderViewModel.downloadImage(url: "https://www.example.com/image.jpg")
Memory Management
上面的例子中, 有个assign(to: on:)
方法, assign给了self的一个属性, 这就造成了循环引用, 导致这个viewmodel不能被释放掉, 如果存在这种情况, 就不要偷懒了, 还是用sink
方法, 在回调里用weak self
.
SwiftUI
- 一个
@Published
属性, 就是一个CurrentValueSubject
自定义Publisher
网络请求转成publisher, 使用eraseToAnyPublisher
func request<T: Decodable>(url: URL) -> AnyPublisher<T, Error> {
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: T.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
.store(in: &subscriptions)
}
如果在里面catch
了, 就可以返回AnyPublisher<T, Never>
Future, Defered
let future = Future<Int, Error> { promise in
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
promise(.success(42))
}
}
let publisher = future.eraseToAnyPublisher()
// deferred
let deferred = Deferred {
Future<Int, Error> { promise in
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
promise(.success(42))
}
}
}
let publisher = deferred.eraseToAnyPublisher()
网友评论