All Projects â†’ inamiy â†’ Harvest

inamiy / Harvest

Licence: mit
đŸŒŸ Harvest: Apple's Combine.framework + State Machine, inspired by Elm.

Programming Languages

swift
15916 projects
elm
856 projects

Projects that are alternatives of or similar to Harvest

qm
QM model-based design tool and code generator based on UML state machines
Stars: ✭ 54 (-84.66%)
Mutual labels:  state-machine
nodify
High performance and modular controls for node-based editors designed for data-binding and MVVM.
Stars: ✭ 282 (-19.89%)
Mutual labels:  state-machine
Swiftrex
Swift + Redux + (Combine|RxSwift|ReactiveSwift) -> SwiftRex
Stars: ✭ 267 (-24.15%)
Mutual labels:  state-machine
react-gizmo
🩎 React Gizmo - UI Finite State Machine for React
Stars: ✭ 39 (-88.92%)
Mutual labels:  state-machine
YokosukaJS
A functional programming-style beat-em-up game engine written in javascript
Stars: ✭ 18 (-94.89%)
Mutual labels:  state-machine
use-tiny-state-machine
A tiny (~700 bytes) react hook to help you write finite state machines
Stars: ✭ 37 (-89.49%)
Mutual labels:  state-machine
statebot
Write more robust and understandable programs. Statebot hopes to make Finite State Machines a little more accessible.
Stars: ✭ 24 (-93.18%)
Mutual labels:  state-machine
Redux Machine
A tiny library (12 lines) for creating state machines in Redux apps
Stars: ✭ 338 (-3.98%)
Mutual labels:  state-machine
esm
Lightweight communicating state machine framework for embedded systems
Stars: ✭ 21 (-94.03%)
Mutual labels:  state-machine
Statemachine
A .net library that lets you build state machines (hierarchical, async with fluent definition syntax and reporting capabilities).
Stars: ✭ 270 (-23.3%)
Mutual labels:  state-machine
fea state machines
A Buffet Of C++17 State Machines
Stars: ✭ 19 (-94.6%)
Mutual labels:  state-machine
cppfsm
A simple, generic, header-only state machine implementation for C++.
Stars: ✭ 47 (-86.65%)
Mutual labels:  state-machine
Workflow Kotlin
A Swift and Kotlin library for making composable state machines, and UIs driven by those state machines.
Stars: ✭ 255 (-27.56%)
Mutual labels:  state-machine
SimpleStateMachineLibrary
📚 A simple library for realization state machines in C# code
Stars: ✭ 30 (-91.48%)
Mutual labels:  state-machine
Gonorth
GoNorth is a story and content planning tool for RPGs and other open world games.
Stars: ✭ 289 (-17.9%)
Mutual labels:  state-machine
free-category
Free categories, free arrows and free categories with monadic actions
Stars: ✭ 21 (-94.03%)
Mutual labels:  state-machine
zedux
⚡ A high-level, declarative, composable form of Redux https://bowheart.github.io/zedux/
Stars: ✭ 43 (-87.78%)
Mutual labels:  state-machine
Pvm
Build workflows, activities, BPMN like processes, or state machines with PVM.
Stars: ✭ 348 (-1.14%)
Mutual labels:  state-machine
Beedle
A tiny library inspired by Redux & Vuex to help you manage state in your JavaScript apps
Stars: ✭ 329 (-6.53%)
Mutual labels:  state-machine
Statecharts.github.io
There is no state but what we make. Feel free to pitch in.
Stars: ✭ 265 (-24.72%)
Mutual labels:  state-machine

đŸŒŸ Harvest

Swift 5.1 Build Status

Apple's Combine.framework (from iOS 13) + State Machine, inspired by Elm.

This is a sister library of the following projects:

Requirement

Xcode 11 (Swift 5.1 / macOS 10.15, iOS 13, ...)

Example

To make a state transition diagram like above with additional effects, follow these steps:

1. Define States and Inputs

// 1. Define `State`s and `Input`s.
enum State {
    case loggedOut, loggingIn, loggedIn, loggingOut
}

enum Input {
    case login, loginOK, logout, logoutOK
    case forceLogout
}

2. Define EffectQueue

enum EffectQueue: EffectQueueProtocol {
    case `default`
    case request

    var flattenStrategy: FlattenStrategy {
        switch self {
        case .default: return .merge
        case .request: return .latest
        }
    }

    static var defaultEffectQueue: EffectQueue {
        .default
    }
}

EffectQueue allows additional side-effects (Effect, a wrapper of Publisher) to be scheduled with a specific FlattenStrategy, such as flatMap (.merge), flatMapLatest (.latest), etc. In above case, we want to automatically cancel previous network requests if occurred multiple times, so we also prepare case request queue with .latest strategy.

