All Projects → marty-suzuki → Fluxcapacitor

marty-suzuki / Fluxcapacitor

Licence: mit
This is what makes the Flux design pattern possible.

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to Fluxcapacitor

redux-reducer-async
Create redux reducers for async behaviors of multiple actions.
Stars: ✭ 14 (-88.89%)
Mutual labels:  flux, flux-architecture
Hover
A very lightweight data store with action reducers and state change listeners.
Stars: ✭ 97 (-23.02%)
Mutual labels:  flux, flux-architecture
fluxiny
~1K implementation of flux architecture
Stars: ✭ 77 (-38.89%)
Mutual labels:  flux, flux-architecture
Smitty
Tiny flux implementation built on mitt
Stars: ✭ 210 (+66.67%)
Mutual labels:  flux, flux-architecture
Delorean
An Agnostic, Complete Flux Architecture Framework
Stars: ✭ 748 (+493.65%)
Mutual labels:  flux, flux-architecture
nanoflux-fusion
Redux-like extension for Nanoflux
Stars: ✭ 15 (-88.1%)
Mutual labels:  flux, flux-architecture
react-workshops
Online react workshops
Stars: ✭ 36 (-71.43%)
Mutual labels:  flux, flux-architecture
AndroidFluxPractice
Android Flux Practice
Stars: ✭ 51 (-59.52%)
Mutual labels:  flux, flux-architecture
universal-routed-flux-demo
The code in this repo is intended for people who want to get started building universal flux applications, with modern and exciting technologies such as Reactjs, React Router and es6.
Stars: ✭ 31 (-75.4%)
Mutual labels:  flux, flux-architecture
mini-swift
Minimal Flux architecture written in Swift.
Stars: ✭ 40 (-68.25%)
Mutual labels:  flux, flux-architecture
Firedux
🔥 🐣 Firebase + Redux for ReactJS
Stars: ✭ 148 (+17.46%)
Mutual labels:  flux, flux-architecture
Fleur
A fully-typed, type inference and testing friendly Flux Framework
Stars: ✭ 74 (-41.27%)
Mutual labels:  flux, flux-architecture
Goes
Go Event Sourcing made easy
Stars: ✭ 144 (+14.29%)
Mutual labels:  flux, flux-architecture
fluxy
Fluxy is a Flux architecture implementation written in Kotlin.
Stars: ✭ 25 (-80.16%)
Mutual labels:  flux, flux-architecture
Vuex-Alt
An alternative approach to Vuex helpers for accessing state, getters and actions that doesn't rely on string constants.
Stars: ✭ 15 (-88.1%)
Mutual labels:  flux, flux-architecture
Nanoflux
A very lightweight and dependency-free Flux implementation
Stars: ✭ 56 (-55.56%)
Mutual labels:  flux, flux-architecture
Fluxxan
Fluxxan is an Android implementation of the Flux Architecture that combines concepts from both Fluxxor and Redux.
Stars: ✭ 80 (-36.51%)
Mutual labels:  flux, flux-architecture
Rx Connect
Glue your state and pure React components with RxJS
Stars: ✭ 86 (-31.75%)
Mutual labels:  flux
Clean State
🐻 A pure and compact state manager, using React-hooks native implementation, automatically connect the module organization architecture. 🍋
Stars: ✭ 107 (-15.08%)
Mutual labels:  flux
Freezer
A tree data structure that emits events on updates, even if the modification is triggered by one of the leaves, making it easier to think in a reactive way.
Stars: ✭ 1,268 (+906.35%)
Mutual labels:  flux

Logo

FluxCapacitor

Build Status Build Status Version License Platform Carthage compatible

FluxCapacitor makes implementing Flux design pattern easily with protocols and typealias.

  • Storable protocol
  • Actionable protocol
  • DispatchState protocol

Requirements

  • Xcode 10.1 or later
  • Swift 4.2 or later
  • iOS 10.0 or later

Installation

CocoaPods

FluxCapacitor is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "FluxCapacitor"

Carthage

If you’re using Carthage, simply add FluxCapacitor to your Cartfile:

github "marty-suzuki/FluxCapacitor"

Usage

This is ViewController sample that uses Flux design pattern. If ViewController calls fetchRepositories method of RepositoryAction, it is reloaded automatically by observed changes of Constant in RepositoryStore after fetched repositories from Github. Introducing how to implement Flux design pattern with FluxCapacitor.

final class UserRepositoryViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!

    private let repositoryAction = RepositoryAction()
    private let repositoryStore = RepositoryStore.instantiate()
    private let userStore = UserStore.instantiate()
    private let dustBuster = DustBuster()
    private let dataSource = UserRepositoryViewDataSource()

    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource.configure(with: tableView)
        observeStore()

        if let user = userStore.selectedUser.value {
            repositoryAction.fetchRepositories(withUserId: user.id, after: nil)
        }
    }

    private func observeStore() {
        repositoryStore.repositories
            .observe(on: .main, changes: { [weak self] _ in
                self?.tableView.reloadData()
            })
            .cleaned(by: dustBuster)
    }
}

