Skip to content

[FME-8356] - Events - Errors Metadata #720

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 9 commits into from
Aug 13, 2025
2 changes: 1 addition & 1 deletion Split/Api/DefaultSplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ extension DefaultSplitClient {
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: SplitEventWithMetadata(type: event, metadata: nil), task: task)
eventsManager.register(event: event, task: task)
}

// MARK: Listeners with Metadata
Expand Down
11 changes: 6 additions & 5 deletions Split/Events/EventsManagerCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator {
private let queue = DispatchQueue(label: "split-event-manager-coordinator")
private let eventsToHandle: Set<SplitInternalEvent> = Set(
[.splitsLoadedFromCache,
.splitsUpdated,
.splitKilledNotification]
.splitsUpdated,
.splitKilledNotification,
.sdkError]
)

func notifyInternalEvent(_ event: SplitInternalEvent) {
notifyInternalEventWithMetadata(SplitInternalEventWithMetadata(event, metadata: nil))
notifyInternalEvent(SplitInternalEventWithMetadata(event, metadata: nil))
}

func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) {
func notifyInternalEvent(_ event: SplitInternalEventWithMetadata) {
if !eventsToHandle.contains(event.type) {
return
}
Expand All @@ -38,7 +39,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator {

self.triggered.insert(event.type)
self.managers.forEach { _, manager in
manager.notifyInternalEvent(event.type)
manager.notifyInternalEvent(event)
}
}
}
Expand Down
21 changes: 12 additions & 9 deletions Split/Events/SplitEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation
let type: SplitEvent
let metadata: EventMetadata?

@objc public init(type: SplitEvent, metadata: EventMetadata? = nil) {
@objc public init(_ type: SplitEvent, metadata: EventMetadata? = nil) {
self.type = type
self.metadata = metadata
}
Expand All @@ -22,17 +22,20 @@ import Foundation
case sdkReadyTimedOut
case sdkReadyFromCache
case sdkUpdated
case sdkError

public func toString() -> String {
switch self {
case .sdkReady:
return "SDK_READY"
case .sdkUpdated:
return "SDK_UPDATE"
case .sdkReadyTimedOut:
return "SDK_READY_TIMED_OUT"
case .sdkReadyFromCache:
return "SDK_READY_FROM_CACHE"
case .sdkReady:
return "SDK_READY"
case .sdkUpdated:
return "SDK_UPDATE"
case .sdkReadyTimedOut:
return "SDK_READY_TIMED_OUT"
case .sdkReadyFromCache:
return "SDK_READY_FROM_CACHE"
case .sdkError:
return "SDK_ERROR"
}
}
}
29 changes: 16 additions & 13 deletions Split/Events/SplitEventsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import Foundation

protocol SplitEventsManager: AnyObject {
func register(event: SplitEvent, task: SplitEventTask)
func register(event: SplitEventWithMetadata, task: SplitEventTask)
func notifyInternalEvent(_ event: SplitInternalEvent)
func notifyInternalEvent(_ event: SplitInternalEventWithMetadata)
func start()
func stop()
func eventAlreadyTriggered(event: SplitEvent) -> Bool
}

class DefaultSplitEventsManager: SplitEventsManager {

private let readingRefreshTime: Int

private var sdkReadyTimeStart: Int64
Expand All @@ -41,11 +42,12 @@ class DefaultSplitEventsManager: SplitEventsManager {
self.executionTimes = [String: Int]()
registerMaxAllowedExecutionTimesPerEvent()

// SDK Timeout Event
if config.sdkReadyTimeOut > 0 {
let readyTimedoutQueue = DispatchQueue(label: "split-event-timedout")
readyTimedoutQueue.asyncAfter(deadline: .now() + .milliseconds(config.sdkReadyTimeOut)) { [weak self] in
guard let self = self else { return }
self.notifyInternalEvent(SplitInternalEvent.sdkReadyTimeoutReached)
self.notifyInternalEvent(.sdkReadyTimeoutReached)
}
}
}
Expand All @@ -62,21 +64,17 @@ class DefaultSplitEventsManager: SplitEventsManager {
func notifyInternalEvent(_ event: SplitInternalEvent) {
notifyInternalEvent(SplitInternalEventWithMetadata(event, metadata: nil))
}

func register(event: SplitEvent, task: SplitEventTask) {
register(event: SplitEventWithMetadata(type: event, metadata: nil), task: task)
}

func register(event: SplitEventWithMetadata, task: SplitEventTask) {
let eventName = event.type.toString()
func register(event: SplitEvent, task: SplitEventTask) {
let eventName = event.toString()
processQueue.async { [weak self] in
guard let self = self else { return }
// If event is already triggered, execute the task
if let times = self.executionTimes(for: eventName), times == 0 {
self.executeTask(event: event, task: task)
return
}
self.subscribe(task: task, to: event.type)
self.subscribe(task: task, to: event)
}
}

Expand Down Expand Up @@ -111,7 +109,6 @@ class DefaultSplitEventsManager: SplitEventsManager {
self.eventsQueue.stop()
self.eventsQueue.stop()
}

}
}

Expand All @@ -125,6 +122,7 @@ class DefaultSplitEventsManager: SplitEventsManager {

executionTimes = [ SplitEvent.sdkReady.toString(): 1,
SplitEvent.sdkUpdated.toString(): -1,
SplitEvent.sdkError.toString(): -1,
SplitEvent.sdkReadyFromCache.toString(): 1,
SplitEvent.sdkReadyTimedOut.toString(): 1]
}
Expand Down Expand Up @@ -159,7 +157,7 @@ class DefaultSplitEventsManager: SplitEventsManager {
switch event.type {
case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated:
if isTriggered(external: .sdkReady) {
trigger(event: SplitEventWithMetadata(type: .sdkUpdated, metadata: event.metadata))
trigger(event: .sdkUpdated)
continue
}
triggerSdkReadyIfNeeded()
Expand All @@ -181,6 +179,9 @@ class DefaultSplitEventsManager: SplitEventsManager {
if !isTriggered(external: .sdkReady) {
trigger(event: .sdkReadyTimedOut)
}
case .sdkError:
let eventWithMetadata = SplitEventWithMetadata(.sdkError, metadata: event.metadata)
trigger(event: eventWithMetadata)
}
}
}
Expand Down Expand Up @@ -211,7 +212,7 @@ class DefaultSplitEventsManager: SplitEventsManager {
}

private func trigger(event: SplitEvent) {
trigger(event: SplitEventWithMetadata(type: event, metadata: nil))
trigger(event: SplitEventWithMetadata(event, metadata: nil))
}

private func trigger(event: SplitEventWithMetadata) {
Expand All @@ -237,13 +238,14 @@ class DefaultSplitEventsManager: SplitEventsManager {
}

private func executeTask(event: SplitEvent, task: SplitEventTask) {
executeTask(event: SplitEventWithMetadata(type: event, metadata: nil), task: task)
executeTask(event: SplitEventWithMetadata(event, metadata: nil), task: task)
}

private func executeTask(event: SplitEventWithMetadata, task: SplitEventTask) {

let eventName = task.event.toString()

// RUN IN BG & RETURN
if task.runInBackground {
TimeChecker.logInterval("Previous to run \(eventName) in Background")

Expand All @@ -255,6 +257,7 @@ class DefaultSplitEventsManager: SplitEventsManager {
return
}

// OR RUN ON MAIN
DispatchQueue.main.async {
TimeChecker.logInterval("Running event on main: \(eventName)")
// UI Updates
Expand Down
9 changes: 5 additions & 4 deletions Split/Events/SplitInternalEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ struct SplitInternalEventWithMetadata {
}

enum EventMetadataType: Int {
case FEATURE_FLAGS_SYNC_ERROR
case SEGMENTS_SYNC_ERROR
case featureFlagsSyncError
case segmentsSyncError

public func toString() -> String {
switch self {
case .FEATURE_FLAGS_SYNC_ERROR:
case .featureFlagsSyncError:
return "FEATURE_FLAGS_SYNC_ERROR"
case .SEGMENTS_SYNC_ERROR:
case .segmentsSyncError:
return "SEGMENTS_SYNC_ERROR"
}
}
Expand All @@ -53,4 +53,5 @@ enum SplitInternalEvent {
case attributesLoadedFromCache
case sdkReadyTimeoutReached
case splitKilledNotification
case sdkError
}
30 changes: 24 additions & 6 deletions Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,13 @@ class BasePeriodicSyncWorker: PeriodicSyncWorker {
Logger.i("Fetch from remote not implemented")
}

func notifyUpdate(_ events: [SplitInternalEvent]) {
events.forEach {
eventsManager.notifyInternalEvent($0)
}
func notifyUpdate(_ event: SplitInternalEvent) {
let withMetadata = SplitInternalEventWithMetadata(event, metadata: nil)
notifyUpdate(withMetadata)
}

func notifyUpdate(_ event: SplitInternalEventWithMetadata) {
eventsManager.notifyInternalEvent(event)
}
}

Expand Down Expand Up @@ -181,10 +184,18 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker {
let changeNumber = splitsStorage.changeNumber
let rbChangeNumber: Int64 = ruleBasedSegmentsStorage.changeNumber
guard let result = try? syncHelper.sync(since: changeNumber, rbSince: rbChangeNumber) else {
let event = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .segmentsSyncError, data: []))
notifyUpdate(event)
return
}

if result.success, result.featureFlagsUpdated || result.rbsUpdated {
notifyUpdate([.splitsUpdated])
// Success
notifyUpdate(.splitsUpdated)
} else if !result.success {
// Fail
let event = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .featureFlagsSyncError, data: []))
notifyUpdate(event)
}
}
}
Expand Down Expand Up @@ -225,10 +236,17 @@ class PeriodicMySegmentsSyncWorker: BasePeriodicSyncWorker {
if result.success {
if result.msUpdated || result.mlsUpdated {
// For now is not necessary specify which entity was updated
notifyUpdate([.mySegmentsUpdated])
notifyUpdate(.mySegmentsUpdated)
}
} else {
// Fail
let event = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .segmentsSyncError, data: []))
notifyUpdate(event)
}
} catch {
// Fail
let event = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .segmentsSyncError, data: []))
notifyUpdate(event)
Logger.e("Problem fetching segments: %@", error.localizedDescription)
}
}
Expand Down
13 changes: 10 additions & 3 deletions Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,23 @@ class RetryableMySegmentsSyncWorker: BaseRetryableSyncWorker {
if result.success {
if !isSdkReadyTriggered() {
// Notifying both to trigger SDK Ready
notifyUpdate([.mySegmentsUpdated])
notifyUpdate([.myLargeSegmentsUpdated])
notifyUpdate(.mySegmentsUpdated)
notifyUpdate(.myLargeSegmentsUpdated)
} else if result.msUpdated || result.mlsUpdated {
// For now is not necessary specify which entity was updated
notifyUpdate([.mySegmentsUpdated])
notifyUpdate(.mySegmentsUpdated)
}
return true
} else {
// Fail
let event = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .segmentsSyncError, data: []))
notifyUpdate(event)
}
} catch {
// Fail
Logger.e("Error while fetching segments in method: \(error.localizedDescription)")
let event = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .segmentsSyncError, data: []))
notifyUpdate(event)
errorHandler?(error)
}
return false
Expand Down
30 changes: 24 additions & 6 deletions Split/FetcherEngine/Refresh/RetryableSyncWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ class BaseRetryableSyncWorker: RetryableSyncWorker {
}
}

func notifyUpdate(_ events: [SplitInternalEvent]) {
events.forEach {
eventsManager.notifyInternalEvent($0)
}
func notifyUpdate(_ event: SplitInternalEvent) {
notifyUpdate(SplitInternalEventWithMetadata(event, metadata: nil))
}

func notifyUpdate(_ event: SplitInternalEventWithMetadata) {
eventsManager.notifyInternalEvent(event)
}

func isSdkReadyTriggered() -> Bool {
Expand Down Expand Up @@ -141,15 +143,23 @@ class RetryableSplitsSyncWorker: BaseRetryableSyncWorker {
let rbChangeNumber = ruleBasedSegmentsStorage.changeNumber
let result = try syncHelper.sync(since: changeNumber, rbSince: rbChangeNumber, clearBeforeUpdate: false)
if result.success {
// Success
if !isSdkReadyTriggered() ||
result.featureFlagsUpdated {
notifyUpdate([.splitsUpdated])
notifyUpdate(.splitsUpdated)
}
resetBackoffCounter()
return true
} else {
// Fail
let eventWithMetadata = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .featureFlagsSyncError, data: []))
notifyUpdate(eventWithMetadata)
}
} catch {
// Fail
Logger.e("Error while fetching splits in method: \(error.localizedDescription)")
let eventWithMetadata = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .featureFlagsSyncError, data: []))
notifyUpdate(eventWithMetadata)
errorHandler?(error)
}
return false
Expand Down Expand Up @@ -217,14 +227,22 @@ class RetryableSplitsUpdateWorker: BaseRetryableSyncWorker {
clearBeforeUpdate: false,
headers: ServiceConstants.controlNoCacheHeader)
if result.success {
// Success
if result.featureFlagsUpdated {
notifyUpdate([.splitsUpdated])
notifyUpdate(.splitsUpdated)
}
resetBackoffCounter()
return true
} else {
// Fail
let eventWithMetadata = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .featureFlagsSyncError, data: []))
notifyUpdate(eventWithMetadata)
}
} catch {
// Fail
Logger.e("Error while fetching splits in method \(#function): \(error.localizedDescription)")
let eventWithMetadata = SplitInternalEventWithMetadata(.sdkError, metadata: EventMetadata(type: .featureFlagsSyncError, data: []))
notifyUpdate(eventWithMetadata)
errorHandler?(error)
}
Logger.d("Feature flag changes are not updated yet")
Expand Down
Loading
Loading