Skip to content

Commit

Permalink
[Issue-7] Add Event Factory
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Pospesel authored and mpospese committed Mar 9, 2023
1 parent 5760d37 commit 919df0a
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Sources/YAnalytics/Protocol/AnalyticsEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,19 @@ public protocol AnalyticsEngine {
/// Track an analytics event
/// - Parameter event: the event to log
func track(event: AnalyticsEvent)

/// Tracks an analytics event
/// - Parameter factory: object that generates the event to log
func track(event factory: AnalyticsEventFactory)
}

/// Default implementation of `track(factory:)`
extension AnalyticsEngine {
/// Tracks an analytics event.
///
/// Extracts the event from the factory and tracks that.
/// - Parameter factory: object that generates the event to log
public func track(event factory: AnalyticsEventFactory) {
track(event: factory.event)
}
}
18 changes: 18 additions & 0 deletions Sources/YAnalytics/Protocol/AnalyticsEventFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// AnalyticsEventFactory.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import Foundation

/// Anything that generates an analytics event.
///
/// Three protocols, `ScreenViewFactory`, `UserPropertyFactory`, and `EventFactory`, conform to `AnalyticsEventFactory`;
/// one for each of the three cases in the `AnalyticsEvent` enum.
public protocol AnalyticsEventFactory {
/// The event
var event: AnalyticsEvent { get }
}
25 changes: 25 additions & 0 deletions Sources/YAnalytics/Protocol/EventFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// EventFactory.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import Foundation

/// Anything that generates an analytics event of type `AnalyticsEvent.event`
public protocol EventFactory: AnalyticsEventFactory {
/// Event name
var name: String { get }
/// Event parameters
var parameters: Metadata? { get }
}

/// Default implementation of `event` property
public extension EventFactory {
/// Generates an event (of type `.event`) from `name` and `parameters`
var event: AnalyticsEvent {
.event(name: name, parameters: parameters)
}
}
29 changes: 29 additions & 0 deletions Sources/YAnalytics/Protocol/ScreenViewFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// ScreenViewFactory.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import Foundation

/// Anything that generates an analytics event of type `AnalyticsEvent.screenView`
public protocol ScreenViewFactory: AnalyticsEventFactory {
/// Screen name
var screenName: String { get }
}

/// Default implementation of `event` property
public extension ScreenViewFactory {
/// Generates an event (of type `.screenView`) from `screenName`
var event: AnalyticsEvent {
.screenView(screenName: screenName)
}
}

/// RawRepresentable (e.g. all string-based enums) conforms to ScreenViewFactory by simply returning its raw value
extension RawRepresentable where RawValue == String {
/// URL path string
public var screenName: String { rawValue }
}
25 changes: 25 additions & 0 deletions Sources/YAnalytics/Protocol/UserPropertyFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// UserPropertyFactory.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import Foundation

/// Anything that generates an analytics event of type `AnalyticsEvent.userProperty`
public protocol UserPropertyFactory: AnalyticsEventFactory {
/// Property name
var name: String { get }
/// Property value
var value: String { get }
}

/// Default implementation of `event` property
public extension UserPropertyFactory {
/// Generates an event (of type `.userProperty`) from `name` and `value`
var event: AnalyticsEvent {
.userProperty(name: name, value: value)
}
}
34 changes: 34 additions & 0 deletions Tests/YAnalyticsTests/Protocols/AnalyticsEngineTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// AnalyticsEngineTests.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import XCTest
@testable import YAnalytics

final class AnalyticsEngineTests: XCTestCase {
func test_trackFactory_tracksEvents() {
// Given
let engine = MockAnalyticsEngine()

// When
MockEvent.allCases.forEach {
engine.track(event: $0)
}

// Then
XCTAssertEqual(engine.allEvents.count, 3)
XCTAssertEqual(engine.screenViews.joined(), "abcdefxyz")
}
}

private extension AnalyticsEngineTests {
enum MockEvent: String, CaseIterable, ScreenViewFactory {
case abc
case def
case xyz
}
}
41 changes: 41 additions & 0 deletions Tests/YAnalyticsTests/Protocols/EventFactoryTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// EventFactoryTests.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import XCTest
@testable import YAnalytics

final class EventFactoryTests: XCTestCase {
func test_event_deliversEvent() throws {
try MockEvent.allCases.forEach {
switch $0.event {
case .event(let name, let parameters):
XCTAssertEqual(name, $0.name)
let parameters = try XCTUnwrap(parameters as? [String: String])
let expected = try XCTUnwrap($0.parameters as? [String: String])
XCTAssertEqual(parameters, expected)
default:
XCTFail("Unexpected event type: \($0.event)")
}
}
}
}

private extension EventFactoryTests {
enum MockEvent: String, EventFactory, CaseIterable {
case abc
case def

var name: String { rawValue }
var parameters: Metadata? {
[
"value": "42\(rawValue.uppercased())",
"type": "dictionary"
]
}
}
}
31 changes: 31 additions & 0 deletions Tests/YAnalyticsTests/Protocols/ScreenViewFactoryTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// ScreenViewFactoryTests.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import XCTest
@testable import YAnalytics

final class ScreenViewFactoryTests: XCTestCase {
func test_event_deliversEvent() {
MockScreenView.allCases.forEach {
switch $0.event {
case .screenView(let screenName):
XCTAssertEqual(screenName, $0.screenName)
XCTAssertEqual(screenName, $0.rawValue)
default:
XCTFail("Unexpected event type: \($0.event)")
}
}
}
}

private extension ScreenViewFactoryTests {
enum MockScreenView: String, ScreenViewFactory, CaseIterable {
case abc
case def
}
}
35 changes: 35 additions & 0 deletions Tests/YAnalyticsTests/Protocols/UserPropertyFactoryTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// UserPropertyFactoryTests.swift
// YAnalytics
//
// Created by Mark Pospesel on 3/8/23.
// Copyright © 2023 Y Media Labs. All rights reserved.
//

import XCTest
@testable import YAnalytics

final class UserPropertyFactoryTests: XCTestCase {
func test_event_deliversEvent() {
MockUserProperty.allCases.forEach {
switch $0.event {
case .userProperty(let name, let value):
XCTAssertEqual(name, $0.name)
XCTAssertEqual(value, $0.value)
XCTAssertEqual(value, $0.name.capitalized)
default:
XCTFail("Unexpected event type: \($0.event)")
}
}
}
}

private extension UserPropertyFactoryTests {
enum MockUserProperty: String, UserPropertyFactory, CaseIterable {
case abc
case def

var name: String { rawValue }
var value: String { rawValue.capitalized }
}
}

0 comments on commit 919df0a

Please sign in to comment.