Dimillian / SwiftUIFlux

A very naive implementation of Redux using Combine BindableObject to serve as an example

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Auto archive state when state changes

ethanyuwang opened this issue · comments

In the movie example app, I saw it uses a timer to archive the state every other 30 seconds.
https://github.com/Dimillian/MovieSwiftUI/blob/2897f8dfd3ea8370fbecacf56a8516f25d71efe8/MovieSwift/MovieSwift/views/components/home/HomeView.swift#L23

I would like the state to archive right after when a user is authenticated to save authenticated: true, so next time when user opens the app it doesn't load the sign-in screen. Is there a way to subscribe to state change and auto archive state when state changes?

Hi @ethanyuwang, sure you can subscribe to state updates using a middleware. Here’s a sketch of how you might do it (error handling is an exercise for you 😇):

let stateArchiverMiddleware: Middleware<FluxState> = { dispatch, getState in
    return { next in
        return { action in
            let state = getState() as! MyAppState
            let folder = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            let file = folder.appendingPathComponent("MyAppState.json")
            try! JSONEncoder().encode(state).write(to: file)

            return next(action)
        }
    }
}

The other thing you’ll need to do if you want to get your state restored is write a routine to read it back in on app launch (again, just an outline — make sure to put some error handling in):

func loadMyAppState() -> MyAppState? {
    let folder = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    let data = try! Data(contentsOf: folder.appendingPathComponent("MyAppState.json")),
    return try? JSONDecoder().decode(MyAppState.self, from: data)
}

You’ll need to specify the middleware and load routine when creating your store:

let myStore = Store<MyAppState>(reducer: myAppReducer, middleware: [stateArchiverMiddleware], state: loadMyAppState() ?? MyAppState())

Hey, I might be late to the party but since we built upon your solution @danhalliday (so thanks for your reply here!) I wanted to mention that calling next(action) at the very end of the function will cause you to save the state before the actual action runs which modifes it. Calling it at the beginning of the closure like let nextAction = next(action) and then returning nextAction will resolve that.