Use ReactiveCocoa to implement responsive programming on iOS platform

Source: Internet
Author: User

Use ReactiveCocoa to implement responsive programming for iOS platform ReactiveCocoa and responsive programming

Before talking about ReactiveCocoa, we should first introduce FRP (Functional Reactive Programming, responsive Programming), which has an example in Wikipedia:

In the imperative programming environment, a = B + c indicates that the expression result is assigned to a, and changing the value of B or c does not affect. However, in responsive programming, the value of a is updated with the update of B or c.

Excel is an example of responsive programming. A cell can contain a nominal value or a formula similar to "= B1 + C1". The value of a cell containing the formula varies according to the value of another cell.

ReactiveCocoa, short for RAC, is an Objective-C practice based on responsive programming ideas. It is an open-source Github project and you can find it here.

For more information about FRP and ReactiveCocoa, see this blog by leezhong.

ReactiveCocoa framework Overview

Let's take a look at the analogy mentioned in leezhong and the blog post to give you a good understanding of ReactiveCocoa:

We can imagine the signal as a faucet, but it is not water, but a glass ball (value). The diameter is the same as the diameter of the pipe. This ensures that the glass balls are arranged in sequence, no side-by-side (data is processed linearly and no concurrency occurs ). The switch of the faucet is disabled by default. It is enabled only when the receiver (subscriber) is available. In this way, as long as a new glass ball comes in, it will be automatically transmitted to the receiver. You can add a filter on the faucet. If it does not match the filter, you can also add a device to change the ball to meet your needs (map ). You can also combine multiple faucets into a new faucet (combineLatest: reduce :), so that as long as one of the faucets has a glass ball, the merged faucet will get the ball.

Next I will introduce each component of the ReactiveCocoa framework one by one.

Streams

Streams is represented by RACStream. It can be seen as a series of glass balls flowing in the water pipe. They pass through sequentially. Before the first glass ball arrives, you cannot get the second glass ball.
RACStream describes this form of linear flowing glass sphere, which is abstract and does not have a great significance. It is generally replaced by higher-level forms such as signals or sequences.

Signals

Signals is represented by the RACSignal class, that is, the faucet mentioned above. The core concept of ReactiveCocoa is Signal, which generally indicates the value to arrive in the future. Imagine glass balls coming out of the faucet one by one, only the receiver (subscriber) can obtain these glass balls (values ).

