Introduction to the

ReactiveCocoa (RAC) : RAC is a Cocoa-based FRP (Functional Reactive Programming) framework.

Install: CocoaPods: pod “ReactiveObjC”

thought

The difficulty in learning a new programming paradigm is not in a programming language, but in learning to think in a different way. Thinking is more important than grammar.

Functional Reactive Programming (FRP) is the core idea of RAC. FRP is a programming paradigm that combines functional and responsive programming paradigms. Familiarize yourself with these two programming paradigms.

Functional programming

How to understand?

If object orientation is the abstraction of data, functional programming is the abstraction of behavior.

It is allowed to pass the function itself as an argument to another function, and to return a function! At its core are functions. In problem solving, immutable values and functions are used, which process one value and map it to another value.

Key point: The argument is a function and the return value is a function

Behavior in real life:

Car wash: the function can be compared to an automatic wash shop, dirty cars in, automatic wash, come out of the car, but the car is clean.

Responsive programming

How to understand?

The idea of data-flow oriented programming behaves like the observer pattern.

The calculations are back and forth, they’re correlated, they change a little bit, and the relationship between them changes as the values change.

Behavior in real life:

Fire prevention: when the indoor smoke emanates to a certain extent, the smoke detector detects the safety limit and sends out the fire alarm in time.

Key point: correlation, follow change

Start learning RAC

The basic subscription process for Signal in RAC

  1. Create a signal

  2. Subscribe to the signal

  3. Send a signal

  4. Cancel the signal

Classes commonly used in RAC

RACSignal

RACSignal is the core RAC class that inherits from the abstract RACStream class. It only sends a signal value when a signal is subscribed.

Cold signal characteristics:

  • Do not change;
  • Passive;

Create cold signal

The createSignal class methods declared in racsignal.h are as follows:

+ (RACSignal<ValueType> *)createSignal:(RACDisposable * _Nullable (^)(id<RACSubscriber> subscriber))didSubscribe RAC_WARN_UNUSED_RESULT;
Copy the code

CreateSignal class method description:

  • Creates a new signal
  • Parameters: DidSubscribe is a block variable, didSubscribe block itself is a C like function pointer variable, didSubscribe to a function that has a RACSubscriber argument, Return a RACDisposable object.
  • Return value: Returns a RACSignal of type.

Ex. :

RACSignal *singalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull) { // Send signal [subscriber sendNext:@"1"]; [Subscriber sendNext:@(2)]; [subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{ NSLog(@"------this is RACDisposable---------"); }]; }]; / / subscribe signal [singalA subscribeNext: ^ (id _Nullable x) {NSLog (@ "-- -- -- -- -- -- -- singalA value is % @ -- -- -- -- -- -- --", x);}]. The result is as follows: 2021-04-10 22:10.30. 127793+0800 RACDemo[14148:238689] -------singalA value is 1------- 2021-04-10 22:10.30. 127938+0800 RACDemo[14148:238689] -------singalA value is 2------- 2021-04-10 22:10.30. 128098+0800 RACDemo[14148:238689] ------this is RACDisposable---------Copy the code

The above process can be broken down like this:

// declare a block variable named didSubscribe with a RACSubscriber type argument, Return a RACDisposable RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber); // For didSubscribe, (block implementation) didSubscribe = ^RACDisposable * (id<RACSubscriber> sub){sub sendNext:@"1"]; [sub sendNext:@(2)]; [sub sendCompleted]; [sub sendError:nil]; return [RACDisposable disposableWithBlock:^{ NSLog(@"----didSubscribe--RACDisposable--------"); }]; }; / / create a signal RACSignal * signalB = [RACSignal createSignal: didSubscribe]; / / subscribe signal [signalB subscribeNext: ^ (id _Nullable x) {NSLog (@ "-- -- -- -- -- - signalB is x % @ -- -- -- -- -- -- -- --", x);}]. The result is as follows: 758873+0800 RACDemo[14258:241973] ------signalB X is 1-------- 2021-04-10 22:13:58.758993+0800 RACDemo[14258:241973] ------signalB X is 2-------- 2021-04-10 22:13:58.759078+0800 RACDemo[14258:241973] ----didSubscribe--RACDisposable-------Copy the code

RACSubscriber

Subscriber: responsible for signaling, itself a protocol; As long as you follow it and implement the methods in the protocol, you become a subscriber.

Several commonly used methods:

- (void)sendNext:(nullable id)value; // send an error to the subscriber - (void)sendError:(nullable NSError *)error; // Tell the subscriber that sending is complete - (void)sendCompleted;Copy the code

RACDisposable

Unsubscribe or clean up resources, send failed or complete, will automatically trigger the unsubscribe method.

// + (instancetype)disposableWithBlock:(void (^)(void))block; // Unsubscribe - (void)dispose;Copy the code

RACSubject

RACSubject t inherits from RACSignal. A subject is considered a signal, and you can manually control the sendNext, sendError, and sendCompleted events. Can help you connect non-RAC Bridges to RAC.

Thermal signal characteristics:

  • The variable;

  • The initiative;

  • Can bridge RAC and non-RAC;

RACSubject exposes only two interfaces, one to create a heat signal object and the other to send a message method.

/// Returns a new subject.
+ (instancetype)subject;

// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value;
Copy the code

Interface for creating heat signals:

+ (instancetype)subject;

When this method is created, subscribers array and Disposable are initialized with two private properties.

Subscribers: The array contains all subscribers in it and is now iterated over in self’s synchronization.

Disposable: Contains all subscriptions of the receiver to other signals. It is mainly used to unsubscribe and clean up resources.

Interface for sending signals:

- (void)sendNext:(nullable ValueType)value;

Redefined the sendNext method for the RACSubscriber protocol.

Create a heat signal

Example: Create a heat signal

RACSubject *subject=[RACSubject *subject]; NSLog(@"subject x is [%@]",x);}]; // Send signal [subject sendNext:@"123"]; // End the subscription [subject sendCompleted]; 2021-04-13 16:48:52.043076+0800 RACDemo[29017:264320] Subject x is [123]Copy the code

