preface

This article is written to record my conceptual understanding, operation steps and experience in the learning process of RxSwift for future review. If there are any mistakes in the article, please correct them. (Cover your face 🤦)

Functional responsive programming

Before learning RxSwift, first to understand the idea of functional responsive programming, we can take functional responsive programming apart, divided into functional programming and responsive programming to understand:

1. Functional programming

Functional Programming, referred to as FP(Functional Programming), is a highly abstract Programming paradigm, which regards computer operations as the calculation of functions in mathematics, while functions written in pure Functional Programming languages have no variables. Therefore, any function, as long as the input is determined, The output is deterministic, and this pure function is called free of side effects. While allowing the use of variables in programming languages, because of the variable state of the function is uncertain, the same input, may get different output, therefore, this function is a side effect.

One of the great things about functional programming is that you can pass a function as an argument to another function and return a function!

Y = f(x) –> x = f(x) –> y = f(f(x))

There is a requirement for the array [1,2,3,4,5,6,7] to get the numbers > 3, + 1 after the numbers, and then print all the even numbers

letArray =,2,3,4,5,6,7 [1]for num in array{
            if num > 3{
                let number = num + 1
                if (number % 2 == 0) {
                    print(number)
                  }
            }
        }
Copy the code

Here we use behavioral thinking to solve this requirement, let’s change our thinking to solve it, using Array’s filter method;

letArray = array,2,3,4,5,6,7 [1]. The filter {$0 > 3}
            .filter{ ($0+1) % 2 == 0 }
            .forEach { print($0)}Copy the code

The array.filter function takes a closure parameter, and the filter method calls each element in the array using the closure we passed in to filter. Based on the closure’s return value, we decide whether to add the element as a qualified element to our search results.

Simply put, we just need to declare the lookup rules in the closure passed in, and we’re done with the lookup operation. We don’t tell the program how to find elements that meet the criteria, we just declare a rule. The biggest benefit of this is that it reduces the amount of code we need to write, making our code look very concise and easy to understand. This approach is an example of functional programming.

Responsive programming

Reactive Programming (RP) is an asynchronous Programming paradigm oriented to data flow and change propagation. This means that static or dynamic data flows can be easily expressed in a programming language, and the associated computational model automatically propagates the changing values through the data flows.

Simply put, this is event-based programming. An event stream is a chronological sequence of events that will occur. When the event stream produces returned data, you can be notified immediately and call the callback function to process the data. (In real life: At home I picked up a mobile booking a take-away, then businesses will receive orders, starts to make delivery, production is completed will inform riders take food, riders take meal orders, then delivery, destination, delivery service, order finished, so that a series of events, first call it flow of events, if in the event flow process, such as merchants have a meal, Or the rider does not pick up the meal, etc., which will result in the event stream not being completed, and we can respond according to the result of any event in the event stream.)

From this small example, you can see the three actions (data) that make up reactive programming: events (data), errors, and terminations. By getting these three reactive actions, we can make different responses in the program.

Personally, it is important to “subscribe/listen” to events before receiving feedback on them. For me, I am the listener of events, i.e., “observer/subscriber”. Listener + observer is the familiar observer mode, so responsive programming is observer mode + control of event flow.

Functional responsive programming

Functional Reactive Programming (FRP) is a combination of Functional Programming and Reactive Programming. Reactive Programming is important while Functional Programming is useful. (The classic frameworks are RAC and RxSwift), it is often said that FRP can make your code as simple as math and business as clear and flowing as water.

RxSwift met

1. RxSwift profile

First, ReactiveX (Rx) is a framework that helps simplify asynchronous programming, which is simply reactive programming based on asynchronous Event sequences, and provides more elegant data binding that responds to new data all the time and processes it sequentially. RxSwift (ReactiveX for Swift) is the Swift version of ReactiveX. The ReactiveX family is very powerful, as powerful as the Venom family, and in addition to RxSwift, which I’ll learn about later, RAC(ReactiveCocoa), RxJava, RxJS, RxKotlin, Rx.NET… And so on.

2.RxSwift imports the configuration

