A list,

1. Functional Reactive Programming (FRP)

According to people around you, once you use RAC ReactivecoA, you will become dependent on it and will not bother to use the old method because it is too good to use. So I recently started learning about the RAC framework, which introduced me to the concept of functional responsive programming. The core idea of RAC is functional + responsive programming. FRP is said to make your code as simple as math and your business as clear and flowing as water. RAC belongs to one of the Rx family, in addition to RAC, there is RxSwift in Swift, RxSwift can see my other article “RxSwift learning (I) — Preliminary Study”.

1) Responsive programming

For example, in an imperative programming environment, a = B + C means assigning the result of an expression to A, and then changing the value of B or C does not affect A. But in reactive programming, the value of A changes as B or C changes. In reactive programming, a = B + C declares a binding relationship. (A is bound to B and C, so changes in B and C will affect A, which is also called change propagation)

2) Functional programming

Functional programming has the following characteristics:

  • A function is a first class, which means that functions are equal to other data types and can be assigned to other variables, passed as arguments to another function, or returned as a value from another function.
  • Closures and Higher-order Functions Functional programming extracts many common operations as higher-order functions, such as Map, filter, and reduce. With these functions, your code is much simpler, which means you can develop faster, and they help others understand your code.
  • Not changing the state does not depend on the external data, and does not change the value of the external data, but returns a new value to you.
  • Recursive functional programming uses recursion as a mechanism for controlling flow
  • Expression is a pure operation that always returns a value. A statement is an operation that does not return a value. Functional programming requires expressions, not statements. In other words, each step is pure operation and has a return value. The reason for that is the motivation of functional programming, which was always going to be more computation than I/O.” Statements are read and write operations and are excluded. Functional programming emphasizes the absence of “side effects,” meaning that functions remain independent, all they do is return a new value, and nothing else, especially not changing the value of an external variable.

2. SubscribeNext function

RAC has a powerful subscription function called subscribeNext

- (RACDisposable *)subscribeNext:(void (^)(ValueType _Nullable x))nextBlock;
Copy the code

This function can subscribe to any object of type RACSignal, with blocks for callbacks. RAC classifies a number of classes that return RACSignal types, which simplifies many troublesome operations and saves a lot of code.

3. RAC import

Pod ‘ReactiveObjC’, ‘~> 3.0.0’ in my Podfile, pod install in the command line, The < reactiveObjc.h > header file is introduced into the project to start using.

Here are a few common examples to illustrate the power of RAC.

2, Elementary common usage

1. Notify NSNotificationCenter

Common writing:

1) Add keyboard pop-up notificationsaddObserver

[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
Copy the code

2) Implement notificationskeyboardWillShow

- (void) keyboardWillShow:(NSNotification *)note {
    NSLog("Keyboard pops up");
}
Copy the code

3) Remove notification in destructorremoveObserver

[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
Copy the code

RAC writing:

[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
    NSLog(@ "% @",x);
}];
Copy the code

2, KVO

For example, when KVO listens for the name attribute to change

Common writing:

1) Add a listeneraddObserver

[self.textField addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
Copy the code

2) In the methodobserveValueForKeyPathThe implementation of listening to the property value changes after processing

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey.id> *)change context:(void *)context {
    if ([@"text" isEqualToString: keyPath] && object == self.textField) {
        NSLog(@ "% @", change); }}Copy the code

3) Remove listeners from destructorsremoveObserver

[self.textField removeObserver:self forKeyPath:@"text"];
Copy the code

RAC writing:

The KVO in RAC is mostly macro defined, so the code is very simple, in the form of RACObserve(TARGET, KEYPATH), TARGET is the listening TARGET, and KEYPATH is the property value to observe.

[RACObserve(self.textField, text) subscribeNext:^(id  _Nullable x) {
    NSLog(@ "% @",x);
}];
self.textField.text = @" How much more";
Copy the code

delegate

Take the textFieldDidBeginEditing agent of UITextField as an example

Common writing:

The need to implement proxy methods increases the amount of code and seems inconvenient.

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    NSLog(@" Start editing");
}
Copy the code

RAC writing:

[[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext: ^ (RACTuple * _Nullable x) {
    NSLog(@ "% @",x);
}];
self.textField.delegate = self;
Copy the code
  • - (RACSignal<RACTuple *> *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol;Method selector argument **@selector:The specific proxy method proxy name parameter to implementFromProtocol: name of the agent corresponding to **.

Here’s what the console prints after it starts editing self.textField:

2019- 05- 16 17:02:07.753157+0800 001---RAC
[54156:4473541] <RACTuple: 0x600002b706b0> (
    "<UITextField: 0x7f84fc013e00; frame = (141 128; 97 30); text = ''; opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x60000272b210>; layer = <CALayer: 0x6000029616c0>>"
)
Copy the code

When the proxy method is triggered, the block callback returns tuple type data.

##4 UIButton button click event

Common writing:

1) Add methods for buttonsaddTarget

[self.button addTarget:self action:@selector(onBtnClick:) forControlEvents:UIControlEventTouchUpInside];
Copy the code

2) Realize the method of button clickingonBtnClick

