Skip to content

Commit

Permalink
Added OnChangeAnalytics
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverfoggin committed Sep 5, 2023
1 parent a7e4f36 commit 1769fb8
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,34 @@ As most analytics will probably be events without any properties the `AnalyticsD

As a personal preference, I tend to use `default: return nil` at the end of it. `nil` is returned from the `AnalyticsReducer` for any action/state combination when you don't want it to send analytics. So it is a lot more convenient to wrap them all up in a `default` case at the end of the switch rather than list out all the actions and return `nil` from each.

### On Change Analytics

You can no trigger analytics from the change of state.

If your state is like...

```
struct State {
var count: Int
}
```

You can now add analytics when the count changes by adding a `.analyticsOnChange` to your reducer.

```
Reduce<State, Action> { state, action in
// feature reducer
}
.analyticsOnChange(of: \.count) { oldValue, newValue in
return .event(
name: "countChanged",
properties: [
"from": "\(oldValue)",
"to": "\(newValue)",
)
}
```

## Custom Analytics Clients

This package only provides an analytics client for logging to the console. Accessible as `AnalyticsClient.consoleLogger` but you can very easily add your own custom clients.
Expand Down
1 change: 0 additions & 1 deletion Sources/ComposableAnalytics/AnalyticsReducer.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Foundation
import ComposableArchitecture
import XCTestDynamicOverlay

public struct AnalyticsReducer<State, Action>: Reducer {
@usableFromInline
Expand Down
53 changes: 53 additions & 0 deletions Sources/ComposableAnalytics/OnChangeReducer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Foundation
import ComposableArchitecture

extension Reducer {
@inlinable
public func analyticsOnChange<V: Equatable>(
of toValue: @escaping (State) -> V,
_ toAnalyticsData: @escaping (V, V) -> AnalyticsData
) -> _OnChangeAnalyticsReducer<Self, V> {
_OnChangeAnalyticsReducer(base: self, toValue: toValue, isDuplicate: ==, toAnalyticsData: toAnalyticsData)
}
}

public struct _OnChangeAnalyticsReducer<Base: Reducer, Value: Equatable>: Reducer {
@usableFromInline
let base: Base

@usableFromInline
let toValue: (Base.State) -> Value

@usableFromInline
let isDuplicate: (Value, Value) -> Bool

@usableFromInline
@Dependency(\.analyticsClient) var analyticsClient

@usableFromInline
let toAnalyticsData: (Value, Value) -> AnalyticsData

@usableFromInline
init(
base: Base,
toValue: @escaping (Base.State) -> Value,
isDuplicate: @escaping (Value, Value) -> Bool,
toAnalyticsData: @escaping (Value, Value) -> AnalyticsData
) {
self.base = base
self.toValue = toValue
self.isDuplicate = isDuplicate
self.toAnalyticsData = toAnalyticsData
}

@inlinable
public func reduce(into state: inout Base.State, action: Base.Action) -> Effect<Base.Action> {
let oldValue = toValue(state)
let effects = self.base.reduce(into: &state, action: action)
let newValue = toValue(state)

return isDuplicate(oldValue, newValue)
? effects
: effects.merge(with: .run { _ in analyticsClient.sendAnalytics(toAnalyticsData(oldValue, newValue)) })
}
}

0 comments on commit 1769fb8

Please sign in to comment.