Skip to content

Commit

Permalink
feat: Enhancements to Plugin interface (#218)
Browse files Browse the repository at this point in the history
  • Loading branch information
crleona authored Aug 24, 2024
1 parent 5108d28 commit 254e03a
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 11 deletions.
8 changes: 8 additions & 0 deletions Sources/Amplitude/Amplitude.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ public class Amplitude {
_ = add(plugin: AnalyticsConnectorPlugin())
_ = add(plugin: AnalyticsConnectorIdentityPlugin())
_ = add(plugin: AmplitudeDestinationPlugin())

// Monitor changes to optOut to send to Timeline
configuration.optOutChanged = { [weak self] optOut in
self?.timeline.onOptOutChanged(optOut)
}
}

convenience init(apiKey: String, configuration: Configuration) {
Expand Down Expand Up @@ -262,13 +267,15 @@ public class Amplitude {
public func setUserId(userId: String?) -> Amplitude {
try? storage.write(key: .USER_ID, value: userId)
state.userId = userId
timeline.onUserIdChanged(userId)
return self
}

@discardableResult
public func setDeviceId(deviceId: String?) -> Amplitude {
try? storage.write(key: .DEVICE_ID, value: deviceId)
state.deviceId = deviceId
timeline.onDeviceIdChanged(deviceId)
return self
}

Expand All @@ -293,6 +300,7 @@ public class Amplitude {
} else {
sessionEvents = self.sessions.endCurrentSession()
}
self.timeline.onSessionIdChanged(sessionId)
self.sessions.assignEventId(events: sessionEvents).forEach { e in
self.timeline.processEvent(event: e)
}
Expand Down
7 changes: 6 additions & 1 deletion Sources/Amplitude/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ public class Configuration {
public var flushQueueSize: Int
public var flushIntervalMillis: Int
public internal(set) var instanceName: String
public var optOut: Bool
public var optOut: Bool {
didSet {
optOutChanged?(optOut)
}
}
public let storageProvider: any Storage
public let identifyStorageProvider: any Storage
public var logLevel: LogLevelEnum
Expand Down Expand Up @@ -44,6 +48,7 @@ public class Configuration {
public internal(set) var autocapture: AutocaptureOptions
public var offline: Bool?
internal let diagonostics: Diagnostics
var optOutChanged: ((Bool) -> Void)?

@available(*, deprecated, message: "Please use the `autocapture` parameter instead.")
public convenience init(
Expand Down
8 changes: 5 additions & 3 deletions Sources/Amplitude/Plugins/BasePlugins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ open class BasePlugin {
public func teardown(){
// Clean up any resources from setup if necessary
}

open func onUserIdChanged(_ userId: String?) {}
open func onDeviceIdChanged(_ deviceId: String?) {}
open func onSessionIdChanged(_ sessionId: Int64) {}
open func onOptOutChanged(_ optOut: Bool) {}
}

open class BeforePlugin: BasePlugin, Plugin {
Expand All @@ -33,7 +38,4 @@ open class UtilityPlugin: BasePlugin, Plugin {

open class ObservePlugin: BasePlugin, Plugin {
public let type: PluginType = .observe

open func onUserIdChanged(_ userId: String?) {}
open func onDeviceIdChanged(_ deviceId: String?) {}
}
23 changes: 16 additions & 7 deletions Sources/Amplitude/Timeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,6 @@ public class Timeline {
}
}

internal func applyClosure(_ closure: (Plugin) -> Void) {
for plugin in plugins {
let mediator = plugin.value
mediator.applyClosure(closure)
}
}

internal func apply(_ closure: (Plugin) -> Void) {
for type in PluginType.allCases {
if let mediator = plugins[type] {
Expand All @@ -66,4 +59,20 @@ public class Timeline {
}
}
}

func onUserIdChanged(_ userId: String?) {
apply { $0.onUserIdChanged(userId) }
}

func onDeviceIdChanged(_ deviceId: String?) {
apply { $0.onDeviceIdChanged(deviceId) }
}

func onSessionIdChanged(_ sessionId: Int64) {
apply { $0.onSessionIdChanged(sessionId) }
}

func onOptOutChanged(_ optOut: Bool) {
apply { $0.onOptOutChanged(optOut) }
}
}
9 changes: 9 additions & 0 deletions Sources/Amplitude/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ public protocol Plugin: AnyObject {
func setup(amplitude: Amplitude)
func execute(event: BaseEvent) -> BaseEvent?
func teardown()
func onUserIdChanged(_ userId: String?)
func onDeviceIdChanged(_ deviceId: String?)
func onSessionIdChanged(_ sessionId: Int64)
func onOptOutChanged(_ optOut: Bool)
}

public protocol EventPlugin: Plugin {
Expand All @@ -138,6 +142,11 @@ extension Plugin {
public func teardown(){
// Clean up any resources from setup if necessary
}

func onUserIdChanged(_ userId: String?) {}
func onDeviceIdChanged(_ deviceId: String?) {}
func onSessionIdChanged(_ sessionId: Int64) {}
func onOptOutChanged(_ optOut: Bool) {}
}

public protocol ResponseHandler {
Expand Down
72 changes: 72 additions & 0 deletions Tests/AmplitudeTests/AmplitudeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,78 @@ final class AmplitudeTests: XCTestCase {
XCTAssertEqual(events.count, 1)
}

func testPluginChangeNotifications() {
class TestPlugin: Plugin {
let type: PluginType = .enrichment

var deviceIdChanged: ((String?) -> Void)?
var sessionIdChanged: ((Int64?) -> Void)?
var userIdChanged: ((String?) -> Void)?
var optOutChanged: ((Bool) -> Void)?

func onDeviceIdChanged(_ deviceId: String?) {
deviceIdChanged?(deviceId)
}

func onSessionIdChanged(_ sessionId: Int64) {
sessionIdChanged?(sessionId)
}

func onUserIdChanged(_ userId: String?) {
userIdChanged?(userId)
}

func onOptOutChanged(_ optOut: Bool) {
optOutChanged?(optOut)
}
}

let testPlugin = TestPlugin()
let amplitude = Amplitude(configuration: Configuration(apiKey: "testPluginChangeNotifications",
flushIntervalMillis: 1000000,
optOut: false,
storageProvider: FakeInMemoryStorage()))
amplitude.add(plugin: testPlugin)
amplitude.waitForTrackingQueue()

let expectedDeviceId = "test_device_id"
let deviceIdExpectation = expectation(description: "Should receive deviceId changes")
testPlugin.deviceIdChanged = { deviceId in
XCTAssertEqual(deviceId, expectedDeviceId)
XCTAssertEqual(amplitude.getDeviceId(), expectedDeviceId)
deviceIdExpectation.fulfill()
}
amplitude.setDeviceId(deviceId: expectedDeviceId)

let expectedSessionId = Int64(Date().timeIntervalSince1970 * 1000)
let sessionIdExpectation = expectation(description: "Should receive sessionId changes")
testPlugin.sessionIdChanged = { sessionId in
XCTAssertEqual(sessionId, expectedSessionId)
XCTAssertEqual(amplitude.getSessionId(), expectedSessionId)
sessionIdExpectation.fulfill()
}
amplitude.setSessionId(timestamp: expectedSessionId)

let expectedUserId = "test_user_id"
let userIdExpectation = expectation(description: "Should receive userId changes")
testPlugin.userIdChanged = { userId in
XCTAssertEqual(userId, expectedUserId)
XCTAssertEqual(amplitude.getUserId(), expectedUserId)
userIdExpectation.fulfill()
}
amplitude.setUserId(userId: expectedUserId)

let optOutExpectation = expectation(description: "Should receive optOut changes")
testPlugin.optOutChanged = { optOut in
XCTAssertTrue(optOut)
XCTAssertTrue(amplitude.configuration.optOut)
optOutExpectation.fulfill()
}
amplitude.configuration.optOut = true

waitForExpectations(timeout: 10)
}

func testContextWithDisableTrackingOptions() {
let apiKey = "testApiKeyForDisableTrackingOptions"
let trackingOptions = TrackingOptions()
Expand Down

0 comments on commit 254e03a

Please sign in to comment.