(1) Manual import

  • Download the latest code from RxSwift GitHub,
  • Will download down the source packageRx.xcodeprojDrag to the project,
  • Project -> Targets -> General -> Embedded BinariesConfiguration items,RxSwift.framework,RxCocoa.frameworkJust add it in
  • In needRxSwiftThe place whereimportCome in

(2) CocoaPods import

# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
    pod 'RxSwift'.'~ > 5.0'
    pod 'RxCocoa'.'~ > 5.0'
end
Copy the code

Replace YOUR_TARGET_NAME with YOUR_TARGET_NAME and in the Podfile directory, terminal type:

$ pod install
Copy the code

🗣🗣🗣 to explain why RxSwift and RxCocoa libraries have been imported, their functions are:

  • RxSwift: It’s just based onSwiftThe language of theRxStandard implementation interface library, soRxSwiftIt doesn’t contain anyCocoaorUIThe class of the aspect.
  • RxCocoa: it is based onRxSwiftforiOSDevelop a library that it passesExtensionMethods to native such asUIControl addedRxMakes it easier to subscribe to and respond to events from these controls.

3. RxSwift features

  • The compound –RxIt’s a synonym for compound and
  • Reuse – High reuse – reduced code
  • Clear – Because declarations are immutable, code functional programming is readable
  • Easy to use – easy to understand, but also abstract asynchronous programming, uniform code style
  • Stable – BecauseRxIs completely unit tested
  • The style of installing X-code is clearly better than native

Is this the case? Here are some common ways to verify it:

(1)KVO

General writing:
func setupKVO() {
    self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    person.name = "\(person.name) +"
    // print(person.name)
}
override func observeValue(forKeyPath keyPath: String? , of object: Any? , change: [NSKeyValueChangeKey : Any]? , context: UnsafeMutableRawPointer?) {print("Response")
    print(change as Any)
}

deinit {
    self.removeObserver(self.person, forKeyPath: "name", context: nil)
}
Copy the code
RxSwift writing:
func setupKVO() {
    self.person.rx.observeWeakly(String.self, "name")
        .subscribe(onNext: { (value) in
            print(value as Any)
        })
        .disposed(by: disposeBag)
}
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    person.name = "\(person.name) +"
    // print(person.name)
}
Copy the code

It feels like the RxSwift version of KVO writes less code and doesn’t have to implement the observer proxy method, regardless of whether the removeObserver method is omitted.

(2)Target Action

General writing:
func setupButton() {
   button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}

@objc func didClickButton() {print("Why did you click on me?")}Copy the code
RxSwift writing:
func setupButton() {
    self.button.rx.tap
      .subscribe(onNext: { () in
            print("Click event")
        })
      .disposed(by: disposeBag)
}
Copy the code

You don’t need to implement the Target Action, so the code is simpler

(3) agent

General writing:
class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("contentOffset: \(scrollView.contentOffset)")}}Copy the code
RxSwift writing:
class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.rx.contentOffset
            .subscribe(onNext: { contentOffset in
                print("contentOffset: \(contentOffset)")
            })
            .disposed(by: disposeBag)
    }
}
Copy the code

No need to implement the proxy method, you can directly get the offset of the ScrollView

(4) notice

General writing:
var testObserver: NSObjectProtocol!

override func viewDidLoad() {
    super.viewDidLoad()

    testObserver = NotificationCenter.default.addObserver(
          forName: .UIApplicationWillEnterForeground,
          object: nil, queue: nil) { (notification) in
        print("Application Will Enter Foreground")
    }
}

deinit {
    NotificationCenter.default.removeObserver(testObserver)
}
Copy the code
RxSwift writing:
override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.rx
        .notification(.UIApplicationWillEnterForeground)
        .subscribe(onNext: { (notification) in
            print("Application Will Enter Foreground")
        })
        .disposed(by: disposeBag)
}
Copy the code

There is no need to pay attention to whether the notification has been removed, and there is no crash because the notification has not been removed

(5) Click gesture

