There will be the hell lot of work to conform to Equitable for all variables of state.
As I am assuming there is no shortcut to achieving Equatable, instead we have to compare each and every variable of struct like below!!
struct Counter: Equatable {
var a=0, b=0, c=0, d=0, e=0, f=0, g=0, h=0, i=0, j=0, k=0
public static func ==(lhs: Counter, rhs: Counter) -> Bool {
return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c && lhs.d == rhs.d && lhs.e == rhs.e && lhs.f == rhs.f && lhs.g == rhs.g && lhs.i == rhs.i && lhs.j == rhs.j && lhs.k == rhs.k
}
}
Also, there is no way in swift to compare instances!!!
Can we achieve this with less overhead with class, I have tried a couple of ways and nothing seems to be working ???
You can compare instances of reference types (class) in Swift. I don't think you should implement your Counter using class, but if you want to: What's the problem? What is not working?
App State:
import ReSwift
struct AppState: StateType {
var counter=Counter()
var counter2=Counter2()
}
class Counter: Equatable {
var data:Int=0
static func ==(lhr: Counter, rhs: Counter) -> Bool {
return lhr == rhs
}
}
struct Counter2 {
var data:Int=0
}
Subscription:
override func viewDidLoad() {
super.viewDidLoad()
mainStore.subscribe(self.state1Subscriber) { state in
state
.select { state in state.counter }
.skipRepeats()
}
}
Reducer:
import ReSwift
// the reducer is responsible for evolving the application state based
// on the actions it receives
func handleAction(action: Action, state: AppState?) -> AppState {
// if no state has been provided, create the default state
var state = state ?? AppState()
switch action {
case _ as CounterActionIncrease:
state.counter.data += 1
case _ as CounterActionDecrease:
state.counter.data -= 1
case _ as CounterActionIncrease2:
state.counter2.data += 1
case _ as CounterActionDecrease2:
state.counter2.data -= 1
case _ as CounterAction1Static:
state.counter.data = state.counter.data
case _ as CounterAction2Static:
state.counter2.data = state.counter2.data
default:
break
}
return state
}
During runtime I am facing a problem:

That should perform an infinite recursion, i.e. a stack overflow because you call == from within the same ==. Can you confirm that the stack trace in Xcode to your left shows the same method call a lot of times, like, thousands of times?
What you want is:
class Counter: Equatable {
var data:Int=0
static func ==(lhr: Counter, rhs: Counter) -> Bool {
return lhr.data == rhs.data
}
}
But that would be value semantics again. So you really want to write it like this:
class Counter { // not equatable!
var data:Int=0
}
And then in the subscription:
override func viewDidLoad() {
super.viewDidLoad()
mainStore.subscribe(self.state1Subscriber) { state in
state
.select { state in state.counter }
.skipRepeats { $0 === $1 }
}
}
(Untested code)
The problem looks same as you mentioned.

However after changing
App State:
import ReSwift
struct AppState: StateType {
var counter=Counter()
var counter2=Counter2()
}
class Counter {
var data:Int=0
}
struct Counter2 {
var data:Int=0
}
State Subscriber:
override func viewDidLoad() {
super.viewDidLoad()
mainStore.subscribe(self.state1Subscriber) { state in
state
.select { state in state.counter }
.skipRepeats{ $0 === $1 }
}
}
Block subscriber:
public class BlockSubscriber<S>: StoreSubscriber {
public typealias StoreSubscriberStateType = S
private let block: (S) -> Void
public init(block: @escaping (S) -> Void) {
self.block = block
}
public func newState(state: S) {
self.block(state)
}
}
Now, even after the trigger of action, subscriber not getting notified!!
Ah sure, the Counter instance now stays the same :) So the instance does not change, hence you don't get any updated state.
You now have to replace the instance with a new one:
import ReSwift
// the reducer is responsible for evolving the application state based
// on the actions it receives
func handleAction(action: Action, state: AppState?) -> AppState {
// if no state has been provided, create the default state
var state = state ?? AppState()
switch action {
case _ as CounterActionIncrease:
state.counter = Counter(data: state.counter.data + 1)
case _ as CounterActionDecrease:
state.counter = Counter(data: state.counter.data - 1)
case _ as CounterActionIncrease2:
state.counter2.data += 1
case _ as CounterActionDecrease2:
state.counter2.data -= 1
case _ as CounterAction1Static:
state.counter.data = state.counter.data
case _ as CounterAction2Static:
state.counter2.data = state.counter2.data
default:
break
}
return state
}
extension Counter {
init(data: Int) {
self.data = data
}
}
It would probably be more convenient to offer Counter.changeData(delta: Int) -> Counter that initializes a new instance for you instead of calling the initializer from the reducer when your objects have many properties _that should stay the same_.
Oh, it worked 馃
I know classes can cause some problems, however, i am assuming it is less painful than update UI each time any remote state changed, your opinion ??
Can i have little info on when reswift started support class for states? initially, I saw states work only with struct.
also is there any better way to achieve listening to only changed state apart from
struct with the manual equitable implementation classes with all its problems with reference typesYou should use struct, and with swift 4.1 on latest version of Xcode you don't have to implement == yourself. If all your properties inside your struct are Equatable, just add Equatable to your struct. The Swift compiler will silently synthesis all your Equatable need.
State with class will create too much headache, reference will mutate in both old and new state, and you'll never get the state diff you want or need. You get all of them or none of them.
Equatable struct are ok. You can even have them mutable var inside, it's ok, as long as you take it out of the state, mutate it and insert it back in your reducers. Well you can also mutate it in place. The newState should only be dispatched when your oldState and newState are not equal then.
Thanks for all help, closing this issue for now
Most helpful comment
You should use struct, and with swift 4.1 on latest version of Xcode you don't have to implement == yourself. If all your properties inside your struct are Equatable, just add Equatable to your struct. The Swift compiler will silently synthesis all your Equatable need.
State with class will create too much headache, reference will mutate in both old and new state, and you'll never get the state diff you want or need. You get all of them or none of them.
Equatable struct are ok. You can even have them mutable var inside, it's ok, as long as you take it out of the state, mutate it and insert it back in your reducers. Well you can also mutate it in place. The newState should only be dispatched when your oldState and newState are not equal then.