- (void)onBtnClick:(UIButton *)sender {
    NSLog("Click the button");
}
Copy the code

RAC writing:

[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
    NSLog(@ "% @",x);
}];
Copy the code

Here is the information printed by the console after the button is clicked:

2019- 05- 16 17:08:43.841912+0800 001---RAC[54247:4481723]
 <UIButton: 0x7fb967d118e0; frame = (166 223; 46 256); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600000942880>>
Copy the code

# # 5, UITextField at that time

RAC writing:

[self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
    NSLog(@ "% @",x);
}];
Copy the code

When you type h in self.textField, the changed text is printed in real time

2019- 05- 16 18:02:58.562309+0800 001---RAC[54530:4536864] h
2019- 05- 16 18:02:59.049225+0800 001---RAC[54530:4536864] hh
2019- 05- 16 18:02:59.288995+0800 001---RAC[54530:4536864] hhh
Copy the code

##6, UITapGestureRecognizer

RAC writing:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
self.label.userInteractionEnabled = YES;
[self.label addGestureRecognizer:tap];
[tap.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
    NSLog(@ "% @",x);
}];
Copy the code

Array and dictionary traversal

Common writing:

The normal way to iterate over arrays and dictionaries is to write a for loop.

RAC writing:

1) Array:

NSArray *array = @[@" How much more".@ "feeling".@Most Chic.];
[array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    NSLog(@ "% @",x);
}];
Copy the code

Array information printed out by the console

2019- 05- 16 17:14:40.546191+0800 001---RAC[54329:4488306All that much2019- 05- 16 17:14:40.546569+0800 001---RAC[54329:4488306] feeling2019- 05- 16 17:14:40.546761+0800 001---RAC[54329:4488306] is the most natural and unrestrainedCopy the code

2) Dictionary:

NSDictionary *dict = @{@"name":@" How much more".@"age":@ "20".@"sex":@ "male"};
[dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    / / tuples
    NSLog(@ "% @",x);
    RACTwoTuple *tuple = (RACTwoTuple *)x;
    NSLog(@"key == %@ , value = %@",tuple[0],tuple[1]);
}];
Copy the code

Array information printed out by the console

2019- 05- 16 17:20:02.538642+0800 001---RAC[54365:4494005] 
<RACTwoTuple: 0x600000c745e0> (
    name,
    "\U51e1\U51e0\U591a"
)
2019- 05- 16 17:20:02.539301+0800 001---RAC[54365:4494005] key == name, value =2019- 05- 16 17:20:02.540359+0800 001---RAC[54365:4494005] 
<RACTwoTuple: 0x600000c7c580> (
    age,
    20
)
2019- 05- 16 17:20:02.540577+0800 001---RAC[54365:4494005] 
key == age , value = 20
2019- 05- 16 17:20:02.542622+0800 001---RAC[54365:4494005] 
<RACTwoTuple: 0x600000c74660> (
    sex,
    "\U7537"
)
2019- 05- 16 17:20:02.542774+0800 001---RAC[54365:4494005] key == sex, value = maleCopy the code

The basic usage process of RAC

Create signal, subscribe signal, send signal

//1: create signal
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    // Subscriber object is not an object
    //3: sends signals
    [subscriber sendNext:@"Cooci"];
        
    // Failed to request network error
    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:10086 userInfo:@{@"key":@ 10086 "error"}];
    [subscriber sendError:error];

    / / RACDisposable destroyed
    return [RACDisposable disposableWithBlock:^{
        NSLog(@" Destroyed");
    }];
}];

//2: subscribe signal
[signal subscribeNext:^(id  _Nullable x) {
    NSLog(@ "% @",x);
}];
// Subscribe to error signals
[signal subscribeError:^(NSError * _Nullable error) {
    NSLog(@ "% @",error);
}];
Copy the code

The above summary refers to and partly extracts the following articles, thanks very much to the following authors for sharing! :

1. “Functional Responsive Programming (FRP) from Beginning to” Giving Up “– Basic Concepts by author ZZFX

2. “Functional Responsive Programming” by Philm_iOS

3, author I just came out to write code “RAC(ReactiveCocoa) Introduction (1) – Basic Introduction”

Reprint please note the original source, shall not be used for commercial communication – any more