Use of Rac in IOS

Time:2022-1-15

The content of this article mainly records the situation of Rac often used in ordinary projects, and does not do too in-depth research.

preface

Reactivecocoa can be said to be a framework that combines functional programming and responsive programming. It can also be called functional responsive programming (FRP) framework. It is emphasized that the biggest advantage of Rac is that it provides a single and unified method to deal with asynchronous behavior, including delegate method, blocks callback, target action mechanism, notifications and KVO

I Import

1. Add in the project’s podfile file

  # RAC
  pod 'ReactiveObjC'

2. Execute the pod install method to import the framework.

3. Import in the class using RAC

//Responsive programming
#import <ReactiveObjC/ReactiveObjC.h>

Because many classes are used in my project, I import them directly from the PCH file

II RAC general use

1).Button add click event
For example, add a click event to a login button

    [[self.loginAccountBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        @strongify(self);
        [self.navigationController pushViewController:[[NSClassFromString(@"GHLoginViewController") alloc] init] animated:YES];
    }];

2). replaceKVO monitor
Listen for some attribute changes, call as long as the attribute changes, and pass the changed value to you.

//For example:
@property(noatomic,assign) int age;
Listen for each assignment of age and generate a callback
[RACObserve(self, age) ]subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];
Listen to the assignment of age, ignore the case with the value of "1", and do not execute the callback
[[RACObserve(self, networkStatus) ignore:@"1"]subscribeNext:^(id  _Nullable x) {
        
}];

//Simulate an event and touch the screen to increase the age
-(void)touchesBegin:(NSSet<UITouch*>*)touches WithEvent:(UIEvent*)event{
    age++;
}

//Listen for changes in the networkstatus value in any class

@interface GHConfigDeviceManager : NSObject
@property (nonatomic) GHNetworkStatus networkStatus;

[[[RACObserve(GHConfigDeviceManager.share, networkStatus) skip:1] distinctUntilChanged] subscribeNext:^(id  _Nullable x) {
        NSLog(@"networkStatus----x=%@",x);
        if ([x intValue] == 2) {
            if (weakself.alertView) {
                [weakself.alertView dismiss:nil];
            }
        }
    }];

3).Listen for textfield textchange

Real time monitoring of input text changes

[[alertView.inputTextField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
        //The length of the nickname ranges from 1 to 20 characters
        [weakalertView.okBtn setTitleColor:(weakalertView.inputTextField.text.length == 0 || weakalertView.inputTextField.text.length > 20) ? ASColorHex(0xC1CCC9) : ASColorHex(0x0BD087) forState:UIControlStateNormal];
        weakalertView.okBtn.enabled = (x.length == 0 || x.length > 20) ? NO : YES;
    }];

4).Listening notification callback

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

5).Gesture execution method monitoring

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
self.lable.userInteractionEnabled = YES;
[self.lable addGestureRecognizer:tap];
[[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
        
}];

6).Array and dictionary traversal
Array traversal

NSArray *array = @[@"111",@"222",@"333",@"444"];
[array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    NSLog(@"x11====%@",x);
}];
2021-05-27 11:22:41.734488+0800 GHome[5071:1101120] x11====222
2021-05-27 11:22:41.734570+0800 GHome[5071:1101120] x11====333
2021-05-27 11:22:41.734634+0800 GHome[5071:1101120] x11====444

Dictionary traversal

NSDictionary *dict = @{@"111":@"-111",@"222":@"-222",@"333":@"-333",@"444":@"-444"};
    [dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    NSLog(@"x22====%@",x);
}];

The following is the printed content. Ractwotuple can be regarded as a two-dimensional array type

2021-05-27 11:19:24.804544+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c09f0> (
    222,
    "-222"
)
2021-05-27 11:19:24.804825+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0bb0> (
    111,
    "-111"
)
2021-05-27 11:19:24.804941+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0bc0> (
    444,
    "-444"
)
2021-05-27 11:19:24.805051+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0be0> (
    333,
    "-333"
)

III Common RAC higher order functions
1). Signal mergingcombineLastest

    RACSignal *accountSignal = self.accountTextField.rac_textSignal;
    RACSignal *passwordSignal = self.passwordTextField.rac_textSignal;

    RAC(self, loginBtn.enabled) = [RACSignal combineLatest:@[accountSignal, passwordSignal] reduce:^id(NSString *account, NSString *password){
        @strongify(self);
        BOOL b;
        b = [account isValidatePhoneNumber];
        return @(b && [password isValidatePassword]);
    }];

Account is the callback value of accountsignal signal and password is the callback value of passwordsignal signal. We process the condition judgment in the reduce callback function and return the judgment result to loginbtn in the form of signal Value of enabled

2).Use of map
In one sentence, summarize the function of map: mainly used forData re encapsulation and transformation
Example 1 listening code_ The value of version is changed, formatted as a string, and finally assigned to the accessorytitle attribute

RAC(self, accessoryTitle) = [RACObserve(self, deviceViewModel.code_version)  map:^id _Nullable(NSString *_Nullable value) {
        return ASStringFormat(@"V%@", value);
}];

Example 2 traverses the value array and reassembles the elements in value to generate ghdevicesettingmodel value, and then regenerates the array array to return

