Reactive Programming (RP) is another Programming paradigm that was introduced in 1997 to simplify asynchronous Programming and provide more elegant data binding.
Initial responsive programming
Functional Reactive Programming (FRP) is also called Functional Reactive Programming (FRP) because it is often combined with Functional Programming.
Well-known, mature, responsive frameworks:
-
ReactiveCocoa
- RAC for short, there are OC and Swift versions
- Liverpoolfc.tv: reactivecocoa. IO /
- Github:github.com/ReactiveCoc…
-
ReactiveX
- Short for Rx, there are many programming language versions, such as RxJava, RxKotlin, RxJS, RxCpp, RxPHP, RxGo, RxDart, RxSwift, etc
- Liverpoolfc.tv: reactivex. IO
- github:github.com/ReactiveX
Swift responsive programming generally uses the RxSwift framework.
Second, the RxSwift
2.1. Installation RxSwift
Source: github.com/ReactiveX/R…
Chinese document (unofficial) : beethOven. Making. IO/RxSwift – Chi…
RxSwift github has a detailed installation tutorial. This tutorial will only show you how to install CocoaPods.
Step 1: Create a Podfile (you can execute pod init directly in the project root directory)
# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'RxSwift', '~>5.0.1'
pod 'RxCocoa', '~>5.0.1'
end
# RxTest and RxBlocking make the most sense in the context of unit/integration testsTarget 'YOUR_TESTING_TARGET' do pod 'RxBlocking', '~>5.0.1' pod 'RxTest', '~>5.0.1' endCopy the code
Step 2: Command line installation Note replace YOUR_TARGET_NAME with your own project name. Then execute Pod Install in the folder where your Podfile resides.
Step 3: Import the module
import RxSwift
import RxCocoa
Copy the code
Module Description:
- RxSwift: Swift implementation of the Rx standard API, excluding any iOS related content
- RxCocoa: Based on RxSwift, it extends many Rx features for iOS UI controls
2.2. The central role of RxSwift
- Observable: Sends events
- Observer: Responsible for subscription
Observable
To monitorObservable
Sent events
The event sent each time is of enumeration type:
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
next
: Carries specific dataerror
: Indicates that error information is carriedObservable
Terminates, no more events are emittedcompleted
: indicates thatObservable
Terminates, no more events are emitted
2.2.1. Create and subscribe an Observable
Sample code:
// Create Observable the first way
var observable = Observable<Int>.create { observer in
/ / message
observer.onNext(1)
observer.onCompleted()
return Disposables.create()
}
/ / equivalent to the
observable = Observable.just(1)
observable = Observable.of(1)
observable = Observable.from([1])
// Create Observable the second way
var observable2 = Observable<Int>.create { observer in
observer.onNext(1)
observer.onNext(2)
observer.onNext(3)
observer.onCompleted()
return Disposables.create()
}
/ / equivalent to the
observable2 = Observable.of(1.2.3)
observable2 = Observable.from([1.2.3])
/ / subscribe to observables
observable.subscribe { event in
switch event {
case .next(let element):
print("switch next", element)
case .error(let error):
print("switch error", error)
case .completed:
print("switch completed")
}
}.dispose()
/ / equivalent to the
observable.subscribe(onNext: {
print("block next".$0)
}, onError: {
print("block error".$0)
}, onCompleted: {
print("block completed")
}, onDisposed: {
print("block dispose")
}).dispose()
/* Output: switch next 1 Switch completed block next 1 block completed block dispose */
Copy the code
Subscribe receives messages wherever an Observable subscribes.
2.2.2. Disposable cancels subscription
Whenever an Observable is subscribed, it returns a Disposable instance. When dispose of the Disposable is called, it unsubscribes.
If you do not need to receive events any more, you are advised to unsubscribe and release resources. There are three common ways to unsubscribe:
First: Unsubscribe now (one-time subscription)
observable.subscribe { event in
print(event)
}.dispose()
Copy the code
Dispose of the Disposable instance is automatically called when the bag is destroyed (deinit)
let bag = DisposeBag()
observable.subscribe { event in
print(event)
}.disposed(by: bag)
Copy the code
Dispose of the Disposable instance is automatically called when self destroys (deinit)
let _ = observable.takeUntil(self.rx.deallocated).subscribe { event in
print(event)
}
Copy the code
2.2.3. Create the Observer
The first is AnyObserver
let observer = AnyObserver<Int>.init { event in
switch event {
case .next(let data):
print(data)
case .completed:
print("completed")
case .error(let error):
print("error", error)
}
}
Observable.just(1).subscribe(observer).dispose() // Output: 1 completed
Copy the code
Second: Binder
let label = UILabel.init(a)let binder = Binder<String>(label) { label, text in
label.text = text
}
Observable.just(1).map({ "Value is\ [$0)" }).subscribe(binder).dispose()
Observable.just(1).map({ "Value is\ [$0)" }).bind(to: binder).dispose()
Copy the code
2.3. Status monitoring
2.3.1. Traditional status monitoring
In development, it is often necessary to monitor various states. The traditional common listening schemes are:
- KVO
- Target-Action
- Notification
- Delegate
- Block Callback
Traditional solutions often have complex dependencies, high coupling, and repetitive non-business code to write.
2.3.2. Status monitoring of RxSwift
Example code 1 (target-action) :
let buttton = UIButton.init(type: .custom)
let btnObservable = buttton.rx.controlEvent(.touchUpInside)
btnObservable.subscribe(onNext: {
print("Button clicked")
}).dispose()
/ / equivalent to the
buttton.rx.tap.subscribe(onNext: {
print("Button clicked")
}).dispose()
Copy the code
Example code two (KVO) :
class Dog: NSObject {
@objc dynamic var name: String?
}
let dog = Dog()
dog.rx.observe(String.self."name").subscribe(onNext: { name in
print("name is", name ?? "nil")
}).disposed(by: bag)
dog.name = "Rich"
dog.name = "Prosperous wealth."
/* output: name is nil name is rich name is rich */
Copy the code
The essence of using KVO is to call addObserver:forKeyPath:, so use runtime. And the initialization instance assigns a value to the property, so the initialization value is also listened for.
Example code 3 (Notification) :
NotificationCenter.default.rx
.notification(UIApplication.didEnterBackgroundNotification)
.subscribe(onNext: { notification in
print("App goes into the background", notification)
}).disposed(by: bag)
Copy the code
Example code 4 (Observable and Observer) :
let slider = UISlider(a)let textField = UITextField(a)// Slider as an Observer
Observable.just(0.2).bind(to: slider.rx.value).disposed(by: bag)
// Slider as an Observable
slider.rx.value.map { "The slider values are\ [$0)" }.bind(to: textField.rx.text).disposed(by: bag)
// textField can also function as an Observer or Observable
textField.rx.text.subscribe(onNext: { text in
print(text ?? "")
}).disposed(by: bag)
Copy the code
Label.rx. text returns Binder type, Binder is essentially ObserverType, so label can only be used as an Observer.
Slider.rx. value returns the ControlProperty type, which complies with the ControlPropertyType protocol, The ControlPropertyType protocol complies with the ObservableType and ObserverType protocols, so it can act as both an Observable and an Observer.
Example code 5 (UITableView) :
let tableView = UITableView.init(frame: .zero, style: .plain)
struct Person {
var name: String?
var age: Int?
}
// Send data
let persons = Observable.just([
Person(name: "Kiki", age: 10),
Person(name: "DaBen", age: 20),
Person(name: "Frank", age: 30)])// Data is bound to tableView
persons.bind(to: tableView.rx.items(cellIdentifier: "cell", cellType: UITableViewCell.self)) { row, person, cell in
cell.textLabel?.text = person.name
cell.detailTextLabel?.text = "\(String(describing: person.age))"
}.disposed(by: bag)
/ / select the cell
tableView.rx.itemSelected.subscribe(onNext: { path in
print("Click on the cell", path)
}).disposed(by: bag)
// Select the data model
tableView.rx.modelSelected(Person.self).subscribe(onNext: { person in
print("Clicked", person)
}).disposed(by: bag)
Copy the code
Note: If you use RxSwift, do not adhere to the proxy and data source protocols, or RxSwift will fail.
For more articles in this series, please pay attention to wechat official account [1024 Planet].