Higher-order functions are commonly used

Binding to bind

Function: the signal can be filtered and then sent to a new signal, similar to message forwarding

typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);
- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block RAC_WARN_UNUSED_RESULT;
Copy the code

Ex. :

// // create signal RACSubject *subject = [RACSubject subject]; RACSignal* singnal=[subject bind:^RACSignalBindBlock _Nonnull {return ^RACSignal*(id value,BOOL *stop){ return[RACReturnSignal return:value]; }; }]; NSLog(@"bind x is [%@]",x);}]; [subject sendNext:@"send bind value 123"]; 2021-04-12 10:16:57.562421+0800 RACDemo[4075:59403] bind x is [send bind value 123]Copy the code

merge

concat

Function: Merge multiple signals into one signal in sequence. The first signal must send sendCompleted to indicate that the execution is complete before the next signal is executed. It is similar to a serial queue.

- (RACSignal *)concat:(RACSignal *)signal RAC_WARN_UNUSED_RESULT;
Copy the code

Ex. :

RACSubject *subject=[RACSubject *subject]; RACSubject * Subject2 =[RACSubject subject]; RACSignal *signal=[Subject concat: Subject2]; {NSLog(@"concat x is [%@]",x);}]; // Send signal [subject sendNext:@" ABC "]; // Subject sendCompleted must be added to indicate the end of the signal, otherwise the next signal cannot be connected [subject sendCompleted]; // Subject2 sends the signal [Subject2 sendNext:@"ABC"]; The result is as follows: 2021-04-12 16:42:14.281303+0800 RACDemo[17888:262262] concat X is [ABC] 2021-04-12 16:42:14.281532+0800 RACDemo[17888:262262] concat x is [ABC]Copy the code

merge

Function: Merge multiple signals into one signal in sequence, where the first signal is executed without adding sendCompleted. As soon as a signal is sent, the merge operation will accept it. No emphasis on finishing one before moving on to the next.

Ex. :

RACSubject *subject=[RACSubject *subject]; RACSubject * Subject2 =[RACSubject subject]; RACSignal *signal=[Subject merge: Subject2]; NSLog(@"merge x is [%@]",x);}]; // Send signal [subject sendNext:@" ABC "]; // Subject2 sends the signal [Subject2 sendNext:@"ABC"]; The result is as follows: 2021-04-12 17:08:23.410822+0800 RACDemo[19726:287122] Merge X is [ABC] 2021-04-12 17:08:23.411005+0800 RACDemo[19726:287122] merge x is [ABC]Copy the code

combineLatest

Function: Merges the latest value of multiple signals into a tuple in sequence

Ex. :