[value.rac_sequence map:^GHDeviceSettingViewModel * _Nullable(NSDictionary *_Nullable value) {
        @strongify(self);
        GHDeviceSettingModel *model = [GHDeviceSettingModel mj_objectWithKeyValues:value];
        return [[GHDeviceSettingViewModel alloc] initWithSettingModel:model deviceViewModel:self.deviceViewModel];
}].array

3). Use of filter
Example 1 the callback is executed only when the content in the textfield input box is less than six bits

self.textField.rac_textSignal filter: BOOL (NSString *_ Nullable value) {
    if (self.textField.text.length>6) f
    self.textField.text = [self.textField.text substringToIndex:6];
}
    return value.length<6;
}] subscribeNext:(NSString *_ Nullable x){
    NSLog(@"filter----%@",x);
];

Filter is often used with sequence
Example 2
Traverse the values of the array datasource and add filter conditions as required. When return yes, it will be added to the returned array array, and when return no, it will not be added to the array array array

NSArray *tempArray = [[self.OTAViewModel.dataSource rac_sequence] filter:^BOOL(GHOTACellViewModel* _Nullable value) {
    if (value.type.intValue == 2) {
            return YES;
    }
    return NO;
}].array;

The advantage of using RAC for traversal is that you can get the newly generated array or dictionary without re creating the array or dictionary

4).Flattenmap mapping
Example: perform secondary encapsulation on the input content

[[self.textField.rac_textSignal flattenMap:__kindof RACSignal *_ Nullable(NSString *
_Nullable value) {
    NSLog( @"%@",value);//+8610086
    return [RACReturnSignal return:[NSString stringWithFormat:@"+86%@",value]];
}] subscribeNext:^(id _Nullable х) {
    Nslog (@ "mapping:% @", x);
}];

III Simple use with MVVM + RAC
Here is a brief introduction to MVVM
As we all know, MVC mode has many problems, such as heavy viewcontroller, lost network logic (there is no location belonging to it), poor testability and so on. Therefore, a new architecture MVVM (a new architecture derived from MVC) with strong maintainability and low coupling will be popular.

Use of Rac in IOS

Picture png

It mainly lies in ViewModel
ViewModel: compared with the newly introduced view model of MVC. It is the place where the view display logic, verification logic, network request and other codes are stored. The only thing to note is that any reference to the view itself should not be placed in the VM. In other words, UIKit should not be introduced into the VM H (for image, some people treat it as data, which depends on personal ideas and does not affect the overall architecture).

For example, when a user logs in to a network request, the logic related to the network request is put into the ViewModel for execution

@interface GHLoginViewModel : NSObject

@property (nonatomic, strong) RACCommand *loginCommand;

@property (nonatomic, strong) RACCommand *refreshTokenCommand;


@interface GHLoginRequest : GHNetworkBaseRequest

///Mobile number \ email
@property (nonatomic, copy) NSString *username;

///Password (the password consists of 8 - 128 characters and cannot be pure numbers or letters)
@property (nonatomic, copy) NSString *password;

///Country code abbreviation
@property (nonatomic, copy) NSString *region_code;

///Mobile number country code
@property (nonatomic, copy) NSNumber *phone_code;

@end
@implementation GHLoginViewModel

- (instancetype)init
{
    self = [super init];
    if (self) {
    }
    return self;
}

- (RACCommand *)loginCommand {
    if (!_loginCommand) {
        _loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(GHLoginRequest* _Nullable input) {
            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                NSString *taskId = [GHNetworkModule.share sendRequest:input cacheComplete:nil networkComplete:^(GHNetworkResponse *response) {
                    if (response.status == GHNetworkResponseStatusSuccess) {
                        GHUserInfo.share.password = input.password;
                        GHUserInfo.share.token = response.data[@"token"];
                        [subscriber sendNext:response.data];
                        [subscriber sendCompleted];
                        [GHUserInfo cacheInfo];
                    } else {
                        [subscriber sendError:response.error];
                    }
                }];
                return [RACDisposable disposableWithBlock:^{
                    [GHNetworkModule.share cancelRequestWithRequestID:taskId];
                }];
            }];
        }];
    }
    return _loginCommand;
}

Receive the request result in the viewcontroller

//Return data processing
    [self.viewModel.loginCommand.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        @strongify(self)
        [GHHudTip hideHUDWithView:self.view];
    }];
    
    //Exception handling
    [self.viewModel.loginCommand.errors subscribeNext:^(NSError * _Nullable x) {
        @strongify(self)
        [GHHudTip hideHUDWithView:self.view];
        [GHHudTip showTips:x.domain];
    }];

Extension and extension:IOS MVVM + RAC from framework to actual combat

Conclusion: the function of Rac is very powerful and practical. Here are just a few. Others need to be explored slowly in development. Refueling ~

Recommended Today

The most detailed actual combat of docker installation redis in history (including the illustration of each step)

Not only teach you to install, but also teach you to delete. Each line of command is illustrated, which is absolutely understandable. If you are interested in building a redis cluster, you can take a look at this article Docker builds redis cluster cluster environmentHope to help you 1、 Docker searches redis images Command: docker […]