Skip to content

Commit

Permalink
Multiple instance write key (#294)
Browse files Browse the repository at this point in the history
* add check for same write key in multiple instances

* Active writekey fixes

---------

Co-authored-by: Brandon Sneed <[email protected]>
  • Loading branch information
alanjcharles and bsneed authored Feb 12, 2024
1 parent 51f56b9 commit 3c49330
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 11 deletions.
28 changes: 27 additions & 1 deletion Sources/Segment/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class Analytics {

static internal let deadInstance = "DEADINSTANCE"
static internal weak var firstInstance: Analytics? = nil
@Atomic static internal var activeWriteKeys = [String]()

/**
This method isn't a traditional singleton implementation. It's provided here
Expand Down Expand Up @@ -58,6 +59,12 @@ public class Analytics {
/// - Parameters:
/// - configuration: The configuration to use
public init(configuration: Configuration) {
if Self.isActiveWriteKey(configuration.values.writeKey) {
fatalError("Cannot initialize multiple instances of Analytics with the same write key")
} else {
Self.addActiveWriteKey(configuration.values.writeKey)
}

store = Store()
storage = Storage(store: self.store, writeKey: configuration.values.writeKey)
timeline = Timeline()
Expand All @@ -74,6 +81,10 @@ public class Analytics {
platformStartup()
}

deinit {
Self.removeActiveWriteKey(configuration.values.writeKey)
}

internal func process<E: RawEvent>(incomingEvent: E) {
guard enabled == true else { return }
let event = incomingEvent.applyRawEventData(store: store)
Expand Down Expand Up @@ -428,11 +439,26 @@ extension Analytics {
Self.firstInstance = self
}
}

/// Determines if an instance is dead.
internal var isDead: Bool {
return configuration.values.writeKey == Self.deadInstance
}

/// Manage active writekeys. It's wrapped in @atomic
internal static func isActiveWriteKey(_ writeKey: String) -> Bool {
Self.activeWriteKeys.contains(writeKey)
}

internal static func addActiveWriteKey(_ writeKey: String) {
Self.activeWriteKeys.append(writeKey)
}

internal static func removeActiveWriteKey(_ writeKey: String) {
Self.activeWriteKeys.removeAll { key in
writeKey == key
}
}
}

// MARK: Operating mode based scheduling
Expand Down
4 changes: 2 additions & 2 deletions Sources/Segment/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class Configuration {
internal var values: Values

/// Initialize a configuration object to pass along to an Analytics instance.
///
///
/// - Parameter writeKey: Your Segment write key value
public init(writeKey: String) {
self.values = Values(writeKey: writeKey)
Expand Down Expand Up @@ -127,7 +127,7 @@ public extension Configuration {
/// let config = Configuration(writeKey: "1234").defaultSettings(defaults)
/// ```
///
/// - Parameter settings:
/// - Parameter settings:
/// - Returns: The current Configuration.
@discardableResult
func defaultSettings(_ settings: Settings?) -> Configuration {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Segment/ObjC/ObjCPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ObjCSegmentMixpanel: NSObject, ObjCPlugin, ObjCPluginShim {
@objc(SEGEventPlugin)
public class ObjCEventPlugin: NSObject, EventPlugin, ObjCPlugin {
public var type: PluginType = .enrichment
public var analytics: Analytics? = nil
public weak var analytics: Analytics? = nil

@objc(executeEvent:)
public func execute(event: ObjCRawEvent?) -> ObjCRawEvent? {
Expand Down
9 changes: 8 additions & 1 deletion Tests/Segment-Tests/Analytics_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ final class Analytics_Tests: XCTestCase {

let traits = MyTraits(email: "[email protected]")
analytics.identify(userId: "brandon", traits: traits)

waitUntilStarted(analytics: analytics)
checkIfLeaked(analytics)
}

func testPluginConfigure() {
Expand All @@ -28,6 +31,8 @@ final class Analytics_Tests: XCTestCase {
XCTAssertNotNil(ziggy.analytics)
XCTAssertNotNil(myDestination.analytics)
XCTAssertNotNil(goober.analytics)

waitUntilStarted(analytics: analytics)
}

func testPluginRemove() {
Expand Down Expand Up @@ -91,6 +96,7 @@ final class Analytics_Tests: XCTestCase {
XCTAssertEqual(ziggy1.receivedInitialUpdate, 1)
XCTAssertEqual(ziggy2.receivedInitialUpdate, 1)

checkIfLeaked(analytics)
}


Expand Down Expand Up @@ -160,6 +166,7 @@ final class Analytics_Tests: XCTestCase {

XCTAssertTrue(anonId != "")
XCTAssertTrue(anonId.count == 36) // it's a UUID y0.
waitUntilStarted(analytics: analytics)
}

func testContext() {
Expand Down Expand Up @@ -560,7 +567,7 @@ final class Analytics_Tests: XCTestCase {
var timeline: Timeline
let type: PluginType
let key: String
var analytics: Analytics?
weak var analytics: Analytics?

init(key: String) {
self.key = key
Expand Down
8 changes: 6 additions & 2 deletions Tests/Segment-Tests/Storage_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class StorageTests: XCTestCase {

let analytics = Analytics(configuration: Configuration(writeKey: "test"))
analytics.storage.hardReset(doYouKnowHowToUseThis: true)

analytics.waitUntilStarted()
// this will crash if it fails.
let j = try! JSON(jsonSettings)
analytics.storage.write(.settings, value: j)
Expand All @@ -70,7 +70,7 @@ class StorageTests: XCTestCase {

func testBasicWriting() throws {
let analytics = Analytics(configuration: Configuration(writeKey: "test"))

analytics.waitUntilStarted()
analytics.identify(userId: "brandon", traits: MyTraits(email: "[email protected]"))

let userInfo: UserInfo? = analytics.store.currentState()
Expand All @@ -91,6 +91,8 @@ class StorageTests: XCTestCase {
let analytics = Analytics(configuration: Configuration(writeKey: "test"))
analytics.storage.hardReset(doYouKnowHowToUseThis: true)

analytics.waitUntilStarted()

var event = IdentifyEvent(userId: "brandon1", traits: try! JSON(with: MyTraits(email: "[email protected]")))
analytics.storage.write(.events, value: event)

Expand Down Expand Up @@ -134,6 +136,8 @@ class StorageTests: XCTestCase {
let analytics = Analytics(configuration: Configuration(writeKey: "test"))
analytics.storage.hardReset(doYouKnowHowToUseThis: true)

analytics.waitUntilStarted()

var event = IdentifyEvent(userId: "brandon1", traits: try! JSON(with: MyTraits(email: "[email protected]")))
analytics.storage.write(.events, value: event)

Expand Down
8 changes: 4 additions & 4 deletions Tests/Segment-Tests/Support/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct MyTraits: Codable {

class GooberPlugin: EventPlugin {
let type: PluginType
var analytics: Analytics?
weak var analytics: Analytics?

init() {
self.type = .enrichment
Expand All @@ -41,7 +41,7 @@ class GooberPlugin: EventPlugin {

class ZiggyPlugin: EventPlugin {
let type: PluginType
var analytics: Analytics?
weak var analytics: Analytics?
var receivedInitialUpdate: Int = 0

var completion: (() -> Void)?
Expand Down Expand Up @@ -79,7 +79,7 @@ class MyDestination: DestinationPlugin {
var timeline: Timeline
let type: PluginType
let key: String
var analytics: Analytics?
weak var analytics: Analytics?
let trackCompletion: (() -> Bool)?

let disabled: Bool
Expand Down Expand Up @@ -114,7 +114,7 @@ class MyDestination: DestinationPlugin {

class OutputReaderPlugin: Plugin {
let type: PluginType
var analytics: Analytics?
weak var analytics: Analytics?

var events = [RawEvent]()
var lastEvent: RawEvent? = nil
Expand Down

0 comments on commit 3c49330

Please sign in to comment.