A simple library for state management with unidirectional data flow
OneWay
? OneWay is still experimental. As such, expect things to break and change in the coming months.
OneWay is a super simple library for state management with unidirectional data flow. The original inspiration came from Flux, The Composable Architecture, ReactorKit and many state management libraries. There are no dependencies on third parties like RxSwift, so you can use OneWay purely. It can not only be used in the presentation layer (e.g. with View or ViewController), but can also be used to simplify complex business logic (e.g. while app launching).
Data Flow
Usage
Implementing a Way
It is easy to think of a Way
as a path through which data passes. You can inherit a Way
and should implement as below. It is also freely customizable and encapsulatable, since Way
is a class.
final class CounterWay: Way<CounterWay.Action, CounterWay.State> {
enum Action {
case increment
case decrement
case twice
}
struct State: Hashable {
var number: Int
}
override func reduce(state: inout State, action: Action) -> SideWay<Action, Never> {
switch action {
case .increment:
state.number += 1
return .none
case .decrement:
state.number -= 1
return .none
case .twice:
return .concat(
.just(.increment),
.just(.increment)
)
}
}
}
Sending Actions
Sending an action to a Way
causes changes in the state
via reduce()
.
let way = CounterWay(initialState: .init(number: 0))
way.send(.increment)
way.send(.decrement)
way.send(.twice)
print(way.currentState.number) // 2
Subscribing a Way
When a value changes, it can receive a new value. It guarantees that the same value does not come down consecutively. In general, you don’t need to add removeDuplicates()
.
way.publisher.number
.sink { number in
print(number)
}
.store(in: &cancellables)
Global States
You can easily subscribe to global states by overriding bind()
.
let globalTextSubject = PassthroughSubject<String, Never>()
let globalNumberSubject = PassthroughSubject<Int, Never>()
final class CustomWay: Way<CustomWay.Action, CustomWay.State> {
// ...
override func bind() -> SideWay<Action, Never> {
return .merge(
globalTextSubject
.map({ Action.saveText($0) })
.eraseToSideWay(),
globalNumberSubject
.map({ Action.saveNumber($0) })
.eraseToSideWay()
)
}
// ...
}
Thread Safe or Not
Way
has a ThreadOption
to consider the multithreaded environment. This option can be passed as an argument to the initializer. Once set, it cannot be changed. In a general environment, it is better to use the default option(current
) for better performance. But, if it is initialized with the current
option, all interactions (i.e. sending actions) with an instance of Way must be done on the same thread.
let way = TestWay(initialState: initialState, threadOption: .current)
let threadSafeWay = TestWay(initialState: initialState, threadOption: .threadSafe)
Requirements
Minimum Version | |
---|---|
Swift | 5.5 |
Xcode | 13.0 |
iOS | 13.0 |
Installation
OneWay is only supported by Swift Package Manager.
To integrate OneWay into your Xcode project using Swift Package Manager, add it to the dependencies value of your Package.swift
:
dependencies: [
.package(url: "https://github.com/DevYeom/OneWay", from: "0.1.0"),
]
License
This library is released under the MIT license. See LICENSE for details.