Skip to content

[FME-8393] - Events - Metadata #718

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

Merged
merged 17 commits into from
Aug 12, 2025
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
4 changes: 4 additions & 0 deletions Split.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@
59FB7C35220329B900ECC96A /* SplitFactoryBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */; };
59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; };
59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; };
5B0162682E4A9C7A0009D3B7 /* SplitEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0162672E4A9C5D0009D3B7 /* SplitEventsTests.swift */; };
5B48D8172DEA2CED00351925 /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; };
5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; };
5BF52DF72DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; };
Expand Down Expand Up @@ -1557,6 +1558,7 @@
59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitFactoryBuilderTests.swift; sourceTree = "<group>"; };
59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalhostSplitsParser.swift; sourceTree = "<group>"; };
59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = "<group>"; };
5B0162672E4A9C5D0009D3B7 /* SplitEventsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitEventsTests.swift; sourceTree = "<group>"; };
5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = "<group>"; };
5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcher.swift; sourceTree = "<group>"; };
5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcherTest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2887,6 +2889,7 @@
isa = PBXGroup;
children = (
C539CAE52D947D2A0050C732 /* Common */,
5B0162672E4A9C5D0009D3B7 /* SplitEventsTests.swift */,
5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */,
C53EDFCC2DD4E10A000DCDBC /* SplitsSyncHelperWithProxyHandlerTests.swift */,
C53EDFCA2DD3E257000DCDBC /* OutdatedSplitProxyHandlerTests.swift */,
Expand Down Expand Up @@ -4429,6 +4432,7 @@
95B1801E2763BF7E002DC9DF /* TelemetryStatsRecorderWorkerTests.swift in Sources */,
95F3F002258D3EE700084AF8 /* HttpEventsRecorderStub.swift in Sources */,
59D84BE3221734F5003DA248 /* LocalhostSplitClientTests.swift in Sources */,
5B0162682E4A9C7A0009D3B7 /* SplitEventsTests.swift in Sources */,
59B2043924F5667A0092F2E9 /* SseNotificationProcessorTest.swift in Sources */,
592C6AC6211B718E002D120C /* SplitEventsManagerTest.swift in Sources */,
95C7569D2696457500696148 /* NotificationHelperStub.swift in Sources */,
Expand Down
35 changes: 28 additions & 7 deletions Split/Api/DefaultSplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,36 @@ extension DefaultSplitClient {
task.event = event
on(event: event, executeTask: task)
}

