Are you going to support SwiftUI? Any example? Thanks.
One easy way is to simple create a shim that pass the state on through as an observable object. Below is a shim that can be used with any ReSwift StateType. It is fairly naive, and probably better ways. But it was enough for my purposes.
import SwiftUI
import ReSwift
// MARK: ReSwift Example Setup
struct AppState: StateType {
var counter: Int = 0
}
struct CounterActionIncrease: Action {}
struct CounterActionDecrease: Action {}
func counterReducer(action: Action, state: AppState?) -> AppState {
var state = state ?? AppState()
switch action {
case _ as CounterActionIncrease:
state.counter += 1
case _ as CounterActionDecrease:
state.counter -= 1
default:
break
}
return state
}
let mainStore = Store<AppState>(
reducer: counterReducer,
state: nil
)
// MARK: ContentView
struct ContentView: View {
// MARK: Private Properties
@ObservedObject private var state = ObservableState(store: mainStore)
// MARK: Body
var body: some View {
VStack {
Text(String(state.current.counter))
Button(action: state.dispatch(CounterActionIncrease())) {
Text("Increase")
}
Button(action: state.dispatch(CounterActionDecrease())) {
Text("Decrease")
}
}
}
}
// MARK: ObservableState
public class ObservableState<T>: ObservableObject where T: StateType {
// MARK: Public properties
@Published public var current: T
// MARK: Private properties
private var store: Store<T>
// MARK: Lifecycle
public init(store: Store<T>) {
self.store = store
self.current = store.state
store.subscribe(self)
}
deinit {
store.unsubscribe(self)
}
// MARK: Public methods
public func dispatch(_ action: Action) {
store.dispatch(action)
}
public func dispatch(_ action: Action) -> () -> Void {
{
self.store.dispatch(action)
}
}
}
extension ObservableState: StoreSubscriber {
// MARK: - <StoreSubscriber>
public func newState(state: T) {
DispatchQueue.main.async {
self.current = state
}
}
}
We have a branch with experimental SwiftUI support: https://github.com/ReSwift/ReSwift/tree/mjarvis/swiftui
Usage is by injecting the store as an environment object, and then accessing it in views.
And for the interested I've made a very lightweight ReSwift for SwiftUI here: https://github.com/Dimillian/SwiftUIFlux
Any news?
Looks like ReSwift for SwiftUI should be very lightweight as SwiftUI has some features like ObservableObject, Published, Bindable, etc.
@megadidar As mentioned, there is experimental support in a branch. as SwiftUI is still an early product, none of the ReSwift core devs have the opportunity to do real-world testing of the branch to understand if its suitable for production work, and thus it will stay out of releases until such testing & validation can be done.
We welcome anyone who can take the reins on properly testing ReSwift with SwiftUI in larger scale environments.
have anyone has a sample to implement it?
@lexuanquynh As mentioned earlier, use https://github.com/ReSwift/ReSwift/tree/mjarvis/swiftui Insert the store into the environment, then use @EnvironmentObject var store: Store<AppState> in your view, and then you can directly use store.state.xyz in the body, and dispatch directly to the store on actions.
have anyone has a sample to implement it?
I would recommend to use @Dimillian library
I figured out a nice way to inject a store and state selector into a SwiftUI View. It's not ReSwift, but you could easily adopt it for ReSwift:
https://github.com/gilbox/Cloe#connect-your-swiftui-view-to-your-store
Is there any sample available of ReSwift+ SwiftUI Implementation ?
@Joy-G Usage is very simple if using the mjarvis/swiftui branch as indicated in https://github.com/ReSwift/ReSwift/issues/424#issuecomment-570263696
I don't believe an example would provide much benefit. We have not had time to do further work in this direction. The branch may also require updates.
Instead of modding SwiftUI to fit ReSwift, I think this repo should learn from SwiftUI to mod ReSwift.
Modding from the counter example above:
struct ContentView: View {
@State var counter = 0
var body: some View {
VStack {
Text(String(counter))
Button(action: self.increase())) {
Text("Increase")
}
Button(action: self.decrease())) {
Text("Decrease")
}
}
}
func increase() {
counter += 1
}
func decrease() {
counter -= 1
}
}
Let's make some observations:
A global store is just not scalable and pointless. Let independent view hierarchies manage their own states.
IMO ReSwift is too much of a direct port from javascript counterparts. Even in Redux the latest trend seems to be leaning more towards functions and getting rid of classes.
I've got rid of the subscribe, reducer, dispatch, extra initializers. But I still maintain a clear state and _actions_ as functions. In this case it can be easily verified to have local impacts from state changes. In cases where it might impact children views in the hierarchy, don't forget we have @EnvironmentObject.
Anyone can add how many objects they need to construct their solutions. The art is how to do it with minimum overheads.
The way I see it, you only need to mark up actions (functions that trigger state change) in some team-negotiated way to distinguish them from functions that doesn't involve state change. And I would argue that this is where POP comes in.
It fits too perfectly to be a coincidence. Contrary to popular belief, I think SwiftUI creators know what they are doing, and Redux is already built-in if you know how to interpret them.
To share some experience, I've never got ReSwift to work in production. Global store is just useless for many shallow view controller hierarchies. I want things decoupled in team environment rather than coupled through global store. The overhead is another deal breaker. When you subscribe something, you introduce hidden state, the very thing you try to avoid with ReSwift.
With value type, the direction should be to eliminate subscribe, reducer, dispatch altogether. The only thing you need is a property observer to trigger effects following state changes. You can build around this concept and arrive at POP Redux.
I could go into more detail if someone is interested. But I think it destroys this repo. ReSwift needs to be rewritten with POP in mind.
@jimlai58 The whole point of Redux and ReSwift is for the global store and unidirectional data flow.
While completely valid approaches, your suggestions here completely defeat those purposes.
There are a lot of people who use ReSwift in production environments (including myself). We will not be making changes to ReSwift which modify its global store + unidirectional-data flow architecture, but are happy to make changes which make it more friendly to SwiftUI without changing general behaviour. (As seen in my branch above -- which makes no modifications to SwiftUI, it simply exposes the store as an ObservableObject)
If you have conflicting direction, please start a new repository / framework which follows those desired behaviours. I would definitely follow along, and perhaps your findings turn out well and could eventually make this repository redundant.
@jimlai586 Having written industry applications the single store is mandatory. For small apps, every developer can tweak around but to override redux on a global scale won't be good at all. As I understand SwiftUI state (as Apple describes it) it is a add-on by apple to make things easy for small apps which has single-source-of-truth with redux in mind. But sooner you'll need to deal with global store and middleware. So the compatibility with the middleware will be the crux. I think (IMHO)
I owe you guys a lot. I just finished my app with ease.
Most helpful comment
One easy way is to simple create a shim that pass the state on through as an observable object. Below is a shim that can be used with any ReSwift
StateType. It is fairly naive, and probably better ways. But it was enough for my purposes.