Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Created January 16, 2024 04:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IanKeen/4a7cccf334c86d2543dd777188a98576 to your computer and use it in GitHub Desktop.
Save IanKeen/4a7cccf334c86d2543dd777188a98576 to your computer and use it in GitHub Desktop.
TCA: Example of creating an Analytics component to get before/after state
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)")
}
}
}
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