diff --git a/README.md b/README.md index 3a85704..7e4aff7 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,23 @@ Calls to `track`, `identify`, etc. are **buffered in-memory** by the proxy and r - On fatal config errors (`401/403/404`), the client enters **disabled** state and drops subsequent calls. - `sentAt` is stamped when the batch is prepared for transmission (just before network send). If you need the original occurrence time, pass your own `timestamp` on each event. +### MetaRouter.Analytics.shared + +Property-style accessor for the live proxy, matching Apple SDK convention (`URLSession.shared`, `UserDefaults.standard`, etc.). Returns the same proxy that `initialize(with:)` returns — call it from anywhere in your app once the SDK has been initialized: + +```swift +// Initialize once at app launch +MetaRouter.Analytics.initialize(with: options) + +// Use anywhere — no need to thread the proxy through your code +MetaRouter.Analytics.shared.track("Button Tapped") +MetaRouter.Analytics.shared.identify("user123") +``` + +`.shared` is safe to call before `initialize(with:)` — the proxy buffers calls until binding completes (same FIFO + replay-on-ready semantics described above). Use the proxy returned from `initialize(with:)` if you prefer dependency-injection style; both refer to the same underlying instance. + +> **Note:** `MetaRouter.Analytics.client()` is deprecated as of this release; use `.shared` instead. Existing call sites will continue to work (with a yellow deprecation warning) until the next major version. + ### Analytics Interface The analytics client provides the following methods: diff --git a/Sources/MetaRouter/MetaRouter.swift b/Sources/MetaRouter/MetaRouter.swift index 7203f87..fbdf4cf 100644 --- a/Sources/MetaRouter/MetaRouter.swift +++ b/Sources/MetaRouter/MetaRouter.swift @@ -43,6 +43,14 @@ public enum MetaRouter { } + /// Idiomatic singleton-style accessor matching Apple SDK conventions + /// (`URLSession.shared`, `UserDefaults.standard`, `FileManager.default`). + /// Returns the same buffered proxy `initialize(with:)` returns — calls + /// made before `initialize` are queued and replayed on bind. + public static var shared: AnalyticsInterface { proxy } + + @available(*, deprecated, renamed: "shared", + message: "Use MetaRouter.Analytics.shared. client() will be removed in v2.0.") public static func client() -> AnalyticsInterface { proxy } public static func reset() { diff --git a/Tests/MetaRouterTests/MetaRouterIntegrationTests.swift b/Tests/MetaRouterTests/MetaRouterIntegrationTests.swift index 131bf8b..8a42dde 100644 --- a/Tests/MetaRouterTests/MetaRouterIntegrationTests.swift +++ b/Tests/MetaRouterTests/MetaRouterIntegrationTests.swift @@ -57,7 +57,7 @@ final class MetaRouterIntegrationTests: XCTestCase { let options = TestDataFactory.makeInitOptions() // Get the proxy (before initialization) - let proxy = MetaRouter.Analytics.client() + let proxy = MetaRouter.Analytics.shared // Make calls before initialization (should be queued) proxy.track("queued_event", properties: nil) @@ -83,7 +83,7 @@ final class MetaRouterIntegrationTests: XCTestCase { // Multiple initialize calls should return the same proxy let client1 = MetaRouter.Analytics.initialize(with: options) let client2 = MetaRouter.Analytics.initialize(with: options) - let client3 = MetaRouter.Analytics.client() + let client3 = MetaRouter.Analytics.shared XCTAssertTrue(client1 === client2, "Multiple initialize calls should return same proxy") XCTAssertTrue(client1 === client3, "Client() should return same proxy as initialize") @@ -309,7 +309,7 @@ final class MetaRouterIntegrationTests: XCTestCase { await fulfillment(of: [expectation], timeout: 5.0) // Verify final state is consistent - let client = MetaRouter.Analytics.client() + let client = MetaRouter.Analytics.shared let debugInfo = await client.getDebugInfo() XCTAssertNotNil(debugInfo) } @@ -334,7 +334,7 @@ final class MetaRouterIntegrationTests: XCTestCase { await fulfillment(of: [expectation], timeout: 5.0) // Should be in a valid state after all operations - let finalClient = MetaRouter.Analytics.client() + let finalClient = MetaRouter.Analytics.shared XCTAssertNotNil(finalClient) } @@ -485,7 +485,7 @@ final class MetaRouterIntegrationTests: XCTestCase { func testSetAdvertisingIdWithProxy() async { // Get proxy before initialization - let proxy = MetaRouter.Analytics.client() + let proxy = MetaRouter.Analytics.shared // Set advertising ID before initialization (should be queued) proxy.setAdvertisingId("QUEUED-IDFA") @@ -555,7 +555,7 @@ final class MetaRouterIntegrationTests: XCTestCase { func testClearAdvertisingIdWithProxyIntegration() async { // Get proxy before initialization - let proxy = MetaRouter.Analytics.client() + let proxy = MetaRouter.Analytics.shared // Set advertising ID before initialization (should be queued) proxy.setAdvertisingId("QUEUED-IDFA") diff --git a/Tests/MetaRouterTests/MetaRouterTests.swift b/Tests/MetaRouterTests/MetaRouterTests.swift index 9a16b0a..59dea5d 100644 --- a/Tests/MetaRouterTests/MetaRouterTests.swift +++ b/Tests/MetaRouterTests/MetaRouterTests.swift @@ -40,9 +40,9 @@ final class MetaRouterTests: XCTestCase { func testClientAndProxyAreConnected() { let options = InitOptions(writeKey: "test-key", ingestionHost: "https://test.com") - let initialProxy = MetaRouter.Analytics.client() + let initialProxy = MetaRouter.Analytics.shared let afterInit = MetaRouter.Analytics.initialize(with: options) - let againProxy = MetaRouter.Analytics.client() + let againProxy = MetaRouter.Analytics.shared XCTAssertTrue(initialProxy === afterInit, "Initialize should return same proxy") XCTAssertTrue(afterInit === againProxy, "Client should return same proxy")