Signal sends the following three events to its receiver (subscriber). It is imagined that the faucet has an indicator to report its working status.-subscribeNext:error:completed:Respond to different events

  • Next the new glass ball (value) flowing from the tap)
  • An error occurs when an error occurs when obtaining a new glass ball. Generally, an NSError object is sent to indicate where the error occurred.
  • All the completed glass balls have arrived successfully. No more glass balls have been added.

    A Signal of a life cycle can send any number of "next" events and an "error" or "completed" event (of course, "error" and "completed" can only appear)

    Subjects

    Subjects is represented by the RACSubject class and can be considered as a "mutable" signal/custom signal. It is a bridge to graft non-RAC code to the Signals world and is very useful. Well... This is very abstract. For example:

    123 RACSubject * letters = [RACSubject subject]; RACSignal * signal = [letters sendNext: @ "a"];

    We can see that@"a"It is just an NSString object. to flow smoothly in a pipe, we need to use the force of RACSubject.

    Commands

    Command is represented as a RACCommand class. For example, a simple registration interface:

    123456789101112131415161718192021 RACSignal * formValid = [RACSignal combineLatest: @ [self. userNameField. rac_textSignal, self. emailField. rac_textSignal,] reduce: ^ (NSString * userName, NSString * email) {return @ (userName. length & gt; 0 & amp; email. length & gt; 0) ;}]; RACCommand * createAccountCommand = [RACCommandcommandWithCanExecuteSignal: formValid]; RACSignal * networkResults = [[[createAccountCommand addSignalBlock: ^ RACSignal * (idvalue) {//... network Interaction code}] switchToLatest] deliverOn: [RACSchedulermainThreadScheduler]; // bind the UI state of the create button and click the event [[self. createButtonrac_signalForControlEvents: UIControlEventTouchUpInside] executeCommand: createAccountCommand];

    Sequences

    Sequence is represented by the RACSequence class, which can be simply regarded as the NSArray In the RAC world. RAC has increased-rac_sequenceThe collection classes such as NSArray can be directly converted to RACSequence.

    Schedulers

    Scheders is represented by the RACScheduler class, similar to GCD, but schedulers support cancellationbut schedulers support cancellation, and always execute serially.

    Simple use of ReactiveCocoa

    Let's take a look at the usage of RAC through some simple examples.

    Subtasks

    Receive-subscribeNext: -subscribeError: -subscribeCompleted:

    1234567 RACSignal * letters = [@ "a B c d e f g h I" componentsSeparatedByString: @ ""]. rac_sequence.signal; // output a B C D... [Letters subscribeNext: ^ (NSString * x) {NSLog (@ "% @", x) ;}];

    Injecting effects

    Injection Effect-doNext: -doError: -doCompleted:, You should understand the following annotations:

    1234567891011121314151617181920 _ Blockunsignedsubscriptions = 0; RACSignal * loggingSignal = [RACSignalcreateSignal: ^ RACDisposable * (id & lt; RACSubscriber & gt; subscriber) {subscriptions ++; [subscribersendCompleted]; returnnil;}]; // loggingSignal = [loggingSignaldoCompleted: ^ {NSLog (@ "about to complete subscri% u", subscriptions);}]; // output: // about to complete subpartition 1 // subpartition 1 [loggingSignalsubscribeCompleted: ^ {NSLog (@ "subpartition % u", subscriptions);}];

    Mapping

    -map:Ing can be seen as the transformation and re-assembly of the glass sphere

    1234567 RACSequence * letters = [@ "a B c d e f g h I" componentsSeparatedByString: @ ""]. rac_sequence; // Contains: aa bb cc dd ee ff gg hh IIRACSequence * mapped = [letters map: ^ (NSString * value) {return [value stringByAppendingString: value];}];

    Filtering

    -filter:Filter. glass balls that do not meet the requirements cannot pass

    1234567 RACSequence * numbers = [@ "1 2 3 4 5 6 7 8 9" componentsSeparatedByString: @ ""]. rac_sequence; // Contains: 2 4 6 8 RACSequence * filtered = [numbersfilter: ^ BOOL (NSString * value) {return (value. intValue % 2) = 0;}];

    Concatenating

    -concat:After splicing a pipe to another pipe

    123456 RACSequence * letters = [@ "a B c d e f g h I" componentsSeparatedByString: @ ""]. rac_sequence; RACSequence * numbers = [@ "1 2 3 4 5 6 7 8 9" componentsSeparatedByString: @ ""]. rac_sequence; // Contains: a B c d e f g h I 1 2 3 4 5 6 7 8 9 RACSequence * concatenated = [letters concat: numbers];

    Flattening

    -flatten:

    Sequences are concatenated

    1234567 RACSequence * letters = [@ "a B c d e f g h I" componentsSeparatedByString: @ ""]. rac_sequence; RACSequence * numbers = [@ "1 2 3 4 5 6 7 8 9" componentsSeparatedByString: @ ""]. rac_sequence; RACSequence * sequenceOfSequences = @ [letters, numbers]. rac_sequence; // Contains: a B c d e f g h I 1 2 3 4 5 6 7 8 9 RACSequence * flattened = [sequenceOfSequencesflatten];

    Signals are merged)

    12345678910111213141516171819202122 RACSubject * letters = [RACSubject subject]; RACSubject * numbers = [RACSubject subject]; RACSignal * random = [RACSignal createSignal: ^ RACDisposable * (id & lt; RACSubscriber & gt; subscriber) {[subscriber sendNext: letters]; [subscriber sendNext: numbers]; [subscriber sendCompleted]; return nil ;}]; RACSignal * flattened = [signalOfSignals flatten]; // Outputs: A 1 B C 2 [flattened subscribeNext: ^ (NSString * x) {NSLog (@ "% @", x) ;}]; [letters sendNext: @ "A"]; [numbers sendNext: @ "1"]; [letters sendNext: @ "B"]; [letters sendNext: @ "C"]; [numbers sendNext: @ "2"];

    Mapping and flattening

    -flattenMap:Map first and then flatten

    123456789101112131415161718192021222324252627282930 RACSequence * numbers = [@ "1 2 3 4 5 6 7 8 9" componentsSeparatedByString: @ ""]. rac_sequence; // Contains: 1 1 2 2 3 3 3 4 5 5 6 6 6 7 8 8 9 RACSequence * extended = [numbersflattenMap: ^ (NSString * num) {return @ [num, num]. rac_sequence;}]; // Contains: 1 _ 3 _ 5 _ 7 _ 9_RACSequence * edited = [numbersflattenMap: ^ (NSString * num) {if (num. intValue % 2 = 0) {return [RACSequenceempty];} else {NSString * newNum = [numstringByAppendingString: @ "_"]; return [RACSequencereturn: newNum];}]; RACSignal * letters = [@ "a B c d e f g h I" componentsSeparatedByString: @ ""]. rac_sequence.signal; [[letters flattenMap: ^ (NSString * letter) {return [character: letter] ;}] subscribeCompleted: ^ {NSLog (@ "All database entries saved successfully. ") ;}];

    Sequencing

    -then:

    12345678910111213 RACSignal * letters = [@ "a B c d e f g h I" componentsSeparatedByString: @ ""]. rac_sequence.signal; // the new TAP only contains: 1 2 3 4 5 6 7 8 9 /// but when receiving, the old tap doNext will still be executed, therefore, a B C D E F G H IRACSignal * sequenced = [[letters doNext: ^ (NSString * letter) {NSLog (@ "% @", letter);}] then: ^ {return [@ "1 2 3 4 5 6 7 8 9" componentsSeparatedByString: @ ""]. rac_sequence.signal;}];

    Merging

    +merge:Merge the faucets mentioned above in flatten

    123456789101112131415 RACSubject * letters = [RACSubjectsubject]; RACSubject * numbers = [RACSubjectsubject]; RACSignal * merged = [RACSignalmerge: @ [letters, numbers]; // Outputs: A 1 B C 2 [mergedsubscribeNext: ^ (NSString * x) {NSLog (@ "% @", x) ;}]; [letterssendNext: @ "A"]; [numberssendNext: @ "1"]; [letterssendNext: @ "B"]; [letterssendNext: @ "C"]; [numberssendNext: @ "2"];

    Combining latest values

    +combineLatest:Retrieve the latest glass ball from each tap at any time

    1234567891011121314151617181920 RACSubject * letters = [RACSubject subject]; RACSubject * numbers = [RACSubject subject]; RACSignal * combined = [RACSignal combineLatest: @ [letters, numbers] reduce: ^ (NSString * letter, NSString * number) {return [letter stringByAppendingString: number] ;}]; // Outputs: B1 B2 C2 C3 [combined subscribeNext: ^ (id x) {NSLog (@ "% @", x) ;}]; [letters sendNext: @ "A"]; [letters sendNext: @ "B"]; [numbers sendNext: @ "1"]; [numbers sendNext: @ "2"]; [letters sendNext: @ "C"]; [numbers sendNext: @ "3"];

    Switching

    -switchToLatest:Retrieve the latest glass ball from the specified faucet

    1234567891011121314151617181920212223 RACSubject * letters = [RACSubjectsubject]; RACSubject * numbers = [RACSubjectsubject]; RACSubject * signalOfSignals = [RACSubjectsubject]; RACSignal * switched = [Outputs]; // Outputs: a B 1 D [switchedsubscribeNext: ^ (NSString * x) {NSLog (@ "% @", x) ;}]; [signalOfSignalssendNext: letters]; [letterssendNext: @ "A"]; [letterssendNext: @ "B"]; [signalOfSignalssendNext: numbers]; [letterssendNext: @ "C"]; [numberssendNext: @ "1"]; [signalOfSignalssendNext: letters]; [numberssendNext: @ "2"]; [letterssendNext: @ "D"];

    Common macro RAC can be seen as the association between values of a certain attribute and some signals.

    1234 RAC (self. submitButton. enabled) = [RACSignal combineLatest: @ [self. usernameField. rac_textSignal, self. passwordField. rac_textSignal] reduce: ^ id (NSString * userName, NSString * password) {return @ (userName. length & gt; = 6 & amp; password. length & gt; = 6) ;}];

    RACObserve listens for attribute changes and uses block KVO

    1234 [RACObserve (self. textField, text) subscribeNext: ^ (NSString * newName) {NSLog (@ "% @", newName);}];

    UI Event

    RAC provides a lot of category for the system UI, which is very good, such as changes to the UITextView and UITextField text boxes.rac_textSignal, UIButton Pressrac_commandAnd so on.

    Last

    With RAC, you don't have to worry about the time when the value will change. You just need to perform a simple step after the data comes.

    After talking about this, let's look back at leezhong's metaphor and the final diagram of the Article. Let's sort it out. I am also a beginner. I am very excited to present this blog. You are welcome to discuss it. If you have any mistakes, please criticize and correct them.

    Reference

    Https://github.com/ReactiveCocoa/ReactiveCocoa

    Https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/FrameworkOverview.md

    Https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/BasicOperators.md

    Http://vimeo.com/65637501
    Http://iiiyu.com/2013/09/11/learning-ios-notes-twenty-eight/
    Http://blog.leezhong.com/ios/2013/06/19/frp-reactivecocoa.htmlhttp://nshipster.com/reactivecocoa/


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.