Rxswift (1) Functional responsive programming idea
RxSwift (II) Sequence core logic analysis
Create, subscribe, and destroy an RxSwift Observable
RxSwift (4) higher-order functions
RxSwift (v)
(6) Dispose source code analysis
RxSwift (7) RxSwift vs. SWIFT usage
RxSwift (x) Basic Usage part 1- Sequence, subscribe, destroy
RxSwift Learning 12 (Basics 3- UI Control Extensions)
- Download the latest code from GitHub: github.com/ReactiveX/R…
1. RxSwift profile
- The role of RxSwift
1) When writing codes, we often need to detect changes of some values (such as textFiled input value changes, data request completion or failure changes) and process them accordingly. In the past, different event-passing methods were used for different situations, such as Delegate, notification, target-Action, KVO, etc. The appearance of RectiveX mechanism (implemented by RxSwift) unify the event passing response method in the program. Replace the usual event-passing methods (delegate, notification, target-Action, etc.) with Rx’s “signal chain” approach. (2) If we usually use MVVM development mode, through RxSwift can obtain more convenient data binding method, making MVVM development more powerful.
- RxSwift and RxCocoa
RxSwift: It is based only on the Rx standard implementation library of the Swift language, so RxSwift does not contain any Cocoa or UI classes. RxCocoa: A library developed for iOS based on RxSwift that adds Rx features to native UI controls via Extension, making it easier to subscribe to and respond to their events.
2. Simple use of RxSwift
2.1 Comparison sample between responsive programming and traditional programming
- Example 2.1
- There is a requirement: the table is showing the song information (song name, and artist) click on any cell, print out the corresponding song information in the console.
- In the traditional way, first we create a Music structure that holds the name of the song and the name of the artist. It also follows the CustomStringConvertible protocol to facilitate debugging of output.
import UIKit
// Song structure
struct Music {
let name: String / / title
let singer: String / / singer
init(name: String, singer: String) {
self.name = name
self.singer = singer
}
}
// Implement CustomStringConvertible protocol to facilitate output debugging
extension Music: CustomStringConvertible {
var description: String {
return Name: ""\(name)Singer:\(singer)"}}Copy the code
2.1.1 Traditional programming
- So let’s write a ViewModel
import Foundation
// Song list data source
struct MusicListViewModel {
let data = [
Music(name: "Unconditional", singer: Eason Chan),
Music(name: "You were a teenager.", singer: "S.H.E"),
Music(name: "The old me.", singer: "Kit Chan"),
Music(name: "In Jupiter", singer: "Hackberry")]}Copy the code
- ViewController code (viewcontroller.swift)
- We then set up the delegate of the UITableView and have the view controller implement the UITableViewDataSource and UITableViewDelegate protocols, as well as the associated protocol methods.
- I’m sure you’ve written this a million times, and there’s nothing to tell. So let’s see, this is 43 lines of code.
import UIKit
import RxSwift
class ViewController: UIViewController {
/ / tableView object
@IBOutlet weak var tableView: UITableView!
// Song list data source
let musicListViewModel = MusicListViewModel(a)override func viewDidLoad(a) {
super.viewDidLoad()
// Set the proxy
tableView.dataSource = self
tableView.delegate = self}}extension ViewController: UITableViewDataSource {
// Returns the number of cells
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return musicListViewModel.data.count
}
// Returns the corresponding cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell")!
letmusic = musicListViewModel.data[indexPath.row] cell.textLabel? .text = music.name cell.detailTextLabel?.text = music.singerreturn cell
}
}
extension ViewController: UITableViewDelegate {
// Cell click
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Information about the song you selected [\(musicListViewModel.data[indexPath.row])】")}}Copy the code
- Let’s look at the programming of Rxswift
2.1.2 Rxswift programming
- right
ViewModel
Do some modification- Here we turn the data property into an observable sequence object (
Observable Squence
), and the contents of the object are exactly the same as those contained in the array. - I’ll talk more about observable sequence objects in a later article. Simply put, a “sequence” can subscribe to these values.
Subscribe
), somewhat similar to “Notice (NotificationCenter
)”
- Here we turn the data property into an observable sequence object (
import RxSwift
// Song list data source
struct MusicListViewModel {
let data = Observable.just([
Music(name: "Unconditional", singer: Eason Chan),
Music(name: "You were a teenager.", singer: "S.H.E"),
Music(name: "The old me.", singer: "Kit Chan"),
Music(name: "In Jupiter", singer: "Hackberry"),])}Copy the code
- View Controller code (
ViewController.swift
)- We no longer need to implement datasource and delegate protocols. I’m going to write some reactive code that binds the data to the UITableView.
- We’re only going to need 31 lines of code here, which is a quarter less code than we had before. And the code is a little cleaner.
-
DisposeBag: Rx automatically interprets the resources bound to the view controller or its owner when it is about to be destroyed. It is implemented through a “subscription disposition mechanism” (similar to removeObserver of NotificationCenter). Rx.items (cellIdentifier:) : This is a wrapper around RX based on the cellForRowAt data source method. In the traditional way we also have a numberOfRowsInSection method, which is no longer needed when we use Rx (Rx already does that for us). Rx. modelSelected: This is a wrapper around RX based on the UITableView delegate callback method didSelectRowAt.
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
/ / tableView object
@IBOutlet weak var tableView: UITableView!
// Song list data source
let musicListViewModel = MusicListViewModel(a)// Responsible for object destruction
let disposeBag = DisposeBag(a)override func viewDidLoad(a) {
super.viewDidLoad()
// Bind data source data to tableView
musicListViewModel.data
.bind(to: tableView.rx.items(cellIdentifier:"musicCell")) { _, music, cell incell.textLabel? .text = music.name cell.detailTextLabel?.text = music.singer }.disposed(by: disposeBag)//tableView click response
tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
print("Information about the song you selected [\(music)】")
}).disposed(by: disposeBag)
}
}
Copy the code
2.2 Observable Describes and creates Observable sequences
-
Observable is the foundation of Rx, so we need to get a basic understanding of it.
-
Observable:
The Observable class, which is the basis of the Rx framework, is called an Observable sequence. What it does is generate a series of events asynchronously, that is, an Observable emits events (Element: T) irregularly over time. These events can also carry data, and their generics are used to specify the type of data carried by the Event. With Observable sequences, we also need an Observer to subscribe to them so that the subscriber can receive the events that an Observable emits from time to time.
-
Event
-
If you look at the RxSwift source code, you can find that the Event is defined as follows:
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
Copy the code
- You can see that an Event is an enumeration, which means that an Observable emits three different types of events:
- next:
next
An event is an event that can carry data, and it can be said to be the “most normal” event. - error:
error
An event represents an error, and it can carry specific error content onceObservable
issuederror event
, thisObservable
So it’s going to terminate, and it’s not going to emit an event anymore. - completed:
completed
The event saidObservable
The emitted event ends normally, just like error, onceObservable
issuedcompleted event
, thisObservable
So it’s going to terminate, and it’s not going to emit an event anymore.
- next:
2.2.1 Comparison between Observable and Sequence
- 1) For better understanding, we can take each of them
Observable
Imagine the Sequence in a Swift:- That is, a
Observable
(ObservableType
) is equivalent to a sequenceSequence
(SequenceType
). ObservableType.subscribe(_:)
The method is essentially the same thing asSequenceType.generate()
- That is, a
- 2) But there are many differences:
- The Swift,
SequenceType
It’s a synchronous cycle, andObservable
It’s asynchronous. Observable
The object automatically passes the Event as a parameter when it has any EventObservableType.subscribe(_:)
Emit, without using the next method.
- The Swift,
2.2.2 Creating an Observable sequence
- There are several ways to create an Observable sequence
- Just () method
(1) The method is initialized by passing in a default value. (2) In the following example, the type of observable is explicitly marked as Observable, which specifies that the data type of the event emitted by the Observable must be Int.
let observable = Observable<Int>.just(5)
Copy the code
- Of () method
(1) The method can accept a variable number of arguments (all of the same type). (2) In the example below, I don’t explicitly declare the generic type of Observable, and Swift will automatically infer the type.
let observable = Observable.of("A"."B"."C")
Copy the code
- The from () method
(1) This method takes an array argument. (2) The elements in the data in the following example are treated as the data content of the Observable event. The result is the same as in the of() example above.
let observable = Observable.from(["A"."B"."C"])
Copy the code
- The empty() method creates an Observable sequence of empty content.
let observable = Observable<Int>.never()
Copy the code
- The never() method creates an Observable sequence that never emits an Event (and never terminates).
let observable = Observable<Int>.never()
Copy the code
- The error() method creates an Observable that does nothing but sends an error.
enum MyError: Error {
case A
case B
}
let observable = Observable<Int>.error(MyError.A)
Copy the code
- The range () method
(1) This method creates an Observable sequence that starts with all values in the range by specifying the start and end values. (2) In the following example, the two methods create the same Observable sequence.
/ / use the range ()
let observable = Observable.range(start: 1.count: 5)
/ / use of ()
let observable = Observable.of(1.2.3 ,4 ,5)
Copy the code
- RepeatElement () this method creates an Observable sequence that emits events for the given element indefinitely (never terminating).
let observable = Observable.repeatElement(1)
Copy the code
- The generate () method
(1) This method creates an Observable that gives an action only when all the provided criteria are true. (2) In the following example, the two methods create the same Observable sequence.
// Use generate()
let observable = Observable.generate(
initialState: 0,
condition: { $0< =10 },
iterate: { $0 + 2})// Use the of() method
let observable = Observable.of(0 , 2 ,4 ,6 ,8 ,10)
Copy the code
- The create () method
(1) The method takes a block and processes each incoming subscription. (2) Here is a simple example. Subscription code has been added for demonstration purposes
// The block has a callback parameter observer that is the subscriber to the Observable
// When a subscriber subscribes to an Observable, it passes the subscriber as an argument to the block to perform something
let observable = Observable<String>.create{observer in
// A.next event was issued to the subscriber, carrying a data "hangge.com"
observer.onNext("hangge.com")
// A.completed event was issued to the subscriber
observer.onCompleted()
Since a subscription has a Disposable return value, we must returen a Disposable at the end
return Disposables.create()
}
// Subscribe tests
observable.subscribe {
print($0)}Copy the code
- Deferred () method
(1) This method is equivalent to creating an Observable factory, which executes deferred Observable creation by passing in a block where the actual sequence object is instantiated. (2) Here is a simple demo:
// Is used to indicate whether it is odd or even
var isOdd = true
// Defer Observable initialization with the Deferred () method, pass the block to initialize the Observable and return it.
let factory : Observable<Int> = Observable.deferred {
// alternate odd and even numbers every time this block is executedisOdd = ! isOdd// Based on the isOdd parameter, determine whether an odd or even Observable is created and returned
if isOdd {
return Observable.of(1.3.5 ,7)}else {
return Observable.of(2.4.6.8)}}// First subscription test
factory.subscribe { event in
print("\(isOdd)", event)
}
// Second subscription test
factory.subscribe { event in
print("\(isOdd)", event)
}
Copy the code
The results are as follows, and we can see that the two observables we subscribe to are different:
(1) The Observable created by this method emits an index element every set period of time. And it’s going to keep going. (2) the following method causes it to be sent every 1 second and is sent from the MainScheduler.
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable.subscribe { event in
print(event)
}
Copy the code
- The timer () method
- (1) There are two ways to use this method. One is to create an Observable sequence that generates a unique element after a set period of time.
// Emits a unique element 0 after 5 seconds
let observable = Observable<Int>.timer(5, scheduler: MainScheduler.instance)
observable.subscribe { event in
print(event)
}
Copy the code
- (2) The other is an Observable sequence that generates an element at intervals after a set period of time.
// Emit an element every 1 second after a delay of 5 seconds
let observable = Observable<Int>.timer(5, period: 1, scheduler: MainScheduler.instance)
observable.subscribe { event in
print(event)
}
Copy the code
2.3 Observable subscription, Event listening, and subscription destruction
2.3.1 observables subscription
- With an Observable, we also subscribe to it using the subscribe() method and receive its events.
2.3.1.1 The first subscription method
- (1) We subscribe to an Observable using subscribe(). The block callback parameter of this method is the emitted event, which we print directly.
let observable = Observable.of("A"."B"."C")
observable.subscribe { event in
print(event)
}
Copy the code
The result is as follows:
The default values set when initializing the Observable sequence are sent sequentially through the.next event. When the Observable completes sending the initial data, it automatically emits a.Completed event.
- (2) If you want to obtain the data of this event, you can obtain it through event. Element.
let observable = Observable.of("A"."B"."C")
observable.subscribe { event in
print(event.element)
}
Copy the code
The running results are as follows:
2.3.1.2 Second subscription method
- (1) RxSwift also provides another
subscribe
Method, which can categorize events:- Different types of events are handled through different block callbacks. (Where onDisposed indicates the callback after the subscription is disposed)
- At the same time, the data carried by the event will be directly unpacked as parameters, which is convenient for us to use.
let observable = Observable.of("A"."B"."C")
observable.subscribe(onNext: { element in
print(element)
}, onError: { error in
print(error)
}, onCompleted: {
print("completed")
}, onDisposed: {
print("disposed")})Copy the code
The running results are as follows:
- (2)
subscribe()
methodsonNext
,onError
,onCompleted
和onDisposed
The four callback block arguments have default values, that is, they are optional. So we could just do itonNext
Regardless of the other cases.
let observable = Observable.of("A"."B"."C")
observable.subscribe(onNext: { element in
print(element)
})
Copy the code
The command output is as follows: A B C
2.3.2 Event Listening
-
Introduce’s elite doOn
(1) We can use the doOn method to listen for the life cycle of events, which will be called before each event is sent. (2) Like SUBSCRIBE, it can handle different types of events through different block callbacks. For example, the do(onNext:) method is called before subscribe(onNext:) and the do(onCompleted:) method is called before subscribe(onCompleted:).
-
Use the sample
let observable = Observable.of("A"."B"."C")
observable
.do(onNext: { element in
print("Intercepted Next:", element)
}, onError: { error in
print("Intercepted the Error:", error)
}, onCompleted: {
print("Intercepted Completed")
}, onDispose: {
print("Intercepted Disposed")
})
.subscribe(onNext: { element in
print(element)
}, onError: { error in
print(error)
}, onCompleted: {
print("completed")
}, onDisposed: {
print("disposed")})Copy the code
2.3.3 Subscription destruction
2.3.3.1 Observable Creation to Termination Process
- (1) One
Observable
Instead of being activated immediately after a sequence is created to emit events, it is activated only when it has been subscribed by someone. - (2)
Observable
Once the sequence is activated, wait until it’s emitted.error
or.completed
的event
After that, it was ended.
2.3.3.2 the dispose () method
– (1) Using this method we can manually cancel a subscription. – (2) If we feel that the subscription is no longer needed, we can call dispose() method to dispose of the subscription to prevent memory leakage. – (3) When a subscription is disposed, an Observable can’t receive the message if it sends an event again. Here is a simple usage example.
let observable = Observable.of("A"."B"."C")
Use the subscription constant to store this subscription method
let subscription = observable.subscribe { event in
print(event)
}
// Call the dispose() method on the subscription
subscription.dispose()
Copy the code
2.3.3.13 DisposeBag
- (1) In addition to dispose() method, we more often use an object called DisposeBag to manage the destruction of multiple subscriptions:
- We can think of a DisposeBag object as a garbage bag into which we can put all of our used subscriptions.
- The DisposeBag then calls its Dispose () method on all subscriptions as it approaches dealloc.
- (2) Below is a simple usage example.
let disposeBag = DisposeBag(a)// First Observable, and its subscription
let observable1 = Observable.of("A"."B"."C")
observable1.subscribe { event in
print(event)
}.disposed(by: disposeBag)
// The second Observable and its subscription
let observable2 = Observable.of(1.2.3)
observable2.subscribe { event in
print(event)
}.disposed(by: disposeBag)
Copy the code
2.4 AnyObserver and Binder
2.4.1 Observer (Observer)
- The role of an Observer, “Observer,” is to listen for an event and then respond to that event. Or anything that responds to an event is an observer. Such as:
- When we click the button, a prompt box pops up. The Observer is an Observer
- When we request a remote JSON data, we print it out. So this “print JSON data” is the Observer
2.4.2 Creating an Observer
2.4.2.1 Create observer directly in subscribe and bind methods
- Created in the SUBSCRIBE method
(1) The most direct way to create an observer is to describe how to respond to an event after the Subscribe method of an Observable. (2) In the following example, the observer is constructed from the following closures: onNext, onError, onCompleted.
let observable = Observable.of("A"."B"."C")
observable.subscribe(onNext: { element in
print(element)
}, onError: { error in
print(error)
}, onCompleted: {
print("completed")})Copy the code
Running results:
- Create an Observable that periodically generates index numbers and displays them on the label tag:
Observable (emits an index every second)
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "Current index:\ [$0 )"}
.bind { [weak self](text) in
// The number of sent indexes is displayed on the label
self? .label.text = text } .disposed(by: disposeBag)Copy the code
2.4.2.2 Creating an Observer using AnyObserver
- AnyObserver can be used to describe any kind of observer.
- Used in conjunction with the SUBSCRIBE method
/ / observer
let observer: AnyObserver<String> = AnyObserver { (event) in
switch event {
case .next(let data):
print(data)
case .error(let error):
print(error)
case .completed:
print("completed")}}let observable = Observable.of("A"."B"."C")
observable.subscribe(observer)
Copy the code
- Use with bindTo method
/ / observer
let observer: AnyObserver<String> = AnyObserver{[weak self] (event) in
switch event {
case .next(let text):
// The number of sent indexes is displayed on the label
self? .label.text = textdefault:
break}}Observable (emits an index every second)
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "Current index:\ [$0 )"}
.bind(to: observer)
.disposed(by: disposeBag)
}
Copy the code
2.4.2.3 Using Binder to create observers
(1) Binder is more focused on specific scenarios than AnyObserver. Binder does not handle error events and ensures that bindings are executed on a given Scheduler (MainScheduler by default). (2) fatalError will be executed in debug and error messages printed in publish.
- Instance 2.4.2.3
/ / observer
let observer: Binder<String> = Binder(label) { (view, text) in
// The number of sent indexes is displayed on the label
view.text = text
}
Observable (emits an index every second)
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "Current index:\ [$0 )"}
.bind(to: observer)
.disposed(by: disposeBag)
Observable (emits an index every second)
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map{$0 % 2= =0 }
.bind(to: button.rx.isEnabled)
.disposed(by: disposeBag)
Copy the code
2.5 Customizing Binding Attributes
-
Sometimes we want UI controls to have some observers by default when they are created, rather than having to create separate observers for them each time. For example, we want all uilabels to have a fontSize binding property that automatically changes the fontSize of the label based on the event value.
-
By extending the UI class here we’ve extended UILabel to add a fontSize binding property.
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
let disposeBag = DisposeBag(a)override func viewDidLoad(a) {
Observable (emits an index every 0.5 seconds)
let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
observable
.map { CGFloat($0) }
.bind(to: label.fontSize) // Zoom in font according to index number
.disposed(by: disposeBag)
}
}
extension UILabel {
public var fontSize: Binder<CGFloat> {
return Binder(self) { label, fontSize in
label.font = UIFont.systemFont(ofSize: fontSize)
}
}
}
Copy the code
- Since you use RxSwift, the more formal way to write this is to extend Reactive. Again, we’re adding a fontSize binding property to UILabel.
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
let disposeBag = DisposeBag(a)override func viewDidLoad(a) {
Observable (emits an index every 0.5 seconds)
let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
observable
.map { CGFloat($0) }
.bind(to: label.rx.fontSize) // Zoom in font according to index number
.disposed(by: disposeBag)
}
}
extension Reactive where Base: UILabel {
public var fontSize: Binder<CGFloat> {
return Binder(self.base) { label, fontSize in
label.font = UIFont.systemFont(ofSize: fontSize)
}
}
}
Copy the code
- RxSwift binding properties (UI Observer)
In fact, RxSwift already provides us with many common bindable properties. UILabel, for example, has two binding attributes, text and attributedText.
extension Reactive where Base: UILabel {
/// Bindable sink for `text` property.
public var text: Binder<String? > {return Binder(self.base) { label, text in
label.text = text
}
}
/// Bindable sink for `attributedText` property.
public var attributedText: Binder<NSAttributedString? > {return Binder(self.base) { label, text in
label.attributedText = text
}
}
}
Copy the code
In the example above, we don’t need to customize the UI observer. We can just use the binding property provided by RxSwift.
Observable (emits an index every second)
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "Current index:\ [$0 )"}
.bind(to: label.rx.text) // The number of sent indexes is displayed on the label
.disposed(by: disposeBag)
Copy the code
2.6 the Subjects, the Variables
When we create an Observable, we prepare all the data that will be sent out in advance, and wait until someone subscribes to it to send the data out via events. But sometimes we want an Observable to dynamically “get” or “produce” new data at runtime and send it out via events. For example, subscribe to the input content of an input box. When a user enters a word, the Observable associated with the input box issues an Event with the input content to notify all subscribers. This can be done using Subjects, which will be described below.
2.6.1 the Subjects
2.6.1.1 introduction of the Subjects
– (1) Subjects is both a subscriber and an Observable: it is a subscriber because it can receive new values dynamically. It is also an Observable, because when Subjects has a new value, it sends the new value to all its subscribers through events.
– (2) There are four types of Subjects, namely, PublishSubject, BehaviorSubject, ReplaySubject and Variable. They have their own characteristics, but also have something in common:
- First of all, they all are
Observable
Their subscribers can receive the new ones they send outEvent
.- until
Subject
a.complete
or.error
的Event
Later, theSubject
It ends, and it doesn’t go out again.next
Events.- For those in the
Subject
Subscribers who subscribe to him after the end will also receive itsubject
Sent out.complete
或.error
的event
And tell the new subscriber that it’s over.- The biggest difference between them is whether a new subscriber can receive it when he first subscribes to it
Subject
Old ones that have been issued beforeEvent
And how many, if any.
– (3) Several commonly used methods of Subject:
onNext(:)
On (.next(:)). This method is equivalent to the subject receiving a.next event.onError(:)
: is a shorthand for on(.error(:)). This method is equivalent to the subject receiving an.error event.onCompleted()
: is a convenient form of on(.completed). This method is equivalent to the subject receiving a.Completed event.
2.6.1.2 PublishSubject
- (1) Basic introduction
The PublishSubject is the most common Subject that can be created without an initial value. PublishSubject subscribers can receive new events issued by the Subject after the subscription from the time they started the subscription, but not events they issued before the subscription.
- (2) The sequence diagram is as follows: The top one is
PublishSubject
. The following two represent two new subscriptions, which are subscribed at different points in time, as you can seePublishSubject
Subscribers will only receive their subscriptionsEvent
.
- Instance 2.6.1.2:
let disposeBag = DisposeBag(a)// Create a PublishSubject
let subject = PublishSubject<String> ()// Since there are currently no subscribers, this information will not be printed to the console
subject.onNext("111")
// Subscribe to subject for the first time
subject.subscribe(onNext: { string in
print("1st subscription:", string)
}, onCompleted:{
print("1st Subscription: onCompleted")
}).disposed(by: disposeBag)
// If there is currently one subscription, this information is printed to the console
subject.onNext("222")
// Subscribe the second time to subject
subject.subscribe(onNext: { string in
print("Second subscription:", string)
}, onCompleted:{
print("Second subscription: onCompleted")
}).disposed(by: disposeBag)
// If there are currently two subscriptions, this information is printed to the console
subject.onNext("333")
// Let the subject end
subject.onCompleted()
// The.next event is emitted when the subject is finished.
subject.onNext("444")
// After the subject completes, all of its subscriptions (including those that completed) will receive the Subject's.completed event.
subject.subscribe(onNext: { string in
print("3rd subscription:", string)
}, onCompleted:{
print("Third subscription: onCompleted")
}).disposed(by: disposeBag)
Copy the code
Running results:
2.6.1.3 BehaviorSubject
– (1) Basic introduction
The BehaviorSubject needs to be created with a default initial value. When a subscriber subscribes to it, the subscriber immediately receives an event emitted on BehaviorSubjects. Then, just like normal, it also receives a new event emitted by the BehaviorSubject.
– (2) The timing diagram is as follows: The BehaviorSubject at the top is BehaviorSubject. The following two subscriptions represent two new subscriptions at different times. You can see that the subscriber of BehaviorSubject starts out with an Event that BehaviorSubjects sent earlier.
- Instance 2.6.1.3
let disposeBag = DisposeBag(a)// Create a BehaviorSubject
let subject = BehaviorSubject(value: "111")
// Subscribe to subject for the first time
subject.subscribe { event in
print("1st subscription:", event)
}.disposed(by: disposeBag)
// Send the next event
subject.onNext("222")
// Send an error event
subject.onError(NSError(domain: "local", code: 0, userInfo: nil))
// Subscribe the second time to subject
subject.subscribe { event in
print("Second subscription:", event)
}.disposed(by: disposeBag)
Copy the code
Running results:
2.6.1.4 ReplaySubject
- (1) Basic introduction
ReplaySubject
You need to set one at creation timebufferSize
Represents the number of events it caches for which it has sent.- For example, a
ReplaySubject
的bufferSize
Set to 2 and it sends out 3.next
的event
, then it will divide the last two (the nearest two)event
Cache it. Now if I have onesubscriber
Subscribe to thisReplaySubject
So thissubscriber
It will immediately receive the first two cached.next
的event
.- If a
subscriber
Subscription has endedReplaySubject
Except that it will receive a cache.next
的event
Also, you will receive the termination.error
or.complete
的event
.
- (2) The sequence diagram is as follows: The top one is
ReplaySubject
(bufferSize
Set it to 2). The following two represent two new subscriptions at different points in time. You can findReplaySubject
Subscribers will receive it right from the startReplaySubject
The two we sent out earlierEvent
(if any).
- Instance 2.6.1.4:
let disposeBag = DisposeBag(a)// Create a ReplaySubject with bufferSize 2
let subject = ReplaySubject<String>.create(bufferSize: 2)
// Send three consecutive next events
subject.onNext("111")
subject.onNext("222")
subject.onNext("333")
// Subscribe to subject for the first time
subject.subscribe { event in
print("1st subscription:", event)
}.disposed(by: disposeBag)
// Send a next event
subject.onNext("444")
// Subscribe the second time to subject
subject.subscribe { event in
print("Second subscription:", event)
}.disposed(by: disposeBag)
// Let the subject end
subject.onCompleted()
// Subscribe subject for the third time
subject.subscribe { event in
print("3rd subscription:", event)
}.disposed(by: disposeBag)
Copy the code
Running results:
2.6.2 Variables
- Note: Since Variable will be deprecated in future releases, it is recommended that you use BehaviorRelay as described below instead of Varible
2.6.2.1 Variable
– (1) Basic introduction
Variable
In fact, yes.BehaviorSubject
So it must also be created with a default initial value.Variable
withBehaviorSubject
To send the last one to its subscribersevent
And the ones that are created after thatevent
.- The difference is,
Variable
It also saves the currently emitted value as its own state. It will also be sent automatically when destroyed.complete
的event
It is not necessary and cannot be given manuallyVariables
sendcompleted
orerror
Events to end it.- In a nutshell
Variable
There is avalue
Property, let’s change thisvalue
The value of the property is equivalent to the call generalSubjects
的onNext()
Method, and this latest oneonNext()
The value of is saved invalue
Property, until we modify it again.Variables
Itself has nosubscribe()
Method, but allSubjects
There is aasObservable()
Methods. We can use this method to return thisVariable
的Observable
Type, get thisObservable
Type we can subscribe to it.
- Instance 2.6.2.1
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
override func viewDidLoad(a) {
super.viewDidLoad()
let disposeBag = DisposeBag(a)// Create a Variable whose initial value is 111
let variable = Variable("111")
// Change the value
variable.value = "222"
// First subscription
variable.asObservable().subscribe {
print("1st subscription:", $0)
}.disposed(by: disposeBag)
// Change the value
variable.value = "333"
// Second subscription
variable.asObservable().subscribe {
print("Second subscription:", $0)
}.disposed(by: disposeBag)
// Change the value
variable.value = "444"}} Note: Due toVariableThe object is initialized within the viewDidLoad() method, so its lifetime is limited to that method. When this method is done, thisVariableThe object is destroyed, and it automatically issues the.Completed event to all its subscribersCopy the code
Running results:
2.6.2.2 BehaviorRelay
- 1) Basic introduction
BehaviorRelay
As aVariable
A replacement has emerged. It’s also true in natureBehaviorSubject
So it must also be created with a default initial value.BehaviorRelay
withBehaviorSubject
To send the last one to its subscribersevent
And the ones that are created after thatevent
.- with
BehaviorSubject
The difference is that manual feeding is not required and cannot be doneBehaviorReply
sendcompleted
orerror
Events to end it (BehaviorRelay
Will not be automatically sent when destroyed.complete
的event
).BehaviorRelay
There is avalue
Property by which we can get the latest value. And what goes through itaccept()
Method can modify values.
- (2) above
Variable
We can change the sample toBehaviorRelay
, the code is as follows:
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
override func viewDidLoad(a) {
super.viewDidLoad()
let disposeBag = DisposeBag(a)// Create a BehaviorRelay with an initial value of 111
let subject = BehaviorRelay<String>(value: "111")
// Change the value
subject.accept("222")
// First subscription
subject.asObservable().subscribe {
print("1st subscription:", $0)
}.disposed(by: disposeBag)
// Change the value
subject.accept("333")
// Second subscription
subject.asObservable().subscribe {
print("Second subscription:", $0)
}.disposed(by: disposeBag)
// Change the value
subject.accept("444")}}Copy the code
Running results:
- (3) If you want to merge the new value to the original value, you can pass
accept()
The methods andvalue
Property to implement. (This is often used with pull and load functions on tables,BehaviorRelay
To save all loaded data)
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
override func viewDidLoad(a) {
super.viewDidLoad()
let disposeBag = DisposeBag(a)// Create a BehaviorRelay that starts with an array of one element
let subject = BehaviorRelay"[String]>(value: ["1"])
// Change the value
subject.accept(subject.value + ["2"."3"])
// First subscription
subject.asObservable().subscribe {
print("1st subscription:", $0)
}.disposed(by: disposeBag)
// Change the value
subject.accept(subject.value + ["4"."5"])
// Second subscription
subject.asObservable().subscribe {
print("Second subscription:", $0)
}.disposed(by: disposeBag)
// Change the value
subject.accept(subject.value + ["6"."Seven"])}}Copy the code
Running results:
2.7 Transformation operators: Buffer, map, flatMap, scan, etc
- Transform operations refer to transformations of the original Observable sequence, similar to the conversions of CollectionType in Swift.
2.7.1 buffer
- (1) Basic introduction
The buffer method is a buffer combination. The first parameter is the buffer time, the second parameter is the number of buffers, and the third parameter is the thread. This method simply caches new elements emitted by an Observable, and when the number of elements reaches a certain number or a certain amount of time passes, it sends the collection of elements out.
- Instance 2.7.1
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag(a)override func viewDidLoad(a) {
let subject = PublishSubject<String> ()// Each cache of 3 elements is combined and emitted together.
// If there are less than 3 arrays in 1 second, the array is also emitted (if there are several arrays, none of them are empty arrays).
subject
.buffer(timeSpan: 1.count: 3, scheduler: MainScheduler.instance)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject.onNext("a")
subject.onNext("b")
subject.onNext("c")
subject.onNext("1")
subject.onNext("2")
subject.onNext("3")}}Copy the code
Running results:
2.7.2 window
- (1) Basic introduction
The window operator is very similar to buffer. Buffers periodically send cached elements, whereas Windows periodically send elements as observables. At the same time, the buffer will not issue the element sequence until the element is collected. Windows emits sequences of elements in real time.
- Instance 2.7.2
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag(a)override func viewDidLoad(a) {
let subject = PublishSubject<String> ()// Every three elements is emitted as a child Observable.
subject
.window(timeSpan: 1.count: 3, scheduler: MainScheduler.instance)
.subscribe(onNext: { [weak self] in
print("subscribe: \ [$0)")
$0.asObservable()
.subscribe(onNext: { print($0) })
.disposed(by: self! .disposeBag) }) .disposed(by: disposeBag) subject.onNext("a")
subject.onNext("b")
subject.onNext("c")
subject.onNext("1")
subject.onNext("2")
subject.onNext("3")}}Copy the code
Running results:
2.7.3 map
- This operator converts an Observable into a new Observable by passing in a function closure.
- Instance 2.7.3
let disposeBag = DisposeBag(a)Observable.of(1.2.3).map{$0 * 10}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Result: 10, 20, 30
2.7.4 flatMap
- (1) Basic introduction
map
It is easy to “raise dimension” when doing transformations. That is, after the transformation, you go from a sequence to a sequence of sequences.- while
flatMap
The operator will apply to the sourceObservable
Apply a conversion method to each element of theObservables
. And then take thoseObservables
The element is merged before being sent out. That is to “flatten” it into oneObservable
Sequence.- This operator is very useful. Such as when
Observable
The element Bunsen has the othersObservable
When, we can put all the childrenObservables
The element is sent.
- Instance 2.7.4
let disposeBag = DisposeBag(a)let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
let variable = Variable(subject1)
variable.asObservable()
.flatMap { $0 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
Copy the code
Running results:
2.7.5 flatMapLatest
- The only difference between flatMapLatest and flatMap is that flatMapLatest only receives the latest value event.
- Example 2.7.5: Here we change the flatMap in the previous example to flatMapLatest
let disposeBag = DisposeBag(a)let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
let variable = Variable(subject1)
variable.asObservable()
.flatMapLatest { $0 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
Copy the code
Running results:
2.7.6 flatMapFirst
- (1) Basic introduction
- FlatMapFirst is the opposite of flatMapLatest: flatMapFirst only receives the initial value event.
- This operator prevents repeated requests: clicking a button to send a request, for example, should not continue until the request is complete. The flatMapFirst operator should be used.
- Example 2.7.6: Here we change flatMapLatest from the previous example to flatMapFirst.
let disposeBag = DisposeBag(a)let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
let variable = Variable(subject1)
variable.asObservable()
.flatMapFirst { $0 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
Copy the code
Running results:
2.7.7 concatMap
- (1) Basic introduction
The only difference between concatMap and flatMap is that a subsequent Observable can emit elements only after the current one has been sent. Or wait for the previous Observable to generate and complete the event before subscribing to the next Observable.
- Instance 2.7.7
let disposeBag = DisposeBag(a)let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
let variable = Variable(subject1)
variable.asObservable()
.concatMap { $0 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
subject1.onCompleted() // The next sequence can only be received after the previous sequence ends
Copy the code
Running results:
2.7.8 scan
- (1) Basic introduction
Scan is to give an initial number, and then continuously take the previous result and the latest value for processing operation.
- Instance 2.7.8
let disposeBag = DisposeBag(a)Observable.of(1.2.3.4.5)
.scan(0) { acum, elem in
acum + elem
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
The results
2.7.9 groupBy
- (1) Basic introduction
The groupBy operator breaks up the source Observable into sub-Observables and sends them out. That is, the operator groups elements by a key and sends the grouped element sequence as an Observable.
- Instance 2.7.9
let disposeBag = DisposeBag(a)// Divide odd and even numbers into two groups
Observable<Int>.of(0.1.2.3.4.5)
.groupBy(keySelector: { (element) -> String in
return element % 2= =0 ? "Even" : "Base"
})
.subscribe { (event) in
switch event {
case .next(let group):
group.asObservable().subscribe({ (event) in
print("The key:\(group.key)The event:\(event)")
})
.disposed(by: disposeBag)
default:
print("")
}
}
.disposed(by: disposeBag)
Copy the code
Running results:
2.8 Filter operators: filter, take, skip, etc
- Filtering refers to sending specific data from a source Observable.
2.8.1 filter
- (1) Basic introduction
This operator is used to filter out certain events that do not meet the requirements.
- Instance 2.8.1
let disposeBag = DisposeBag(a)Observable.of(2.30.22.5.60.3.40 ,9).filter{$0 > 10
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
2.8.2 distinctUntilChanged
- This operator is used to filter out continuously repeated events.
- Instance 2.8.2
let disposeBag = DisposeBag(a)Observable.of(1.2.3.1.1.4)
.distinctUntilChanged()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running results:
2.8.3 single
- (1) Basic introduction
Restrict sending of only one event, or the first event that satisfies the condition. An error event is raised if there are multiple events or no events. If there is only one event, no error event is raised.
- Instance 2.8.3
let disposeBag = DisposeBag(a)Observable.of(1.2.3.4)
.single{ $0= =2 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Observable.of("A"."B"."C"."D")
.single()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running results:
2.8.4 elementAt
- This method implementation only handles events at a specified location.
- Instance 2.8.4
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4)
.elementAt(2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running result: 3
2.8.5 ignoreElements
- (1) Basic introduction
This operator can ignore all elements and only issue an error or completed event. We can use the ignoreElements operator if we don’t care about any of the Observable’s elements and just want to know when the Observable terminates.
- Instance 2.8.5
let disposeBag = DisposeBag(a)Observable.of(1.2.3.4)
.ignoreElements()
.subscribe{
print($0)
}
.disposed(by: disposeBag)
Copy the code
Result: completed
2.8.6 take
- This implementation sends only the first n events in the Observable sequence, and.completed is automatically completed when the number is satisfied.
- Instance 2.8.6
let disposeBag = DisposeBag(a)Observable.of(1.2.3.4)
.take(2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running results: 1 2
2.8.7 takeLast
- This implementation sends only the last n events in the Observable sequence.
- Instance 2.8.7
let disposeBag = DisposeBag(a)Observable.of(1.2.3.4)
.takeLast(1)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running result: 4
2.8.8 skip
- This method skips the first n events emitted by the source Observable sequence.
- Instance 2.8.8
let disposeBag = DisposeBag(a)Observable.of(1.2.3.4)
.skip(2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running results: 3 4
2.8.9 Sample
- In addition to subscribes to the source Observable, Sample can also monitor another Observable, namely, notifier.
- Each time a Notifier event is received, the latest event is fetched from the source sequence and sent. If there is no event in the source sequence between notifier events, no value is sent.
- Instance 2.8.9
let disposeBag = DisposeBag(a)let source = PublishSubject<Int> ()let notifier = PublishSubject<String>()
source
.sample(notifier)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
source.onNext(1)
// Make the source sequence receive the receive message
notifier.onNext("A")
source.onNext(2)
// Make the source sequence receive the receive message
notifier.onNext("B")
notifier.onNext("C")
source.onNext(3)
source.onNext(4)
// Make the source sequence receive the receive message
notifier.onNext("D")
source.onNext(5)
// Make the source sequence receive the receive message
notifier.onCompleted()
Copy the code
Running results:
1, 2, 4, 5
2.8.10 debounce
- (1) Basic introduction
- The debounce operator, which can be used to filter out high-frequency elements, emits only the element after which no new element has been created for a period of time.
- In other words, the element in the queue will be filtered out if the interval between the element and the next element is less than the specified interval.
- Debounce is often used when a user types, instead of sending an event for each letter typed, it waits for the last event to be taken.
- Instance 2.8.10:
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag(a)override func viewDidLoad(a) {
// Define the value of each event and the time it was sent
let times = [
[ "value": 1."time": 0.1 ],
[ "value": 2."time": 1.1 ],
[ "value": 3."time": 1.2 ],
[ "value": 4."time": 1.2 ],
[ "value": 5."time": 1.4 ],
[ "value": 6."time": 2.1]]// Generate the corresponding Observable and subscribe
Observable.from(times)
.flatMap { item in
return Observable.of(Int(item["value"]!) ) .delaySubscription(Double(item["time"]!) , scheduler:MainScheduler.instance)
}
.debounce(0.5, scheduler: MainScheduler.instance) // Emit only elements more than 0.5 seconds apart from the next one
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
}
Copy the code
Running results: 1 5 6
2.9 Conditional and Boolean operators: AMb, takeWhile, skipWhile, etc
- Conditional and Boolean operations emit or transform Observables based on conditions, or perform Boolean operations on them.
2.9.1 amb
-
When multiple Observables are passed to the AMB operator, it takes the first emitted element or Observable that generated the event, and emits only its element. And ignore the other Observables.
-
Instance 2.9.1
let disposeBag = DisposeBag(a)let subject1 = PublishSubject<Int> ()let subject2 = PublishSubject<Int> ()let subject3 = PublishSubject<Int>()
subject1
.amb(subject2)
.amb(subject3)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject2.onNext(1)
subject1.onNext(20)
subject2.onNext(2)
subject1.onNext(40)
subject3.onNext(0)
subject2.onNext(3)
subject1.onNext(60)
subject3.onNext(0)
subject3.onNext(0)
Copy the code
Running results: 1, 2, 3
2.9.2 takeWhile
-
The method in turn determines whether each value of the Observable sequence satisfies a given condition. It completes automatically when the first value that does not meet the condition appears.
-
Instance 2.9.2
let disposeBag = DisposeBag(a)Observable.of(2.3.4.5.6)
.takeWhile { $0 < 4 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running results: 2 3
2.9.3 takeUntil
- Basic introduction
- In addition to subscriptions
Observable
Outside,takeUntil
Method we can also monitor another oneObservable
, i.e.,notifier
.- if
notifier
A value orcomplete
Notice, then sourceObservable
Automatically completes, and stops sending events.
- Instance 2.9.3
let disposeBag = DisposeBag(a)let source = PublishSubject<String> ()let notifier = PublishSubject<String>()
source
.takeUntil(notifier)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
source.onNext("a")
source.onNext("b")
source.onNext("c")
source.onNext("d")
// Stop receiving messages
notifier.onNext("z")
source.onNext("e")
source.onNext("f")
source.onNext("g")
Copy the code
A, B, C, d
2.9.4 skipWhile
- Basic introduction
- This method is used to skip all previous events that meet the criteria.
- Once an event that does not meet the criteria is encountered, it will not be skipped afterwards.
- Instance 2.9.4
let disposeBag = DisposeBag(a)Observable.of(2.3.4.5.6)
.skipWhile { $0 < 4 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
}
Copy the code
Running results: 4 5 6
2.9.5 skipUntil
- Basic introduction
- Ditto below
takeUntil
The same,skipUntil
In addition to subscriptionsObservable
Outside,skipUntil
Method we can also monitor another oneObservable
, i.e.,notifier
。- with
takeUntil
On the contrary. The sourceObservable
Sequence events are skipped by default untilnotifier
A value orcomplete
Notice.
- Instance 2.9.5
let disposeBag = DisposeBag(a)let source = PublishSubject<Int> ()let notifier = PublishSubject<Int>()
source
.skipUntil(notifier)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
source.onNext(1)
source.onNext(2)
source.onNext(3)
source.onNext(4)
source.onNext(5)
// Start receiving messages
notifier.onNext(0)
source.onNext(6)
source.onNext(7)
source.onNext(8)
// Still receiving messages
notifier.onNext(0)
source.onNext(9)
Copy the code
Running result: 6 7 8 9
2.10 Combine operators such as startWith, Merge, and zip
2.10.1 startWith
- Basic introduction:
This method inserts event elements before the Observable sequence starts. These pre-inserted event messages are emitted before the event message is emitted
- Illustration:
- Instance 2.10.1
let disposeBag = DisposeBag(a)Observable.of("2"."3")
.startWith("1")
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running results:
1 2 3
- Example 2.10.1.1: Multiple data can be inserted
let disposeBag = DisposeBag(a)Observable.of("2"."3")
.startWith("a")
.startWith("b")
.startWith("c")
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
Copy the code
Running results:
c b a 2 3
2.10.2 merge
- Basic introduction:
This method merges multiple (two or more) Observable sequences into one Observable.
- Illustration:
- Instance 2.10.1
let disposeBag = DisposeBag(a)let subject1 = PublishSubject<Int> ()let subject2 = PublishSubject<Int> ()Observable.of(subject1, subject2)
.merge()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext(20)
subject1.onNext(40)
subject1.onNext(60)
subject2.onNext(1)
subject1.onNext(80)
subject1.onNext(100)
subject2.onNext(1)
Copy the code
Running results:
20 40 60 1 80 100 1
2.10.3 zip
- Basic introduction:
This method compresses multiple (two or more) Observables into a single Observable. And it waits until each Observable event corresponds to one another before merging.
- Illustration:
- Instance 2.10.1
let disposeBag = DisposeBag(a)let subject1 = PublishSubject<Int> ()let subject2 = PublishSubject<String> ()Observable.zip(subject1, subject2) {
"\ [$0)\ [$1)"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext(1)
subject2.onNext("A")
subject1.onNext(2)
subject2.onNext("B")
subject2.onNext("C")
subject2.onNext("D")
subject1.onNext(3)
subject1.onNext(4)
subject1.onNext(5)
Copy the code
Running results:
1A 2B 3C 4D
- Zip is often used to consolidate network requests:
For example, if we want to send two requests at the same time, only after both requests are successful, we can combine the results of the two requests and continue processing. This can be done with zip.
// First request
let userRequest: Observable<User> = API.getUser("me")
// Second request
let friendsRequest: Observable<Friends> = API.getFriends("me")
// Combine the two requests
Observable.zip(userRequest, friendsRequest) {
user, friends in
// Combine two signals into one signal and compress it into a tuple to return (both signals were successful)
return (user, friends)
}
.observeOn(MainScheduler.instance) // This is added because the request is in the background thread, and the following bindings are in the foreground thread.
.subscribe(onNext: { (user, friends) in
// Bind data to the interface
/ /...
})
.disposed(by: disposeBag)
Copy the code
2.10.4 combineLatest
- Basic introduction:
- This method also combines multiple (two or more)
Observable
Sequence elements are merged.- But with the
zip
The difference is, whenever any one of themObservable
When a new event is emitted, it will send eachObservable
The latest event element of the sequence is merged.
- Illustration:
- Instance 2.10.1
let disposeBag = DisposeBag(a)let subject1 = PublishSubject<Int> ()let subject2 = PublishSubject<String> ()Observable.combineLatest(subject1, subject2) {
"\ [$0)\ [$1)"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext(1)
subject2.onNext("A")
subject1.onNext(2)
subject2.onNext("B")
subject2.onNext("C")
subject2.onNext("D")
subject1.onNext(3)
subject1.onNext(4)
subject1.onNext(5)
Copy the code
Running results:
2.10.5 withLatestFrom
- Basic introduction:
This method merges two Observable sequences into one. Each time the self queue emits an element, the latest value is fetched from the second sequence.
-
Illustration:
-
Instance 2.10.1
let disposeBag = DisposeBag(a)let subject1 = PublishSubject<String> ()let subject2 = PublishSubject<String>()
subject1.withLatestFrom(subject2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("A")
subject2.onNext("1")
subject1.onNext("B")
subject1.onNext("C")
subject2.onNext("2")
subject1.onNext("D")
Copy the code
Running results:
1 1 2
2.10.6 switchLatest
- Basic introduction:
switchLatest
It’s kind of like other languagesswitch
Method to transform the flow of events.- Like the one that was listening in
subject1
I can change it throughvariable
The inside of thevalue
Change the event source. Become a monitorsubject2
.
- Illustration:
- Instance 2.10.1
let disposeBag = DisposeBag(a)let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
let variable = Variable(subject1)
variable.asObservable()
.switchLatest()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("B")
subject1.onNext("C")
// Change the event source
variable.value = subject2
subject1.onNext("D")
subject2.onNext("2")
// Change the event source
variable.value = subject1
subject2.onNext("3")
subject1.onNext("E")
Copy the code
Running results: