Created
January 16, 2024 04:27
-
-
Save IanKeen/4a7cccf334c86d2543dd777188a98576 to your computer and use it in GitHub Desktop.
TCA: Example of creating an Analytics component to get before/after state
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct MyFeature: Reducer { | |
struct State { | |
var count = 0 | |
} | |
enum Action { case inc } | |
var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case .inc: | |
state.count += 1 | |
return .none | |
} | |
} | |
MyAnalytics() | |
} | |
} | |
struct MyAnalytics: AnalyticsReducer { | |
@Dependency(\.analyticsClient) private var analyticsClient | |
func analytics(before: MyFeature.State, after: MyFeature.State, action: MyFeature.Action) -> Effect<MyFeature.Action> { | |
switch action { | |
case .inc: | |
return analyticsClient.send("Count increased from \(before.count) to \(after.count)") | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public protocol AnalyticsReducer { | |
associatedtype State | |
associatedtype Action | |
func analytics(before: State, after: State, action: Action) -> Effect<Action> | |
} | |
public struct _AnalyticsReducer<Base: Reducer, Analytics: AnalyticsReducer>: Reducer where Analytics.State == Base.State, Analytics.Action == Base.Action { | |
@usableFromInline | |
let base: Base | |
@usableFromInline | |
let analytics: Analytics | |
@usableFromInline | |
init( | |
base: Base, | |
analytics: Analytics | |
) { | |
self.base = base | |
self.analytics = analytics | |
} | |
@inlinable | |
public func reduce(into state: inout Base.State, action: Base.Action) -> Effect<Base.Action> { | |
let before = state | |
let baseEffects = self.base.reduce(into: &state, action: action) | |
let after = state | |
return .merge( | |
baseEffects, | |
analytics.analytics(before: before, after: after, action: action) | |
) | |
} | |
} | |
extension ReducerBuilder { | |
@inlinable | |
public static func buildPartialBlock<R0: Reducer, R1: AnalyticsReducer>(accumulated: R0, next: R1) -> _AnalyticsReducer<R0, R1> | |
where R0.State == State, R0.Action == Action { | |
return .init(base: accumulated, analytics: next) | |
} | |
@inlinable | |
public static func buildExpression<R: AnalyticsReducer>(_ expression: R) -> R | |
where R.State == State, R.Action == Action { | |
return expression | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment