Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    Christian Tietze
    @DivineDominion
    @nanocba When you use skipRepeats and narrow down to changes of some substate, the substate selector and equality checks will be triggered for the "micro-updates" but the views should not redraw.
    nanocba
    @nanocba
    thanks @DivineDominion. But how can you make the views not to redraw with those micro updates? Do you put some logic on the newState method that prevents that from happening?
    @bgntsty_twitter thanks that's insightful.
    using skip(when: ==) you can prevent views from updating. You subscribe the views to a substate only, not the whole app state, for this to take effect
    nanocba
    @nanocba
    @DivineDominion oh yes, my app state conforms to equatable so my derived sub-states so I get that for free and that's working incredibly well. But here's the scenario I was trying to expose: on my reducer I have Action A performing Logic A, then Action B performing Logic B, but then from a middleware I'm intercepting Action C that dispatches Action A and Action B. The two dispatches are causing two view updates since they both modify the state. So I'm wondering what would be the best way to group these modifications together so there's just one view update at the end while still being able to reuse Logic A and Logic B.
    Christian Tietze
    @DivineDominion
    Here's two impulses I'd follow to see where they lead: :one: make the middleware not reuse these actions but dispatch its own stuff. Aka reconsider what your actions mean and if they apply in all cases, or if it's just a convenient implementation detail. (Code duplication is just a heuristic: if it means different things, the same code can live in two places just fine and actually improve the overall architecture.) :two: go for a solely code-based fix: wrap the action dispatching in BeginningXUpdates/EndingXUpdates actions that toggle a Bool in your state. Views are updated only after EndingXUpdates turns off that flag again. Just like table views offer beginUpdates/endUpdates.
    Christian Tietze
    @DivineDominion
    Maybe more inspiration:
    # 202007111018 Coalesce multiple state updates to trigger a single view update
    #reswift #ui #middleware
    
    What if you have an action `A` that is processed in a middleware that dispatches `B` and `C` in sequence, both affecting a substate that will be rendered in the view, but you want to trigger only 1, not 2 updates in the UI?
    
    1. Can you **debounce/throttle** the subscription?
    2. **Double-buffering:** Can you change `B` to a variant `Btemp` and `C` to `Ctemp` and reduce the change to a **different, temporary substate**? That state could even be `private`. When you're finished, you copy the result over to the real, UI-visible state.
    4. **Versioning:** Update the UI only when the `Version<T>` number increases. Update versions only when you're finished. Similar to double-buffering, but you can work with a **scratch/draft/proposed version**. -- Similar to the staging area in git VS the commit history.
    3. **Coalescing actions:** Introduce `BeginningUpdates`/`EndingUpdates` actions that flick a boolean switch on/off. If it's on, the UI doesn't receive updates. If it's off, it receives updates again. Everything in between is effectively coalesced.
    nanocba
    @nanocba
    @DivineDominion wow! I really like many of those techniques, specifically the debounce one since I think you can wrap the dispatch calls into a debounce block so this handling will be transparent for the UI. Thanks for sharing this. Really helpful.
    nanocba
    @nanocba
    Actually, I need to correct myself: looking at the code, it doesn't seem to be possible to debounce subscription from outside the store class. So I think the debounce needs to be implemented in newState.
    Dario
    @Diarrhio
    Anybody here using ReSwift in a real time type AR application? I'm wondering how suitable the store is for invoking actions through the store at 30-60fps. Our app is already extremely CPU (and GPU) heavy due to the nature of our AR tracking. I've so far avoided invoking any actions through the store (our store/state is broken up into subsystems which are shielded from one another other than through invocations of actions through the store). Some of our subsystems need to invoke code on others subsystems, sometimes at 30-60fps. To avoid executing actions through the store, I've ended having subsystem A putting a function pointer on its state, and then having subsystem B calling the function pointer when needed. I'm not fond of this and I'd just rather have subsystem B just invoke a regular action that subsystem A handles. Before I spend a bunch of time refactoring and profiling both approaches, does anybody already have experience with this? Or do the authors of ReSwift know if the store is fast/lean enough to handle something like this?
    Dario
    @Diarrhio
    I should add that we have something like 11 subsystems and we lean heavily on Redux to manage state and also to just keep code isolated and siloed. Each subsystem has its own actions, reducers, middleware, etc. I guess in particular, having 11 reducers and middleware functions being called at 60fps, each doing its own switch/case on the action, is my primary concern, on top of whatever machinery exists in the ReSwift core to do what it does when each action is dispatched
    nanocba
    @nanocba
    Here's how I achieved debouncing view updates (thanks @DivineDominion!). I had StateObserver and PresenterType from before so it was easy to add this functionality for every view conforming to StateObserver.
    protocol PresenterType {
        associatedtype State: Equatable
        associatedtype ViewModel
        func viewModel(state: State) -> ViewModel
    }
    
    protocol StateObserver: StoreSubscriber {
        associatedtype Presenter: PresenterType where Presenter.State == StoreSubscriberStateType
    
        typealias StateKeyPath = KeyPath<AppState,Presenter.State>
    
        var store: AppStore { get }
        var presenter: Presenter { get }
        var viewModel: Presenter.ViewModel { get set }
        var subscription: StateKeyPath { get }
    
        func reload()
    }
    
    extension StateObserver {
        func unsubscribe() {
            store.unsubscribe(self)
        }
    
        func subscribe() {
            store.subscribe(self) { subscription in
                subscription.select({$0[keyPath: self.subscription]})
            }
        }
    
        func newState(state: Presenter.State) {
            viewModel = presenter.viewModel(state: state)
            Debounce.input(state, comparedAgainst: self.current, delay: 0.02) { input in
                self.reload()
            }
        }
    
        private var current: Presenter.State {
            store.state[keyPath: subscription]
        }
    }
    Malcolm Jarvis
    @mjarvis
    @Diarrhio I think you'll need to do some performance testing yourself to see if it can be applicable in your case.
    Generally my recommendation is if its desirable to have a redux like approach in a real-time system is to have a separate store dedicated to that system thats very bare-bones + optimized. That store can be run on a separate thread, and it can dispatch out to the main store (via middleware) + fetch content as needed.
    Dario
    @Diarrhio
    @mjarvis not a bad idea. Was just curious if anybody already had experience with this already (perhaps with a hard yes or no answer). I'll do some testing and see what the performance costs are. Thanks!
    Christian Tietze
    @DivineDominion
    @nanocba Can you share an example viewModel(state:) factory method implementation? Am curious what you do there
    nanocba
    @nanocba

    @nanocba Can you share an example viewModel(state:) factory method implementation? Am curious what you do there

    That's on the presenter's side. Basically, a presenter always take some state and converts that into the view model that view needs. Note that the concept of view model is not that from MVVM. These are pure view models. They don't have any functions, just properties that map 1:1 to view properties. The type of things you see inside this viewModel(state:) method is always transformations from state to view model, such as if the state exposes a date and you need to display it in the view, this is the place where you format the Date into a String.

    Christian Tietze
    @DivineDominion
    gotcha, thanks!
    Tim DeGraw
    @timdegraw_twitter
    Can anyone shed some light on the state ReSwift? The last release was over a year ago so I'm curious if it is still being maintained.
    Christian Tietze
    @DivineDominion
    I wanted to publish a Swift 5.x-built binary release for a while, guess I should put this onto my todo list for tomorrow for once :)
    @timdegraw_twitter The lib works, is used in production, alive and kicking
    Tim DeGraw
    @timdegraw_twitter
    Cool! Thank you. I'm working on a project that has it integrated and I was trying to decide whether to build on top of it or find an alternative. Thanks for letting me know. I'll keep my eye out for the update ;)
    Christian Tietze
    @DivineDominion
    There're some large-scale changes in the pipeline that nobody ever got around to finish up that could make the approach look a bit more like modern Swift, but if you dig classic OOP for the subscribers, you'll be fine with the current state of the framework 👍
    (And if you don't, it's easy to write closure-based convenience subscribers :))
    Tim DeGraw
    @timdegraw_twitter
    Sounds great
    I'm curious, is there built in support to persist to Realm/CoreData? Or do you projects usually serialize to disk?
    Christian Tietze
    @DivineDominion
    ReSwift only comes with the state/subscriber/action/reducer flow, the rest is up to you
    With CoreData, I usually represent the app's core in my own model objects and then serialize/deserialize them using CoreData. It goes against the framework grain a bit, because then you could just as well use a dumb SQLite DB, but I don't want to weave CoreData into every aspect of my app. It's pretty invasive when you follow most tutorials.
    Tim DeGraw
    @timdegraw_twitter
    Interesting. I actually have written any apps that use CoreData. But I've used Realm quite a bit and I like it.
    Our current implementation is really basic and it works great. The issue is that it doesn't persist to disk so I'm trying to figure out how to weave that in and also work the current flow using DI for testability.
    nanocba
    @nanocba
    Anyone has play around with ReThunk and having them to return a promise? The idea is to be able to write code like
    dispatch(thunk).then { // this closure is called once the promise in thunk is fulfilled }. That greatly helps increasing reusability of thunks IMHO.
    Malcolm Jarvis
    @mjarvis
    @nanocba You can wrap the thunk creation in a function such as func foo(params) -> (Thunk, Promise), then use let (thunk, promise) = thunk(params); dispatch(thunk); promise.then { ... } -- This can be wrapped into helper functions and/or extensions on the base types such that you can do so without boilerplate, giving you what you're asking for.
    (eg extension Store { func dispatch(_ thunkAndPromise: (Thunk, Promise)) -> Promise { ... })
    Mycroft Canner
    @mycroftcanner

    How would you model state in a table view driven by an NSFetchResultsController?

    I can let the NSFetchResultsControllerDelegateupdate the table view by inserting, updating, moving and deleting view and when the delegate methodcontrollerDidChangeContent(_:)is called, update the state using the fetch results controller's propertyfetchedObjects`.

    If I use the FRC fetchedObjects and convert my entities to structs I will lose the benefits of faults (large objects can be represented by a fault instead, until they really are needed.) unless I just map fetchedObjects to their objectIDs and only keep the objectIDs in the state and create a state for a cell lazily when it is needed.

    Malcolm Jarvis
    @mjarvis
    I probably would not mix NSFetchResultsController and ReSwift. I'd either get everything into ReSwift somehow (id references or otherwise), or I'd just use NSFetchResultsController directly.
    Xiaoyu Guo
    @MichaelGuoXY
    Hi guys, anyone has any suggestions for this scenario? View A has a state StateA, View B has a state StateB, and both of them share some StateCommon, the question is what's the best practice here to dispatch an action to modify the StateCommon to be adopted by StateA and StateB which are respectively subscribed by View A and View B?
    To be more clear:
    View A subscribes StateA, View B subscribes StateB
    How to let StateA and StateB share some common state? And if any changes happened to that common StateCommon, how to let StateA and StateB know? so that View A and View B would know as well.
    Christian Tietze
    @DivineDominion
    @MichaelGuoXY E.g. instead of newState(_ state: StateB), subscribe to newState(_ state: ViewBState) with struct ViewBState { let stateB: StateB; let commonState: CommonState } (or use tuples)
    Luciano Polit
    @LucianoPolit

    Hey everyone, I hope you are doing good. I would like to share with you a new framework that I have been working on in the last couple of months. It’s called Caesura and it’s powered by ReSwift.
    Main features:

    • Modularization. It’s intended to be feature driven. It decouples your app into small independent modules. Your state will not grow in size any more.
    • Action driven navigation. It’s as flexible as UIKit is.
    • A different way to interact with your API than ReThunk. It’s easy to use and super testable.
    • Optional built-in modules: time traveling, crash detector and reporter, session injection, and more...

    Please, visit the repository for more information and I strongly recommend you to try the example project to see what the framework is capable of.
    Any suggestion is more than welcome, as well as your star ;)
    Cheers!

    Malcolm Jarvis
    @mjarvis
    @LucianoPolit Thanks for the information! I'll be sure to take a look.
    Xiaoyu Guo
    @MichaelGuoXY

    Hey guys, it's me again :) Hope you are doing well. I've been using ReSwift for a year now and there is always some concern in my mind: is it really a good practice to keep all the states/substates in the memory?

    Say I have a view, which dispatches an action to its middleware, the middleware talks to the network service and fetches the data back, after that it fires an action to set the data to its viewState, then the view observed its state changes and reflects that. Everything works perfect. But if I keep doing this for each modules in my app, will it keep increasing the memory usage and some day it might be too high?

    I would like to know what are the todos and not-todos when dealing with various modules with various data (info displayed/stored in the state).

    Malcolm Jarvis
    @mjarvis
    @MichaelGuoXY it is important to keep an eye on memory usage as you test & build your app. Continuously increasing your state can result in memory problems, yes.
    Shawn Koh
    @shawnkoh
    hi all, does anyone know why attempting to dispatch a thunk (from ReSwift-Thunk) leads to recursive dispatch?
    CleanShot 2020-10-06 at 23.57.57.png
    dispatching the fetchProjects thunk leads to a recursive dispatch and crashes the app.
    Malcolm Jarvis
    @mjarvis
    @shawnkoh I don't see anything in that that would cause a recursive dispatch. Could you put together a small example project for us to try?
    Shawn Koh
    @shawnkoh
    oh apologies i think i forgot to set equatable on the structs
    haha
    that feels like something that should be enforced by the compiler though, i wonder if store.subscriber's select function should require the struct to conform to Equatable
    Malcolm Jarvis
    @mjarvis
    There is no requirement for equatable structs in state. ReSwift supports non-equatable state, it just can't do repeat skipping. Can you provide an example please?