Code Monkey home page Code Monkey logo

rxstateflow's Introduction

RxStateFlow

Platform support Swift 3 compatible Carthage compatible CocoaPods Compatible License MIT

Introduction

An implementation of a Persistent Reactive Unidirectional Date Flow using Realm and RxSwift. RxStateFlow provides an architecture where the core idea is that your code is built around a Model of your application state, a way to update your model, and a way to view your model.

Using RxStateFlow makes it very easy to have the app state persist (between app launches), reactive and accessible across classes and threads.

Architecture

State

Since the framework uses Realm a State is anything that conforms to StateObject which actually is a Realm Model sub-class - this way the application state always persist between app launches and can be observed on any thread.

class AppState: StateObject {
    fileprivate(set) dynamic var counter: Int = 0

    override func react(to event: Event) {
        switch event {
        case CounterEvent.increase:
            counter += 1
        default:
            break
        }
    }
}

Use composition to create state for more complicated cases than this. Parent states can react to events however they wish, although this will in most cases involve delegating to substates default behavior.

class AnotherObject: StateObject {
    fileprivate(set) dynamic var value: String = ""

    override func react(to event: Event) {
    }
}

class AppState: StateObject {
    fileprivate(set) dynamic var counter: Int = 0
    fileprivate(set) dynamic var another: AnotherObject

    override func react(to event: Event) {
        another.react(to: event)
    }
}

State Changes

RxStateFlow allows only state changes through events. Events are small pieces of data which describe a state change and don't contain any code. They are consumed by the store and forwarded to method react(to event: Event) on the root state. This method will handle the events by implementing a different state change for each event.

Event

Can trigger a state update. Events can be defined as struct or enums. Here are some examples:

The simplest form.

struct UpdateCounter: Event { }

As enums.

enum CounterEvent: Event {
    case increase
    case decrease
    case reset
}

Pass some data along with the event.

struct UpdateCounter: Event {
    let value: Int
}

Generics work as well.

struct Update<T>: Event {
    var value: T
}

Command

Command helps you to interact with the Store in a safe and consistent way where you need events to be dispatched to a store at a later point. Useful for networking, working with databases, or any other asynchronous operation.

In addition, it can be used to perform a conditional dispatch instead of checking the necessary state directly in the view or view controller, which avoids any sort of complicated business logic in the view.

struct IncreaseCounter: Command {
    func execute(state: AppState, store: Store<AppState>) {
        if state.counter < 10 {
            store.dispatch(event: CounterEvent.increase)
        }
    }
}

// to dispatch a command
store.dispatch(command: IncreaseCounter())

Store

Holds the application state and responsible for dispatching events and commands. Received events will in turn update the state by calling react(to event: Event) on the root state. Whenever the state in the store changes, the store will notify all observers.

Create a shared global Store used by your entire application.

let store = Store<AppState>()

NOTE: DO NOT produce side effects, make async calls, or use impure functions like NSDate() in react(to event: Event).

Views

In a RxStateFlow app your views update when your state changes. Your views become simple visualizations of the current app state.

By subscribing to the state, we ensure that whenever this view controller is visible it is up to date with the latest application state. Upon initial subscription, the store will send the latest state to the subscriber's update function.

class ViewController: UIViewController {
    internal var bag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Subscribe to store changes
        store.state.asDriver().drive(onNext: { state in
            // update UI
            }).addDisposableTo(bag)
    }
}

Middleware

Middleware is great to perform tasks around an event. Each middleware gets called every time an event is passed in, before and after application state update. Middleware is not allowed to mutate the state, but it gets a copy of the state along with the event. Useful for logging, analytics, error handling, and other side effects.

struct LoggingMiddleware: Middleware {

    func before(event: Event, state: AppState) {
        switch event {
        case CounterEvent.increase:
            print("About to increase counter")
        default:
            break
        }
    }

    func after(event: Event, state: AppState) {
        switch event {
        case CounterEvent.increase:
            print("Counter increased")
        default:
            break
        }
    }
}

Requirements

  • iOS 9.0+
  • Xcode 8.1+
  • Swift 3

Examples

Follow these 3 steps to run Example project: Clone RxStateFlow repository, open RxStateFlow workspace and run the Example project.

The Example project consist of:

  • Events
  • Commands (Conditional dispatch)
  • History middleware
  • Logging middleware
  • Error handling

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

To install RxStateFlow, simply add the following line to your Podfile:

pod 'RxStateFlow'

To correctly compile for Swift 3, paste the following at the bottom of your Podfile:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_VERSION'] = '3.0'
    end
  end
end

Carthage

Carthage is a simple, decentralized dependency manager for Cocoa.

To install RxStateFlow, simply add the following line to your Cartfile:

github "mmadjer/RxStateFlow"

Credits

  • Thanks a lot to Marin Todorov from where the idea originated.
  • Also to Jason Larsen - some implementation details were provided by his library.

rxstateflow's People

Contributors

mmadjer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

level-11

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.