Measure elapsed time for each sent action (Debug Tool)

Hello :wave:

I have began writing something that I hope can be similar to the Redux-DevTools, a basic version at least, for the app I am rewriting in TCA. I have something super basic right now that at least receives the names of all actions that are sent from the iOS App to the macOS debug tool. I thought this would be a good idea as another iOS project that uses a custom Redux architecture has a tool just like this and it's super useful.

However, after getting the actions sent and displaying the names I started to look at how to get the elapsed time. In the other architecture I can see and understand the way it's used, but I cannot get my head around how I can do it here. I understand that I can have a tool without elapsed time, but I would really like to have this as part of the debug tool. It's in any implementation of a Redux-DevTools clone I have come across, so it has it's purpose of course :sweat_smile:

Would you have any advice for obtaining the elapsed time for each action that is sent? :smile: :heart: Any guidance at all would be very very much appreciated!! :pray: :pray: :pray:

@mbrandonw - I wonder if it's possible to have the ability to override or enhance the Store or at least it's send function? :thinking:

I have looked at this from a few angles and while I have managed to measure some actions I can't ensure every is measured as there are also the returned Effects to consider as well!

Have you considered just using a high level reducer that wraps your root reducer and measures the time by comparing timestamps before and after calling the wrapped reducer?

@VinceMikhailKearney Have you seen reducer.signpost() as documented from the README? https://github.com/pointfreeco/swift-composable-architecture#debugging

It adds timing information to all reducer actions and effects via os_signpost.

I had put this part to the side in order to get other parts of the tool working and they're up to speed now :smile:

lukeredpath; I had considered something similar but it was a bit too convoluted for my liking. Although this approach is nicer.

However, as stephencelis had pointed out there is a reducer.signpost() that I could potentially utilise as well :smiley:
Thanks for this Stephen!! I can certainly look into this and see if I can use it the way I wish!

I was able to use an approach like this in the end:

private var startTime = CFAbsoluteTimeGetCurrent()
public let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
    Reducer { _, action, _ in
        switch action {
        case .log:
            return .none
        default:
            startTime = CFAbsoluteTimeGetCurrent()
            return .none
        }
    },
    reducerA.pullback(
        state: \.reducerAState,
        action: /.self,
        environment: { ReducerAEnvironment(mainQueue: $0.mainQueue) }
    ),
    reducerB.pullback(
        state: \.reducerBState,
        action: /.self,
        environment: { ReducerBEnvironment(mainQueue: $0.mainQueue) }
    ),
    Reducer { state, action, environment in
        switch action {
        case .testAction:
            return Effect(value: .log(action: action, state: state, dispatchTime: 0))

        case let .log(action, state, dispatchTime):
            let endTime = CFAbsoluteTimeGetCurrent() - startTime
            Logger.shared.log(action: action, state: state, dispatchTime: TimeInterval(exactly: endTime)!)
            return .none

        default:
            return Effect(value: .log(action: action, state: state, dispatchTime: 0))
        }
    }
)

Now I can send the dispatch time to the debug DevTools and we're good to go :smiley: Just incase anyone wants the same thing.

Terms of Service

Privacy Policy

Cookie Policy