Dispatcher

First of all, implementing DispatchState. It connects Action and Store, but it plays a role that don't depend directly each other.

extension Dispatcher {
    enum Repository: DispatchState {
        typealias RelatedStoreType = RepositoryStore
        typealias RelatedActionType = RepositoryAction

        case isRepositoryFetching(Bool)
        case addRepositories([GithubApiSession.Repository])
        case removeAllRepositories
    }
}

Store

Implementing Store with Storable protocol. func reduce(with:_) is called when Dispatcher dispatches DispatchStateType. Please update store's value with Associated Values.

final class RepositoryStore: Storable {
    typealias DispatchStateType = Dispatcher.Repository

    let isRepositoryFetching: Constant<Bool>
    private let _isRepositoryFetching = Variable<Bool>(false)

    let repositories: Constant<[Repository]>
    private let _repositories = Variable<[Repository]>([])

    required init() {
        self.isRepositoryFetching = Constant(_isRepositoryFetching)
        self.repositories = Constant(_repositories)
    }

    func reduce(with state: Dispatcher.Repository) {
        switch state {
        case .isRepositoryFetching(let value):
            _isRepositoryFetching.value = value
        case .addRepositories(let value):
            _repositories.value.append(contentsOf: value)
        case .removeAllRepositories:
            _repositories.value.removeAll()
        }
    }
}

If you want to use any store, please use XXXStore.instantiate(). That static method returns its reference or new instance. If you want to unregister any store from Dispatcher, please call xxxStore.clear().

Action

Implementing Action with Actionable protocol. If you call invoke method, it can dispatch value related DispatchStateType.

final class RepositoryAction: Actionable {
    typealias DispatchStateType = Dispatcher.Repository

    private let session: ApiSession

    init(session: ApiSession = .shared) {
        self.session = session
    }

    func fetchRepositories(withUserId id: String, after: String?) {
        invoke(.isRepositoryFetching(true))
        let request = UserNodeRequest(id: id, after: after)
        _ = session.send(request) { [weak self] in
            switch $0 {
            case .success(let value):
                self?.invoke(.addRepositories(value.nodes))
            case .failure:
                break
            }
            self?.invoke(.isRepositoryFetching(false))
        }
    }
}

Observe changes with Constant<Element> / Variable<Element>

You can initialize a store with instantiate(). If reference of store is left, that method returns remained one. If reference is not left, that method returns new instance. You can observe changes by Constant or Variable. When called observe, it returns Dust. So, clean up with DustBuster.

let dustBuster = DustBuster()

func observeStore() {
    // Get store instance
    let store = RepositoryStore.instantiate()

    // Observer changes of repositories that is `Constant<[Github.Repository]>`.
    store.repositories
        .observe(on: .main) { value in
            // do something
        }
        .cleaned(by: dustBuster)
}

dustbuster Robert Zemeckis (1989) Back to the future Part II, Universal Pictures

Constant<Element> and Variable<Element>

Variable<Element> has getter and setter of Element.

let intVal = Variable<Int>(0)
intVal.value = 1
print(intVal.value) // 1

Constant<Element> has only getter of Element. So, you can initialize Constant with Variable. Variable shares its value with Constant.

let variable = Variable<Int>(0)
let constant = Constant(variable)
variable.value = 1
print(variable.value) // 1
print(constant.value) // 1

In addition, Constant that initialize with some Variable, it can use same observation.

let variable = Variable<Int>(0)
let constant = Constant(variable)

_ = variable.observe { value in
    print(value) // 0 -> 10
}

_ = constant.observe { value in
    print(value) // 0 -> 10
}

variable.value = 10

with RxSwift

You can use FluxCapacitor with RxSwift like this link.

Or implement func asObservable() like this.

// Constant
extension PrimitiveValue where Trait == ImmutableTrait {
    func asObservable() -> Observable<Element> {
        return Observable.create { [weak self] observer in
            let dust = self?.observe { observer.onNext($0) }
            return Disposables.create { dust?.clean() }
        }
    }
}

Example

To run the example project, clone the repo, and run pod install and carthage update from the Example directory first. In addition, you must set Github Personal access token.

// ApiSessionType.swift
extension ApiSession: ApiSessionType {
    static let shared: ApiSession = {
        let token = "" // Your Personal Access Token
        return ApiSession(injectToken: { InjectableToken(token: token) })
    }()
}

Application structure is like below.

flux_image

GithubKitForSample is used in this sample project.

Additional

Flux + MVVM Sample is here.

Migration Guide

FluxCapacitor 0.10.0 Migration Guide

Author

marty-suzuki, [email protected]

License

FluxCapacitor is available under the MIT license. See the LICENSE file for more info.

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