diff --git a/ArchDiagram.png b/ArchDiagram.png index 9a1794e..4e22f02 100644 Binary files a/ArchDiagram.png and b/ArchDiagram.png differ diff --git a/DLAnalytics/Impl/AnalyticsManager.swift b/DLAnalytics/Impl/AnalyticsManager.swift index 296748f..d098e88 100644 --- a/DLAnalytics/Impl/AnalyticsManager.swift +++ b/DLAnalytics/Impl/AnalyticsManager.swift @@ -38,6 +38,7 @@ final class AnalyticsManager { // MARK: - CombineEvent public struct CombineEvent: AnalyticsEvent { + public private(set) var type: String public var name: String public var payload: [String: Any] } @@ -79,10 +80,36 @@ extension AnalyticsManager: AnalyticsService { } services.forEach { - var combinedPayload = userProperty - combinedPayload.merge(dict: event.payload) - let combinedEvent = CombineEvent(name: event.name, payload: combinedPayload) - $0.send(event: combinedEvent) + if $0.allowEvents.contains(event.type) { + let combineEvent = buildCombineEvent(event) + $0.send(event: combineEvent) + } } } + + /// Reset all data related to the user e.g user logout + func send(event: AnalyticsEvent, from viewController: ViewController) { + var services = [AnalyticsService]() + readWriteLock.read { + services = self.analyticsServices + } + + services.forEach { + if $0.allowEvents.isEmpty || $0.allowEvents.contains(event.type) { + let combineEvent = buildCombineEvent(event) + $0.send(event: combineEvent, from: viewController) + } + } + } + + /// Combine event form share event with the current event + private func buildCombineEvent(_ event: AnalyticsEvent) -> CombineEvent { + var combinedPayload = userProperty + combinedPayload.merge(dict: event.payload) + return CombineEvent( + type: event.type, + name: event.name, + payload: combinedPayload + ) + } } diff --git a/DLAnalytics/Model/Analytics.swift b/DLAnalytics/Model/Analytics.swift index 2201825..64e6b06 100644 --- a/DLAnalytics/Model/Analytics.swift +++ b/DLAnalytics/Model/Analytics.swift @@ -33,4 +33,9 @@ public enum Analytics { public static func reset() { AnalyticsManager.sharedInstance.reset() } + + /// Support some cases want to track from or present some screens inside specific consumer (Analytics services) + public static func send(event: AnalyticsEvent, from viewController: ViewController) { + AnalyticsManager.sharedInstance.send(event: event, from: viewController) + } } diff --git a/DLAnalytics/Protocol/AnalyticsEvent.swift b/DLAnalytics/Protocol/AnalyticsEvent.swift index 2ab3f5d..ecef7ef 100644 --- a/DLAnalytics/Protocol/AnalyticsEvent.swift +++ b/DLAnalytics/Protocol/AnalyticsEvent.swift @@ -9,6 +9,11 @@ import Foundation public protocol AnalyticsEvent { + var type: String { get } var name: String { get } var payload: [String: Any] { get } } + +public extension AnalyticsEvent { + var type: String { "\(Self.self)" } +} diff --git a/DLAnalytics/Protocol/AnalyticsService.swift b/DLAnalytics/Protocol/AnalyticsService.swift index 578ce45..4cd70f3 100644 --- a/DLAnalytics/Protocol/AnalyticsService.swift +++ b/DLAnalytics/Protocol/AnalyticsService.swift @@ -7,8 +7,18 @@ // import Foundation +#if os(iOS) +import UIKit +public typealias ViewController = UIViewController +#elseif os(macOS) +import AppKit +public typealias ViewController = NSViewController +#endif public protocol AnalyticsService { + /// Whitelist events handle by the service. Handle all events as default if it does not specify + var allowEvents: Set { get } + /// To support identify the user we need to help set these properties as global properties func setUserIdentifyProperty(_ property: [String: String]) @@ -17,4 +27,13 @@ public protocol AnalyticsService { /// Send an event to Analytics func send(event: AnalyticsEvent) + + /// Send an event to Analytics from a ViewController + func send(event: AnalyticsEvent, from viewController: ViewController) +} + +public extension AnalyticsService { + var allowEvents: Set { Set() } + + func send(event: AnalyticsEvent, from viewController: ViewController) {} } diff --git a/README.md b/README.md index c2a895b..497897b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Platform](https://img.shields.io/cocoapods/p/DLAnalytics.svg?style=flat)](http://cocoapods.org/pods/DLAnalytics) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -An abstract Analytics Framework supports: +Abstract Analytics Framework supports: - Unify Analytics. - Modularize, Centralize Analytics. @@ -26,18 +26,27 @@ An abstract Analytics Framework supports: ``` class ClientAnalyticsImpl: AnalyticsService { + // Specify whitelist events. Accept all events by default + var allowEvents: Set { + Set(arrayLiteral: "\(InputOTPEvent.self)", "\(CheckoutEvent.self)") + } + func setUserIdentifyProperty(_ property: [String : String]) { - print("setUserIdentifyProperty: To support identify the user") + // To support identify the user" } func reset() { - print("reset: To reset all data related to the user e.g user logout") + // reset all data related to the user e.g user logout" } func send(event: AnalyticsEvent) { - // Here is the specific Analytics implementation e.g FireBaseAnalytics, MixPanel, etc. + // Specific Analytics implementation e.g FireBaseAnalytics, MixPanel, etc. print("### Send an event name: \(event.name), payload = \(event.payload)") } + + func send(event: AnalyticsEvent, from viewController: DLAnalytics.ViewController) { + print("### Send an event name: \(event.name), controller = \(ViewController.self)" ) + } } ``` @@ -85,11 +94,11 @@ Analytics.registerAnalyticsService(analyticsService) ``` /// Simulate tracking event InputOTP success Analytics.send(event: InputOTPEvent.inputOTPSuccess()) -Analytics.send(event: CheckoutEvent.success) +Analytics.send(event: CheckoutEvent.success, from viewController: checkoutVC) /// Output: Send an event name: InputOTP, payload = ["OTPValid": "1"] -Send an event name: Checkout_Success, payload = [:] +Send an event name: Checkout_Success, controller = CheckoutViewController ``` ## Installation