RACSubject *subject = [RACSubject *subject]; RACSubject * Subject2 = [RACSubject subject]; RACSignal *signal = [RACSignal combineLatest: @[subject, subject2]]; NSLog(@"combineLatest x is [%@]",x);}]; // Send signal [subject sendNext:@"a"]; // Send the signal [subject2 sendNext:@"b"]; // Send the signal [subject2 sendNext:@"A"]; // Send signal [subject sendNext:@"B"]; 2021-04-12 17:27:35.030304+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003AF40B0 > (a, B)] 2021-04-12 17:27:35.030604+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003AE4EE0 > (a, a)] 2021-04-12 17:27:35.030805+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003ae85c0> ( B, A )]Copy the code

mapping

flattenMap

Function: Used for signal within a signal, mapping the contents of the source signal to a new signal. The signal is of type RACSignal

- (RACSignal *)flattenMap:(__kindof RACSignal * _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
Copy the code

Example: after mapping values, return a new signal

// // create signal RACSubject *subject = [RACSubject subject]; // Bind signal, RACSignal * flattener mapsignal =[subject flattener map :^__kindof RACSignal * _Nullable(id _Nullable value) { Value =[NSString stringWithFormat:@" flattening a mathematical mapsignal that maps the flattener: %@",value]; return [RACReturnSignal return:value];}]; [id _Nullable x) {NSLog(@" flattener x is [%@]",x);}]; // Send signal [subject sendNext:@"1234"]; 2021-04-11 08:30:20.425283+0800 RACDemo[5943:81326] flattener mapsignal x is [flattener mapsignal x is a flattenerCopy the code

map

Function: Maps the value of the source signal to a new value, which can be of any type

- (RACSignal *)map:(id _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
Copy the code

Example: Return the processed string

RACSubject *subject = [RACSubject *subject]; RACSignal *bindSignal = [subject map:^id _Nullable(id _Nullable value) {NSString * STR = [NSString Return STR;}]; stringWithFormat:@" map data: %@", value]; {NSLog(@"map x is [%@]",x);}]; // Send data [subject sendNext:@1]; 2021-04-11 08:34:34.676217+0800 RACDemo[6105:85742] map x is [map processed data: 1]Copy the code

Example: Return a new signal

RACSubject *subject2=[RACSubject subject]; BindSignal2 =[subject2 Map :^RACSignal * _Nullable(id _Nullable value) { Return a new signal object value=[NSString stringWithFormat:@" modify value %@",value]; return [RACReturnSignal return:value];}]; [bindSignal2 subscribeNext:^(id _Nullable x) {NSLog(@" return value is [%@]",x);}]; // Send the signal [subject2 sendNext:@(2)]; The following result is displayed: 2021-04-11 08:41:03.927420+0800 RACDemo[6274:90310] The returned value is the signal [<RACReturnSignal: 0x600001E9D400 > name:]Copy the code

filter

filter

Function: Filter out the corresponding signal through condition judgment

- (RACSignal<ValueType> *)filter:(BOOL (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
Copy the code

Example: Filter out the string starting with ABC

RACSubject *subject=[RACSubject *subject]; RACSignal *signal= [subject filter:^BOOL(id _Nullable value) {NSString * STR =value; if ([STR) hasPrefix:@"ABC"]) { return NO; }else{ return YES; } }]; // subscribeNext:^(id _Nullable x) {NSLog(@" [%@]",x);}]; // Send signal [subject sendNext:@"abc123"]; // Send signal [subject sendNext:@"ABC123"]; The result is as follows: 2021-04-11 10:44:03.423107+0800 RACDemo[10384:185744]Copy the code

ignore

Function: filter signal value, ignore the value of XXX signal, ignore according to the value

- (RACSignal<ValueType> *)ignore:(nullable ValueType)value RAC_WARN_UNUSED_RESULT;
Copy the code

Example: Ignore the signal with the value ABC123

RACSubject* subject=[RACSubject * subject]; RACSignal *signal=[subject ignore:@"ABC123"]; RACSignal *signal=[subject ignore:@"ABC123"]; // signal subscribeNext:^(id _Nullable x) {NSLog(@"ignore x is [%@]",x);}]; // Send signal [subject sendNext:@"ABC123"]; // Send signal [subject sendNext:@"abc123"]; 2021-04-11 10:37:35.490486+0800 RACDemo[10205:180228] Ignore x is [abc123]Copy the code

then

- (RACSignal *)then:(RACSignal * (^)(void))block RAC_WARN_UNUSED_RESULT;
Copy the code

Effect: The value sent by the previous signal is ignored. The next signal is executed only after sendCompleted is sent by the previous signal. Otherwise, the next signal will be ignored

Example: Ignore the Subject signal and then execute the Subject2 signal

RACSubject *subject = [RACSubject *subject]; RACSubject * Subject2 = [RACSubject subject]; RACSignal = [subject then:^RACSignal * _Nonnull{return subject2;}]; // signal subscribeNext:^(id _Nullable x) {NSLog(@"then x is [%@]",x);}]; // Send signal [subject sendNext:@"a"]; // sendCompleted must be added to indicate that the subject signal is completed. Otherwise, [subject sendCompleted] is invalid. // Send the signal [subject2 sendNext:@"b"]; // Send the signal [subject2 sendNext:@"A"]; The result is as follows: 2021-04-12 17:45:59.354229+0800 RACDemo[22106:321645] then X is [B] 2021-04-12 17:45:59.354343+0800 RACDemo[22106:321645] then x is [A]Copy the code

take

Function: obtain the first to n signal in sequence

- (RACSignal<ValueType> *)take:(NSUInteger)count RAC_WARN_UNUSED_RESULT;
Copy the code

Example: get the signal from the 1st ~ 4th time

RACSubject* subject=[RACSubject * subject]; RACSignal *signal=[subject take:4]; RACSignal *signal=[subject take:4]; {NSLog(@"take x is [%@]",x);}]; // Send signal [subject sendNext:@"ABC123"]; // Send signal [subject sendNext:@"123ABC"]; // Send signal [subject sendNext:@"abc123"]; // Send signal [subject sendNext:@" 123ABC "]; // Send signal [subject sendNext:@"123"]; // Send signal [subject sendNext:@" ABC "]; The result is as follows: 2021-04-11 10:59:05.655107+0800 RACDemo[10795:197326] take X is [ABC123] 2021-04-11 10:59:05.655298+0800 RACDemo[10795:197326] take x is [123ABC] 2021-04-11 10:59:05.655436+0800 RACDemo[10795:197326] take x is [abc123] 2021-04-11 10:59:05.655573+0800 RACDemo[10795:197326] take x is [123ABC]Copy the code

skip

Function: Skip the signal from 1st to n times

Example: skip from 1st to 3rd signal

RACSubject* subject=[RACSubject * subject]; RACSignal *signal=[subject skip:3]; {NSLog(@"skip x is [%@]",x);}]; // Send signal [subject sendNext:@"123"]; // Send signal [subject sendNext:@" ABC "]; // Send signal [subject sendNext:@"ABC"]; // Send signal [subject sendNext:@" 123ABC "]; 2021-04-11 11:18:20.866481+0800 RACDemo[11294:209783] skip x is [123ABC]Copy the code

