So, back to ReSwift. On the ReSwift site, where they explain the role they are trying to fill, they note a few key concepts. One is the fact that MVC for Cocoa applications is not necessarily intuitive, and can create additional complexity because of the need to use callbacks, delegates, and notifications in order to pass stateful information through the app. The second main issue that they are trying to address is that developers can introduce inconsistent code because each developer might have a different preference on how to propagate state in the app. On top of these issues is the overarching concern that state can be a difficult thing to maintain and a framework to maintain it might help mitigate the most common issues seen while trying to maintain app sate.
When ReSwift is implemented, it manifests as a globally accessible
AppState object that lives in the AppDelegate. In order for state to be changed, an event has has to be dispatched to the
AppState, through a strongly typed
Action that enforces that the correct type of object is passed through. This object is then accessed in a `reducer` which executes code depending on the type of
Action and maintains an updated reference of the dispatched object on the globally accessible
State management for mobile applications, particularly in iOS is a tricky thing, and its understandable why the ReSwift team is trying to introduce a solution for it. However, I think that there are more simple solutions for the problems they are trying to address, and the solutions they provide introduce new risks and problem areas.
One of their goals is to mitigate the use of complex systems like callbacks, delegates, and notifications in order to pass stateful information around the app. However, using RxSwift would mitigate this as well. The use of
Observables makes most usages of callbacks and delegates obsolete, and notifications are likewise converted to global
ViewModel that your
ViewController can be bound to, that is updated based on model changes hosted in the
ViewModel. However, there is also the ability to use RxSwift in independent classes that, for example, make a service call, update a model in a database, or similar instructions that need to be reacted to. Stateful information can be passed by an
Observable, which is observed by any group of code that needs to execute something based on the resulting state.
Additionally, they note that code inconsistency is an issue that they are trying to provide a solution for. They do make the note that "You can circumvent this issue by style guides and code reviews but you cannot automatically verify the adherence to these guidelines," and while it is generally true that a style guide and code review can't catch issues as quickly as a developer can write it, there are still ways to address this. For one, part of the style guide can be an architectural pattern for passing state as strongly typed models instead of weakly typed objects that are casted to the object that is needed at compile time. Additionally, these style guide can be enforced with rules in libraries like SwiftLint, which throw compiler warnings or errors, depending on configuration, in order to catch these issues at compile time, instead of waiting for a code review to enforce it.
State problems in iOS largely manifest as a result of state needing to be maintained over multiple screens, and ReSwift, which is based on Redux, is built around the idea of single-screen applications. In order to use Redux for an application with multiple screens, the navigation state needs to be preserved in the
AppState. This poses an additional problem, since the navigation stack is maintained internally by the
NavigationController, which belongs to the
ViewController. Currently, there is a standalone library by the ReSwift team called the ReSwift Router which is in a very early version at this time, and has some problems that have yet to be resolved, but they highlight some issues that pose significant quality issues.
As it is, the
NavigationController natively has a back button that pops the last
ViewController off of the stack. Unfortunately, this does not operate through the
AppState so the navigation stack maintained by the operating system becomes out of sync with what is being maintained by the
AppState. We could override the back button with a custom implementation that uses the
AppState to navigate, as per the pattern, but reimplementing a control provided by the Operating System, as well as maintaining a duplicate stack, should be an indication that the pattern is not appropriate for the use case.
Aside from these concerns, the architecture of the
AppState enforces globally accessible
Singletons, which has responsibilities associated with any and all screens in the application. The
AppState becomes responsible for maintaining app state variable, propagating those state changes to subscribers, invoking reducer logic, and maintaining the navigation stack. While you could look at these and say it all falls under the umbrella of 'maintaining state', the reality is that there isn't one large bucket that fits all instances of state management. State that needs to be maintained for an entire user session on multiple screens and state that is exists to update a component on a single screen should not be treated the same way. The scope of the
AppState quickly becomes a tremendous object with ambiguous responsibility, and as a result, it introduces maintainability concerns.
I think that if an application is a single screen, ReSwift can easily fit the objectives of the application. Likewise, if the application has a unidirectional flow, I could see some merit to using a library like this. Unfortunately, the second your app starts doing more complex flows, which may include redirecting, or allowing the user to navigate backwards or forwards at will, it introduces more logistical problems than i think ReSwift addresses. Redux was designed to address the issues of responding to lots of state changes on a single-screen web application, and when that pattern is ported into an environment like iOS where a large amount of the application state is maintained implicitly by the operating system itself, it creates a very clear logistical hurdle of manually trying to stay in sync with the same state that is being maintained by the operating system.