1. Introduction Functional Responsive Programming is becoming an increasingly popular programming approach for Swift developers today, for the simple reason that it makes complex asynchronous code easy to write and understand. In this post, I will compare two of the most popular functional reactive programming libraries available on GitHub — RxSwift vs ReactiveCocoa. First, we will briefly review what functional reactive programming is, and then compare the two frameworks in detail. By the end of this article, you will have decided which framework is right for you! 2. What is Functional Responsive Programming (FRP) Even before Apple announced Swift, functional reactive programming had grown in popularity in recent years as a contrast to object-oriented programming. From Haskell to Javascript, you can find support for functional reactive programming inspired by it. Why is that? What special support does functional reactive programming provide? And perhaps most importantly, how can you use this technique in Swift programming? FRP is a programming paradigm created by Conal Elliott. He defined very specific semantics about FRP (see https://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming). To achieve a more concise definition, FRP combines two other functional concepts: l Reactive Programming: Focuses on asynchronous data streams that you can listen to and react to quickly. For more information, see https://gist.github.com/staltz/868e7e9bc2a7b8c1f754. l Functional Programming: Emphasizes the use of mathematical language-style functions, immutability, and expressiveness to implement computations, and minimizes the use of variables and states. Please refer to Baidu Encyclopedia (http://baike.baidu.com/link?url=oTiEJsaX5ibGvK3R-BK6J55dGOXf3-Zzn5e1uBpvWDx4AjCV8ykQtRWz3RmuUefjD6IzL_QZcyedkvMB8sEyhK) to learn more about this programming paradigm. (I) A simple example Of course, the easiest way to understand the above concepts is to use a simple example. Now, let's build a small program that will locate the user's location and notify him when he is close to a cafe. If you want to develop the above program using functional reactive programming, you need to: 1. Create a stream that emits location events that you can respond to. 2. You would then filter the location events to determine which ones correspond to being near a café and send the appropriate alert. The code to implement the above functions using ReactiveCocoa programming is roughly as follows:
A brief analysis is given below. 1. locationProducer is responsible for emitting an event whenever the location changes. Note: This is called a "signal" in ReactiveCocoa programming and a "sequence" in RxSwift. 2. Then, use functional programming techniques to respond to location updates. The filter method performs exactly the same function as the filter operation on the array, and is responsible for passing each value to the function ifLocationNearCoffeeShops. If the function returns true, the event is allowed to proceed to the next step. 3. ***, startWithNext forms a subscription to the filtered signal. So every time an event arrives, the expression in the closure is executed. The above code looks very similar to the code for converting value arrays. But there is something special here that is "smart" - this code is executed asynchronously; as the position event occurs, the filter function and the closure expression are called accordingly. Sure, you might find the syntax a little strange, but hopefully the basic intent of this code should be clear to you. That's the beauty of functional programming: it's a declarative language. It shows you what's happening, not the details of how it's done. (II) Event Conversion In the location example above, you just learned how to observe the stream, and you didn’t really do much with the events other than filtering out locations near coffee shops. Another fundamental point of FRP is to combine these events and transform them into something meaningful. To do this, you use (but are not limited to) higher-order functions. As expected, you’ll encounter the following frequently in Swift functional programming: map, filter, reduce, combine, and zip. Let’s modify the location example above to skip repeating the location information and convert the incoming location information (corresponding to the CLLocation structure) into a more human-friendly message.
Let's analyze the two newly added lines of code above: 1. First, we apply the skipRepeats operation to the signal emitted by locationProducer. Note that this operation does not simulate the meaning of an array, but is unique to ReactiveCocoa. The function it performs is easy to understand: filter out repeated events. 2. After executing the filter function, call map to convert the event data from one type to another, possibly from CLLocation to String. At this point, you should understand the advantages of FRP programming: l Simple and powerful; l The declarative expression makes the code easier to understand; l Complex processes become easier to manage and describe. 3. Introduction to ReactiveCocoa and RxSwift framework Now that you have a better understanding of what FRP is and how it can help you make complex asynchronous flows more manageable, let’s look at two of the most popular FRP frameworks today — ReactiveCocoa and RxSwift — and take a closer look at why you might choose one over the other. Before we get into the details, let’s take a brief look at the history of each framework. 1. ReactiveCocoa Framework The ReactiveCocoa framework (https://github.com/ReactiveCocoa/ReactiveCocoa) is published on the GitHub website. While working on the GitHub Mac client, the developers found themselves struggling with the data flow of their application. They found inspiration from Microsoft's ReactiveExtensions (an FRP framework for C#) framework and then developed their own Objective-C implementation. Swift was announced while the team was working on their Objective-C 3.0 version. They realized that Swift's functional nature complemented ReactiveCocoa perfectly, so they immediately started implementing 3.0 in Swift. This 3.0 version fully utilizes currying and pipe-forward operators. Swift 2.0 introduced protocol-oriented programming, which led to another major change in the evolution of the ReactiveCocoa API. As version 4.0 is approaching, the pipe-forward operator supports protocol extensions. At the time of writing, ReactiveCocoa has become a popular library with over 13,000 stars on GitHub. (2) RxSwift framework Microsoft's ReactiveExtensions inspired a large number of frameworks to add FRP concepts to JavaScript, Java, Scala, and many other languages. This eventually led to the formation of ReactiveX. ReactiveX is actually a development group that can create a set of common APIs for FRP implementations; this will allow different framework developers to work together. As a result, RxScala developers familiar with Scala can relatively easily transition to the equivalent implementation in Java - RxJava. RxSwift is a relatively recent addition to ReactiveX, and therefore is not yet as popular as ReactiveCocoa (which has around 4,000 stars on GitHub at the time of writing). However, the fact that RxSwift is part of ReactiveX will undoubtedly help its popularity and longevity. Interestingly, RxSwift and ReactiveCocoa share a common ancestor implementation - ReactiveExtensions! 4. Comparison between ReactiveCocoa and RxSwift frameworks Following what we have mentioned above, let’s now focus on the details. There are many differences between ReactiveCocoa and RxSwift frameworks in terms of FRP support. Here we will only discuss a few key parts. 1. Hot and cold signals Imagine that you need to make a network request, parse the response, and display relevant information to the user, such as code similar to the following:
When you subscribe to a signal (using startWithNext), a network request is initiated. These signals are called "cold" signals because they are "frozen" - until you actually subscribe to them. On the other hand are "hot" signals. When you subscribe to one of these, it may have already started; therefore, you may be observing the third or fourth corresponding event. A typical example is typing on the keyboard. It doesn't really make sense to say when you "started" typing, and the same is true for server requests. Let’s review: l The cold signal is: you initiate it when you subscribe to it. Each new subscriber initiates this work. Subscribing to requestFlow three times means making three network requests. l A hot signal is: events can already be sent. New subscribers do not start it. Usually, UI interactions are hot signals. ReactiveCocoa provides corresponding types for hot signals and cold signals: Signal<T, E> and SignalProducer<T, E>. However, RxSwift uses a structure Observable<T> that is suitable for both types. Is it necessary to provide different types to represent hot and cold signals? Personally, I find it important to understand the semantics of a signal, as it better describes how it can be used in a specific context. This can make a big difference when dealing with complex systems. Regardless of whether different types are available, just knowing what hot and cold signals are is important. Let's say you're dealing with a hot signal, but it turns out to be a cold signal, for every new subscriber you'll be starting side effects, which can have a huge impact on your application. A common example is that there are three or four places in your application that want to observe network requests, and for every new subscription you'll be starting a different request. (II) Error handling Before we talk about error handling, it is helpful to briefly recap the nature of events dispatched in RxSwift and ReactiveCocoa. In both frameworks, there are three main events provided: 1. Next<T>: This event is sent whenever a new value (of type T) is pushed into the event stream. In the locator example above, T is CLLocation. 2. Completed: Indicates that the event stream has ended. After this event occurs, no Next<T> or Error<E> will be sent. 3. Error: indicates an error. In the server request example above, if you have a server error, this event will be sent. E describes the data type that conforms to the ErrorType protocol. After this event occurs, Next or Completed will not be sent. You may have noticed in the previous discussion of hot and cold signals that Signal<T,E> and SignalProducer<T,E> in ReactiveCocoa both use two parameterized types, while RxSwift's Observable<T> only provides one. The second type (E) refers to a type that conforms to the ErrorType protocol. In RxSwift, this type is ignored and is internally treated as a type that conforms to the ErrorType protocol. So, what does all this mean? In layman's terms, it means that errors can be emitted in a number of different ways in RxSwift:
The above code creates a signal (or in RxSwift terms, an observable sequence) and immediately emits an error. Here is an alternative way of expressing it:
Because an Observable only requires that errors must be of a type that conforms to the ErrorType protocol, you can send almost anything you want. But there is also a little awkward problem, please refer to the following code:
The above code will not work because the function handleError expects a MyDomainSpecificError type instead of an ErrorType. To do this, you are forced to do two things: 1. Try to convert the error to MyDomanSpecificError. 2. When the error cannot be converted to MyDomanSpecificError, handle the situation yourself. The first point can be easily fixed with the as? syntax technique, but the second case is a bit harder to handle. A potential solution is to introduce an Unknown type:
In ReactiveCocoa, because you do type "fixup" when creating a Signal<T, E> or SignalProducer<T, E>, the compiler will complain if you try to send something else. So, bottom line: in ReactiveCocoa, the compiler will only allow errors that send the same thing you expect. This is another aspect of ReactiveCocoa that deserves praise! (III) UI Binding Standard iOS APIs, such as UIKit, do not use FRP. In order to use RxSwift or ReactiveCocoa, you must bridge these APIs, such as converting device taps into signals or observable objects. As you can imagine, there’s a lot of power involved; to that end, both ReactiveCocoa and RxSwift provide a lot of bridging functions and binding support out of the box. ReactiveCocoa brings a lot of good stuff from its early Objective C days. You'll find that a lot of work has been done to bridge the gap for use with Swift. This includes UI bindings, and other operators that are not currently translated to Swift. Of course, it's a little weird that you're using some data types that are not part of the Swift API (such as RACSignal), which will force users to convert Objective C types to Swift types. Not only that, I feel like we spend more time discussing the source code than the documentation, which is slowly falling behind the times. However, it should be noted that the documentation is indeed excellent from a theoretical point of view, but it does not focus more on practical aspects. To remedy this situation, you can review part of the ReactiveCocoa tutorial on your own. On the other hand, the RxSwift bindings are easy to use! Not only does it provide a huge catalog of categories (https://github.com/ReactiveX/RxSwift/blob/master/Documentation/API.md), it also provides a lot of examples (https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Examples.md), and more complete documentation (https://github.com/ReactiveX/RxSwift/tree/master/Documentation). For some people, this is enough reason to choose RxSwift over ReactiveCocoa. This is another aspect of RxSwift that deserves praise! (IV) Community support ReactiveCocoa has been around much longer than RxSwift. There are many people who can continue to work on it, and there are quite a few online tutorials. In addition, the StackOverflow website also has a sub-forum dedicated to it (https://stackoverflow.com/questions/tagged/reactive-cocoa). ReactiveCocoa has a dedicated Slack group, but it's small, with only 209 people, so there are often many questions that go unanswered. In a pinch, I sometimes have to contact ReactiveCocoa core members for help, assuming others have similar needs. Still, you're most likely to find some online tutorials that explain your problem. RxSwift is naturally newer than ReactiveCocoa, and currently it basically presents a state of "many people watching one person's performance". It also has a dedicated Slack group with 961 members, and faces a much larger amount of discussion than this number. If you have related questions, you can always find someone in this group to help you. (V) Which one is better to use Reader Ash Furrow's advice: If you're a beginner, it doesn't matter which framework you start with. Yes, there are a lot of technical differences, but they're all interesting for beginners. Try one framework, then another. Decide for yourself which one is right for you, and then you'll understand why you chose that one. If you are a beginner, I also recommend you to do this. In fact, only when you have accumulated enough experience will you appreciate the subtle difference between the two. However, if you have a specific role where you need to choose one or the other and don’t have time to experiment, here’s what I recommend: It is recommended to choose the ReactiveCocoa framework: l If you want to describe your system better, and use different types to distinguish hot signals from cold signals, and use typed parameters for error handling, these will bring surprises to your system development. l If you want to use a large-scale testing framework, used by many people and applied in many projects. It is recommended to choose the RxSwift framework: l If UI binding is important to your project. l If you are a FRP newbie and need timely help information. l If you already know RxJS or RxJava, then since these two frameworks and the RxSwift framework belong to the Reactivex organization, once you are familiar with one of them, the rest is just a matter of syntax. V. Summary Whether you choose RxSwift or ReactiveCocoa framework, you will not regret it. Both are extremely powerful frameworks that will help you better describe your system. It is worth noting that once you choose RxSwift or ReactiveCocoa framework, switching back and forth between the two is just a matter of hours. In my experience, the key to switching from ReactiveCocoa to RxSwift is to be familiar with its error handling technology. In short, the most critical issue is to overcome the FRP programming technology, not the specific implementation. ***, here are a few links for you to use as references on your way to learning the above two frameworks: l Conal Elliott's blog (http://conal.net/blog/); l Conal Elliott’s great answer on Stackoverflow (https://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming); l André Staltz's important article "Why I cannot say FRP but I just did" (https://medium.com/@andrestaltz/why-i-cannot-say-frp-but-i-just-did-d5ffaa23973b#.62dnhk32p); l RxSwift code repository (https://github.com/ReactiveX/RxSwift); l ReactiveCocoa code repository (https://github.com/ReactiveCocoa/ReactiveCocoa); l Rex code repository (https://github.com/neilpa/Rex); l A treasure trove of FRP for iOS developers (https://gist.github.com/JaviLorbada/4a7bd6129275ebefd5a6). l RxSwift discussion resources (http://rx-marin.com/). Original title: ReactiveCocoa vs RxSwift [Exclusively translated by 51CTO.com. Please indicate the source when reprinting on partner sites] |
<<: Understanding Android naming conventions
>>: How to achieve O&M automation and fault self-healing in the gaming industry
When I went to KFC to buy a chicken burger two da...
Review expert: Lu Binshen, Department of Oral and...
2018 is the second half of the Internet, and it i...
Recently, Japanese wafer giant Sumco decided to c...
The core of love assistance "2021 Private Tu...
The scientific expedition team drilled ice cores ...
As a veteran who has been in the APP promotion in...
Communities have always been controversial, espec...
Written in front In recent years, especially this...
I believe many people have had this experience - ...
The higher the temperature, the faster cockroache...
Preface: User operation is the most important par...
The Chinese Academy of Sciences and the Qinghai P...
Today's article will comprehensively introduc...
On March 15 , Baidu Xiong Zhanghao officially lau...