Skip to content

Commit ebf0a93

Browse files
committed
Example of singular enrichment
1 parent 945bddc commit ebf0a93

File tree

6 files changed

+95
-4
lines changed

6 files changed

+95
-4
lines changed

Sources/Segment/Analytics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ public class Analytics {
9393
Self.removeActiveWriteKey(configuration.values.writeKey)
9494
}
9595

96-
internal func process<E: RawEvent>(incomingEvent: E) {
96+
internal func process<E: RawEvent>(incomingEvent: E, enrichments: [EnrichmentClosure]? = nil) {
9797
guard enabled == true else { return }
9898
let event = incomingEvent.applyRawEventData(store: store)
9999

100-
_ = timeline.process(incomingEvent: event)
100+
_ = timeline.process(incomingEvent: event, enrichments: enrichments)
101101

102102
let flushPolicies = configuration.values.flushPolicies
103103
for policy in flushPolicies {

Sources/Segment/Errors.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public enum AnalyticsError: Error {
2727
case jsonUnknown(Error)
2828

2929
case pluginError(Error)
30+
31+
case enrichmentError(String)
3032
}
3133

3234
extension Analytics {

Sources/Segment/Events.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,57 @@ extension Analytics {
224224
process(incomingEvent: event)
225225
}
226226
}
227+
228+
// MARK: - Enrichment event signatures
229+
230+
extension Analytics {
231+
// Tracks an event performed by a user, including some additional event properties.
232+
/// - Parameters:
233+
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
234+
/// - properties: Properties specific to the named event. For example, an event with
235+
/// the name 'Purchased a Shirt' might have properties like revenue or size.
236+
/// - enrichments: Enrichments to be applied to this specific event only, or `nil` for none.
237+
public func track<P: Codable>(name: String, properties: P?, enrichments: [EnrichmentClosure]?) {
238+
do {
239+
if let properties = properties {
240+
let jsonProperties = try JSON(with: properties)
241+
let event = TrackEvent(event: name, properties: jsonProperties)
242+
process(incomingEvent: event, enrichments: enrichments)
243+
} else {
244+
let event = TrackEvent(event: name, properties: nil)
245+
process(incomingEvent: event, enrichments: enrichments)
246+
}
247+
} catch {
248+
reportInternalError(error, fatal: true)
249+
}
250+
}
251+
252+
/// Tracks an event performed by a user.
253+
/// - Parameters:
254+
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
255+
/// - enrichments: Enrichments to be applied to this specific event only, or `nil` for none.
256+
public func track(name: String, enrichments: [EnrichmentClosure]?) {
257+
track(name: name, properties: nil as TrackEvent?, enrichments: enrichments)
258+
}
259+
260+
/// Tracks an event performed by a user, including some additional event properties.
261+
/// - Parameters:
262+
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
263+
/// - properties: A dictionary or properties specific to the named event.
264+
/// For example, an event with the name 'Purchased a Shirt' might have properties
265+
/// like revenue or size.
266+
/// - enrichments: Enrichments to be applied to this specific event only, or `nil` for none.
267+
public func track(name: String, properties: [String: Any]?, enrichments: [EnrichmentClosure]?) {
268+
var props: JSON? = nil
269+
if let properties = properties {
270+
do {
271+
props = try JSON(properties)
272+
} catch {
273+
reportInternalError(error, fatal: true)
274+
}
275+
}
276+
let event = TrackEvent(event: name, properties: props)
277+
process(incomingEvent: event, enrichments: enrichments)
278+
}
279+
280+
}

Sources/Segment/Plugins/Context.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,11 @@ public class Context: PlatformPlugin {
160160
// other stuff?? ...
161161
}
162162

163+
public static func insertOrigin(event: RawEvent?, data: [String: Any]) -> RawEvent? {
164+
guard var working = event else { return event }
165+
if let newContext = try? working.context?.add(value: data, forKey: "__eventOrigin") {
166+
working.context = newContext
167+
}
168+
return working
169+
}
163170
}

Sources/Segment/Timeline.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,21 @@ public class Timeline {
2525
}
2626

2727
@discardableResult
28-
internal func process<E: RawEvent>(incomingEvent: E) -> E? {
28+
internal func process<E: RawEvent>(incomingEvent: E, enrichments: [EnrichmentClosure]? = nil) -> E? {
2929
// apply .before and .enrichment types first ...
3030
let beforeResult = applyPlugins(type: .before, event: incomingEvent)
3131
// .enrichment here is akin to source middleware in the old analytics-ios.
32-
let enrichmentResult = applyPlugins(type: .enrichment, event: beforeResult)
32+
var enrichmentResult = applyPlugins(type: .enrichment, event: beforeResult)
33+
34+
if let enrichments {
35+
for closure in enrichments {
36+
if let result = closure(enrichmentResult) as? E {
37+
enrichmentResult = result
38+
} else {
39+
Analytics.reportInternalError(AnalyticsError.enrichmentError("The given enrichment attempted to change the event type!"))
40+
}
41+
}
42+
}
3343

3444
// once the event enters a destination, we don't want
3545
// to know about changes that happen there. those changes

Tests/Segment-Tests/Analytics_Tests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,24 @@ final class Analytics_Tests: XCTestCase {
958958
XCTAssertEqual(outputReader.lastEvent?.anonymousId, "blah-111")
959959
XCTAssertEqual(anonIdGenerator.currentId, "blah-111")
960960
XCTAssertEqual(outputReader.lastEvent?.anonymousId, anonIdGenerator.currentId)
961+
}
962+
963+
func testSingularEnrichment() throws {
964+
let analytics = Analytics(configuration: Configuration(writeKey: "test"))
965+
let outputReader = OutputReaderPlugin()
966+
analytics.add(plugin: outputReader)
967+
968+
waitUntilStarted(analytics: analytics)
961969

970+
let addEventOrigin: EnrichmentClosure = { event in
971+
return Context.insertOrigin(event: event, data: [
972+
"type": "mobile"
973+
])
974+
}
975+
976+
analytics.track(name: "enrichment check", enrichments: [addEventOrigin])
977+
978+
let trackEvent: TrackEvent? = outputReader.lastEvent as? TrackEvent
979+
XCTAssertEqual(trackEvent?.context?.value(forKeyPath: "__eventOrigin.type"), "mobile")
962980
}
963981
}

0 commit comments

Comments
 (0)