3. Create EffectMapping (Effect-wise reducer)

// NOTE: `EffectID` is useful for manual effect cancellation, but not used in this example.
typealias EffectID = Never

typealias Harvester = Harvest.Harvester<Input, State>
typealias EffectMapping = Harvester.EffectMapping<EffectQueue, EffectID>
typealias Effect = Harvester.Effect<Input, EffectQueue, EffectID>

// Additional effects while state-transitioning.
let loginOKPublisher = /* show UI, setup DB, request APIs, ..., and send `Input.loginOK` */
let logoutOKPublisher = /* show UI, clear cache, cancel APIs, ..., and send `Input.logoutOK` */
let forceLogoutOKPublisher = /* do something more special, ..., and send `Input.logoutOK` */

let canForceLogout: (State) -> Bool = [.loggingIn, .loggedIn].contains

let mappings: [EffectMapping] = [

  /*  Input   |   fromState => toState     |      Effect       */
  /* ----------------------------------------------------------*/
    .login    | .loggedOut  => .loggingIn  | Effect(loginOKPublisher, queue: .request),
    .loginOK  | .loggingIn  => .loggedIn   | .empty,
    .logout   | .loggedIn   => .loggingOut | Effect(logoutOKPublisher, queue: .request),
    .logoutOK | .loggingOut => .loggedOut  | .empty,

    .forceLogout | canForceLogout => .loggingOut | Effect(forceLogoutOKPublisher, queue: .request)
]

EffectMapping is Redux's Reducer or Elm's Update pure function that also returns Effect during the state-transition. Note that queue: .request is specified so that those effects will be handled in the same queue with .latest strategy. Instead of writing it as a plain function with pattern-matching, you can also write in a fancy markdown-table-like syntax as shown above.

4. Setup Harvester (state machine)

// Prepare input pipe for sending `Input` to `Harvester`.
let inputs = PassthroughSubject<Input, Never>()

var cancellables: [AnyCancellable] = []

// Setup state machine.
let harvester = Harvester(
    state: .loggedOut,
    input: inputs,
    mapping: .reduce(.first, mappings),  // combine mappings using `reduce` helper
    scheduler: DispatchQueue.main
)

// Observe state-transition replies (`.success` or `.failure`).
harvester.replies.sink { reply in
    print("received reply = \(reply)")
}.store(in: &cancellables)

// Observe current state changes.
harvester.state.sink { state in
    print("current state = \(state)")
}.store(in: &cancellables)

NOTE: func reduce is declared to combine multiple mappings into one.

5. And let's test!

let send = inputs.send

expect(harvester.state) == .loggedIn    // already logged in
send(Input.logout)
expect(harvester.state) == .loggingOut  // logging out...
// `logoutOKPublisher` will automatically send `Input.logoutOK` later
// and transit to `State.loggedOut`.

expect(harvester.state) == .loggedOut   // already logged out
send(Input.login)
expect(harvester.state) == .loggingIn   // logging in...
// `loginOKPublisher` will automatically send `Input.loginOK` later
// and transit to `State.loggedIn`.

// đŸ‘šđŸœ < But wait, there's more!
// Let's send `Input.forceLogout` immediately after `State.loggingIn`.

send(Input.forceLogout)                       // đŸ’„đŸ’ŁđŸ’„
expect(harvester.state) == .loggingOut  // logging out...
// `forceLogoutOKublisher` will automatically send `Input.logoutOK` later
// and transit to `State.loggedOut`.

Please notice how state-transitions, effect calls and cancellation are nicely performed. If your cancellation strategy is more complex than just using FlattenStrategy.latest, you can also use Effect.cancel to manually stop specific EffectID.

Note that any sizes of State and Input will work using Harvester, from single state (like above example) to covering whole app's states (like React.js + Redux architecture).

Using Feedback effect model as alternative

Instead of using EffectMapping with fine-grained EffectQueue model, Harvest also supports Feedback system as described in the following libraries:

See inamiy/ReactiveAutomaton#12 for more discussion.

Composable Architecture with SwiftUI

Pull Request #8 introduced HarvestStore and HarvestOptics frameworks for Composable Architecture, especially focused on SwiftUI.

  • HarvestStore: 2-way bindable Store optimized for SwiftUI
  • HarvestOptics: Input & state lifting helpers using FunOptics

See Harvest-SwiftUI-Gallery for the examples.

See also Babylonpartners/ios-playbook#171 for further related discussion.

  • [ ] TODO: Write documentation

References

  1. ReactiveAutomaton (using ReactiveSwift)
  2. RxAutomaton (using RxSwift)
  3. iOSDC 2016 (Tokyo, in Japanese) (2016/08/20)
  4. iOSConf SG (Singapore, in English) (2016/10/20-21)

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].