Latest 0.4.0
Homepage https://github.com/marty-suzuki/FluxCapacitor
License MIT
Platforms ios 9.0
Authors

Logo

Build Status
Build Status
Version
License
Platform
Carthage compatible

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

  • Storable protocol
  • Actionable protocol
  • DispatchValue protocol

Requirements

  • Xcode 9 or later
  • Swift 4 or later
  • iOS 9.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 with subscribe method of 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 {
            repositoryAction.fetchRepositories(withUserId: user.id, after: nil)
        }
    }

    private func observeStore() {
        repositoryStore.subscribe { [weak self] changes in
            DispatchQueue.main.async {
                switch changes {
                case .addRepositories,
                     .removeAllRepositories,
                     .isRepositoryFetching:
                    self?.tableView.reloadData()
                }
            }
        }
        .cleaned(by: dustBuster)
    }
}

Dispatcher

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

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

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

Store

Implementing Store with Storable protocol. If you call register method, that closure returns dispatched value related DispatchValueType. Please update store’s value with Associated Values.

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

    private(set) var isRepositoryFetching = false
    private(set) var repositories: [Repository] = []

    init(dispatcher: Dispatcher) {
        register { [weak self] in
            switch $0 {
            case .isRepositoryFetching(let value):
                self?.isRepositoryFetching = value
            case .addRepositories(let value):
                self?.repositories.append(contentsOf: value)
            case .removeAllRepositories:
                self?.repositories.removeAll()
            }
        }
    }

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

Action

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

final class RepositoryAction: Actionable {
    typealias DispatchValueType = 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

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 store’s subscribe method. When called subscribe, it returns Dust. So, clean up with DustBuster.

let dustBuster = DustBuster()

func observeStore() {
    RepositoryStore.instantiate().subscribe {
        switch $0 {
        case .addRepositories,
             .removeAllRepositories,
             .isRepositoryFetching:
            break
        }
    }
    .cleaned(by: dustBuster)
}

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

with RxSwift

You can use FluxCapacitor with RxSwift like this link.

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.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    ApiSession.shared.token = "/** Github Personal access token **/"
    return true
}

Application structure is like below.

flux_image

GithubKitForSample is used in this sample project.

Additional

Flux + MVVM Sample is here.

Author

marty-suzuki, s1180183@gmail.com

License

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

Latest podspec

{
    "name": "FluxCapacitor",
    "version": "0.4.0",
    "summary": "This is what makes the Flux design pattern possible.",
    "homepage": "https://github.com/marty-suzuki/FluxCapacitor",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    },
    "authors": {
        "Taiki Suzuki": "s1180183@gmail.com"
    },
    "source": {
        "git": "https://github.com/marty-suzuki/FluxCapacitor.git",
        "tag": "0.4.0"
    },
    "social_media_url": "https://twitter.com/marty_suzuki",
    "platforms": {
        "ios": "9.0"
    },
    "source_files": "FluxCapacitor/*.{swift}",
    "pushed_with_swift_version": "3.1"
}

Pin It on Pinterest

Share This