Related reading:
Reactivecocoa Code Practice-UI Component's RAC signal operation
Reactivecocoa code Practice-More thinking
Objective
RAC has the following advantages over previous development models: providing a unified messaging mechanism, providing a variety of fantastic and efficient signal manipulation methods, and reducing multiple dependencies with MVVM design patterns and RAC macro bindings.
RAC's theoretical knowledge is very deep, including FRP, higher-order functions, cold and thermal signals, RAC Operation, and the lifecycle of the signal, which are described in these documents. But because of the nature of the RAC itself, it may sound easy to get started.
* This article is also from a comparison of grounding gas from the angle of the beginning. Because now to do a perfect 100% of the whole project Reactivecocoa architecture is not very realistic, most projects will have a lot of historical baggage, we can only gradually closer to the RAC, a section of disgusting code refactoring, so that the logic function clearer.
This section is primarily a simple record of my previous refactoring of network requests.
I. Reconstruction of COMMON requests
Old code structure diagram:
It is common practice for previous code controllers to call the service directly in a method that requires a connection to the network and get a callback.
CONTROLLER.M ************************************
//Controller in one of the methods
-(void) requestfortop{
[ Mdsbezelactivityview activityViewForView:self.view withlabel:@ "Loading ..."];
Directly invoke the request method in the service
[Sxfeedbackservice requestforfeedbacksummarysuccess:^ (Nsdictionary *result) {
[ Mdsbezelactivityview Removeview];
After successful correlation processing
} failure:^ (Afhttprequestoperation *operation, Nserror *error) {
[Mdsbezelactivityview Removeview] ;
Related processing after failure
}];
Structure diagram after refactoring:
After using the RAC Rewrite, controller does not directly call Service,controller to achieve the purpose of the request by controlling the execution of a command. Once the value of the binding has been changed, it comes to the Racobserve callback method. And if the request fails, it is passed to the Execute's Subscribeerror callback method in the form of an error signal. Executing can be used to listen for command execution.
CONTROLLER.M ************************************ @property (nonatomic,strong) Sxfeedbackmainviewmodel *viewModel
;
-(void) viewdidload{[self addracobserve];//Set binding-(void) addracobserve{@weakify (self) When the page is first loaded; [[Racobserve (Self.viewmodel, topnumentity) Skip:] subscribenext:^ (ID x)
{@strongify (self);//Bind ViewModel value once changed to come here.
}]; ////Where the request was originally used-(void) requestfortop {[Self.viewModel.fetchFeedbackSummaryCommand Execute:nil] subscribeerror:^ (
Nserror *error) {//error handling}]; [[self.viewModel.fetchFeedbackSummaryCommand.executing Skip:] subscribenext:^ (NSNumber *executing) {if ([executing boolvalue]) {[Mdsbezelactivityview activityViewForView:self.view withlabel:@ ' load ... '];}
else{[Mdsbezelactivityview Removeview];
}//VIEWMODEL.M ************************************-(instancetype) init {self = [super init];
[Self setupraccommand];
return self; //initialization set an instruction to open a request-(void) Setupraccommand {@weakify (self); _fetchfeedbacksummarycommand = [[Raccommand alloc] Initw IthsiGnalblock:^racsignal * (ID input) {return [racsignal createsignal:^racdisposable * (id<racsubscriber> subscriber)
{//Here is a more thorough approach is to write the request directly as a operation, but most of the network layer of the project should have manager or signature, etc. to change directly to that structure may be more complex, so this code like RAC and direct request combination. [Sxmerchantautorityservice requestforfeedbacksummarysuccess:^ (Nsdictionary *result) {@strongify (self);//[subscriber sendcompleted] after a successful callback;
failure:^ (afhttprequestoperation *operation, Nserror *error) {[subscriber senderror:error];}];
return nil;
}];
}]; }
Two. Request to pass parameters
The above is an ordinary request, that is, the request address is written dead or from the global variable stitching parameters. If you need to pass in a number of parameters, controller can not directly contact the service, so need to use ViewModel as a medium to transfer values, there are two methods of transmission.
1. Through the ViewModel properties
This method can be used for fewer parameters, one or two. Add some attributes directly to the ViewModel, and then controller assign a value to the attribute at the appropriate time. Invoking the service method in Raccommand in ViewModel directly from its own property when a parameter is required.
CONTROLLER.M ************************************
self.viewModel.isAccess = self.isaccess;
[Self requestfortop];
ViewModel.h ************************************
//input Parameters
/**
* is the United States or comments * * *
@property ( Nonatomic, assign) BOOL isaccess;
VIEWMODEL.M ************************************
_fetchfeedbacksummarycommand = [[RACCommand alloc] Initwithsignalblock:^racsignal * (ID input) {return
[racsignal createsignal:^racdisposable * (id< racsubscriber> subscriber) {
[Sxmerchantautorityservice requestForFeedbackSummaryWithType:self.isAccess success:^ (Nsdictionary *result) {
//Success
} failure:^ (Afhttprequestoperation *operation, Nserror *error) {
//Failed
}];
return nil;
}];
If you set some property bindings for ViewModel and controller with a RAC macro, you can also omit the step of manually assigning the ViewModel set method. (Dong Platinum ran blog park)
2. Passing values through the Execute method parameter
This method can be applied to many parameters and cannot be listed as ViewModel attribute. It is recommended that you set up an object model and then build and assign the model before the Execute method, and then pass it as a parameter.
For example, this common list class has multiple parameters for the request method:
Service.h ************************************
/**
* Get Evaluation list * *
+ (void) Requestforfeedbacklistwithsource: (BOOL) Isfromweb
dealid: (nsinteger) Dealid poiid
: (nsinteger) poiid
LabelName: (NSString *) labelname
type: (NSString *) type
readstatus: (NSString *) Readstatus
replystatus :(NSString *) replystatus
limit: (NSNumber *) limit
offset: (NSNumber *) offset
success: (void (^) ( Nsdictionary *result)) success
The old method in the controller request method is to invoke the service's request interface directly, which is no longer listed here, and the following is a list of the methods of RAC.
CONTROLLER.M ************************************-(void) Requestfordatawithtype: (int) type {//------
Pass an input model to Raccomand.
Sxfeedbacklistrequestmodel *input = [Sxfeedbacklistrequestmodel new]; Input.replystatus = Self.replystatus;
This can also be written as a factory method input.readstatus = Self.readstatus;
Input.ismeituan = Self.ismeituan;
Input.dealid = Self.dealid;
Input.poiid = self.poiid;
Input.type = Self.type;
Input.labelname = LabelName;
Input.offset = @ (Self.offset);
Input.limit = @ (); The input above is passed here as a parameter to [[Self.viewModel.fetchFeedbackListCommand Execute:input] subscribenext:^ (ID x) {//------
Handle the correct operation here.
} error:^ (Nserror *error) {//------handle the failed operation here.
}]; }//VIEWMODEL.M ************************************-(void) Setupraccommand {_fetchfeedbacklistcommand = [ Raccommand alloc]initwithsignalblock:^racsignal * (Sxfeedbacklistrequestmodel *input) {return [RACSignal createSignal : ^racdisposable * (id<racsubscriber> subscriber) {//The parameters passed in front of execute will be uploaded to this place [Sxmerchantautorityservice REQUESTFOrFeedbacklistWithSource:input.isFormWeb dealid:input.dealid poiid:input.poiid labelName:input.labelName type: Input.type readStatus:input.readStatus replyStatus:input.replyStatus limit:input.limit offset:input.offset Success : ^ (nsdictionary *result) {@strongify (self);//Some actions [subscriber sendcompleted];} failure:^ (Afhttprequestoperation *ope
Ration, Nserror *error) {[subscriber senderror:error];}];
return nil;
}];
}]; }
You might think that in this command you have to strip out every attribute of the previous model to the parameters and the behavior is somewhat redundant. You can rewrite the method in the previous service with a lot of parameters so that you only need to pass in a model. Then command here you can directly into the model, anyway, it is not troublesome to take out the method inside. My side has taken into consideration the compatibility of other non-RAC places has not changed.
Three. All requests are completed before elimination of toast
Here is a concept similar to the request combo. When all requests are complete, the Progresshud in the load is eliminated, and if the Dispatch dispatch group is available under the normal architecture, the RAC implementation is very simple, the main method is to judge the state of a command by executing signal. The combinelatest operation is then used to monitor the state of multiple command, and the combinelatest operation is characterized by a plurality of signals that are monitored as soon as a change is made to make all the signals a tuple return.
Monitor executing
racsignal *hud = [Racsignal combinelatest:@[self.viewmodel.fetchfeedbacklistcommand.executing, Self.viewModel.fetchFeedbackSummaryCommand.executing]];
[Hud subscribenext:^ (Ractuple *x) {
if (![ X.first boolvalue]&&! [X.second Boolvalue]) {
[Mdsbezelactivityview Removeview];
} else{
[Mdsbezelactivityview activityViewForView:self.view withlabel:@ ' loading ... '];
This proposal is written together with the previous racobserve. can also be changed to the wording of the filter.
You can load HUD code in the front, and then directly control the elimination of HUD
[HUD Filter:^bool (Ractuple *x) {return
![ X.first boolvalue]&&! [X.second boolvalue];
}] subscribenext:^ (id x) {
[Mdsbezelactivityview Removeview];
There is another way to implement this requirement, which is to call the @selector method only if all the signals in the array emit a sendnext signal, and the three parameters of the method are the three Sendnext rac_liftselector. All of them are back. Unified packaging, which applies mainly to three requests are asynchronous without dependencies.
@weakify (self);
[[Self Rac_liftselector: @selector (DoWithA:withB:withC) Withsignalsfromarray:@[signala,signalb,signalc]] subscribeerror:^ (Nserror *error) {
@strongify (self);
[Mdsbezelactivityview Removeview];
} completed:^{
[Mdsbezelactivityview Removeview];
Combinelatest and Liftselector Two kinds of combo methods have a certain difference, specific use can be combined with demand. The former is that every request comes back with a callback, and the latter is all back and calls the method. (Dong Platinum ran blog park)
Four. Transfer of results data
If you want all the requests to be done, all the data will be available, and then refresh the interface, using the same method that is used to uniformly eliminate toast. Change the toast line of code to [Self.tableview Reloaddata] or other code.
Because now the mainstream is to be able to slim body controller, so it is generally recommended that the business logic, judgment, calculation, stitching strings in the ViewModel, and finally directly to the required data returned, the controller is responsible only for the data directly after the display interface. The following example is a method of obtaining text on a textual label
CONTROLLER.M ************************************//Viewdidload RAC (self.replycountlabel,text) = RACObserve (
Self.viewmodel, Replycountlabeltitle); VIEWMODEL.M ************************************ _fetchnewsdetailcommand = [[Raccommand alloc]initWithSignalBlock : ^racsignal * (ID input) {return [racsignal createsignal:^racdisposable * (id<racsubscriber> subscriber) {@
Strongify (self); [Self requestfornewsdetailsuccess:^ (nsdictionary *result)
{//This eliminates some null code Self.detailmodel = [sxnewsdetailentity detailwithdict:result[self.newsmodel.docid]];//There are other operations in the middle that omit
Nsinteger count = [Self.newsModel.replyCount intvalue]; Here is the direct return of the mosaic title, the reality will also encounter more complex logic if ([Self.newsModel.replyCount intvalue] >) {self.replycountbtntitle = [nsstring str
ingwithformat:@ "%.F million Thread", count/.]
}else{self.replycountbtntitle = [nsstring stringwithformat:@ '%ld thread ', Count];}
[Subscriber sendcompleted];
} failure:^ (Afhttprequestoperation *operation, Nserror *error) {[subscriber senderror:error];}]; Return NIL
}]; }];
When refactoring, you can put more controller properties such as models, or arrays, into ViewModel. The self.replymodels in the previous controller was changed to self. Viewmodel.replymodels.
ViewModel.h ************************************/** * Similar News * * @property (Nonatomic,strong) Nsarray *similarnews;
/** * Search Keywords * * * @property (Nonatomic,strong) Nsarray *keywordsearch;
/** * Get search Results array command/* @property (nonatomic, strong) Raccommand *fetchnewsdetailcommand; VIEWMODEL.M ************************************//command to invoke a successful callback in the request method self.similarnews = [
Sxsimilarnewsentity objectarraywithkeyvaluesarray:result[self.newsmodel.docid][@ "Relative_sys"];
Self.keywordsearch = result[self.newsmodel.docid][@ "Keyword_search"];
[Subscriber sendcompleted]; CONTROLLER.M ************************************//casually took a method for example-(cgfloat) TableView: (UITableView *) TableView Heightforheaderinsection: (nsinteger) Section {switch (section) {Case:return self.webView.height; break; Case:return S Elf.viewModel.replyModels.count >?
: Cgfloat_min;
Break Case:return self.viewModel.similarNews.count >?
: Cgfloat_min;
Break
Default:return cgfloat_min;
Break }
}
After a reasonable separation, it should be controller. Only some UI controls, ViewModel, store model attributes, commands, and some business logic operations or judgments.
Some of these demo code interested can be fork the code here https://github.com/dsxNiubility/SXNews. It used to be a small project with a dirt method, and now the old code moved to the oldest branch, and the master branch continued to make some RAC-related changes.
The above is a small set to introduce the Reactivecocoa code practice of-RAC Network request refactoring related content, I hope to help!