ReSwift/ReSwift

Name: ReSwift

Owner: ReSwift

Description: Unidirectional Data Flow in Swift - Inspired by Redux

Created: 2015-12-15 07:50:56.0

Updated: 2018-01-19 07:27:18.0

Pushed: 2018-01-02 17:21:22.0

Homepage: http://reswift.github.io/ReSwift/

Size: 6063

Language: Swift

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

ReSwift

Build Status Code coverage status CocoaPods Compatible Platform support License MIT

Supported Swift Versions: Swift 3.2, 4.0

For Swift 2.2 Support use Release 2.0.0 or earlier.

Introduction

ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift. ReSwift helps you to separate three important concerns of your app's components:

The ReSwift library is tiny - allowing users to dive into the code, understand every single line and hopefully contribute.

ReSwift is quickly growing beyond the core library, providing experimental extensions for routing and time traveling through past app states!

Excited? So are we ?

Check out our public gitter chat!

Table of Contents

About ReSwift

ReSwift relies on a few principles:

For a very simple app, that maintains a counter that can be increased and decreased, you can define the app state as following:

ct AppState: StateType {
var counter: Int = 0

You would also define two actions, one for increasing and one for decreasing the counter. In the Getting Started Guide you can find out how to construct complex actions. For the simple actions in this example we can define empty structs that conform to action:

ct CounterActionIncrease: Action {}
ct CounterActionDecrease: Action {}

Your reducer needs to respond to these different action types, that can be done by switching over the type of action:

 counterReducer(action: Action, state: AppState?) -> AppState {
var state = state ?? AppState()

switch action {
case _ as CounterActionIncrease:
    state.counter += 1
case _ as CounterActionDecrease:
    state.counter -= 1
default:
    break
}

return state

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:

mainStore = Store<AppState>(
reducer: counterReducer,
state: nil


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, StoreSubscriber {

@IBOutlet var counterLabel: UILabel!

override func viewWillAppear(_ animated: Bool) {
    mainStore.subscribe(self)
}

override func viewWillDisappear(_ animated: Bool) {
    mainStore.unsubscribe(self)
}

func newState(state: AppState) {
    counterLabel.text = "\(state.counter)"
}

@IBAction func increaseButtonTapped(_ sender: UIButton) {
    mainStore.dispatch(
        CounterActionIncrease()
    )
}

@IBAction func decreaseButtonTapped(_ sender: UIButton) {
    mainStore.dispatch(
        CounterActionDecrease()
    )
}


The newState method will be called by the Store 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 ReSwift'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 CounterExample project.

You can also watch this talk on the motivation behind ReSwift.

Why ReSwift?

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.

ReSwift 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.

This architecture provides further benefits beyond improving your code base:

The ReSwift tooling is still in a very early stage, but aforementioned prospects excite me and hopefully others in the community as well!

Getting Started Guide

A Getting Started Guide that describes the core components of apps built with ReSwift lives here.

To get an understanding of the core principles we recommend reading the brilliant redux documentation.

Installation

CocoaPods

You can install ReSwift via CocoaPods by adding it to your Podfile:

frameworks!

ce 'https://github.com/CocoaPods/Specs.git'
form :ios, '8.0'

'ReSwift'

And run pod install.

Carthage

You can install ReSwift via Carthage by adding the following line to your Cartfile:

ub "ReSwift/ReSwift"
Swift Package Manager

You can install ReSwift via Swift Package Manager by adding the following line to your Package.swift:

rt PackageDescription

package = Package(
[...]
dependencies: [
    .Package(url: "https://github.com/ReSwift/ReSwift.git", majorVersion: XYZ)
]

Checking out Source Code

After checking out the project run pod install to get the latest supported version of SwiftLint, which we use to ensure a consistent style in the codebase.

Demo

Using this library you can implement apps that have an explicit, reproducible state, allowing you, among many other things, to replay and rewind the app state, as shown below:

Extensions

This repository contains the core component for ReSwift, the following extensions are available:

Example Projects

Production Apps with Open Source Code

Contributing

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.

Credits

Get in touch

If you have any questions, you can find the core team on twitter:

We also have a public gitter chat!


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.