diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index 016b5d6d..10d06afd 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -26,6 +26,34 @@ public class Analytics { public var timeline: Timeline + static internal let deadInstance = "DEADINSTANCE" + static internal weak var firstInstance: Analytics? = nil + + /** + This method isn't a traditional singleton implementation. It's provided here + to ease migration from analytics-ios to analytics-swift. Rather than return a + singleton, it returns the first instance of Analytics created, OR an instance + who's writekey is "DEADINSTANCE". + + In the case of a dead instance, an assert will be thrown when in DEBUG builds to + assist developers in knowning that `shared()` is being called too soon. + */ + static func shared() -> Analytics { + if let a = firstInstance { + if a.isDead == false { + return a + } + } + + #if DEBUG + if isUnitTesting == false { + assert(true == false, "An instance of Analytice does not exist!") + } + #endif + + return Analytics(configuration: Configuration(writeKey: deadInstance)) + } + /// Initialize this instance of Analytics with a given configuration setup. /// - Parameters: /// - configuration: The configuration to use @@ -40,6 +68,8 @@ public class Analytics { storage.analytics = self + checkSharedInstance() + // Get everything running platformStartup() } @@ -142,8 +172,8 @@ extension Analytics { } } + /// Returns a list of currently active flush policies. public var flushPolicies: [FlushPolicy] { - get { configuration.values.flushPolicies } @@ -332,3 +362,25 @@ extension Analytics { track(name: "Deep Link Opened", properties: jsonProperties) } } + +// MARK: Private Stuff + +extension Analytics { + private func checkSharedInstance() { + // is firstInstance a dead one? If so, override it. + if let firstInstance = Self.firstInstance { + if firstInstance.isDead { + Self.firstInstance = self + } + } + // is firstInstance nil? If so, set it. + if Self.firstInstance == nil { + Self.firstInstance = self + } + } + + /// Determines if an instance is dead. + internal var isDead: Bool { + return configuration.values.writeKey == Self.deadInstance + } +} diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index 81d3d473..5ea8e05f 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -620,4 +620,25 @@ final class Analytics_Tests: XCTestCase { XCTAssertTrue(destHit) } + + func testSharedInstance() { + Analytics.firstInstance = nil + + let dead = Analytics.shared() + XCTAssertTrue(dead.isDead) + + let alive = Analytics(configuration: Configuration(writeKey: "1234")) + XCTAssertFalse(alive.isDead) + + let shared = Analytics.shared() + XCTAssertFalse(shared.isDead) + + XCTAssertTrue(alive === shared) + + let alive2 = Analytics(configuration: Configuration(writeKey: "ABCD")) + let shared2 = Analytics.shared() + XCTAssertFalse(alive2 === shared2) + XCTAssertTrue(shared2 === shared) + + } }