Name: ReactiveReSwift
Owner: ReSwift
Description: Unidirectional Data Flow in Swift via FRP - Inspired by Elm
Created: 2016-12-05 03:42:26.0
Updated: 2018-05-10 03:14:18.0
Pushed: 2018-02-16 04:14:37.0
Homepage: http://reswift.github.io/ReactiveReSwift
Size: 8353
Language: Swift
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
ReactiveReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift, derived from ReSwift. ReactiveReSwift helps you to separate three important concerns of your app's components:
The ReactiveReSwift library is tiny - allowing users to dive into the code, understand every single line and hopefully contribute.
Check out our public gitter chat!
ReactiveReSwift relies on a few principles:
For a very simple app, one that maintains a counter that can be increased and decreased, you can define the app state as following:
ct AppState {
t counter: Int
You would also define two actions, one for increasing and one for decreasing the counter. For the simple actions in this example we can use an enum that conforms to action:
AppAction: Action {
case increase
case decrease
Your reducer needs to respond to these different actions, that can be done by switching over the value of action:
appReducer: Reducer<AppState> = { action, state in
switch action as? AppAction {
case .increase?:
return AppState(counter: state.counter + 1)
case .decrease?:
return AppState(counter: state.counter - 1)
default:
return state
}
A single Reducer
should only deal with a single field of the state struct. You can nest multiple reducers within your main reducer to provide separation of concerns.
In order to have a predictable app state, it is important that the reducer is always free of side effects, it receives the current app state and an action and returns the new app state.
To maintain our state and delegate the actions to the reducers, we need a store. Let's call it mainStore
and define it as a global constant, for example in the app delegate file:
initialState = AppState(counter: 0)
mainStore = Store(
ducer: appReducer,
servable: ObservableProperty(initialState)
pplicationMain
s AppDelegate: UIResponder, UIApplicationDelegate {
[...]
Lastly, your view layer, in this case a view controller, needs to tie into this system by subscribing to store updates and emitting actions whenever the app state needs to be changed:
s CounterViewController: UIViewController {
ivate let disposeBag = SubscriptionReferenceBag()
BOutlet var counterLabel: UILabel!
erride func viewDidLoad() {
disposeBag += mainStore.observable.subscribe { [weak self] state in
self?.counterLabel.text = "\(state.counter)"
}
BAction func increaseButtonTapped(sender: UIButton) {
mainStore.dispatch(
AppAction.increase
)
BAction func decreaseButtonTapped(sender: UIButton) {
mainStore.dispatch(
AppAction.decrease
)
The mainStore.observable.subscribe
block will be called by the ObservableStore
whenever a new app state is available, this is where we need to adjust our view to reflect the latest app state.
Button taps result in dispatched actions that will be handled by the store and its reducers, resulting in a new app state.
This is a very basic example that only shows a subset of ReactiveReSwift's features, read the Getting Started Guide to see how you can build entire apps with this architecture. For a complete implementation of this example see the ReactiveCounterExample project.
You can also watch this talk on the motivation behind ReSwift.
Here are some examples of what your code would look like if you were to leverage certain FRP libraries when writing your application.
Model-View-Controller (MVC) is not a holistic application architecture. Typical Cocoa apps defer a lot of complexity to controllers since MVC doesn't offer other solutions for state management, one of the most complex issues in app development.
Apps built upon MVC often end up with a lot of complexity around state management and propagation. We need to use callbacks, delegations, Key-Value-Observation and notifications to pass information around in our apps and to ensure that all the relevant views have the latest state.
This approach involves a lot of manual steps and is thus error prone and doesn't scale well in complex code bases.
It also leads to code that is difficult to understand at a glance, since dependencies can be hidden deep inside of view controllers. Lastly, you mostly end up with inconsistent code, where each developer uses the state propagation procedure they personally prefer. You can circumvent this issue by style guides and code reviews but you cannot automatically verify the adherence to these guidelines.
ReactiveReSwift attempts to solve these problem by placing strong constraints on the way applications can be written. This reduces the room for programmer error and leads to applications that can be easily understood - by inspecting the application state data structure, the actions and the reducers.
A common design pattern with Redux and its derivates is to observe your store using functional reactive programming (FRP), which the user takes care of using very similar looking boilerplate, regardless of the FRP library they've chosen.
Instead of pushing that onto the user, and to encourage people to use FRP, ReactiveReSwift provides protocols to conform to so that the underlying Store
can directly use the observables from your preferred library without subclassing.
ReactiveReSwift also comes with an extremely simple implementation of a functional reactive observable. This ObservableProperty
type allows you to use ReactiveReSwift without any other FRP libraries and not lose any of the functionality provided by ReSwift. That said, we do still highly encourage you to use a functional reactive library with ReactiveReSwift.
The documentation for ReactiveReSwift can be found here. To get an understanding of the core principles we recommend reading the brilliant redux documentation.
You can install ReactiveReSwift via CocoaPods by adding it to your Podfile
:
frameworks!
ce 'https://github.com/CocoaPods/Specs.git'
form :ios, '8.0'
'ReactiveReSwift'
And run pod install
.
You can install ReactiveReSwift via Carthage by adding the following line to your Cartfile
:
ub "ReSwift/ReactiveReSwift"
You can install ReactiveReSwift via Swift Package Manager by adding the following line to your Package.swift
:
rt PackageDescription
package = Package(
[...]
dependencies: [
.Package(url: "https://github.com/ReSwift/ReactiveReSwift.git", majorVersion: XYZ)
]
Codable
and Decodable
protocols provided by Swift 4.There's still a lot of work to do here! We would love to see you involved! You can find all the details on how to get started in the Contributing Guide.
If you have any questions, you can find the core team on twitter:
We also have a public gitter chat!