switchToLatest

Function: the signal in the signal, get the latest signal sent by the signal in the signal, when sending multiple internal signals, only accept the latest internal signal

Ex. :

RACSubject* subject=[RACSubject * subject]; RACSubject * innerSubject=[RACSubject subject]; RACSubject * innerSubject1=[RACSubject subject]; // Subscribe signal, [subject.switchToLatest subscribeNext:^(id _Nullable x) {NSLog(@"switchToLatest x is [%@]",x); }]; //subject sends the innerSubject signal [subject sendNext:innerSubject]; // Subject sends the innerSubject1 signal [Subject sendNext:innerSubject1]; // Send a signal with innerSubject [innerSubject sendNext:@" ABC "]; // Send the signal via innerSubject1 [innerSubject1 sendNext:@" 123ABC "]; 2021-04-11 11:41:27.553097+0800 RACDemo[12001:229779] switchToLatest x is [123ABC]Copy the code

distinctUntilChanged

Role: Ignore repeated signals

Example: After execution, the duplicate 1 is ignored

RACSubject* subject=[RACSubject * subject]; // Subscribe signal, Only the signal and the last is not subscribe and will response [subject. DistinctUntilChanged subscribeNext: ^ (id _Nullable x) {NSLog (@ "distinctUntilChanged is x [%@]",x); }]; // Send signal [subject sendNext:@"1"]; // Send signal [subject sendNext:@"1"]; // Send signal [subject sendNext:@"123"]; The result is as follows: 2021-04-11 11:53:14.833259+0800 RACDemo[12347:240090] distinctUntilChanged X is [1] 2021-04-11 11:53:14.833524+0800 RACDemo[12347:240090] distinctUntilChanged x is [123]Copy the code

conclusion

The high cohesion and low coupling characteristics of RAC are well suited to the use of MVVM. So if you are currently developing using the MVVM pattern, try the RAC framework. The above is an introduction to common RAC classes, functions, and macros. Instead of analyzing the underlying principles of each function, it mainly introduces how RAC is applied to the business. The steps of learning a new framework are usually to anticipate its use, then understand why, followed by a detailed analysis of some of the features.