private func on(event: SplitEvent, executeTask task: SplitEventTask) {
if event != .sdkReadyFromCache,
eventsManager.eventAlreadyTriggered(event: event) {
Logger.w("A handler was added for \(event.toString()) on the SDK, " +
"which has already fired and won’t be emitted again. The callback won’t be executed.")

private func on(event: SplitEvent, executeTask task: SplitEventActionTask) {
if event != .sdkReadyFromCache, eventsManager.eventAlreadyTriggered(event: event) {
Logger.w("A handler was added for \(event.toString()) on the SDK, which has already fired and won’t be emitted again. The callback won’t be executed.")
return
}
eventsManager.register(event: event, task: task)
eventsManager.register(event: SplitEventWithMetadata(type: event, metadata: nil), task: task)
}

// MARK: Listeners with Metadata
public func on(event: SplitEvent, executeWithMetadata action: @escaping SplitActionWithMetadata) {
on(event: event, runInBackground: true, queue: nil, executeWithMetadata: action)
}

public func on(event: SplitEvent, runInBackground: Bool, executeWithMetadata: @escaping SplitActionWithMetadata) {
on(event: event, runInBackground: runInBackground, queue: nil, executeWithMetadata: executeWithMetadata)
}

public func on(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue? = nil, executeWithMetadata action: @escaping SplitActionWithMetadata) {
on(event: event, runInBackground: runInBackground, queue: queue, action: action)
}

public func on(event: SplitEvent, queue: DispatchQueue, action: @escaping SplitActionWithMetadata) {
on(event: event, runInBackground: true, queue: queue, action: action)
}

private func on(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue? = nil, action: @escaping SplitActionWithMetadata) {
guard let factory = clientManager?.splitFactory else { return }
let task = SplitEventActionTask(action: action, event: event, runInBackground: runInBackground, factory: factory, queue: queue)
on(event: event, executeTask: task)
}
}

Expand Down
12 changes: 12 additions & 0 deletions Split/Api/FailHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ class FailedClient: SplitClient {
func on(event: SplitEvent,
queue: DispatchQueue, execute action: @escaping SplitAction) {
}

func on(event: SplitEvent, executeWithMetadata: @escaping SplitActionWithMetadata) {
/* Intentionally unimplemented */
}

func on(event: SplitEvent, runInBackground: Bool, executeWithMetadata: @escaping SplitActionWithMetadata) {
/* Intentionally unimplemented */
}

func on(event: SplitEvent, queue: DispatchQueue, action: @escaping SplitActionWithMetadata) {
/* Intentionally unimplemented */
}

func track(trafficType: String, eventType: String) -> Bool {
return false
Expand Down
12 changes: 12 additions & 0 deletions Split/Api/LocalhostSplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ public final class LocalhostSplitClient: NSObject, SplitClient {
eventsManager.register(event: event, task: task)
}
}

public func on(event: SplitEvent, executeWithMetadata: @escaping SplitActionWithMetadata) {
/* Intentionally unimplemented */
}

public func on(event: SplitEvent, runInBackground: Bool, executeWithMetadata: @escaping SplitActionWithMetadata) {
/* Intentionally unimplemented */
}

public func on(event: SplitEvent, queue: DispatchQueue, action: @escaping SplitActionWithMetadata) {
/* Intentionally unimplemented */
}

public func track(trafficType: String, eventType: String) -> Bool {
return true
Expand Down
7 changes: 7 additions & 0 deletions Split/Api/SplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Foundation

public typealias SplitAction = () -> Void
public typealias SplitActionWithMetadata = (EventMetadata) -> Void

@objc public protocol SplitClient {

Expand All @@ -33,9 +34,15 @@ public typealias SplitAction = () -> Void
@objc(getTreatmentsWithConfigForSplits:attributes:evaluationOptions:)
func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult]


// MARK: Events Listeners
func on(event: SplitEvent, execute action: @escaping SplitAction)
func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction)
func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction)

func on(event: SplitEvent, executeWithMetadata: @escaping SplitActionWithMetadata)
func on(event: SplitEvent, runInBackground: Bool, executeWithMetadata: @escaping SplitActionWithMetadata)
func on(event: SplitEvent, queue: DispatchQueue, action: @escaping SplitActionWithMetadata)

// MARK: Track feature
func track(trafficType: String, eventType: String) -> Bool
Expand Down
15 changes: 10 additions & 5 deletions Split/Common/Structs/BlockingQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,23 @@ class GenericBlockingQueue<Item> {

// Protocol to allow mocking
protocol InternalEventBlockingQueue {
func add(_ item: SplitInternalEvent)
func take() throws -> SplitInternalEvent
func add(_ item: SplitInternalEventWithMetadata)
func take() throws -> SplitInternalEventWithMetadata
func stop()
}

class DefaultInternalEventBlockingQueue: InternalEventBlockingQueue {
let blockingQueue = GenericBlockingQueue<SplitInternalEvent>()
func add(_ item: SplitInternalEvent) {
let blockingQueue = GenericBlockingQueue<SplitInternalEventWithMetadata>()

func add(_ item: SplitInternalEventWithMetadata) {
blockingQueue.add(item)
}

func add(_ item: SplitInternalEvent) {
blockingQueue.add(SplitInternalEventWithMetadata(item, metadata: nil))
}

func take() throws -> SplitInternalEvent {
func take() throws -> SplitInternalEventWithMetadata {
let value = try blockingQueue.take()
return value
}
Expand Down
17 changes: 13 additions & 4 deletions Split/Events/EventsManagerCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ protocol SplitEventsManagerCoordinator: SplitEventsManager {
}

class MainSplitEventsManager: SplitEventsManagerCoordinator {

private var defaultManager: SplitEventsManager?
private var managers = [Key: SplitEventsManager]()
private var triggered = Set<SplitInternalEvent>()
Expand All @@ -23,17 +24,21 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator {
.splitsUpdated,
.splitKilledNotification]
)

func notifyInternalEvent(_ event: SplitInternalEvent) {
if !eventsToHandle.contains(event) {
notifyInternalEventWithMetadata(SplitInternalEventWithMetadata(event, metadata: nil))
}

func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) {
if !eventsToHandle.contains(event.type) {
return
}
queue.async { [weak self] in
guard let self = self else { return }

self.triggered.insert(event)
self.triggered.insert(event.type)
self.managers.forEach { _, manager in
manager.notifyInternalEvent(event)
manager.notifyInternalEvent(event.type)
}
}
}
Expand Down Expand Up @@ -77,4 +82,8 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator {
}

func register(event: SplitEvent, task: SplitEventTask) {}

func register(event: SplitEventWithMetadata, task: any SplitEventTask) {
/* Intentionally unimplemented */
}
}
10 changes: 10 additions & 0 deletions Split/Events/SplitEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@

import Foundation

@objcMembers public class SplitEventWithMetadata: NSObject {
let type: SplitEvent
let metadata: EventMetadata?

@objc public init(type: SplitEvent, metadata: EventMetadata? = nil) {
self.type = type
self.metadata = metadata
}
}

@objc public enum SplitEvent: Int {
case sdkReady
case sdkReadyTimedOut
Expand Down
33 changes: 21 additions & 12 deletions Split/Events/SplitEventActionTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,39 @@ import Foundation
class SplitEventActionTask: SplitEventTask {

private var eventHandler: SplitAction?
private var eventHandlerWithMetadata: SplitActionWithMetadata?
private var queue: DispatchQueue?

var event: SplitEvent
var runInBackground: Bool = false
var factory: SplitFactory

init(action: @escaping SplitAction,
event: SplitEvent,
runInBackground: Bool = false,
factory: SplitFactory,
queue: DispatchQueue? = nil) {

self.eventHandler = action
self.event = event
self.runInBackground = runInBackground
self.queue = queue
self.factory = factory
init(action: @escaping SplitActionWithMetadata, event: SplitEvent, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) {
self.eventHandlerWithMetadata = action
self.event = event
self.runInBackground = runInBackground
self.queue = queue
self.factory = factory
}

init(action: @escaping SplitAction, event: SplitEvent, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) {
self.eventHandler = action
self.event = event
self.runInBackground = runInBackground
self.queue = queue
self.factory = factory
}

func takeQueue() -> DispatchQueue? {
defer { queue = nil }
return queue
}

func run() {
func run(_ metadata: EventMetadata?) {
eventHandler?()

if let metadata = metadata {
eventHandlerWithMetadata?(metadata)
}
}
}
2 changes: 1 addition & 1 deletion Split/Events/SplitEventTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ protocol SplitEventTask {
var event: SplitEvent { get }
var runInBackground: Bool { get }
func takeQueue() -> DispatchQueue?
func run()
func run(_ metadata: EventMetadata?)
}
Loading
Loading