General writing:
func setupGestureRecognizer() {let tap = UITapGestureRecognizer()
    tap.addTarget(self, action: #selector(singleTap(_:)))
    self.label.addGestureRecognizer(tap)
    self.label.isUserInteractionEnabled = true
    
}
    
@objc func singleTap(_ tapGesture: UITapGestureRecognizer) {
    print("Why did you click on me?")}Copy the code
RxSwift writing:
func setupGestureRecognizer() {let tap = UITapGestureRecognizer()
    self.label.addGestureRecognizer(tap)
    self.label.isUserInteractionEnabled = true
    tap.rx.event.subscribe(onNext: { (tap) in
        print(tap.view)
    })
    .disposed(by: disposeBag)
  }
Copy the code

There are fewer ways to implement the gesture trigger, and the code is more brief

(6) the Timer Timer

General writing:
var testtimer = Timer()

func setupTimer() {
    testtimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UpdateTimer), userInfo: nil, repeats: true)
    testtimer.fire();
    RunLoop.current.add(testtimer, forMode: .common)
}

@objc func UpdateTimer() {

   print("timer start")}Copy the code
RxSwift writing:
var timer: Observable<Int>!

func setupTimer() {
    timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    timer.subscribe(onNext: { (num) in
        print(num)
    })
    .disposed(by: disposeBag)
}

Copy the code

The nice thing about the timer is that you don’t have to worry about the Scrollview on the page

After looking at these small examples 🌰, I feel that RxSwift is much simpler than the general Swift writing method, simply “SAO general operation”.

4. Basic knowledge of RxSwift core logic

ReactiveX is reactive programming based on asynchronous Event sequences, and provides more elegant data binding. You can respond to new data all the time and process it sequentially. To respond to data, you need an observer.

The core concept of RxSwift can be understood as an Observer subscribing to an Observable. Observers respond to events or sequences of events that can be emitted by the observed sequence.

To understand the concept of these three classes:

  • Observable<T>This class isRxThe basis of the framework is what we might call an observable sequence. What it does is it can asynchronously produce a series ofEvent(event), i.eObservable<T>Objects are issued irregularly over timeevent(element : T)Such a thing.
  • And theseEventIt can also carry data, it’s generic<T>That’s what it’s used to specifyEventThe type of data carried.
  • So with the observable sequence, we need another oneObserverTo subscribe to it so that the subscriber can receive itObservable<T>From time to timeEvent.

Since an Observable is an Observable sequence that asynchronously generates a series of events, look at the RxSwift/ event. swift source code

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

Events are defined as an enumerated value, which means that an Observable can emit three different types of events:

  • next:nextEvents are the ones that can carry data<T>It can be said that it is an ordinary event

  • error:errorAn event represents an error, and it can carry specific error content onceObservableissuederror event, thisObservableIt will terminate, and it will never be sent againEvent ‘indicates an event.

  • completed:completedThe event saidObservableThe incident issued ended normally, followederrorAgain, onceObservableissuedcompleted event, thisObservableIt will terminate, and it will never be sent againeventThe event

Sequence listening has three steps: 1. Create sequence, 2. Subscribe sequence, 3. When a sequence is created and subscribed to, whenever an event sends a sequence message, the sent message can be listened for in the closure of the sequence subscription.

Let’s create an observable sequence to get a feel for it:

// Pass a closure in the create() function to process each incoming subscriptionlet ob = Observable<Any>.create { (observer) -> Disposable inObserver.onnext (onCompleted and onError can only be sent once)"Hello?")
            observer.onCompleted()
//            observer.onError(NSError.init(domain: "loser", code: 10010, userInfo: nil))
            returnDisposables. Create () // Step 2: Subscribe info // When we subscribe to Observable messages, all Observable events will be notified to us through the onNext closure.let _ = ob.subscribe(onNext: { (text) in
            print("Subscribe to :\(text)"}, onError: {(error)in
            print("error: \(error)"}, onCompleted: {// This is called back when the sequence is completed.print("Complete") {})print("Destroyed") 
        }
        .disposed(by: disposeBag)
Copy the code

DisposeBag: Rx automatically interprets 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).

conclusion

This is the core logic of RxSwift responsiveness, where there is a bit of a puzzle: why can observable sequences subscribe to signals sent by observers?

To be continued…… RxSwift learning — Revisiting core Logic

Note: Thanks for the reference documents below

RxSwift Chinese document

Navigation song Swift