A small analytics facade for Swift: one track/identify API, fanned out to as many pluggable
backends as you register. No third-party dependencies, works on iOS and macOS.
Licensed under MIT.
It ships two library targets:
- AnalyticsKit —
AnalyticsTracker, theAnalyticsProviderprotocol, and three ready-made providers:ConsoleAnalyticsProvider,NoOpAnalyticsProvider,CompositeAnalyticsProvider. - AnalyticsKitMock —
SpyAnalyticsProvider, a test double that records every call it receives.
.package(url: "https://github.com/PedroAugusto01/AnalyticsKit", from: "1.0.0").product(name: "AnalyticsKit", package: "AnalyticsKit"),
.product(name: "AnalyticsKitMock", package: "AnalyticsKit"), // test targets onlyAnalyticsTracker holds a list of AnalyticsProviders and forwards every call to all of them:
let tracker = AnalyticsTracker(providers: [
ConsoleAnalyticsProvider(),
MyVendorAnalyticsProvider(apiKey: "..."),
])
tracker.identify(userID: "user-123", traits: ["plan": "pro"])
tracker.track("screen_view", parameters: ["screen": "home"])Call sites depend on AnalyticsTracker (or, for code that only needs one leg of it, directly
on AnalyticsProvider), never on a specific vendor SDK.
struct MyVendorAnalyticsProvider: AnalyticsProvider {
func track(_ event: AnalyticsEvent) {
MyVendorSDK.log(event.name, event.parameters)
}
func identify(userID: String, traits: [String: String]) {
MyVendorSDK.setUser(userID, traits)
}
}ConsoleAnalyticsProvider— prints every call (printby default; pass asinkclosure to redirect it, e.g. toos_log).NoOpAnalyticsProvider— discards every call. Useful as a default in previews/tests, so call sites never need to check fornil.CompositeAnalyticsProvider— forwards to a fixed list of providers, when something expects a singleAnalyticsProviderbut you want it to reach several backends.
import AnalyticsKit
import AnalyticsKitMock
func testTracksCheckoutButtonTap() {
let spy = SpyAnalyticsProvider()
let tracker = AnalyticsTracker(providers: [spy])
let viewModel = CheckoutViewModel(tracker: tracker)
viewModel.didTapCheckout()
XCTAssertEqual(spy.trackedEvents.map(\.name), ["checkout_tapped"])
}CheckoutViewModel only needs to depend on AnalyticsTracker, never on a specific provider —
that's what makes the swap possible.
swift run AnalyticsKitDemoswift test