Me: How can you wrap NSTextView events to go through a ReSwift store? ChatGPT: It’s not clear what you’re asking. Could you please provide more information or context? I’m a large language model trained by OpenAI, so I don’t have the ability to browse the internet or access information outside of what I was trained on. I can provide general information and advice based on that training, but I may not be able to answer specific questions about events in NSTextView or ReSwift stores.
I may be 7 years late or so – but until last week, I didn’t realize that didSet property observers would fire when the observed property hasn’t actually changed. All you need is a mutating func that doesn’t even need to mutate. This can be illustrated with a simple piece of code:
The SwiftRex project is similar to ReSwift in design: it’s a library to design applications with a unidirectional data flow underpinning. In contrast to ReSwift, which is around for years and years already, SwiftRex has had a fresh start and supports Combine, RxSwift, and ReactiveSwift to create state change subscriptions. That’s a nice touch. I’ll have to experiment with the library some day, but so far it looks nice and the README is full of sexy ASCII diagrams.
As an exercise, I wrote a simple iOS scene transition from a login form (root view controller) to a success scene. I was experimenting with the Model-View-Presenter approach where view and presenter know each other, but communicate through protocols, so you program to an interface, not the implementation. I ended up using a simple state machine to represent the view’s state, and employed form validation that is represented in the types. I like how the result reads, so here’s a rundown of the exercise:
This blog post was originally meant as a reply to the ReSwift issue “Any way to control the priority of subscription callbacks?” – If you find asking yourself this question, this should help. This implementation detail is hidden by design. If you find yourself wanting to affect the order of subscription callbacks, there’s a concept of a sequence waiting to be extracted somehow. Or, the other way around: if you want to affect the order, your design is wonky.
While I was refactoring my app’s state recently, I noticed that there are virtually no dev logs about The Archive and how I manage state and events in this app. It’s my largest app so far, still the amount of reflection and behind-the-scenes info is pretty sparse. This post outlines my recent changes to the app’s state. It’s a retelling of the past 5 weeks or so.
Vinh Nguyen found that his ReSwift status updates became slow.
There were too many subscribers.
Objects would react to state updates by dispatching a new action immediately. (ReSwift action dispatching happens synchronously.)
His app state ends up containing a lot of objects in a 3-level hierarchy that mimicks the hierarchy of view components on screen. In a drawing or otherwise canvas based graphics app, it seems. It doesn’t make sense to have each objects on the canvas responds to state updates when one other object updates on screen. Instead, you’ll want to at least minimize the amount of updates that get passed through.
Vinh implemented a custom diff or “delta update” for the 2nd level in his 3-level hierarchy of objects because they were few enough to be performant during state updates, and could easily manage their child objects.
Read about his discovery of state update bottlenecks on his blog.
He solved the second problem, newState callbacks triggering the dispatch of another action, by enqueuing the dispatch in an asynchronous block on the main queue, which is the queue ReSwift uses:
Sure, this enqueues the action dispatch until the current execution is finished. But you have to take care about other actions being dispatched in between now, and if that is a problem. (E.g. another subscriber type reacting to the same state update with another action.)
I had prefered another solution initially: subscribe to updates in the top level Canvas object, then delegate down the view hierarchy as needed. Every sub-component that wants to fire an action tell the Canvas about this, which enqueues the actions, and then processes the queue after all sub-component updates are finished. A bit like in game development where the game loop ensures there is just 1 point of action handling per run. But then again, Vinh’s approach does exactly that: it enqueues action dispatching until later, ensuring the current run loop run isn’t interrupted. Also, my approach to delegation would make everything just so much more complicated in the app code.
I wonder is it’d be beneficial if the ReSwift store operated on a high priority queue that is not the main queue all the time. Then you can dispatch actions synchronously from view components on the main queue, waiting for the result, or asynchronously.
I will have to think more about the consequences of an approach like this before I suggest anything to anybody, though. I don’t do a lot of concurrent programming in my apps, and when I do, I contain it very strictly; on the downside, I don’t have developed any instinct regarding implications of using multiple queues.
I am using ReSwift in my app. I like ReSwift because I can create a representation of my app in a “State” Swift module without any UIKit/AppKit dependency. There, I put all the data that the app is supposed to represent at any given moment in time. To get a feeling for this, have a look at two apps of mine:
Say you extract the ReSwift-based state module with all your app’s state types, reducers, and middleware into its own framework target. Until you add middlewares to the mix, it’s easy to keep this coherent. Injecting side-effects through middleware is not easy to do, though, as long as you provide the Store instance from your state module. The store takes an array of Middleware upon initialization. They cannot be changed later. The state module would then depend on your app module with all its network request services and whatnot. That’s the wrong way around, also resulting in a circular dependency. Instead, you have two options to add side-effects that are inherently part of your app’s module, not of the state module:
When you have a reference to your store of ReSwift.StoreType, you can use the current state property to get to a value directly. This means you don’t even need to make the object a store subscriber to grant access to some store state. I don’t recommend doing this in real code, of course. This is just like any global variable and comes with the usual problems.
For example, I have a button, after user pressed it I want to start some process if I’m in state A. So, I have to write an action creator, which will check current state and then return correct action, or not action at all. Then dispatch this action from the same place.
Redux.js and ReSwift behave quite differently. Redux recommendations may not apply in all cases.
Clearing up Redux.js/ReSwift confusions
Action Creators in ReSwift
“Action creators” is a rather specialized term in Redux.js, but the type is defined like this in ReSwift as of v4:
So contrary to general advice, you do have access to the state at the point of dispatching the action. You don’t need a Thunk implementation, although Thunks or Epics can help in more complex cases.
“Action Creators help encapsulate action creation details”
A common benefit of Action Creators in Redux is their function as a factory.
Redux.js actions are object literals. Redux actions, if you were to write them in Swift, would be more like Dictionarys. If you come from an Objective-C background, you know both the good and bad of dictionaries: they are flexible, but you can cause trouble with typos, and the compiler won’t catch changes to values or keys if they are stringified. That’s how Action Creators in Redux.js provide the convenience of factories (as in “Factory”, the Gang of Four design pattern).
The ReSwift.Action type is usually implemented in custom value types (structs), though. ReSwift does not suffer from that problems of Redux.js actions. That means by creating a custom action type in ReSwift, the benefit of centralizing action creation in one function goes away. The initializer of your type does provide this already.
That paints a totally different picture.
And that’s probably why ReSwift.Store.ActionCreator passes in the state: to provide any benefit at all. At the cost of a different kind of API than Redux.
Application to the Question
Recall the question:
For example, I have a button, after user pressed it I want to start some process if I’m in state A. So, I have to write an action creator, which will check current state and then return correct action, or not action at all. Then dispatch this action from the same place.
There are a couple of ways to achieve that.
If you have access to the store variable to call dispatch, you also have access to its current state property. You can ask the store for the state the app is in and act accordingly. Usually, you’d write store subscribers to get “push notifications” of store changes, but in cases like this you can also ask the store.
That means the following would be a totally valid implementation:
Since ReSwift Stores are not supposed to receive dispatch commands from different threads, you can rely on the state from line 1 to be the same in the last line, where you dispatch.
TL;DR: you do not need ActionCreator to achieve this. But you can if you like to write “east-oriented” code, leaning on callbacks instead of property queries:
store.dispatch { state, _ in
if state.someSubstate == "A" {
return ActionWhenInStateA()
} else {
return ActionWhenNotInStateA()
}
}
I totally forgot to bring this up: ReSwift version 4 was released 13 days ago!
The coolest part (in my opinion) is to skip duplicate states in subscriptions if you want to. The state has to be equatable somehow to do this with the default subscription measurements. But then again you’re using this anyway right now with ReSwift 3.x, only for every subscriber.
The skipRepeats in the subscription builder/configuration block will be used automatically if the selected state conforms to Equatable. But you can use your own skipRepeats implementation if needed.
In theory, you could add map and filter and whatnot to the underlying Subscription type that’s being configured in the block you pass to subscribe(), though I don’t see much value in this at the moment.
I wrote about using NSUndoManager + ReSwift with a Middleware. You could think of the resulting UndoMiddleware as some kind of observer of events that pass the ReSwift.Store and which puts the opposites of incoming actions on the undo stack. Why a Middleware? Because making an action undoable is a side effect.
Automatic saving of changes in some user interface component to a file should be handled differently when you employ ReSwift. In short, you have to extract the state information from the “save file” action and store it in the overall app’s state somehow. I append these to a collection of PendingFileChanges, a ReSwift.StateType that is part of my overall app state.
Say you are like me and work with ReSwift for unidirectional data flow goodness and employ RxSwift for your reactive cravings. You want to dispatch a ReSwift.Action when an RxSwift.Observable signal produces a new value. How do you write tests for that wiring? In code, it could look like this:
I use a logging middleware in all my apps that use ReSwift. I love to see the event log. It makes me think the app is healthy when it tells me what’s going on. All this happens when I start the knowledge management app I’m working on in about half a second (reformatted a bit to make reading easier):
Let’s say you write a plain text editor that can work with multiple text files at a time. As a backbone for processing information, you use ReSwift to model the data flow in a clean fashion. The user has 10 files open, changes the text in 1 file’s tab. The user leaves the text field and your awesome autosaving is triggered.
In a client meeting yesterday we tried to figure out how to animate scene transitions with swiping left/right when ReSwift is the single source of truth of the app state. What goes into the app state? How do you animate that? Should the % of the transition be part of the app state for some reason? (Spoiler: Nope.)
I watched “Managing Consistency of Immutable Models” by Peter Livesey where Peter shows how RocketData works. Very worth the time! I’d call RocketData a uni-directional data source. It’s uni-directional because you set up the DataProvider as the source once, wire update notifications to view updates via the delegate property, and you’re done with the setup. Update events include:
It’s much like MVVM, whereas here the view model is the store’s state.
This Flux-like approach is still a big improvement over convoluted view controllers or not thinking about data flow at all. But I wonder if the gain is so much higher compared to properly separated MVC. Who reads the store’s state? Only the views? Then the state is truly a view model; but how do side-effect populate to the rest of the app, like renaming or deleting things? I’d tend towards dispatching the change event to other stores, too, which may or may not react to the event. Then the view component’s state is updated independently from the database, say, using the same event dispatch mechanism.
To model the flow of truth on a component basis (which is still uni-directional: from store to view) compared to a single global app state (ReSwift) sounds like a very easy to do component redesign. You can start with anything and “fluxify” it. Then move on to the next component that’s particularly convoluted. You may not even want to convert all your components to this data flow approach when some components are simple enough as they are. Symmetry of component design will suffer, of course, but if you’re aware of this and conclude it’s better not to convert everything, that’s fine.
With ReSwift, I interpret the global app state to be a state with real (Domain) Model objects. To make a state displayable, I derive view models from it. This came very natural to me. But Ben’s posts got me thinking: could there be situations where keeping view model state around is better than keeping “real” model state?
It took me a while to grok the use of “Middleware” in ReSwift. ReSwift’s own tests illustrate what you can do with it: you can filter or modify actions before they are passed to the Reducers, for example.
As soon as you write a piece of software, you “architect” it. Can’t get around that; but if you do not do it consciously, the resulting structure may not be great. Taking ownership of the process is important to change the result and create maintainable software. When we write/architect software, we worry about two things:
The upcoming changes in TableFlip’s user experience made me ponder how I structured the app’s modules. The bottleneck I created in my app is simple: there’s one presenter which transforms the model into a view model; the presenter defines a protocol for the view it expects and passes the view model to the object that satisfies this contract.
They moved to a Flux-inspired “unidirectional flow” approach where state changes are performed in a Store (in the backend, if you will) and updates pushed to the user interface (the frontend). No ad-hoc view updates, no shortcuts. User events are emitted, handled by the store, and state updates affect the view. That’s it. So it’s always obvious how the user interface got into the state it’s in: all the details are plainly visible in the current state object.
Ben’s post is very deep and detailed. You’ll learn a ton from it:
architectural discussion: why “unidirection flow”, and why Flux?
setup of actions/events and the store type
basic state–view bindings via Reactive Cocoa
how testing stores (and views) is straightforward with this setup
I can’t wait for the next post in this series. Most of this easily applies to ReSwift, too, of course. And guess who just got inspired to refactor TableFlip so the model and view become even more loosely coupled?
Today was the second time during the development of TableFlip that I started to implement a new feature in the wrong way: starting with an explicit event type that is triggered by pressing a button in the user interface. This is a 1:1 mapping of user intent to an event that performs changes in the model. Next time I’ll start from another point of view instead to not rush too many minuscule changes until I hit a roadblock and hate myself. Here’s what went wrong.
Now that TableFlip is nearing completion, I want to share details of how I created this piece of software with you. Today, I’ll start with the bigger picture: the application architecture. Earlier this year, I got to know ReSwift from a talk by Benjamin Encz which I loved.
After reading Sam’s post, I discussed a code snippet with him. Shortsightedly, I told him I didn’t like the following snippet because it controls project instead of doing the object its own job: It made me wonder if we can do better, though, with standard OOP practice. My proposal was a simple toggle method:
Sam Ritchie wrote “Building a Unidirectional Data Flow app with Realm” the other day. I haven’t worked with Realm before, but the state propagation seems super interesting now that I dabble with ReSwift from time to time. His post excited me to try out Realm, soon.
Benjamin Encz’s presentation “Unidirectional Data Flow in Swift” about ReSwift features global app state: there’s one AppState type that acts as the facade to model and navigation state which is the single point of truth of every state in the app. This is a game changer when you suffer from massive view controller syndrome. In this post, I’d like to show you how he envisions the state of an app and what a next step could look like.
I think I found something better than closures to handle events. Instead of dispatching closures that are themselves the changes that should be performed, simply declare the change.
enumAction{caseResizeBanana(BananaId,Size)}
Now you need some kind of action handler, which Benjamin Encz calls “reducer” in his presentation “Unidirectional Data Flow in Swift”. Think of a list of reducers as a Chain of Responsibility only without the final consummation of the action. It is passed throughout the whole responder chain. Why reducer? Because they operate similar to the reduce function. The end result will be the combined result of all reducer’s mutations.
This is cool, because no component needs to know what such an action entails – none except the appropriate reducers.
If closures were great because where you type them you can see what is going on (more like “how” in my opinion), a simple action enum is even simpler, telling what should happen.
Works well with CQRS, I imagine. Will have to write a demo app using CQRS sometime to validate.
The current project is called ReSwift and code is available on GitHub.