Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple instance write key #294

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading