From b30c63296b62a1d3ab3909dfae23542e431fbbb2 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 9 Apr 2025 14:11:24 -0300 Subject: [PATCH 01/20] Added new SplitInternalEvent wrapper to support metadata --- Split/Events/SplitInternalEvent.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Split/Events/SplitInternalEvent.swift b/Split/Events/SplitInternalEvent.swift index 4c9521204..ddc466084 100644 --- a/Split/Events/SplitInternalEvent.swift +++ b/Split/Events/SplitInternalEvent.swift @@ -18,3 +18,8 @@ enum SplitInternalEvent { case sdkReadyTimeoutReached case splitKilledNotification } + +struct InternalEvent { + let type: SplitInternalEvent + let metadata: [String: String]? +}gi From b4b4e256604f20c29c91d39795826d33b819d3bf Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 9 Apr 2025 14:11:57 -0300 Subject: [PATCH 02/20] Fixed typo --- Split/Events/SplitInternalEvent.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Events/SplitInternalEvent.swift b/Split/Events/SplitInternalEvent.swift index ddc466084..159156e1e 100644 --- a/Split/Events/SplitInternalEvent.swift +++ b/Split/Events/SplitInternalEvent.swift @@ -22,4 +22,4 @@ enum SplitInternalEvent { struct InternalEvent { let type: SplitInternalEvent let metadata: [String: String]? -}gi +} From 70d6a33549e9043f45f586acf54282e91ab6a33f Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 9 Apr 2025 18:20:15 -0300 Subject: [PATCH 03/20] Changing branch --- Split/Common/Structs/BlockingQueue.swift | 10 +-- Split/Events/EventsManagerCoordinator.swift | 5 +- Split/Events/SplitEventsManager.swift | 85 +++++++++++-------- Split/Events/SplitInternalEvent.swift | 11 ++- .../Refresh/PeriodicSyncWorker.swift | 9 +- .../Refresh/RetryableSegmentsSyncWorker.swift | 6 +- .../Refresh/RetryableSyncWorker.swift | 8 +- .../Refresh/SplitsSyncHelper.swift | 3 +- .../Sync/FeatureFlagsSynchronizer.swift | 2 +- 9 files changed, 84 insertions(+), 55 deletions(-) diff --git a/Split/Common/Structs/BlockingQueue.swift b/Split/Common/Structs/BlockingQueue.swift index 8fd45f67f..df5bfbb6f 100644 --- a/Split/Common/Structs/BlockingQueue.swift +++ b/Split/Common/Structs/BlockingQueue.swift @@ -73,18 +73,18 @@ class GenericBlockingQueue { // 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() - func add(_ item: SplitInternalEvent) { + let blockingQueue = GenericBlockingQueue() + func add(_ item: SplitInternalEventWithMetadata) { blockingQueue.add(item) } - func take() throws -> SplitInternalEvent { + func take() throws -> SplitInternalEventWithMetadata { let value = try blockingQueue.take() return value } diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index 3ddf4fe93..f817b1fc9 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -14,6 +14,7 @@ protocol SplitEventsManagerCoordinator: SplitEventsManager { } class MainSplitEventsManager: SplitEventsManagerCoordinator { + private var defaultManager: SplitEventsManager? private var managers = [Key: SplitEventsManager]() private var triggered = Set() @@ -24,7 +25,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { .splitKilledNotification] ) - func notifyInternalEvent(_ event: SplitInternalEvent) { + func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String : String]? = nil) { if !eventsToHandle.contains(event) { return } @@ -33,7 +34,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { self.triggered.insert(event) self.managers.forEach { _, manager in - manager.notifyInternalEvent(event) + manager.notifyInternalEvent(event, metadata) } } } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index 56774453b..af3808dd6 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -10,12 +10,21 @@ import Foundation protocol SplitEventsManager: AnyObject { func register(event: SplitEvent, task: SplitEventTask) - func notifyInternalEvent(_ event: SplitInternalEvent) + func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: String]?) func start() func stop() func eventAlreadyTriggered(event: SplitEvent) -> Bool } +/* This overload is intentionally kept for backwards compatibility. + It allows calling `notifyInternalEvent(.event)` without needing to pass `nil` as metadata. + Do not remove unless all usages have migrated to the new signature. */ +extension SplitEventsManager { + func notifyInternalEvent(_ event: SplitInternalEvent) { + notifyInternalEvent(event, nil) + } +} + class DefaultSplitEventsManager: SplitEventsManager { private let readingRefreshTime: Int @@ -23,7 +32,7 @@ class DefaultSplitEventsManager: SplitEventsManager { private var subscriptions = [SplitEvent: [SplitEventTask]]() private var executionTimes: [String: Int] - private var triggered: [SplitInternalEvent] + private var triggered: [SplitInternalEventWithMetadata] private let processQueue: DispatchQueue private let dataAccessQueue: DispatchQueue private var isStarted: Bool @@ -35,7 +44,7 @@ class DefaultSplitEventsManager: SplitEventsManager { self.isStarted = false self.sdkReadyTimeStart = Date().unixTimestampInMiliseconds() self.readingRefreshTime = 300 - self.triggered = [SplitInternalEvent]() + self.triggered = [SplitInternalEventWithMetadata]() self.eventsQueue = DefaultInternalEventBlockingQueue() self.executionTimes = [String: Int]() registerMaxAllowedExecutionTimesPerEvent() @@ -48,11 +57,15 @@ class DefaultSplitEventsManager: SplitEventsManager { } } } - - func notifyInternalEvent(_ event: SplitInternalEvent) { + + func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: String]? = nil) { + notifyInternalEventWithMetadata(SplitInternalEventWithMetadata(type: event, metadata: metadata)) + } + + func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) { processQueue.async { [weak self] in if let self = self { - Logger.v("Event \(event) notified") + Logger.i("Event \(event.type) notified - Details: \(event.metadata ?? [:])") self.eventsQueue.add(event) } } @@ -128,7 +141,7 @@ class DefaultSplitEventsManager: SplitEventsManager { return isRunning } - private func takeEvent() -> SplitInternalEvent? { + private func takeEvent() -> SplitInternalEventWithMetadata? { do { return try eventsQueue.take() } catch BlockingQueueError.hasBeenStopped { @@ -145,33 +158,33 @@ class DefaultSplitEventsManager: SplitEventsManager { return } self.triggered.append(event) - switch event { - case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated: - if isTriggered(external: .sdkReady) { - trigger(event: .sdkUpdated) - continue + switch event.type { + case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated: + if isTriggered(external: .sdkReady) { + Logger.i("SDK Updated with \(event.metadata?.description)") + trigger(event: .sdkUpdated) + continue + } + self.triggerSdkReadyIfNeeded() + + case .mySegmentsLoadedFromCache, .myLargeSegmentsLoadedFromCache, .splitsLoadedFromCache, .attributesLoadedFromCache: + Logger.v("Event \(event.type) triggered") + if isTriggered(internal: .splitsLoadedFromCache), + isTriggered(internal: .mySegmentsLoadedFromCache), + isTriggered(internal: .myLargeSegmentsLoadedFromCache), + isTriggered(internal: .attributesLoadedFromCache) { + trigger(event: SplitEvent.sdkReadyFromCache) + } + case .splitKilledNotification: + if isTriggered(external: .sdkReady) { + trigger(event: .sdkUpdated) + continue + } + case .sdkReadyTimeoutReached: + if !isTriggered(external: .sdkReady) { + trigger(event: SplitEvent.sdkReadyTimedOut) + } } - self.triggerSdkReadyIfNeeded() - - case .mySegmentsLoadedFromCache, .myLargeSegmentsLoadedFromCache, - .splitsLoadedFromCache, .attributesLoadedFromCache: - Logger.v("Event \(event) triggered") - if isTriggered(internal: .splitsLoadedFromCache), - isTriggered(internal: .mySegmentsLoadedFromCache), - isTriggered(internal: .myLargeSegmentsLoadedFromCache), - isTriggered(internal: .attributesLoadedFromCache) { - trigger(event: SplitEvent.sdkReadyFromCache) - } - case .splitKilledNotification: - if isTriggered(external: .sdkReady) { - trigger(event: .sdkUpdated) - continue - } - case .sdkReadyTimeoutReached: - if !isTriggered(external: .sdkReady) { - trigger(event: SplitEvent.sdkReadyTimedOut) - } - } } } @@ -241,9 +254,13 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func isTriggered(internal event: SplitInternalEvent) -> Bool { + private func isTriggered(internal event: SplitInternalEventWithMetadata) -> Bool { return triggered.filter { $0 == event }.count > 0 } + + private func isTriggered(internal event: SplitInternalEvent) -> Bool { + return isTriggered(internal: SplitInternalEventWithMetadata(type: event, metadata: nil)) + } // MARK: Safe Data Access func executionTimes(for eventName: String) -> Int? { diff --git a/Split/Events/SplitInternalEvent.swift b/Split/Events/SplitInternalEvent.swift index 159156e1e..cdd96f006 100644 --- a/Split/Events/SplitInternalEvent.swift +++ b/Split/Events/SplitInternalEvent.swift @@ -19,7 +19,16 @@ enum SplitInternalEvent { case splitKilledNotification } -struct InternalEvent { +struct SplitInternalEventWithMetadata: Equatable { let type: SplitInternalEvent let metadata: [String: String]? + + init(type: SplitInternalEvent, metadata: [String : String]? = nil) { + self.type = type + self.metadata = metadata + } + + static func == (lhs: SplitInternalEventWithMetadata, rhs: SplitInternalEventWithMetadata) -> Bool { + return lhs.type == rhs.type && lhs.metadata == rhs.metadata + } } diff --git a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift index 66065d934..20477dfeb 100644 --- a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift @@ -130,9 +130,10 @@ class BasePeriodicSyncWorker: PeriodicSyncWorker { Logger.i("Fetch from remote not implemented") } - func notifyUpdate(_ events: [SplitInternalEvent]) { + func notifyUpdate(_ events: [SplitInternalEventWithMetadata]) { events.forEach { - eventsManager.notifyInternalEvent($0) + eventsManager.notifyInternalEvent($0.type) + Logger.i("Discarding metadata \($0.metadata?.description)") } } } @@ -172,7 +173,7 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker { return } if result.success, result.featureFlagsUpdated { - notifyUpdate([.splitsUpdated]) + notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated)]) } } } @@ -213,7 +214,7 @@ class PeriodicMySegmentsSyncWorker: BasePeriodicSyncWorker { if result.success { if result.msUpdated || result.mlsUpdated { // For now is not necessary specify which entity was updated - notifyUpdate([.mySegmentsUpdated]) + notifyUpdate([SplitInternalEventWithMetadata(type: .mySegmentsUpdated, metadata: ["Fetching from: ": "Remote"]) ]) } } } catch { diff --git a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift index 36973b66b..04f0a2a4b 100644 --- a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift @@ -46,11 +46,11 @@ class RetryableMySegmentsSyncWorker: BaseRetryableSyncWorker { if result.success { if !isSdkReadyTriggered() { // Notifying both to trigger SDK Ready - notifyUpdate([.mySegmentsUpdated]) - notifyUpdate([.myLargeSegmentsUpdated]) + notifyUpdate([SplitInternalEventWithMetadata(type: .mySegmentsUpdated, metadata: [:])]) + notifyUpdate([SplitInternalEventWithMetadata(type: .myLargeSegmentsUpdated, metadata: [:])]) } else if result.msUpdated || result.mlsUpdated { // For now is not necessary specify which entity was updated - notifyUpdate([.mySegmentsUpdated]) + notifyUpdate([SplitInternalEventWithMetadata(type: .mySegmentsUpdated, metadata: [:])]) } return true } diff --git a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift index 17d91c78b..82d89175a 100644 --- a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift @@ -79,9 +79,9 @@ class BaseRetryableSyncWorker: RetryableSyncWorker { } } - func notifyUpdate(_ events: [SplitInternalEvent]) { + func notifyUpdate(_ events: [SplitInternalEventWithMetadata]) { events.forEach { - eventsManager.notifyInternalEvent($0) + eventsManager.notifyInternalEvent($0.type, $0.metadata) } } @@ -164,7 +164,7 @@ class RetryableSplitsSyncWorker: BaseRetryableSyncWorker { if result.success { if !isSdkReadyTriggered() || result.featureFlagsUpdated { - notifyUpdate([.splitsUpdated]) + notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: ["Flags": flagsSpec])]) } resetBackoffCounter() return true @@ -221,7 +221,7 @@ class RetryableSplitsUpdateWorker: BaseRetryableSyncWorker { headers: ServiceConstants.controlNoCacheHeader) if result.success { if result.featureFlagsUpdated { - notifyUpdate([.splitsUpdated]) + notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: [:]) ]) } resetBackoffCounter() return true diff --git a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift index 83bcfd4c0..432d6796c 100644 --- a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift +++ b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift @@ -124,7 +124,8 @@ class SplitsSyncHelper { splitsStorage.clear() } firstFetch = false - if splitsStorage.update(splitChange: splitChangeProcessor.process(splitChange)) { + let processedSplits = splitChangeProcessor.process(splitChange) + if splitsStorage.update(splitChange: processedSplits) { featureFlagsUpdated = true } Logger.i("Feature flag definitions have been updated") diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index b733627c2..e3fd42164 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -40,7 +40,7 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { syncWorkerFactory: SyncWorkerFactory, broadcasterChannel: SyncEventBroadcaster, syncTaskByChangeNumberCatalog: ConcurrentDictionary - = ConcurrentDictionary(), += ConcurrentDictionary(), splitsFilterQueryString: String, flagsSpec: String, splitEventsManager: SplitEventsManager) { From b216585e135a1e963ee7a3171d1ef4b335b5e537 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 10 Apr 2025 10:01:28 -0300 Subject: [PATCH 04/20] Changing branch --- Split/Events/SplitEventsManager.swift | 1 + Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift | 5 +++-- Split/FetcherEngine/Refresh/SplitsSyncHelper.swift | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index af3808dd6..2fc926462 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -11,6 +11,7 @@ import Foundation protocol SplitEventsManager: AnyObject { func register(event: SplitEvent, task: SplitEventTask) func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: String]?) + func notifyInternalEventWithMetadata() func start() func stop() func eventAlreadyTriggered(event: SplitEvent) -> Bool diff --git a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift index 20477dfeb..9f876c2db 100644 --- a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift @@ -172,8 +172,9 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker { guard let result = try? syncHelper.sync(since: splitsStorage.changeNumber) else { return } - if result.success, result.featureFlagsUpdated { - notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated)]) + if result.success, result.splitNames.count > 0 { + notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: result.splitNames)]) + } } } diff --git a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift index 432d6796c..25b30f0fd 100644 --- a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift +++ b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift @@ -18,7 +18,7 @@ class SplitsSyncHelper { struct FetchResult { let till: Int64 - let featureFlagsUpdated: Bool + let splitNames: [String] } private let splitFetcher: HttpSplitFetcher @@ -113,6 +113,7 @@ class SplitsSyncHelper { var firstFetch = true var nextSince = since var featureFlagsUpdated = false + var splitNames = [String] while true { clearCache = clearCache && firstFetch let splitChange = try self.splitFetcher.execute(since: nextSince, @@ -126,13 +127,14 @@ class SplitsSyncHelper { firstFetch = false let processedSplits = splitChangeProcessor.process(splitChange) if splitsStorage.update(splitChange: processedSplits) { - featureFlagsUpdated = true + splitNames => processedSplits.archivedSplits.compactMap(\.name) + splitNames => processedSplits.activeSplits.compactMap(\.name) } Logger.i("Feature flag definitions have been updated") // Line below commented temporary for debug purposes // Logger.v(splitChange.description) if newSince == newTill, newTill >= since { - return FetchResult(till: newTill, featureFlagsUpdated: featureFlagsUpdated) + return FetchResult(till: newTill, splitNames: splitNames) } nextSince = newTill } From 7cc41c47c76dbb500b3df2d56f06b76256314781 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 10 Apr 2025 11:20:04 -0300 Subject: [PATCH 05/20] Saving before changing branch --- Split/Events/EventsManagerCoordinator.swift | 6 +++++- Split/Events/SplitEventsManager.swift | 6 +++--- Split/Events/SplitInternalEvent.swift | 6 +++--- .../Refresh/PeriodicSyncWorker.swift | 14 ++++++++++---- .../Refresh/RetryableSyncWorker.swift | 5 ++--- .../Refresh/SplitsSyncHelper.swift | 18 +++++++++--------- .../Streaming/PushNotificationManager.swift | 9 ++++++++- 7 files changed, 40 insertions(+), 24 deletions(-) diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index f817b1fc9..41f982466 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -25,7 +25,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { .splitKilledNotification] ) - func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String : String]? = nil) { + func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String : Any]? = nil) { if !eventsToHandle.contains(event) { return } @@ -38,6 +38,10 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { } } } + + func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) { + notifyInternalEvent(event.type, event.metadata) + } func start() {} diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index 2fc926462..bd3950c3b 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -10,8 +10,8 @@ import Foundation protocol SplitEventsManager: AnyObject { func register(event: SplitEvent, task: SplitEventTask) - func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: String]?) - func notifyInternalEventWithMetadata() + func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: Any]?) + func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) func start() func stop() func eventAlreadyTriggered(event: SplitEvent) -> Bool @@ -59,7 +59,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: String]? = nil) { + func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: Any]? = nil) { notifyInternalEventWithMetadata(SplitInternalEventWithMetadata(type: event, metadata: metadata)) } diff --git a/Split/Events/SplitInternalEvent.swift b/Split/Events/SplitInternalEvent.swift index cdd96f006..f67fa824d 100644 --- a/Split/Events/SplitInternalEvent.swift +++ b/Split/Events/SplitInternalEvent.swift @@ -21,14 +21,14 @@ enum SplitInternalEvent { struct SplitInternalEventWithMetadata: Equatable { let type: SplitInternalEvent - let metadata: [String: String]? + let metadata: [String: Any]? - init(type: SplitInternalEvent, metadata: [String : String]? = nil) { + init(type: SplitInternalEvent, metadata: [String : Any]? = nil) { self.type = type self.metadata = metadata } static func == (lhs: SplitInternalEventWithMetadata, rhs: SplitInternalEventWithMetadata) -> Bool { - return lhs.type == rhs.type && lhs.metadata == rhs.metadata + return lhs.type == rhs.type } } diff --git a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift index 9f876c2db..1bb25ae99 100644 --- a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift @@ -132,8 +132,8 @@ class BasePeriodicSyncWorker: PeriodicSyncWorker { func notifyUpdate(_ events: [SplitInternalEventWithMetadata]) { events.forEach { - eventsManager.notifyInternalEvent($0.type) - Logger.i("Discarding metadata \($0.metadata?.description)") + eventsManager.notifyInternalEventWithMetadata($0) + Logger.i("Passing metadata \(String(describing: $0.metadata?.description))") } } } @@ -172,8 +172,14 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker { guard let result = try? syncHelper.sync(since: splitsStorage.changeNumber) else { return } - if result.success, result.splitNames.count > 0 { - notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: result.splitNames)]) + if result.success, result.featureFlagsUpdated.count > 0 { + + var updatedFlags = result.featureFlagsUpdated + for flag in updatedFlags { + updatedFlags.append(flag) + } + + notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: ["Updated Flags": updatedFlags])]) } } diff --git a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift index 82d89175a..a37b1b4bc 100644 --- a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift @@ -162,8 +162,7 @@ class RetryableSplitsSyncWorker: BaseRetryableSyncWorker { do { let result = try syncHelper.sync(since: changeNumber, clearBeforeUpdate: clearCache) if result.success { - if !isSdkReadyTriggered() || - result.featureFlagsUpdated { + if !isSdkReadyTriggered() || result.featureFlagsUpdated.count > 0 { notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: ["Flags": flagsSpec])]) } resetBackoffCounter() @@ -220,7 +219,7 @@ class RetryableSplitsUpdateWorker: BaseRetryableSyncWorker { clearBeforeUpdate: false, headers: ServiceConstants.controlNoCacheHeader) if result.success { - if result.featureFlagsUpdated { + if result.featureFlagsUpdated.count > 0 { notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: [:]) ]) } resetBackoffCounter() diff --git a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift index 25b30f0fd..d5c992731 100644 --- a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift +++ b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift @@ -11,7 +11,7 @@ import Foundation struct SyncResult { let success: Bool let changeNumber: Int64 - let featureFlagsUpdated: Bool + let featureFlagsUpdated: [String] } class SplitsSyncHelper { @@ -85,8 +85,9 @@ class SplitsSyncHelper { var nextSince = since var attemptCount = 0 let goalTill = till ?? -10 + var result = FetchResult(till: 0, splitNames: []) while attemptCount < maxAttempts { - let result = try fetchUntil(since: nextSince, + result = try fetchUntil(since: nextSince, till: useTillParam ? till : nil, clearBeforeUpdate: clearBeforeUpdate, headers: headers) @@ -95,13 +96,13 @@ class SplitsSyncHelper { if nextSince >= goalTill { return SyncResult(success: true, changeNumber: nextSince, - featureFlagsUpdated: result.featureFlagsUpdated) + featureFlagsUpdated: result.splitNames) } Thread.sleep(forTimeInterval: backoffCounter.getNextRetryTime()) attemptCount+=1 } - return SyncResult(success: false, changeNumber: nextSince, featureFlagsUpdated: false) + return SyncResult(success: false, changeNumber: nextSince, featureFlagsUpdated: result.splitNames) } func fetchUntil(since: Int64, @@ -112,8 +113,8 @@ class SplitsSyncHelper { var clearCache = clearBeforeUpdate var firstFetch = true var nextSince = since - var featureFlagsUpdated = false - var splitNames = [String] + var splitNames: [String] = [] + while true { clearCache = clearCache && firstFetch let splitChange = try self.splitFetcher.execute(since: nextSince, @@ -127,12 +128,11 @@ class SplitsSyncHelper { firstFetch = false let processedSplits = splitChangeProcessor.process(splitChange) if splitsStorage.update(splitChange: processedSplits) { - splitNames => processedSplits.archivedSplits.compactMap(\.name) - splitNames => processedSplits.activeSplits.compactMap(\.name) + splitNames += processedSplits.archivedSplits.compactMap(\.name) + splitNames += processedSplits.activeSplits.compactMap(\.name) } Logger.i("Feature flag definitions have been updated") // Line below commented temporary for debug purposes - // Logger.v(splitChange.description) if newSince == newTill, newTill >= since { return FetchResult(till: newTill, splitNames: splitNames) } diff --git a/Split/Network/Streaming/PushNotificationManager.swift b/Split/Network/Streaming/PushNotificationManager.swift index 38aa4f28f..59e46de8e 100644 --- a/Split/Network/Streaming/PushNotificationManager.swift +++ b/Split/Network/Streaming/PushNotificationManager.swift @@ -134,7 +134,14 @@ class DefaultPushNotificationManager: PushNotificationManager { } Logger.d("Streaming authentication success") - let connectionDelay = result.sseConnectionDelay + var connectionDelay: Int64 = 0 + + #if DEBUG + connectionDelay = 1 + #else + connectionDelay = result.sseConnectionDelay + #endif + self.broadcasterChannel.push(event: .pushDelayReceived(delaySeconds: connectionDelay)) let lastId = lastConnId.value if connectionDelay > 0 { From 919c603a96c3e4437c9cdc621614a6782958c02c Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 10 Apr 2025 11:53:04 -0300 Subject: [PATCH 06/20] Stable point. At least new flags are reported internally --- Split/Events/SplitEventsManager.swift | 1 - Split/Localhost/LocalhostSynchronizer.swift | 2 +- Split/Network/Streaming/SyncUpdateWorker.swift | 10 ++++++++-- Split/Network/Sync/FeatureFlagsSynchronizer.swift | 6 +++--- Split/Network/Sync/Synchronizer.swift | 6 +++--- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index bd3950c3b..02ec7e1c9 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -162,7 +162,6 @@ class DefaultSplitEventsManager: SplitEventsManager { switch event.type { case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated: if isTriggered(external: .sdkReady) { - Logger.i("SDK Updated with \(event.metadata?.description)") trigger(event: .sdkUpdated) continue } diff --git a/Split/Localhost/LocalhostSynchronizer.swift b/Split/Localhost/LocalhostSynchronizer.swift index 7bc59ea28..28ca13511 100644 --- a/Split/Localhost/LocalhostSynchronizer.swift +++ b/Split/Localhost/LocalhostSynchronizer.swift @@ -44,7 +44,7 @@ class LocalhostSynchronizer: FeatureFlagsSynchronizer { func notifyKilled() { } - func notifyUpdated() { + func notifyUpdated(flagList: [String]) { } func pause() { diff --git a/Split/Network/Streaming/SyncUpdateWorker.swift b/Split/Network/Streaming/SyncUpdateWorker.swift index 0a87fbd68..80cd6a02e 100644 --- a/Split/Network/Streaming/SyncUpdateWorker.swift +++ b/Split/Network/Streaming/SyncUpdateWorker.swift @@ -66,8 +66,14 @@ class SplitsUpdateWorker: UpdateWorker { since: previousChangeNumber, till: notification.changeNumber) Logger.v("Split update received: \(change)") - if self.splitsStorage.update(splitChange: self.splitChangeProcessor.process(change)) { - self.synchronizer.notifyFeatureFlagsUpdated() + + let processedFlags = self.splitChangeProcessor.process(change) + + if self.splitsStorage.update(splitChange: processedFlags) { + var updatedFlags: [String] = processedFlags.activeSplits.compactMap(\.name) + updatedFlags += processedFlags.archivedSplits.compactMap(\.name) + self.synchronizer.notifyFeatureFlagsUpdated(flagList: updatedFlags) + } self.telemetryProducer?.recordUpdatesFromSse(type: .splits) return diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index e3fd42164..2b5f9cb25 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -15,7 +15,7 @@ protocol FeatureFlagsSynchronizer { func startPeriodicSync() func stopPeriodicSync() func notifyKilled() - func notifyUpdated() + func notifyUpdated(flagList: [String]) func pause() func resume() func destroy() @@ -142,8 +142,8 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { splitEventsManager.notifyInternalEvent(.splitKilledNotification) } - func notifyUpdated() { - splitEventsManager.notifyInternalEvent(.splitsUpdated) + func notifyUpdated(flagList: [String]) { + splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: ["Updated flags:":flagList] )) } func pause() { diff --git a/Split/Network/Sync/Synchronizer.swift b/Split/Network/Sync/Synchronizer.swift index 4a8320fc7..9e3e796eb 100644 --- a/Split/Network/Sync/Synchronizer.swift +++ b/Split/Network/Sync/Synchronizer.swift @@ -29,7 +29,7 @@ protocol Synchronizer: ImpressionLogger { func startRecordingTelemetry() func stopRecordingTelemetry() func pushEvent(event: EventDTO) - func notifyFeatureFlagsUpdated() + func notifyFeatureFlagsUpdated(flagList: [String]) func notifySegmentsUpdated(forKey key: String) func notifyLargeSegmentsUpdated(forKey key: String) func notifySplitKilled() @@ -200,8 +200,8 @@ class DefaultSynchronizer: Synchronizer { } } - func notifyFeatureFlagsUpdated() { - featureFlagsSynchronizer.notifyUpdated() + func notifyFeatureFlagsUpdated(flagList: [String]) { + featureFlagsSynchronizer.notifyUpdated(flagList: flagList) } func notifySegmentsUpdated(forKey key: String) { From d4168036a001dd4ae30e9ce52733a7dbbe4afd4e Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 10 Apr 2025 15:36:23 -0300 Subject: [PATCH 07/20] Changing branch --- Split/Api/DefaultSplitClient.swift | 24 +++++------- Split/Api/FailHelpers.swift | 6 +-- Split/Api/LocalhostSplitClient.swift | 17 +++++---- Split/Api/SplitClient.swift | 6 +-- Split/Api/SplitClientManager.swift | 4 +- Split/Events/EventsManagerCoordinator.swift | 2 +- Split/Events/SplitEvent.swift | 21 +++++++++++ Split/Events/SplitEventActionTask.swift | 12 ++---- Split/Events/SplitEventTask.swift | 4 +- Split/Events/SplitEventsManager.swift | 41 +++++++++++++-------- 10 files changed, 79 insertions(+), 58 deletions(-) diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index bbf3aa8a7..c4eedead6 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -58,25 +58,19 @@ public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClie // MARK: Events extension DefaultSplitClient { - public func on(event: SplitEvent, execute action: @escaping SplitAction) { + public func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { on(event: event, runInBackground: false, queue: nil, execute: action) } - public func on(event: SplitEvent, runInBackground: Bool, - execute action: @escaping SplitAction) { + public func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) { on(event: event, runInBackground: runInBackground, queue: nil, execute: action) } - public func on(event: SplitEvent, - queue: DispatchQueue, execute action: @escaping SplitAction) { + public func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) { on(event: event, runInBackground: true, queue: queue, execute: action) } - - private func on(event: SplitEvent, - runInBackground: Bool, - queue: DispatchQueue?, - execute action: @escaping SplitAction) { - + + private func on(event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { guard let factory = clientManager?.splitFactory else { return } @@ -89,10 +83,10 @@ extension DefaultSplitClient { 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, " + + private func on(event: SplitEventWithMetadata, executeTask task: SplitEventActionTask) { + if event.type != .sdkReadyFromCache, + eventsManager.eventAlreadyTriggered(event: event.type) { + Logger.w("A handler was added for \(event.type.toString()) on the SDK, " + "which has already fired and won’t be emitted again. The callback won’t be executed.") return } diff --git a/Split/Api/FailHelpers.swift b/Split/Api/FailHelpers.swift index 6f90ba7b7..86e2353b8 100644 --- a/Split/Api/FailHelpers.swift +++ b/Split/Api/FailHelpers.swift @@ -54,14 +54,14 @@ class FailedClient: SplitClient { return [:] } - func on(event: SplitEvent, execute action: @escaping SplitAction) { + func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { } - func on(event: SplitEvent, runInBackground: Bool, + func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) { } - func on(event: SplitEvent, + func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) { } diff --git a/Split/Api/LocalhostSplitClient.swift b/Split/Api/LocalhostSplitClient.swift index 2aaacca6b..c099c7a64 100644 --- a/Split/Api/LocalhostSplitClient.swift +++ b/Split/Api/LocalhostSplitClient.swift @@ -51,10 +51,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient { private let key: Key weak var clientManger: SplitClientManager? - init(key: Key, splitsStorage: SplitsStorage, - clientManager: SplitClientManager?, - eventsManager: SplitEventsManager? = nil, - evaluator: Evaluator) { + init(key: Key, splitsStorage: SplitsStorage, clientManager: SplitClientManager?, eventsManager: SplitEventsManager? = nil, evaluator: Evaluator) { self.eventsManager = eventsManager self.key = key self.splitsStorage = splitsStorage @@ -121,20 +118,24 @@ public final class LocalhostSplitClient: NSObject, SplitClient { return results } - public func on(event: SplitEvent, runInBackground: Bool, + public func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) { on(event: event, runInBackground: runInBackground, queue: nil, execute: action) } - public func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction) { + public func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) { on(event: event, runInBackground: true, queue: queue, execute: action) } - public func on(event: SplitEvent, execute action: @escaping SplitAction) { + public func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { on(event: event, runInBackground: false, queue: nil, execute: action) } - private func on(event: SplitEvent, runInBackground: Bool, + private func on(event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { + on(eventWithMetadata: event, runInBackground: runInBackground, queue: queue, execute: action) + } + + private func on(eventWithMetadata event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { guard let factory = clientManger?.splitFactory else { return } diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index ff4df2005..b44fc248a 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -33,9 +33,9 @@ public typealias SplitAction = () -> Void @objc(getTreatmentsWithConfigForSplits:attributes:evaluationOptions:) func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] - 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: SplitEventWithMetadata, execute action: @escaping SplitAction) + func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) + func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) // MARK: Track feature func track(trafficType: String, eventType: String) -> Bool diff --git a/Split/Api/SplitClientManager.swift b/Split/Api/SplitClientManager.swift index 6cdbf743a..400afda1d 100644 --- a/Split/Api/SplitClientManager.swift +++ b/Split/Api/SplitClientManager.swift @@ -80,7 +80,7 @@ class DefaultClientManager: SplitClientManager { eventsManagerCoordinator.start() if let producer = telemetryProducer { - defaultClient?.on(event: .sdkReadyFromCache) { + defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: ["Cesar":"Guzman"])) { data in DispatchQueue.general.async { [weak self] in if let self = self { producer.recordTimeUntilReadyFromCache(self.telemetryStopwatch?.interval() ?? 0) @@ -88,7 +88,7 @@ class DefaultClientManager: SplitClientManager { } } - defaultClient?.on(event: .sdkReady) { + defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { data in DispatchQueue.general.async { [weak self] in if let self = self { producer.recordTimeUntilReady(self.telemetryStopwatch?.interval() ?? 0) diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index 41f982466..fc7518d66 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -81,5 +81,5 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { } } - func register(event: SplitEvent, task: SplitEventTask) {} + func register(event: SplitEventWithMetadata, task: SplitEventActionTask) {} } diff --git a/Split/Events/SplitEvent.swift b/Split/Events/SplitEvent.swift index d2561e2d9..0264c8ead 100644 --- a/Split/Events/SplitEvent.swift +++ b/Split/Events/SplitEvent.swift @@ -26,3 +26,24 @@ import Foundation } } } + +@objcMembers +public class SplitEventWithMetadata: NSObject { + let type: SplitEvent + let metadata: [String: Any]? + + @objc public init(type: SplitEvent, metadata: [String : Any]? = nil) { + self.type = type + self.metadata = metadata + } + + public override func isEqual(_ object: Any?) -> Bool { + guard let other = object as? SplitEventWithMetadata else { return false } + return self.type == other.type + } + + public override var hash: Int { + return type.hashValue + } +} + diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index be1368d04..8c29c0ec3 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -11,15 +11,11 @@ class SplitEventActionTask: SplitEventTask { private var eventHandler: SplitAction? private var queue: DispatchQueue? - var event: SplitEvent + var event: SplitEventWithMetadata var runInBackground: Bool = false var factory: SplitFactory - init(action: @escaping SplitAction, - event: SplitEvent, - runInBackground: Bool = false, - factory: SplitFactory, - queue: DispatchQueue? = nil) { + init(action: @escaping SplitAction, event: SplitEventWithMetadata, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { self.eventHandler = action self.event = event @@ -33,7 +29,7 @@ class SplitEventActionTask: SplitEventTask { return queue } - func run() { - eventHandler?() + func run() -> Any? { + eventHandler?(event.metadata) } } diff --git a/Split/Events/SplitEventTask.swift b/Split/Events/SplitEventTask.swift index 1655e2b25..c14cf5faf 100644 --- a/Split/Events/SplitEventTask.swift +++ b/Split/Events/SplitEventTask.swift @@ -8,8 +8,8 @@ import Foundation protocol SplitEventTask { - var event: SplitEvent { get } + var event: SplitEventWithMetadata { get } var runInBackground: Bool { get } func takeQueue() -> DispatchQueue? - func run() + func run() -> Any? } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index 02ec7e1c9..d9ab4c305 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -9,7 +9,7 @@ import Foundation protocol SplitEventsManager: AnyObject { - func register(event: SplitEvent, task: SplitEventTask) + func register(event: SplitEventWithMetadata, task: SplitEventActionTask) func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: Any]?) func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) func start() @@ -72,16 +72,16 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - func register(event: SplitEvent, task: SplitEventTask) { - let eventName = event.toString() + func register (event: SplitEventWithMetadata, task: SplitEventActionTask) { + let eventName = event.type.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) + self.executeTask(eventWithMetadata: event, task: task) return } - self.subscribe(task: task, to: event) + self.subscribe(task: task, to: event.type) } } @@ -162,7 +162,7 @@ class DefaultSplitEventsManager: SplitEventsManager { switch event.type { case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated: if isTriggered(external: .sdkReady) { - trigger(event: .sdkUpdated) + trigger(event: SplitEventWithMetadata(type: .sdkUpdated, metadata: event.metadata)) continue } self.triggerSdkReadyIfNeeded() @@ -206,12 +206,17 @@ class DefaultSplitEventsManager: SplitEventsManager { isTriggered(internal: .splitsUpdated), isTriggered(internal: .myLargeSegmentsUpdated), !isTriggered(external: .sdkReady) { - self.trigger(event: SplitEvent.sdkReady) + self.trigger(event: .sdkReady) } } - + private func trigger(event: SplitEvent) { - let eventName = event.toString() + trigger(event: SplitEventWithMetadata(type: event, metadata: nil)) + } + + private func trigger(event: SplitEventWithMetadata) { + let eventName = event.type.toString() + let eventMetadata = event.metadata?.debugDescription // If executionTimes is zero, maximum executions has been reached if executionTimes(for: eventName) == 0 { @@ -225,16 +230,16 @@ class DefaultSplitEventsManager: SplitEventsManager { Logger.d("Triggering SDK event \(eventName)") // If executionTimes is lower than zero, execute it without limitation - if let subscriptions = getSubscriptions(for: event) { + if let subscriptions = getSubscriptions(for: event.type) { for task in subscriptions { - executeTask(event: event, task: task) + executeTask(eventWithMetadata: event, task: task) } } } - private func executeTask(event: SplitEvent, task: SplitEventTask) { + private func executeTask(eventWithMetadata event: SplitEventWithMetadata, task: SplitEventTask) -> Any? { - let eventName = task.event.toString() + let eventName = task.event.type.toString() if task.runInBackground { TimeChecker.logInterval("Previous to run \(eventName) in Background") @@ -242,15 +247,19 @@ class DefaultSplitEventsManager: SplitEventsManager { let queue = task.takeQueue() ?? DispatchQueue.general queue.async { TimeChecker.logInterval("Running \(eventName) in Background queue \(queue)") - task.run() + let taskResult = task.run() + print(taskResult.debugDescription) + //return taskResult } - return + return nil } DispatchQueue.main.async { TimeChecker.logInterval("Running event on main: \(eventName)") // UI Updates - task.run() + let taskResult = task.run() + print(taskResult.debugDescription) + return taskResult } } From 06f66f12131d9f115561a5fcc5f769a561f523b3 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 10 Apr 2025 15:59:33 -0300 Subject: [PATCH 08/20] Stable point. It seems all events now support metadata --- Split/Api/DefaultSplitClient.swift | 2 +- Split/Api/SplitClient.swift | 2 +- Split/Events/SplitEventActionTask.swift | 4 ++-- Split/Events/SplitEventTask.swift | 2 +- Split/Events/SplitEventsManager.swift | 11 +++-------- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index c4eedead6..208eaee77 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -57,7 +57,7 @@ public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClie // MARK: Events extension DefaultSplitClient { - + public func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { on(event: event, runInBackground: false, queue: nil, execute: action) } diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index b44fc248a..496b98c16 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -8,7 +8,7 @@ import Foundation -public typealias SplitAction = () -> Void +public typealias SplitAction = (_ data: Any?) -> Void @objc public protocol SplitClient { diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index 8c29c0ec3..2e55718e0 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -29,7 +29,7 @@ class SplitEventActionTask: SplitEventTask { return queue } - func run() -> Any? { - eventHandler?(event.metadata) + func run(_ data: Any? = nil) -> Any? { + eventHandler?(data) } } diff --git a/Split/Events/SplitEventTask.swift b/Split/Events/SplitEventTask.swift index c14cf5faf..74ebf98b9 100644 --- a/Split/Events/SplitEventTask.swift +++ b/Split/Events/SplitEventTask.swift @@ -11,5 +11,5 @@ protocol SplitEventTask { var event: SplitEventWithMetadata { get } var runInBackground: Bool { get } func takeQueue() -> DispatchQueue? - func run() -> Any? + func run(_ data: Any?) -> Any? } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index d9ab4c305..dfaf913c4 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -237,7 +237,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func executeTask(eventWithMetadata event: SplitEventWithMetadata, task: SplitEventTask) -> Any? { + private func executeTask(eventWithMetadata event: SplitEventWithMetadata, task: SplitEventTask) { let eventName = task.event.type.toString() @@ -247,19 +247,14 @@ class DefaultSplitEventsManager: SplitEventsManager { let queue = task.takeQueue() ?? DispatchQueue.general queue.async { TimeChecker.logInterval("Running \(eventName) in Background queue \(queue)") - let taskResult = task.run() - print(taskResult.debugDescription) - //return taskResult + let taskResult = task.run(event.metadata) } - return nil } DispatchQueue.main.async { TimeChecker.logInterval("Running event on main: \(eventName)") // UI Updates - let taskResult = task.run() - print(taskResult.debugDescription) - return taskResult + let taskResult = task.run(event.metadata) } } From 759e7ae6d368692cabd9f2ca1abd3ea8015669cd Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 11 Apr 2025 11:19:30 -0300 Subject: [PATCH 09/20] Stable point: Now segments updates info is also passed to the customer --- Split/Api/DefaultSplitClient.swift | 7 ++- Split/Api/FailHelpers.swift | 6 +++ Split/Api/LocalhostSplitClient.swift | 5 ++ Split/Api/SplitClient.swift | 3 +- Split/Api/SplitClientManager.swift | 2 +- Split/Common/Structs/BlockingQueue.swift | 10 ++-- Split/Events/EventsManagerCoordinator.swift | 8 +-- ...ternalEvent.swift => SplitEventCase.swift} | 10 ++-- Split/Events/SplitEventsManager.swift | 29 ++++++----- .../Refresh/PeriodicSyncWorker.swift | 10 ++-- .../Refresh/RetryableSegmentsSyncWorker.swift | 50 ++++++++----------- .../Refresh/RetryableSyncWorker.swift | 6 +-- .../Sync/FeatureFlagsSynchronizer.swift | 2 +- 13 files changed, 77 insertions(+), 71 deletions(-) rename Split/Events/{SplitInternalEvent.swift => SplitEventCase.swift} (66%) diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index 208eaee77..d77f2bcf1 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -12,7 +12,7 @@ import Foundation typealias DestroyHandler = () -> Void public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClient { - + private var storageContainer: SplitStorageContainer private var key: Key private let config: SplitClientConfig @@ -58,6 +58,11 @@ public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClie // MARK: Events extension DefaultSplitClient { + public func on(event: SplitEvent, perform: SplitAction?) { + guard let perform = perform else { return } + on(event: SplitEventWithMetadata(type: event, metadata: nil), execute: perform) + } + public func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { on(event: event, runInBackground: false, queue: nil, execute: action) } diff --git a/Split/Api/FailHelpers.swift b/Split/Api/FailHelpers.swift index 86e2353b8..7d74e8563 100644 --- a/Split/Api/FailHelpers.swift +++ b/Split/Api/FailHelpers.swift @@ -53,6 +53,12 @@ class FailedClient: SplitClient { func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] { return [:] } + + public func on(event: SplitEvent, perform: SplitAction?) { + } + + func on(event: SplitEvent, perform: @escaping ([String : Any]?) -> Void) { + } func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { } diff --git a/Split/Api/LocalhostSplitClient.swift b/Split/Api/LocalhostSplitClient.swift index c099c7a64..dd8a68c5b 100644 --- a/Split/Api/LocalhostSplitClient.swift +++ b/Split/Api/LocalhostSplitClient.swift @@ -117,6 +117,11 @@ public final class LocalhostSplitClient: NSObject, SplitClient { } return results } + + public func on(event: SplitEvent, perform: SplitAction?) { + guard let perform = perform else { return } + on(event: SplitEventWithMetadata(type: event, metadata: nil), execute: perform) + } public func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) { diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index 496b98c16..3ea0351a8 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -33,10 +33,11 @@ public typealias SplitAction = (_ data: Any?) -> Void @objc(getTreatmentsWithConfigForSplits:attributes:evaluationOptions:) func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] + func on(event: SplitEvent, perform: SplitAction?) -> Void func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) - + // MARK: Track feature func track(trafficType: String, eventType: String) -> Bool func track(trafficType: String, eventType: String, value: Double) -> Bool diff --git a/Split/Api/SplitClientManager.swift b/Split/Api/SplitClientManager.swift index 400afda1d..e85cd0fc1 100644 --- a/Split/Api/SplitClientManager.swift +++ b/Split/Api/SplitClientManager.swift @@ -80,7 +80,7 @@ class DefaultClientManager: SplitClientManager { eventsManagerCoordinator.start() if let producer = telemetryProducer { - defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: ["Cesar":"Guzman"])) { data in + defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { data in DispatchQueue.general.async { [weak self] in if let self = self { producer.recordTimeUntilReadyFromCache(self.telemetryStopwatch?.interval() ?? 0) diff --git a/Split/Common/Structs/BlockingQueue.swift b/Split/Common/Structs/BlockingQueue.swift index df5bfbb6f..8fd45f67f 100644 --- a/Split/Common/Structs/BlockingQueue.swift +++ b/Split/Common/Structs/BlockingQueue.swift @@ -73,18 +73,18 @@ class GenericBlockingQueue { // Protocol to allow mocking protocol InternalEventBlockingQueue { - func add(_ item: SplitInternalEventWithMetadata) - func take() throws -> SplitInternalEventWithMetadata + func add(_ item: SplitInternalEvent) + func take() throws -> SplitInternalEvent func stop() } class DefaultInternalEventBlockingQueue: InternalEventBlockingQueue { - let blockingQueue = GenericBlockingQueue() - func add(_ item: SplitInternalEventWithMetadata) { + let blockingQueue = GenericBlockingQueue() + func add(_ item: SplitInternalEvent) { blockingQueue.add(item) } - func take() throws -> SplitInternalEventWithMetadata { + func take() throws -> SplitInternalEvent { let value = try blockingQueue.take() return value } diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index fc7518d66..dec9f3af1 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -17,15 +17,15 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { private var defaultManager: SplitEventsManager? private var managers = [Key: SplitEventsManager]() - private var triggered = Set() + private var triggered = Set() private let queue = DispatchQueue(label: "split-event-manager-coordinator") - private let eventsToHandle: Set = Set( + private let eventsToHandle: Set = Set( [.splitsLoadedFromCache, .splitsUpdated, .splitKilledNotification] ) - func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String : Any]? = nil) { + func notifyInternalEvent(_ event: SplitEventCase, _ metadata: [String : Any]? = nil) { if !eventsToHandle.contains(event) { return } @@ -39,7 +39,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { } } - func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) { + func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) { notifyInternalEvent(event.type, event.metadata) } diff --git a/Split/Events/SplitInternalEvent.swift b/Split/Events/SplitEventCase.swift similarity index 66% rename from Split/Events/SplitInternalEvent.swift rename to Split/Events/SplitEventCase.swift index f67fa824d..a2c3640e8 100644 --- a/Split/Events/SplitInternalEvent.swift +++ b/Split/Events/SplitEventCase.swift @@ -7,7 +7,7 @@ import Foundation -enum SplitInternalEvent { +enum SplitEventCase { case mySegmentsUpdated case myLargeSegmentsUpdated case splitsUpdated @@ -19,16 +19,16 @@ enum SplitInternalEvent { case splitKilledNotification } -struct SplitInternalEventWithMetadata: Equatable { - let type: SplitInternalEvent +struct SplitInternalEvent: Equatable { + let type: SplitEventCase let metadata: [String: Any]? - init(type: SplitInternalEvent, metadata: [String : Any]? = nil) { + init(type: SplitEventCase, metadata: [String : Any]? = nil) { self.type = type self.metadata = metadata } - static func == (lhs: SplitInternalEventWithMetadata, rhs: SplitInternalEventWithMetadata) -> Bool { + static func == (lhs: SplitInternalEvent, rhs: SplitInternalEvent) -> Bool { return lhs.type == rhs.type } } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index dfaf913c4..d612e6f3b 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -10,8 +10,8 @@ import Foundation protocol SplitEventsManager: AnyObject { func register(event: SplitEventWithMetadata, task: SplitEventActionTask) - func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: Any]?) - func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) + func notifyInternalEvent(_ event: SplitEventCase, _ metadata: [String: Any]?) + func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) func start() func stop() func eventAlreadyTriggered(event: SplitEvent) -> Bool @@ -21,7 +21,7 @@ protocol SplitEventsManager: AnyObject { It allows calling `notifyInternalEvent(.event)` without needing to pass `nil` as metadata. Do not remove unless all usages have migrated to the new signature. */ extension SplitEventsManager { - func notifyInternalEvent(_ event: SplitInternalEvent) { + func notifyInternalEvent(_ event: SplitEventCase) { notifyInternalEvent(event, nil) } } @@ -33,7 +33,7 @@ class DefaultSplitEventsManager: SplitEventsManager { private var subscriptions = [SplitEvent: [SplitEventTask]]() private var executionTimes: [String: Int] - private var triggered: [SplitInternalEventWithMetadata] + private var triggered: [SplitInternalEvent] private let processQueue: DispatchQueue private let dataAccessQueue: DispatchQueue private var isStarted: Bool @@ -45,7 +45,7 @@ class DefaultSplitEventsManager: SplitEventsManager { self.isStarted = false self.sdkReadyTimeStart = Date().unixTimestampInMiliseconds() self.readingRefreshTime = 300 - self.triggered = [SplitInternalEventWithMetadata]() + self.triggered = [SplitInternalEvent]() self.eventsQueue = DefaultInternalEventBlockingQueue() self.executionTimes = [String: Int]() registerMaxAllowedExecutionTimesPerEvent() @@ -54,19 +54,19 @@ class DefaultSplitEventsManager: SplitEventsManager { 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(SplitEventCase.sdkReadyTimeoutReached) } } } - func notifyInternalEvent(_ event: SplitInternalEvent, _ metadata: [String: Any]? = nil) { - notifyInternalEventWithMetadata(SplitInternalEventWithMetadata(type: event, metadata: metadata)) + func notifyInternalEvent(_ event: SplitEventCase, _ metadata: [String: Any]? = nil) { + notifyInternalEventWithMetadata(SplitInternalEvent(type: event, metadata: metadata)) } - func notifyInternalEventWithMetadata(_ event: SplitInternalEventWithMetadata) { + func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) { processQueue.async { [weak self] in if let self = self { - Logger.i("Event \(event.type) notified - Details: \(event.metadata ?? [:])") + Logger.v("Event \(event.type) notified - Metadata: \(event.metadata ?? [:])") self.eventsQueue.add(event) } } @@ -142,7 +142,7 @@ class DefaultSplitEventsManager: SplitEventsManager { return isRunning } - private func takeEvent() -> SplitInternalEventWithMetadata? { + private func takeEvent() -> SplitInternalEvent? { do { return try eventsQueue.take() } catch BlockingQueueError.hasBeenStopped { @@ -216,7 +216,6 @@ class DefaultSplitEventsManager: SplitEventsManager { private func trigger(event: SplitEventWithMetadata) { let eventName = event.type.toString() - let eventMetadata = event.metadata?.debugDescription // If executionTimes is zero, maximum executions has been reached if executionTimes(for: eventName) == 0 { @@ -258,12 +257,12 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func isTriggered(internal event: SplitInternalEventWithMetadata) -> Bool { + private func isTriggered(internal event: SplitInternalEvent) -> Bool { return triggered.filter { $0 == event }.count > 0 } - private func isTriggered(internal event: SplitInternalEvent) -> Bool { - return isTriggered(internal: SplitInternalEventWithMetadata(type: event, metadata: nil)) + private func isTriggered(internal event: SplitEventCase) -> Bool { + return isTriggered(internal: SplitInternalEvent(type: event, metadata: nil)) } // MARK: Safe Data Access diff --git a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift index 1bb25ae99..10d245bb2 100644 --- a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift @@ -130,10 +130,9 @@ class BasePeriodicSyncWorker: PeriodicSyncWorker { Logger.i("Fetch from remote not implemented") } - func notifyUpdate(_ events: [SplitInternalEventWithMetadata]) { + func notifyUpdate(_ events: [SplitInternalEvent]) { events.forEach { eventsManager.notifyInternalEventWithMetadata($0) - Logger.i("Passing metadata \(String(describing: $0.metadata?.description))") } } } @@ -179,7 +178,7 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker { updatedFlags.append(flag) } - notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: ["Updated Flags": updatedFlags])]) + notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: ["Updated Flags": updatedFlags])]) } } @@ -219,9 +218,8 @@ class PeriodicMySegmentsSyncWorker: BasePeriodicSyncWorker { mlsTill: myLargeSegmentsStorage.changeNumber, headers: nil) if result.success { - if result.msUpdated || result.mlsUpdated { - // For now is not necessary specify which entity was updated - notifyUpdate([SplitInternalEventWithMetadata(type: .mySegmentsUpdated, metadata: ["Fetching from: ": "Remote"]) ]) + if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty { + notifyUpdate([SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Updated segments": result.msUpdated + result.mlsUpdated ])]) } } } catch { diff --git a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift index 04f0a2a4b..67e1e9e59 100644 --- a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift @@ -46,11 +46,10 @@ class RetryableMySegmentsSyncWorker: BaseRetryableSyncWorker { if result.success { if !isSdkReadyTriggered() { // Notifying both to trigger SDK Ready - notifyUpdate([SplitInternalEventWithMetadata(type: .mySegmentsUpdated, metadata: [:])]) - notifyUpdate([SplitInternalEventWithMetadata(type: .myLargeSegmentsUpdated, metadata: [:])]) - } else if result.msUpdated || result.mlsUpdated { - // For now is not necessary specify which entity was updated - notifyUpdate([SplitInternalEventWithMetadata(type: .mySegmentsUpdated, metadata: [:])]) + notifyUpdate([SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Segments updated" : result.msUpdated ])]) + notifyUpdate([SplitInternalEvent(type: .myLargeSegmentsUpdated, metadata: ["Large segments updated" : result.mlsUpdated ])]) + } else if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty { + notifyUpdate([SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Segments updated" : result.msUpdated + result.mlsUpdated ])]) } return true } @@ -71,8 +70,8 @@ struct SegmentsSyncResult { let success: Bool let msChangeNumber: Int64 let mlsChangeNumber: Int64 - let msUpdated: Bool - let mlsUpdated: Bool + let msUpdated: [String] + let mlsUpdated: [String] } protocol SegmentsSyncHelper { @@ -86,8 +85,8 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper { struct FetchResult { let msTill: Int64 let mlsTill: Int64 - let msUpdated: Bool - let mlsUdated: Bool + let msUpdated: [String] + let mlsUpdated: [String] } private let segmentsFetcher: HttpMySegmentsFetcher @@ -165,7 +164,7 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper { msChangeNumber: result.msTill, mlsChangeNumber: result.mlsTill, msUpdated: result.msUpdated, - mlsUpdated: result.mlsUdated) + mlsUpdated: result.mlsUpdated) } attemptCount+=1 if attemptCount < maxAttempts { @@ -175,25 +174,20 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper { return SegmentsSyncResult(success: false, msChangeNumber: -1, mlsChangeNumber: -1, - msUpdated: false, - mlsUpdated: false) + msUpdated: [], + mlsUpdated: []) } - private func fetchUntil(till: Int64?, - headers: HttpHeaders? = nil) throws -> FetchResult { + private func fetchUntil(till: Int64?, headers: HttpHeaders? = nil) throws -> FetchResult { - let oldChange = SegmentChange(segments: mySegmentsStorage.getAll().asArray(), - changeNumber: mySegmentsStorage.changeNumber) + let oldChange = SegmentChange(segments: mySegmentsStorage.getAll().asArray(), changeNumber: mySegmentsStorage.changeNumber) - let oldLargeChange = SegmentChange(segments: myLargeSegmentsStorage.getAll().asArray(), - changeNumber: myLargeSegmentsStorage.changeNumber) + let oldLargeChange = SegmentChange(segments: myLargeSegmentsStorage.getAll().asArray(), changeNumber: myLargeSegmentsStorage.changeNumber) - var prevChange = AllSegmentsChange(mySegmentsChange: oldChange, - myLargeSegmentsChange: oldLargeChange) + var prevChange = AllSegmentsChange(mySegmentsChange: oldChange, myLargeSegmentsChange: oldLargeChange) + while true { - guard let change = try segmentsFetcher.execute(userKey: userKey, - till: till, - headers: headers) else { + guard let change = try segmentsFetcher.execute(userKey: userKey, till: till, headers: headers) else { throw HttpError.unknown(code: -1, message: "Segment result is null") } @@ -201,10 +195,8 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper { let myLargeSegmentsChange = change.myLargeSegmentsChange if !isOutdated(change, prevChange) { - let msChanged = changeChecker.mySegmentsHaveChanged(old: oldChange, - new: mySegmentsChange) - let mlsChanged = changeChecker.mySegmentsHaveChanged(old: oldLargeChange, - new: myLargeSegmentsChange) + let msChanged = changeChecker.mySegmentsHaveChanged(old: oldChange, new: mySegmentsChange) + let mlsChanged = changeChecker.mySegmentsHaveChanged(old: oldLargeChange, new: myLargeSegmentsChange) Logger.d("Checking my segments update") checkAndUpdate(isChanged: msChanged, change: mySegmentsChange, storage: mySegmentsStorage) Logger.d("Checking my large segments update") @@ -212,8 +204,8 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper { return FetchResult(msTill: mySegmentsChange.unwrappedChangeNumber, mlsTill: myLargeSegmentsChange.unwrappedChangeNumber, - msUpdated: msChanged, - mlsUdated: mlsChanged) + msUpdated: mySegmentsChange.segments.compactMap {$0.name}, + mlsUpdated: myLargeSegmentsChange.segments.compactMap {$0.name}) } prevChange = change } diff --git a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift index a37b1b4bc..73e8e5a94 100644 --- a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift @@ -79,7 +79,7 @@ class BaseRetryableSyncWorker: RetryableSyncWorker { } } - func notifyUpdate(_ events: [SplitInternalEventWithMetadata]) { + func notifyUpdate(_ events: [SplitInternalEvent]) { events.forEach { eventsManager.notifyInternalEvent($0.type, $0.metadata) } @@ -163,7 +163,7 @@ class RetryableSplitsSyncWorker: BaseRetryableSyncWorker { let result = try syncHelper.sync(since: changeNumber, clearBeforeUpdate: clearCache) if result.success { if !isSdkReadyTriggered() || result.featureFlagsUpdated.count > 0 { - notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: ["Flags": flagsSpec])]) + notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: ["Flags": result.featureFlagsUpdated ])]) } resetBackoffCounter() return true @@ -220,7 +220,7 @@ class RetryableSplitsUpdateWorker: BaseRetryableSyncWorker { headers: ServiceConstants.controlNoCacheHeader) if result.success { if result.featureFlagsUpdated.count > 0 { - notifyUpdate([SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: [:]) ]) + notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: [:]) ]) } resetBackoffCounter() return true diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index 2b5f9cb25..70ae3a9fe 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -143,7 +143,7 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { } func notifyUpdated(flagList: [String]) { - splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEventWithMetadata(type: .splitsUpdated, metadata: ["Updated flags:":flagList] )) + splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .splitsUpdated, metadata: ["Updated flags:":flagList] )) } func pause() { From 12617af5ba641496be9342f8e486c40e8605b74c Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 15 Apr 2025 10:59:31 -0300 Subject: [PATCH 10/20] Improving definitions --- Split/Events/SplitEventActionTask.swift | 2 +- Split/Events/SplitEventTask.swift | 2 +- Split/Events/SplitEventsManager.swift | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index 2e55718e0..d88f46c4b 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -29,7 +29,7 @@ class SplitEventActionTask: SplitEventTask { return queue } - func run(_ data: Any? = nil) -> Any? { + func run(_ data: Any? = nil) { eventHandler?(data) } } diff --git a/Split/Events/SplitEventTask.swift b/Split/Events/SplitEventTask.swift index 74ebf98b9..f07cd5615 100644 --- a/Split/Events/SplitEventTask.swift +++ b/Split/Events/SplitEventTask.swift @@ -11,5 +11,5 @@ protocol SplitEventTask { var event: SplitEventWithMetadata { get } var runInBackground: Bool { get } func takeQueue() -> DispatchQueue? - func run(_ data: Any?) -> Any? + func run(_ data: Any?) } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index d612e6f3b..6fe856edd 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -246,14 +246,14 @@ class DefaultSplitEventsManager: SplitEventsManager { let queue = task.takeQueue() ?? DispatchQueue.general queue.async { TimeChecker.logInterval("Running \(eventName) in Background queue \(queue)") - let taskResult = task.run(event.metadata) + task.run(event.metadata) } } DispatchQueue.main.async { TimeChecker.logInterval("Running event on main: \(eventName)") // UI Updates - let taskResult = task.run(event.metadata) + task.run(event.metadata) } } From a3aad66f5d4ef77a7805331361918e186e8474fc Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Sun, 20 Apr 2025 03:13:02 -0300 Subject: [PATCH 11/20] Working flawlessly with all events (even for key). Bug of background event running in Main too: solved --- Split/Api/DefaultSplitClient.swift | 22 ++++++++++++++ Split/Api/FailHelpers.swift | 3 ++ Split/Api/LocalhostSplitClient.swift | 18 ++++++++++++ Split/Api/SplitClient.swift | 6 ++-- Split/Api/SplitClientManager.swift | 4 +-- Split/Events/SplitEventActionTask.swift | 17 ++++++++--- Split/Events/SplitEventsManager.swift | 29 ++++++++++--------- .../Refresh/RetryableSyncWorker.swift | 2 +- Split/Localhost/LocalhostClientManager.swift | 4 +-- Split/Localhost/LocalhostSynchronizer.swift | 2 +- Split/Network/Sync/ByKeyFacade.swift | 6 ++-- .../Sync/FeatureFlagsSynchronizer.swift | 1 + 12 files changed, 85 insertions(+), 29 deletions(-) diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index d77f2bcf1..0816760d3 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -87,6 +87,25 @@ extension DefaultSplitClient { task.event = event on(event: event, executeTask: task) } + + public func on(event: SplitEvent, performWithMetadata action: SplitActionWithMetadata?) { + guard let action = action else { return } + onWithMetadata(event:SplitEventWithMetadata(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) + } + + private func onWithMetadata(event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitActionWithMetadata) { + guard let factory = clientManager?.splitFactory else { + return + } + + let task = SplitEventActionTask(action: action, event: event, + runInBackground: runInBackground, + factory: factory, + queue: queue) + task.event = event + on(event: event, executeTask: task) + } + private func on(event: SplitEventWithMetadata, executeTask task: SplitEventActionTask) { if event.type != .sdkReadyFromCache, @@ -97,6 +116,9 @@ extension DefaultSplitClient { } eventsManager.register(event: event, task: task) } + + + } // MARK: Treatment / Evaluation diff --git a/Split/Api/FailHelpers.swift b/Split/Api/FailHelpers.swift index 7d74e8563..50277542e 100644 --- a/Split/Api/FailHelpers.swift +++ b/Split/Api/FailHelpers.swift @@ -13,6 +13,9 @@ import Foundation /// class FailedClient: SplitClient { + func on(event: SplitEvent, performWithMetadata: SplitActionWithMetadata?) { + + } func getTreatment(_ split: String) -> String { return SplitConstants.control diff --git a/Split/Api/LocalhostSplitClient.swift b/Split/Api/LocalhostSplitClient.swift index dd8a68c5b..2f9cf44dc 100644 --- a/Split/Api/LocalhostSplitClient.swift +++ b/Split/Api/LocalhostSplitClient.swift @@ -122,6 +122,11 @@ public final class LocalhostSplitClient: NSObject, SplitClient { guard let perform = perform else { return } on(event: SplitEventWithMetadata(type: event, metadata: nil), execute: perform) } + + public func on(event: SplitEvent, performWithMetadata: SplitActionWithMetadata?) { + guard let performWithMetadata = performWithMetadata else { return } + on(eventWithMetadata: SplitEventWithMetadata(type: event, metadata: nil), runInBackground: false, queue: nil, execute: performWithMetadata) + } public func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) { @@ -152,6 +157,19 @@ public final class LocalhostSplitClient: NSObject, SplitClient { eventsManager.register(event: event, task: task) } } + + private func on(eventWithMetadata event: SplitEventWithMetadata, runInBackground: Bool, + queue: DispatchQueue?, execute action: @escaping SplitActionWithMetadata) { + + guard let factory = clientManger?.splitFactory else { return } + if let eventsManager = self.eventsManager { + let task = SplitEventActionTask(action: action, event: event, + runInBackground: runInBackground, + factory: factory, + queue: queue) + eventsManager.register(event: event, task: task) + } + } public func track(trafficType: String, eventType: String) -> Bool { return true diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index 3ea0351a8..9d0c9a1b4 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -8,7 +8,8 @@ import Foundation -public typealias SplitAction = (_ data: Any?) -> Void +public typealias SplitAction = () -> Void +public typealias SplitActionWithMetadata = (_ data: Any?) -> Void @objc public protocol SplitClient { @@ -32,8 +33,9 @@ public typealias SplitAction = (_ data: Any?) -> Void func getTreatmentWithConfig(_ split: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> SplitResult @objc(getTreatmentsWithConfigForSplits:attributes:evaluationOptions:) func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] - + func on(event: SplitEvent, perform: SplitAction?) -> Void + func on(event: SplitEvent, performWithMetadata: SplitActionWithMetadata?) -> Void func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) diff --git a/Split/Api/SplitClientManager.swift b/Split/Api/SplitClientManager.swift index e85cd0fc1..43cbbff1b 100644 --- a/Split/Api/SplitClientManager.swift +++ b/Split/Api/SplitClientManager.swift @@ -80,7 +80,7 @@ class DefaultClientManager: SplitClientManager { eventsManagerCoordinator.start() if let producer = telemetryProducer { - defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { data in + defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { DispatchQueue.general.async { [weak self] in if let self = self { producer.recordTimeUntilReadyFromCache(self.telemetryStopwatch?.interval() ?? 0) @@ -88,7 +88,7 @@ class DefaultClientManager: SplitClientManager { } } - defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { data in + defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { DispatchQueue.general.async { [weak self] in if let self = self { producer.recordTimeUntilReady(self.telemetryStopwatch?.interval() ?? 0) diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index d88f46c4b..a36fe0253 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -8,15 +8,23 @@ import Foundation class SplitEventActionTask: SplitEventTask { - + private var eventHandler: SplitAction? + private var eventHandlerWithMetadata: SplitActionWithMetadata? private var queue: DispatchQueue? var event: SplitEventWithMetadata var runInBackground: Bool = false var factory: SplitFactory + init(action: @escaping SplitActionWithMetadata, event: SplitEventWithMetadata, 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: SplitEventWithMetadata, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { - self.eventHandler = action self.event = event self.runInBackground = runInBackground @@ -29,7 +37,8 @@ class SplitEventActionTask: SplitEventTask { return queue } - func run(_ data: Any? = nil) { - eventHandler?(data) + func run(_ data: Any?) { + eventHandler?() + eventHandlerWithMetadata?(data) } } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index 6fe856edd..e0c31f857 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -27,6 +27,7 @@ extension SplitEventsManager { } class DefaultSplitEventsManager: SplitEventsManager { + private let readingRefreshTime: Int private var sdkReadyTimeStart: Int64 @@ -38,6 +39,8 @@ class DefaultSplitEventsManager: SplitEventsManager { private let dataAccessQueue: DispatchQueue private var isStarted: Bool private var eventsQueue: InternalEventBlockingQueue + + private let lock = NSLock() init(config: SplitClientConfig) { self.processQueue = DispatchQueue(label: "split-evt-mngr-process", attributes: .concurrent) @@ -86,16 +89,14 @@ class DefaultSplitEventsManager: SplitEventsManager { } func start() { - dataAccessQueue.sync { - if self.isStarted { - return + lock.lock() + if !isStarted { + isStarted = true } - self.isStarted = true - } + lock.unlock() + processQueue.async { [weak self] in - if let self = self { - self.processEvents() - } + self?.processEvents() } } @@ -248,12 +249,12 @@ class DefaultSplitEventsManager: SplitEventsManager { TimeChecker.logInterval("Running \(eventName) in Background queue \(queue)") task.run(event.metadata) } - } - - DispatchQueue.main.async { - TimeChecker.logInterval("Running event on main: \(eventName)") - // UI Updates - task.run(event.metadata) + } else { + DispatchQueue.main.async { + TimeChecker.logInterval("Running event on main: \(eventName)") + // UI Updates + task.run(event.metadata) + } } } diff --git a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift index 73e8e5a94..d8ccbd39e 100644 --- a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift @@ -220,7 +220,7 @@ class RetryableSplitsUpdateWorker: BaseRetryableSyncWorker { headers: ServiceConstants.controlNoCacheHeader) if result.success { if result.featureFlagsUpdated.count > 0 { - notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: [:]) ]) + notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: ["Metadata" : result.featureFlagsUpdated.description]) ]) } resetBackoffCounter() return true diff --git a/Split/Localhost/LocalhostClientManager.swift b/Split/Localhost/LocalhostClientManager.swift index 6679e6859..bcc70ee2d 100644 --- a/Split/Localhost/LocalhostClientManager.swift +++ b/Split/Localhost/LocalhostClientManager.swift @@ -92,8 +92,8 @@ class LocalhostClientManager: SplitClientManager { let newGroup = LocalhostComponentsGroup(client: newClient, eventsManager: newEventsManager) clients.setValue(newGroup, forKey: key.matchingKey) eventsManagerCoordinator.add(newEventsManager, forKey: key) - newEventsManager.notifyInternalEvent(.mySegmentsUpdated) - newEventsManager.notifyInternalEvent(.myLargeSegmentsUpdated) + newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Metadata forKey segments": ""])) + newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .myLargeSegmentsUpdated, metadata: ["Metadata forKey largeSegments": ""])) return newClient } diff --git a/Split/Localhost/LocalhostSynchronizer.swift b/Split/Localhost/LocalhostSynchronizer.swift index 28ca13511..3785ca971 100644 --- a/Split/Localhost/LocalhostSynchronizer.swift +++ b/Split/Localhost/LocalhostSynchronizer.swift @@ -74,7 +74,7 @@ class LocalhostSynchronizer: FeatureFlagsSynchronizer { // Update will remove all records before insert new ones _ = self.featureFlagsStorage.update(splitChange: change) - self.eventsManager.notifyInternalEvent(.splitsUpdated) + self.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .splitsUpdated, metadata: ["Splits updated":"\(featureFlags.values.description)"])) } } } diff --git a/Split/Network/Sync/ByKeyFacade.swift b/Split/Network/Sync/ByKeyFacade.swift index 5f7212bdf..7642dad39 100644 --- a/Split/Network/Sync/ByKeyFacade.swift +++ b/Split/Network/Sync/ByKeyFacade.swift @@ -77,7 +77,7 @@ class DefaultByKeyFacade: ByKeyFacade { func loadAttributesFromCache(forKey key: String) { doInAll(forMatchingKey: key) { group in group.attributesStorage.loadLocal() - group.eventsManager.notifyInternalEvent(.attributesLoadedFromCache) + group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .attributesLoadedFromCache, metadata: ["Metadata":"Attributes from cache ready"])) } TimeChecker.logInterval("Time until attributes loaded from cache") } @@ -122,13 +122,13 @@ class DefaultByKeyFacade: ByKeyFacade { func notifyMySegmentsUpdated(forKey key: String) { doInAll(forMatchingKey: key) { group in - group.eventsManager.notifyInternalEvent(.mySegmentsUpdated) + group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Metadata":"Segments updated for key \(key)"])) } } func notifyMyLargeSegmentsUpdated(forKey key: String) { doInAll(forMatchingKey: key) { group in - group.eventsManager.notifyInternalEvent(.myLargeSegmentsUpdated) + group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .myLargeSegmentsUpdated, metadata: ["Metadata":"Large segments updated for key \(key)"])) } } diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index 70ae3a9fe..5d3d466fe 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -85,6 +85,7 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { splitsStorage.loadLocal() if splitsStorage.getAll().count > 0 { self.splitEventsManager.notifyInternalEvent(.splitsLoadedFromCache) + self.splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .attributesLoadedFromCache, metadata: ["Metadata":"Splits from cache ready"])) } self.broadcasterChannel.push(event: .splitLoadedFromCache) Logger.v("Notifying Splits loaded from cache") From 534ec8c1177419ec32ff4c4af60ae5c8d3c32b66 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 12 May 2025 20:18:34 -0300 Subject: [PATCH 12/20] Stable --- CHANGES.txt | 2 +- README.md | 2 +- Split/Api/DefaultSplitClient.swift | 83 ++++++++-------- Split/Api/FailHelpers.swift | 12 +-- Split/Api/LocalhostSplitClient.swift | 26 ++--- Split/Api/SplitClient.swift | 105 +++++++++----------- Split/Api/SplitClientManager.swift | 4 +- Split/Engine/DefaultTreatmentManager.swift | 4 +- Split/Events/EventsManagerCoordinator.swift | 10 +- Split/Events/SplitEvent.swift | 19 ++-- Split/Events/SplitEventActionTask.swift | 8 +- Split/Events/SplitEventCase.swift | 10 +- Split/Events/SplitEventTask.swift | 4 +- Split/Events/SplitEventsManager.swift | 50 +++++----- 14 files changed, 158 insertions(+), 181 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 753ae5eee..afa7531da 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -306,7 +306,7 @@ - Added client dependency to event background task 0.2.0: (Apr 25, 2018) - - Added events listener. Available events: SplitEvent.sdkReady and SplitEvent.sdkReadyTimedOut + - Added events listener. Available events: SplitEventCase.sdkReady and SplitEventCase.sdkReadyTimedOut 0.1.5: (Mar 19, 2018) - Added first load from cache diff --git a/README.md b/README.md index c1a533bd4..e7b006628 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ let factory = factoryBuilder.build() let client = factory?.client // Subscribe to SDK READY event and evaluate your feature flag -client?.on(event: SplitEvent.sdkReady) { +client?.on(event: SplitEventCase.sdkReady) { if let client = client { let treatment = client.getTreatment("my_first_feature_flag") if treatment == "on" { diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index 0816760d3..e137bf8b5 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -12,7 +12,7 @@ import Foundation typealias DestroyHandler = () -> Void public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClient { - + private var storageContainer: SplitStorageContainer private var key: Key private let config: SplitClientConfig @@ -58,65 +58,60 @@ public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClie // MARK: Events extension DefaultSplitClient { - public func on(event: SplitEvent, perform: SplitAction?) { - guard let perform = perform else { return } - on(event: SplitEventWithMetadata(type: event, metadata: nil), execute: perform) - } - - public func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { - on(event: event, runInBackground: false, queue: nil, execute: action) - } - - public func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) { - on(event: event, runInBackground: runInBackground, queue: nil, execute: action) - } - - public func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) { - on(event: event, runInBackground: true, queue: queue, execute: action) - } - - private func on(event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { + private func onWithMetadata(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute actionWithMetadata: @escaping SplitActionWithMetadata) { guard let factory = clientManager?.splitFactory else { return } - let task = SplitEventActionTask(action: action, event: event, + let task = SplitEventActionTask(action: actionWithMetadata, event: event.type, runInBackground: runInBackground, factory: factory, queue: queue) - task.event = event - on(event: event, executeTask: task) + task.event = event.type + on(event: event.type, executeTask: task) } - public func on(event: SplitEvent, performWithMetadata action: SplitActionWithMetadata?) { - guard let action = action else { return } - onWithMetadata(event:SplitEventWithMetadata(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) - } - - private func onWithMetadata(event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitActionWithMetadata) { - guard let factory = clientManager?.splitFactory else { - return - } - - let task = SplitEventActionTask(action: action, event: event, - runInBackground: runInBackground, - factory: factory, - queue: queue) - task.event = event - on(event: event, executeTask: task) - } + public func on(event: SplitEventCase, performWithMetadata action: SplitActionWithMetadata?) { + guard let action = action else { return } + onWithMetadata(event: SplitEvent(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) + } - private func on(event: SplitEventWithMetadata, executeTask task: SplitEventActionTask) { - if event.type != .sdkReadyFromCache, - eventsManager.eventAlreadyTriggered(event: event.type) { - Logger.w("A handler was added for \(event.type.toString()) on the SDK, " + + private func on(event: SplitEventCase, 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: SplitEvent(type: event, metadata: nil), task: task) } + private func on(event: SplitEventCase, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { + 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) + } + + public func on(event: SplitEventCase, execute action: @escaping SplitAction) { + on(event: event, runInBackground: false, queue: nil, execute: action) + } + + public func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) { + on(event: event, runInBackground: runInBackground, queue: nil, execute: action) + } + + public func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) { + on(event: event, runInBackground: true, queue: queue, execute: action) + } + } diff --git a/Split/Api/FailHelpers.swift b/Split/Api/FailHelpers.swift index 50277542e..584754d3c 100644 --- a/Split/Api/FailHelpers.swift +++ b/Split/Api/FailHelpers.swift @@ -13,7 +13,7 @@ import Foundation /// class FailedClient: SplitClient { - func on(event: SplitEvent, performWithMetadata: SplitActionWithMetadata?) { + func on(event: SplitEventCase, performWithMetadata: SplitActionWithMetadata?) { } @@ -57,20 +57,20 @@ class FailedClient: SplitClient { return [:] } - public func on(event: SplitEvent, perform: SplitAction?) { + public func on(event: SplitEventCase, perform: SplitAction?) { } - func on(event: SplitEvent, perform: @escaping ([String : Any]?) -> Void) { + func on(event: SplitEventCase, perform: @escaping ([String : Any]?) -> Void) { } - func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { + func on(event: SplitEventCase, execute action: @escaping SplitAction) { } - func on(event: SplitEventWithMetadata, runInBackground: Bool, + func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) { } - func on(event: SplitEventWithMetadata, + func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) { } diff --git a/Split/Api/LocalhostSplitClient.swift b/Split/Api/LocalhostSplitClient.swift index 2f9cf44dc..3e22f05de 100644 --- a/Split/Api/LocalhostSplitClient.swift +++ b/Split/Api/LocalhostSplitClient.swift @@ -118,39 +118,39 @@ public final class LocalhostSplitClient: NSObject, SplitClient { return results } - public func on(event: SplitEvent, perform: SplitAction?) { + public func on(event: SplitEventCase, perform: SplitAction?) { guard let perform = perform else { return } - on(event: SplitEventWithMetadata(type: event, metadata: nil), execute: perform) + on(event: event, execute: perform) } - public func on(event: SplitEvent, performWithMetadata: SplitActionWithMetadata?) { + public func on(event: SplitEventCase, performWithMetadata: SplitActionWithMetadata?) { guard let performWithMetadata = performWithMetadata else { return } - on(eventWithMetadata: SplitEventWithMetadata(type: event, metadata: nil), runInBackground: false, queue: nil, execute: performWithMetadata) + on(eventWithMetadata: SplitEvent(type: event, metadata: nil), runInBackground: false, queue: nil, execute: performWithMetadata) } - public func on(event: SplitEventWithMetadata, runInBackground: Bool, + public func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) { on(event: event, runInBackground: runInBackground, queue: nil, execute: action) } - public func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) { + public func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) { on(event: event, runInBackground: true, queue: queue, execute: action) } - public func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) { + public func on(event: SplitEventCase, execute action: @escaping SplitAction) { on(event: event, runInBackground: false, queue: nil, execute: action) } - private func on(event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { - on(eventWithMetadata: event, runInBackground: runInBackground, queue: queue, execute: action) + private func on(event: SplitEventCase, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { + on(eventWithMetadata: SplitEvent(type: event, metadata: nil), runInBackground: runInBackground, queue: queue, execute: action) } - private func on(eventWithMetadata event: SplitEventWithMetadata, runInBackground: Bool, + private func on(eventWithMetadata event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { guard let factory = clientManger?.splitFactory else { return } if let eventsManager = self.eventsManager { - let task = SplitEventActionTask(action: action, event: event, + let task = SplitEventActionTask(action: action, event: event.type, runInBackground: runInBackground, factory: factory, queue: queue) @@ -158,12 +158,12 @@ public final class LocalhostSplitClient: NSObject, SplitClient { } } - private func on(eventWithMetadata event: SplitEventWithMetadata, runInBackground: Bool, + private func on(eventWithMetadata event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitActionWithMetadata) { guard let factory = clientManger?.splitFactory else { return } if let eventsManager = self.eventsManager { - let task = SplitEventActionTask(action: action, event: event, + let task = SplitEventActionTask(action: action, event: event.type, runInBackground: runInBackground, factory: factory, queue: queue) diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index 9d0c9a1b4..874ee077a 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -9,93 +9,80 @@ import Foundation public typealias SplitAction = () -> Void -public typealias SplitActionWithMetadata = (_ data: Any?) -> Void +public typealias SplitActionWithMetadata = (_ data: NSDictionary?) -> Void @objc public protocol SplitClient { + + // MARK: Listeners for customer + func on(event: SplitEventCase, performWithMetadata: SplitActionWithMetadata?) -> Void + func on(event: SplitEventCase, execute action: @escaping SplitAction) + func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) + func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) + - // MARK: Evaluation feature + // MARK: Treatments func getTreatment(_ split: String, attributes: [String: Any]?) -> String func getTreatment(_ split: String) -> String - @objc(getTreatmentsForSplits:attributes:) func getTreatments(splits: [String], - attributes: [String: Any]?) -> [String: String] - + @objc(getTreatmentsForSplits:attributes:) + func getTreatments(splits: [String], attributes: [String: Any]?) -> [String: String] + + // With config func getTreatmentWithConfig(_ split: String) -> SplitResult func getTreatmentWithConfig(_ split: String, attributes: [String: Any]?) -> SplitResult - @objc(getTreatmentsWithConfigForSplits:attributes:) func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?) -> [String: SplitResult] - // MARK: Evaluation with Properties + // With Properties func getTreatment(_ split: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> String - @objc(getTreatmentsForSplits:attributes:evaluationOptions:) func getTreatments(splits: [String], - attributes: [String: Any]?, - evaluationOptions: EvaluationOptions?) -> [String: String] + @objc(getTreatmentsForSplits:attributes:evaluationOptions:) + func getTreatments(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: String] func getTreatmentWithConfig(_ split: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> SplitResult @objc(getTreatmentsWithConfigForSplits:attributes:evaluationOptions:) func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] - func on(event: SplitEvent, perform: SplitAction?) -> Void - func on(event: SplitEvent, performWithMetadata: SplitActionWithMetadata?) -> Void - func on(event: SplitEventWithMetadata, execute action: @escaping SplitAction) - func on(event: SplitEventWithMetadata, runInBackground: Bool, execute action: @escaping SplitAction) - func on(event: SplitEventWithMetadata, queue: DispatchQueue, execute action: @escaping SplitAction) + // By Flagset + func getTreatmentsByFlagSet(_ flagSet: String, attributes: [String: Any]?) -> [String: String] + func getTreatmentsByFlagSets(_ flagSets: [String], attributes: [String: Any]?) -> [String: String] + func getTreatmentsWithConfigByFlagSet(_ flagSet: String, attributes: [String: Any]?) -> [String: SplitResult] + func getTreatmentsWithConfigByFlagSets(_ flagSets: [String], attributes: [String: Any]?) -> [String: SplitResult] + func getTreatmentsByFlagSet(_ flagSet: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: String] + func getTreatmentsByFlagSets(_ flagSets: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: String] + func getTreatmentsWithConfigByFlagSet(_ flagSet: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] + func getTreatmentsWithConfigByFlagSets(_ flagSets: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] - // MARK: Track feature + + // MARK: Tracking func track(trafficType: String, eventType: String) -> Bool func track(trafficType: String, eventType: String, value: Double) -> Bool func track(eventType: String) -> Bool func track(eventType: String, value: Double) -> Bool + @objc(trackWithTrafficType:eventType:properties:) + func track(trafficType: String, eventType: String, properties: [String: Any]?) -> Bool + @objc(trackWithTrafficType:eventType:value:properties:) + func track(trafficType: String, eventType: String, value: Double, properties: [String: Any]?) -> Bool + @objc(trackWithEventType:properties:) + func track(eventType: String, properties: [String: Any]?) -> Bool + @objc(trackWithEventType:value:properties:) + func track(eventType: String, value: Double, properties: [String: Any]?) -> Bool - // MARK: Persistent attributes feature - - /// Creates or updates the value for the given attribute + + // MARK: Persistence + /// Creates or updates the value for the given attribute/ func setAttribute(name: String, value: Any) -> Bool - - /// Retrieves the value of a given attribute so it can be checked by the customer if needed + /// Retrieves the value of a given attribute so it can be checked by the customer if needed/ func getAttribute(name: String) -> Any? - - /// It will create or update all the given attributes + /// It will create or update all the given attributes/ func setAttributes(_ values: [String: Any]) -> Bool - - /// Retrieve the full attributes map + /// Retrieve the full attributes map/ func getAttributes() -> [String: Any]? - - /// Removes a given attribute from the map + /// Removes a given attribute from the map/ func removeAttribute(name: String) -> Bool - - /// Clears all attributes stored in the SDK. + /// Clears all attributes stored in the SDK./ func clearAttributes() -> Bool - - // MARK: Client lifecycle + + + // MARK: Lifecycle func flush() func destroy() func destroy(completion: (() -> Void)?) - - @objc(trackWithTrafficType:eventType:properties:) func track(trafficType: String, - eventType: String, - properties: [String: Any]?) -> Bool - - @objc(trackWithTrafficType:eventType:value:properties:) func track(trafficType: String, - eventType: String, - value: Double, - properties: [String: Any]?) -> Bool - - @objc(trackWithEventType:properties:) func track(eventType: String, - properties: [String: Any]?) -> Bool - - @objc(trackWithEventType:value:properties:) func track(eventType: String, - value: Double, - properties: [String: Any]?) -> Bool - - // MARK: Evaluation with flagsets - func getTreatmentsByFlagSet(_ flagSet: String, attributes: [String: Any]?) -> [String: String] - func getTreatmentsByFlagSets(_ flagSets: [String], attributes: [String: Any]?) -> [String: String] - func getTreatmentsWithConfigByFlagSet(_ flagSet: String, attributes: [String: Any]?) -> [String: SplitResult] - func getTreatmentsWithConfigByFlagSets(_ flagSets: [String], attributes: [String: Any]?) -> [String: SplitResult] - - // MARK: Evaluation with flagsets and properties - func getTreatmentsByFlagSet(_ flagSet: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: String] - func getTreatmentsByFlagSets(_ flagSets: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: String] - func getTreatmentsWithConfigByFlagSet(_ flagSet: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] - func getTreatmentsWithConfigByFlagSets(_ flagSets: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult] } diff --git a/Split/Api/SplitClientManager.swift b/Split/Api/SplitClientManager.swift index 43cbbff1b..9777010ed 100644 --- a/Split/Api/SplitClientManager.swift +++ b/Split/Api/SplitClientManager.swift @@ -80,7 +80,7 @@ class DefaultClientManager: SplitClientManager { eventsManagerCoordinator.start() if let producer = telemetryProducer { - defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { + defaultClient?.on(event: .sdkReadyFromCache) { DispatchQueue.general.async { [weak self] in if let self = self { producer.recordTimeUntilReadyFromCache(self.telemetryStopwatch?.interval() ?? 0) @@ -88,7 +88,7 @@ class DefaultClientManager: SplitClientManager { } } - defaultClient?.on(event: SplitEventWithMetadata(type: .sdkReadyFromCache, metadata: nil)) { + defaultClient?.on(event: .sdkReadyFromCache) { DispatchQueue.general.async { [weak self] in if let self = self { producer.recordTimeUntilReady(self.telemetryStopwatch?.interval() ?? 0) diff --git a/Split/Engine/DefaultTreatmentManager.swift b/Split/Engine/DefaultTreatmentManager.swift index 73594b32e..8eeed9f46 100644 --- a/Split/Engine/DefaultTreatmentManager.swift +++ b/Split/Engine/DefaultTreatmentManager.swift @@ -337,8 +337,8 @@ extension DefaultTreatmentManager { } private func isSdkReady() -> Bool { - return eventsManager.eventAlreadyTriggered(event: SplitEvent.sdkReadyFromCache) || - eventsManager.eventAlreadyTriggered(event: SplitEvent.sdkReady) + return eventsManager.eventAlreadyTriggered(event: SplitEventCase.sdkReadyFromCache) || + eventsManager.eventAlreadyTriggered(event: .sdkReady) } private func checkAndLogIfDestroyed(logTag: String) -> Bool { diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index dec9f3af1..e97486cde 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -17,15 +17,15 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { private var defaultManager: SplitEventsManager? private var managers = [Key: SplitEventsManager]() - private var triggered = Set() + private var triggered = Set() private let queue = DispatchQueue(label: "split-event-manager-coordinator") - private let eventsToHandle: Set = Set( + private let eventsToHandle: Set = Set( [.splitsLoadedFromCache, .splitsUpdated, .splitKilledNotification] ) - func notifyInternalEvent(_ event: SplitEventCase, _ metadata: [String : Any]? = nil) { + func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: NSDictionary? = nil) { if !eventsToHandle.contains(event) { return } @@ -55,7 +55,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { } } - func eventAlreadyTriggered(event: SplitEvent) -> Bool { + func eventAlreadyTriggered(event: SplitEventCase) -> Bool { return defaultManager?.eventAlreadyTriggered(event: event) ?? false } @@ -81,5 +81,5 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { } } - func register(event: SplitEventWithMetadata, task: SplitEventActionTask) {} + func register(event: SplitEvent, task: SplitEventActionTask) {} } diff --git a/Split/Events/SplitEvent.swift b/Split/Events/SplitEvent.swift index 0264c8ead..338440040 100644 --- a/Split/Events/SplitEvent.swift +++ b/Split/Events/SplitEvent.swift @@ -1,5 +1,5 @@ // -// SplitEvent.swift +// SplitEventCase.swift // Split // // Created by Sebastian Arrubia on 4/17/18. @@ -7,7 +7,7 @@ import Foundation -@objc public enum SplitEvent: Int { +@objc public enum SplitEventCase: Int { case sdkReady case sdkReadyTimedOut case sdkReadyFromCache @@ -27,23 +27,18 @@ import Foundation } } -@objcMembers -public class SplitEventWithMetadata: NSObject { - let type: SplitEvent - let metadata: [String: Any]? +@objcMembers public class SplitEvent: NSObject { + let type: SplitEventCase + let metadata: NSDictionary? - @objc public init(type: SplitEvent, metadata: [String : Any]? = nil) { + @objc public init(type: SplitEventCase, metadata: NSDictionary? = nil) { self.type = type self.metadata = metadata } public override func isEqual(_ object: Any?) -> Bool { - guard let other = object as? SplitEventWithMetadata else { return false } + guard let other = object as? SplitEvent else { return false } return self.type == other.type } - - public override var hash: Int { - return type.hashValue - } } diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index a36fe0253..4a475f426 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -12,11 +12,11 @@ class SplitEventActionTask: SplitEventTask { private var eventHandler: SplitAction? private var eventHandlerWithMetadata: SplitActionWithMetadata? private var queue: DispatchQueue? - var event: SplitEventWithMetadata + var event: SplitEventCase var runInBackground: Bool = false var factory: SplitFactory - init(action: @escaping SplitActionWithMetadata, event: SplitEventWithMetadata, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { + init(action: @escaping SplitActionWithMetadata, event: SplitEventCase, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { self.eventHandlerWithMetadata = action self.event = event self.runInBackground = runInBackground @@ -24,7 +24,7 @@ class SplitEventActionTask: SplitEventTask { self.factory = factory } - init(action: @escaping SplitAction, event: SplitEventWithMetadata, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { + init(action: @escaping SplitAction, event: SplitEventCase, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { self.eventHandler = action self.event = event self.runInBackground = runInBackground @@ -37,7 +37,7 @@ class SplitEventActionTask: SplitEventTask { return queue } - func run(_ data: Any?) { + func run(_ data: NSDictionary?) { eventHandler?() eventHandlerWithMetadata?(data) } diff --git a/Split/Events/SplitEventCase.swift b/Split/Events/SplitEventCase.swift index a2c3640e8..ec686cb92 100644 --- a/Split/Events/SplitEventCase.swift +++ b/Split/Events/SplitEventCase.swift @@ -7,7 +7,7 @@ import Foundation -enum SplitEventCase { +enum SplitInternalEventCase { case mySegmentsUpdated case myLargeSegmentsUpdated case splitsUpdated @@ -20,15 +20,15 @@ enum SplitEventCase { } struct SplitInternalEvent: Equatable { - let type: SplitEventCase - let metadata: [String: Any]? + let type: SplitInternalEventCase + let metadata: NSDictionary? - init(type: SplitEventCase, metadata: [String : Any]? = nil) { + init(type: SplitInternalEventCase, metadata: NSDictionary? = nil) { self.type = type self.metadata = metadata } static func == (lhs: SplitInternalEvent, rhs: SplitInternalEvent) -> Bool { - return lhs.type == rhs.type + return lhs.type == rhs.type } } diff --git a/Split/Events/SplitEventTask.swift b/Split/Events/SplitEventTask.swift index f07cd5615..5c69d7398 100644 --- a/Split/Events/SplitEventTask.swift +++ b/Split/Events/SplitEventTask.swift @@ -8,8 +8,8 @@ import Foundation protocol SplitEventTask { - var event: SplitEventWithMetadata { get } + var event: SplitEventCase { get } var runInBackground: Bool { get } func takeQueue() -> DispatchQueue? - func run(_ data: Any?) + func run(_ data: NSDictionary?) } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index e0c31f857..ec1b906ae 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -9,19 +9,19 @@ import Foundation protocol SplitEventsManager: AnyObject { - func register(event: SplitEventWithMetadata, task: SplitEventActionTask) - func notifyInternalEvent(_ event: SplitEventCase, _ metadata: [String: Any]?) + func register(event: SplitEvent, task: SplitEventActionTask) + func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: NSDictionary?) func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) func start() func stop() - func eventAlreadyTriggered(event: SplitEvent) -> Bool + func eventAlreadyTriggered(event: SplitEventCase) -> Bool } /* This overload is intentionally kept for backwards compatibility. It allows calling `notifyInternalEvent(.event)` without needing to pass `nil` as metadata. Do not remove unless all usages have migrated to the new signature. */ extension SplitEventsManager { - func notifyInternalEvent(_ event: SplitEventCase) { + func notifyInternalEvent(_ event: SplitInternalEventCase) { notifyInternalEvent(event, nil) } } @@ -32,7 +32,7 @@ class DefaultSplitEventsManager: SplitEventsManager { private var sdkReadyTimeStart: Int64 - private var subscriptions = [SplitEvent: [SplitEventTask]]() + private var subscriptions = [SplitEventCase: [SplitEventTask]]() private var executionTimes: [String: Int] private var triggered: [SplitInternalEvent] private let processQueue: DispatchQueue @@ -57,12 +57,12 @@ class DefaultSplitEventsManager: SplitEventsManager { 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(SplitEventCase.sdkReadyTimeoutReached) + self.notifyInternalEvent(.sdkReadyTimeoutReached) } } } - func notifyInternalEvent(_ event: SplitEventCase, _ metadata: [String: Any]? = nil) { + func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: NSDictionary? = nil) { notifyInternalEventWithMetadata(SplitInternalEvent(type: event, metadata: metadata)) } @@ -75,7 +75,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - func register (event: SplitEventWithMetadata, task: SplitEventActionTask) { + func register (event: SplitEvent, task: SplitEventActionTask) { let eventName = event.type.toString() processQueue.async { [weak self] in guard let self = self else { return } @@ -100,7 +100,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - func eventAlreadyTriggered(event: SplitEvent) -> Bool { + func eventAlreadyTriggered(event: SplitEventCase) -> Bool { var isTriggered = false processQueue.sync { isTriggered = self.isTriggered(external: event) @@ -129,10 +129,10 @@ class DefaultSplitEventsManager: SplitEventsManager { */ private func registerMaxAllowedExecutionTimesPerEvent() { - executionTimes = [ SplitEvent.sdkReady.toString(): 1, - SplitEvent.sdkUpdated.toString(): -1, - SplitEvent.sdkReadyFromCache.toString(): 1, - SplitEvent.sdkReadyTimedOut.toString(): 1] + executionTimes = [ SplitEventCase.sdkReady.toString(): 1, + SplitEventCase.sdkUpdated.toString(): -1, + SplitEventCase.sdkReadyFromCache.toString(): 1, + SplitEventCase.sdkReadyTimedOut.toString(): 1] } private func isRunning() -> Bool { @@ -163,7 +163,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: SplitEvent(type: .sdkUpdated, metadata: event.metadata)) continue } self.triggerSdkReadyIfNeeded() @@ -174,7 +174,7 @@ class DefaultSplitEventsManager: SplitEventsManager { isTriggered(internal: .mySegmentsLoadedFromCache), isTriggered(internal: .myLargeSegmentsLoadedFromCache), isTriggered(internal: .attributesLoadedFromCache) { - trigger(event: SplitEvent.sdkReadyFromCache) + trigger(event: SplitEventCase.sdkReadyFromCache) } case .splitKilledNotification: if isTriggered(external: .sdkReady) { @@ -183,14 +183,14 @@ class DefaultSplitEventsManager: SplitEventsManager { } case .sdkReadyTimeoutReached: if !isTriggered(external: .sdkReady) { - trigger(event: SplitEvent.sdkReadyTimedOut) + trigger(event: SplitEventCase.sdkReadyTimedOut) } } } } // MARK: Helper functions. - func isTriggered(external event: SplitEvent) -> Bool { + func isTriggered(external event: SplitEventCase) -> Bool { var triggered = false dataAccessQueue.sync { if let times = executionTimes[event.toString()] { @@ -211,11 +211,11 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func trigger(event: SplitEvent) { - trigger(event: SplitEventWithMetadata(type: event, metadata: nil)) + private func trigger(event: SplitEventCase) { + trigger(event: SplitEvent(type: event, metadata: nil)) } - private func trigger(event: SplitEventWithMetadata) { + private func trigger(event: SplitEvent) { let eventName = event.type.toString() // If executionTimes is zero, maximum executions has been reached @@ -237,9 +237,9 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func executeTask(eventWithMetadata event: SplitEventWithMetadata, task: SplitEventTask) { + private func executeTask(eventWithMetadata event: SplitEvent, task: SplitEventTask) { - let eventName = task.event.type.toString() + let eventName = task.event.toString() if task.runInBackground { TimeChecker.logInterval("Previous to run \(eventName) in Background") @@ -262,7 +262,7 @@ class DefaultSplitEventsManager: SplitEventsManager { return triggered.filter { $0 == event }.count > 0 } - private func isTriggered(internal event: SplitEventCase) -> Bool { + private func isTriggered(internal event: SplitInternalEventCase) -> Bool { return isTriggered(internal: SplitInternalEvent(type: event, metadata: nil)) } @@ -275,7 +275,7 @@ class DefaultSplitEventsManager: SplitEventsManager { return times } - func subscribe(task: SplitEventTask, to event: SplitEvent) { + func subscribe(task: SplitEventTask, to event: SplitEventCase) { dataAccessQueue.async { [weak self] in guard let self = self else { return } var subscriptions = self.subscriptions[event] ?? [SplitEventTask]() @@ -284,7 +284,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func getSubscriptions(for event: SplitEvent) -> [SplitEventTask]? { + private func getSubscriptions(for event: SplitEventCase) -> [SplitEventTask]? { var subscriptions: [SplitEventTask]? dataAccessQueue.sync { subscriptions = self.subscriptions[event] From c82fb59a582f741a2aefb1245b4ce6ec7a1babc2 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 13 May 2025 13:01:22 -0300 Subject: [PATCH 13/20] Using SplitMetadata object for flexibility --- Split/Api/DefaultSplitClient.swift | 2 +- Split/Api/FailHelpers.swift | 23 ++++++------------- Split/Api/LocalhostSplitClient.swift | 5 ++-- Split/Api/SplitClient.swift | 4 ++-- Split/Events/EventsManagerCoordinator.swift | 6 ++++- Split/Events/SplitEvent.swift | 14 +++++++++-- Split/Events/SplitEventActionTask.swift | 4 ++-- Split/Events/SplitEventCase.swift | 4 ++-- Split/Events/SplitEventTask.swift | 2 +- Split/Events/SplitEventsManager.swift | 14 +++++++---- .../Refresh/PeriodicSyncWorker.swift | 7 ++++-- .../Refresh/RetryableSegmentsSyncWorker.swift | 10 +++++--- .../Refresh/RetryableSyncWorker.swift | 6 +++-- Split/Localhost/LocalhostClientManager.swift | 4 ++-- Split/Localhost/LocalhostSynchronizer.swift | 3 ++- Split/Network/Sync/ByKeyFacade.swift | 9 +++++--- .../Sync/FeatureFlagsSynchronizer.swift | 9 ++++++-- 17 files changed, 76 insertions(+), 50 deletions(-) diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index e137bf8b5..36760589f 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -71,7 +71,7 @@ extension DefaultSplitClient { on(event: event.type, executeTask: task) } - public func on(event: SplitEventCase, performWithMetadata action: SplitActionWithMetadata?) { + public func on(event: SplitEventCase, executeWithMetadata action: SplitActionWithMetadata?) { guard let action = action else { return } onWithMetadata(event: SplitEvent(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) } diff --git a/Split/Api/FailHelpers.swift b/Split/Api/FailHelpers.swift index 584754d3c..bd91d69eb 100644 --- a/Split/Api/FailHelpers.swift +++ b/Split/Api/FailHelpers.swift @@ -10,12 +10,9 @@ import Foundation /// To avoid crashing host app this dummy components will be returned /// on Failed init -/// class FailedClient: SplitClient { - func on(event: SplitEventCase, performWithMetadata: SplitActionWithMetadata?) { - - } + func on(event: SplitEventCase, executeWithMetadata: SplitActionWithMetadata) {} func getTreatment(_ split: String) -> String { return SplitConstants.control @@ -57,14 +54,11 @@ class FailedClient: SplitClient { return [:] } - public func on(event: SplitEventCase, perform: SplitAction?) { - } + public func on(event: SplitEventCase, perform: SplitAction?) {} - func on(event: SplitEventCase, perform: @escaping ([String : Any]?) -> Void) { - } + func on(event: SplitEventCase, perform: @escaping ([String : Any]?) -> Void) {} - func on(event: SplitEventCase, execute action: @escaping SplitAction) { - } + func on(event: SplitEventCase, execute action: @escaping SplitAction) {} func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) { @@ -146,14 +140,11 @@ class FailedClient: SplitClient { return [:] } - func setUserConsent(enabled: Bool) { - } + func setUserConsent(enabled: Bool) {} - func flush() { - } + func flush() {} - func destroy() { - } + func destroy() {} func destroy(completion: (() -> Void)?) { completion?() diff --git a/Split/Api/LocalhostSplitClient.swift b/Split/Api/LocalhostSplitClient.swift index 3e22f05de..3eeb59944 100644 --- a/Split/Api/LocalhostSplitClient.swift +++ b/Split/Api/LocalhostSplitClient.swift @@ -123,9 +123,8 @@ public final class LocalhostSplitClient: NSObject, SplitClient { on(event: event, execute: perform) } - public func on(event: SplitEventCase, performWithMetadata: SplitActionWithMetadata?) { - guard let performWithMetadata = performWithMetadata else { return } - on(eventWithMetadata: SplitEvent(type: event, metadata: nil), runInBackground: false, queue: nil, execute: performWithMetadata) + public func on(event: SplitEventCase, executeWithMetadata: @escaping SplitActionWithMetadata) { + on(eventWithMetadata: SplitEvent(type: event, metadata: nil), runInBackground: false, queue: nil, execute: executeWithMetadata) } public func on(event: SplitEventCase, runInBackground: Bool, diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index 874ee077a..7f420c6b8 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -9,13 +9,13 @@ import Foundation public typealias SplitAction = () -> Void -public typealias SplitActionWithMetadata = (_ data: NSDictionary?) -> Void +public typealias SplitActionWithMetadata = (_ data: SplitMetadata?) -> Void @objc public protocol SplitClient { // MARK: Listeners for customer - func on(event: SplitEventCase, performWithMetadata: SplitActionWithMetadata?) -> Void func on(event: SplitEventCase, execute action: @escaping SplitAction) + func on(event: SplitEventCase, executeWithMetadata: @escaping SplitActionWithMetadata) -> Void func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index e97486cde..f0caee470 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -24,8 +24,12 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { .splitsUpdated, .splitKilledNotification] ) + + func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: SplitMetadata? = nil) { + notifyInternalEvent(event, metadata: metadata) + } - func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: NSDictionary? = nil) { + func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata? = nil) { if !eventsToHandle.contains(event) { return } diff --git a/Split/Events/SplitEvent.swift b/Split/Events/SplitEvent.swift index 338440040..e16628200 100644 --- a/Split/Events/SplitEvent.swift +++ b/Split/Events/SplitEvent.swift @@ -27,11 +27,21 @@ import Foundation } } +@objc public class SplitMetadata: NSObject { + var type: String = "" + var value: String = "" + + init(type: String, value: String) { + self.type = type + self.value = value + } +} + @objcMembers public class SplitEvent: NSObject { let type: SplitEventCase - let metadata: NSDictionary? + let metadata: SplitMetadata? - @objc public init(type: SplitEventCase, metadata: NSDictionary? = nil) { + @objc public init(type: SplitEventCase, metadata: SplitMetadata? = nil) { self.type = type self.metadata = metadata } diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index 4a475f426..f61cf4554 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -37,8 +37,8 @@ class SplitEventActionTask: SplitEventTask { return queue } - func run(_ data: NSDictionary?) { + func run(_ metadata: SplitMetadata?) { eventHandler?() - eventHandlerWithMetadata?(data) + eventHandlerWithMetadata?(metadata) } } diff --git a/Split/Events/SplitEventCase.swift b/Split/Events/SplitEventCase.swift index ec686cb92..1a6989588 100644 --- a/Split/Events/SplitEventCase.swift +++ b/Split/Events/SplitEventCase.swift @@ -21,9 +21,9 @@ enum SplitInternalEventCase { struct SplitInternalEvent: Equatable { let type: SplitInternalEventCase - let metadata: NSDictionary? + let metadata: SplitMetadata? - init(type: SplitInternalEventCase, metadata: NSDictionary? = nil) { + init(_ type: SplitInternalEventCase, metadata: SplitMetadata? = nil) { self.type = type self.metadata = metadata } diff --git a/Split/Events/SplitEventTask.swift b/Split/Events/SplitEventTask.swift index 5c69d7398..aa398347c 100644 --- a/Split/Events/SplitEventTask.swift +++ b/Split/Events/SplitEventTask.swift @@ -11,5 +11,5 @@ protocol SplitEventTask { var event: SplitEventCase { get } var runInBackground: Bool { get } func takeQueue() -> DispatchQueue? - func run(_ data: NSDictionary?) + func run(_ metadata: SplitMetadata?) } diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index ec1b906ae..7437356ea 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -10,7 +10,7 @@ import Foundation protocol SplitEventsManager: AnyObject { func register(event: SplitEvent, task: SplitEventActionTask) - func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: NSDictionary?) + func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: SplitMetadata?) func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) func start() func stop() @@ -62,14 +62,18 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: NSDictionary? = nil) { - notifyInternalEventWithMetadata(SplitInternalEvent(type: event, metadata: metadata)) + func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: SplitMetadata? = nil) { + notifyInternalEventWithMetadata(SplitInternalEvent(event, metadata: metadata)) + } + + func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata) { + notifyInternalEventWithMetadata(SplitInternalEvent(event, metadata: metadata)) } func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) { processQueue.async { [weak self] in if let self = self { - Logger.v("Event \(event.type) notified - Metadata: \(event.metadata ?? [:])") + Logger.v("Event \(event.type) notified - Metadata: \(event.metadata ?? nil)") self.eventsQueue.add(event) } } @@ -263,7 +267,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } private func isTriggered(internal event: SplitInternalEventCase) -> Bool { - return isTriggered(internal: SplitInternalEvent(type: event, metadata: nil)) + return isTriggered(internal: SplitInternalEvent(event, metadata: nil)) } // MARK: Safe Data Access diff --git a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift index 10d245bb2..b72736f3c 100644 --- a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift @@ -178,7 +178,8 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker { updatedFlags.append(flag) } - notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: ["Updated Flags": updatedFlags])]) + let metadata = SplitMetadata(type: "Updated Flags", value: updatedFlags.description) + notifyUpdate([SplitInternalEvent(.splitsUpdated, metadata: metadata)]) } } @@ -219,7 +220,9 @@ class PeriodicMySegmentsSyncWorker: BasePeriodicSyncWorker { headers: nil) if result.success { if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty { - notifyUpdate([SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Updated segments": result.msUpdated + result.mlsUpdated ])]) + + let metadata = SplitMetadata(type: "Updated segments", value: (result.msUpdated + result.mlsUpdated).description) + notifyUpdate([SplitInternalEvent(.mySegmentsUpdated, metadata: metadata)]) } } } catch { diff --git a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift index 67e1e9e59..c57466ea3 100644 --- a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift @@ -44,12 +44,16 @@ class RetryableMySegmentsSyncWorker: BaseRetryableSyncWorker { mlsTill: changeNumbers.mlsChangeNumber, headers: getHeaders()) if result.success { + let msMetadata = SplitMetadata(type: "Segments updated", value: result.msUpdated.description) + if !isSdkReadyTriggered() { // Notifying both to trigger SDK Ready - notifyUpdate([SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Segments updated" : result.msUpdated ])]) - notifyUpdate([SplitInternalEvent(type: .myLargeSegmentsUpdated, metadata: ["Large segments updated" : result.mlsUpdated ])]) + notifyUpdate([SplitInternalEvent(.mySegmentsUpdated, metadata: msMetadata)]) + let mlsMetadata = SplitMetadata(type: "Large segments updated", value: result.mlsUpdated.description) + notifyUpdate([SplitInternalEvent(.myLargeSegmentsUpdated, metadata: mlsMetadata)]) } else if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty { - notifyUpdate([SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Segments updated" : result.msUpdated + result.mlsUpdated ])]) + + notifyUpdate([SplitInternalEvent(.mySegmentsUpdated, metadata: msMetadata)]) } return true } diff --git a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift index d8ccbd39e..bce8bf057 100644 --- a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift @@ -163,7 +163,8 @@ class RetryableSplitsSyncWorker: BaseRetryableSyncWorker { let result = try syncHelper.sync(since: changeNumber, clearBeforeUpdate: clearCache) if result.success { if !isSdkReadyTriggered() || result.featureFlagsUpdated.count > 0 { - notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: ["Flags": result.featureFlagsUpdated ])]) + let metadata = SplitMetadata(type: "Flags", value: result.featureFlagsUpdated.description) + notifyUpdate([SplitInternalEvent(.splitsUpdated, metadata: metadata)]) } resetBackoffCounter() return true @@ -220,7 +221,8 @@ class RetryableSplitsUpdateWorker: BaseRetryableSyncWorker { headers: ServiceConstants.controlNoCacheHeader) if result.success { if result.featureFlagsUpdated.count > 0 { - notifyUpdate([SplitInternalEvent(type: .splitsUpdated, metadata: ["Metadata" : result.featureFlagsUpdated.description]) ]) + let metadata = SplitMetadata(type: "Data", value: result.featureFlagsUpdated.description) + notifyUpdate([SplitInternalEvent(.splitsUpdated, metadata: metadata)]) } resetBackoffCounter() return true diff --git a/Split/Localhost/LocalhostClientManager.swift b/Split/Localhost/LocalhostClientManager.swift index bcc70ee2d..04dfcdf4e 100644 --- a/Split/Localhost/LocalhostClientManager.swift +++ b/Split/Localhost/LocalhostClientManager.swift @@ -92,8 +92,8 @@ class LocalhostClientManager: SplitClientManager { let newGroup = LocalhostComponentsGroup(client: newClient, eventsManager: newEventsManager) clients.setValue(newGroup, forKey: key.matchingKey) eventsManagerCoordinator.add(newEventsManager, forKey: key) - newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Metadata forKey segments": ""])) - newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .myLargeSegmentsUpdated, metadata: ["Metadata forKey largeSegments": ""])) + newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.mySegmentsUpdated, metadata: SplitMetadata(type: "Metadata forKey segments", value: ""))) + newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.myLargeSegmentsUpdated, metadata: SplitMetadata(type: "Metadata forKey largeSegments", value: ""))) return newClient } diff --git a/Split/Localhost/LocalhostSynchronizer.swift b/Split/Localhost/LocalhostSynchronizer.swift index 3785ca971..7fe97569e 100644 --- a/Split/Localhost/LocalhostSynchronizer.swift +++ b/Split/Localhost/LocalhostSynchronizer.swift @@ -74,7 +74,8 @@ class LocalhostSynchronizer: FeatureFlagsSynchronizer { // Update will remove all records before insert new ones _ = self.featureFlagsStorage.update(splitChange: change) - self.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .splitsUpdated, metadata: ["Splits updated":"\(featureFlags.values.description)"])) + let metadata = SplitMetadata(type: "Splits Updated", value: featureFlags.values.description) + self.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.splitsUpdated, metadata: metadata)) } } } diff --git a/Split/Network/Sync/ByKeyFacade.swift b/Split/Network/Sync/ByKeyFacade.swift index 7642dad39..9b1c06b78 100644 --- a/Split/Network/Sync/ByKeyFacade.swift +++ b/Split/Network/Sync/ByKeyFacade.swift @@ -77,7 +77,8 @@ class DefaultByKeyFacade: ByKeyFacade { func loadAttributesFromCache(forKey key: String) { doInAll(forMatchingKey: key) { group in group.attributesStorage.loadLocal() - group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .attributesLoadedFromCache, metadata: ["Metadata":"Attributes from cache ready"])) + let metadata = SplitMetadata(type: "Metadata", value: "Attributes from cache ready") + group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.attributesLoadedFromCache, metadata: metadata)) } TimeChecker.logInterval("Time until attributes loaded from cache") } @@ -122,13 +123,15 @@ class DefaultByKeyFacade: ByKeyFacade { func notifyMySegmentsUpdated(forKey key: String) { doInAll(forMatchingKey: key) { group in - group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .mySegmentsUpdated, metadata: ["Metadata":"Segments updated for key \(key)"])) + let metadata = SplitMetadata(type: "Metadata", value: "Segments updated for key \(key)") + group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.mySegmentsUpdated, metadata: metadata)) } } func notifyMyLargeSegmentsUpdated(forKey key: String) { doInAll(forMatchingKey: key) { group in - group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .myLargeSegmentsUpdated, metadata: ["Metadata":"Large segments updated for key \(key)"])) + let metadata = SplitMetadata(type: "Metadata", value: "Large segments updated for key \(key)") + group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.myLargeSegmentsUpdated, metadata: metadata)) } } diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index 5d3d466fe..2b4aeb756 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -85,7 +85,11 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { splitsStorage.loadLocal() if splitsStorage.getAll().count > 0 { self.splitEventsManager.notifyInternalEvent(.splitsLoadedFromCache) - self.splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .attributesLoadedFromCache, metadata: ["Metadata":"Splits from cache ready"])) + + // Trigger event + let metadata = SplitMetadata(type: "Metadata", value: "Splits from cache ready") + let event = SplitInternalEvent(.attributesLoadedFromCache, metadata: metadata) + self.splitEventsManager.notifyInternalEventWithMetadata(event) } self.broadcasterChannel.push(event: .splitLoadedFromCache) Logger.v("Notifying Splits loaded from cache") @@ -144,7 +148,8 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { } func notifyUpdated(flagList: [String]) { - splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(type: .splitsUpdated, metadata: ["Updated flags:":flagList] )) + let metadata = SplitMetadata(type: "Updated Flags", value: flagList.description) + splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.splitsUpdated, metadata: metadata)) } func pause() { From e027f53ef369645f005fbe6def57102b43e37817 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 13 May 2025 13:49:23 -0300 Subject: [PATCH 14/20] Legibility improved --- Split/Api/LocalhostSplitClient.swift | 14 +++--- Split/Events/EventsManagerCoordinator.swift | 19 +++---- Split/Events/SplitEvent.swift | 49 ++++++++++--------- Split/Events/SplitEventCase.swift | 27 +++++----- Split/Events/SplitEventsManager.swift | 28 +++++------ .../Refresh/PeriodicSyncWorker.swift | 2 +- .../Refresh/RetryableSyncWorker.swift | 2 +- Split/Localhost/LocalhostClientManager.swift | 4 +- Split/Localhost/LocalhostSynchronizer.swift | 2 +- Split/Network/Sync/ByKeyFacade.swift | 6 +-- .../Sync/FeatureFlagsSynchronizer.swift | 4 +- 11 files changed, 78 insertions(+), 79 deletions(-) diff --git a/Split/Api/LocalhostSplitClient.swift b/Split/Api/LocalhostSplitClient.swift index 3eeb59944..5a2efc05b 100644 --- a/Split/Api/LocalhostSplitClient.swift +++ b/Split/Api/LocalhostSplitClient.swift @@ -61,6 +61,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient { super.init() } + // MARK: Treatments public func getTreatment(_ split: String, attributes: [String: Any]?) -> String { return getTreatmentWithConfig(split).treatment } @@ -118,6 +119,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient { return results } + // MARK: Events Listeners public func on(event: SplitEventCase, perform: SplitAction?) { guard let perform = perform else { return } on(event: event, execute: perform) @@ -170,6 +172,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient { } } + // MARK: Tracking public func track(trafficType: String, eventType: String) -> Bool { return true } @@ -202,11 +205,10 @@ public final class LocalhostSplitClient: NSObject, SplitClient { return true } - public func setUserConsent(enabled: Bool) { - } + public func setUserConsent(enabled: Bool) {} - public func flush() { - } + // MARK: Lifecycle + public func flush() {} public func destroy() { splitsStorage.destroy() @@ -218,7 +220,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient { } } -// MARK: Persistent attributes feature +// MARK: Persistence extension LocalhostSplitClient { public func setAttribute(name: String, value: Any) -> Bool { @@ -246,7 +248,7 @@ extension LocalhostSplitClient { } } -// MARK: TreatmentBySets Feature +// MARK: By Flagset extension LocalhostSplitClient { public func getTreatmentsByFlagSet(_ flagSet: String, attributes: [String: Any]?) -> [String: String] { return [String: String]() diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index f0caee470..6cf5ff40a 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -14,7 +14,7 @@ protocol SplitEventsManagerCoordinator: SplitEventsManager { } class MainSplitEventsManager: SplitEventsManagerCoordinator { - + private var defaultManager: SplitEventsManager? private var managers = [Key: SplitEventsManager]() private var triggered = Set() @@ -25,11 +25,16 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { .splitKilledNotification] ) - func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: SplitMetadata? = nil) { - notifyInternalEvent(event, metadata: metadata) + // MARK: Notifications + func notifyInternalEvent(_ event: SplitInternalEventCase) { + notifyInternalEvent(event, metadata: SplitMetadata(type: "", value: "")) + } + + func notifyInternalEvent(_ event: SplitInternalEvent) { + notifyInternalEvent(event.type, metadata: event.metadata ?? SplitMetadata(type: "", value: "")) } - func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata? = nil) { + func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata) { if !eventsToHandle.contains(event) { return } @@ -38,14 +43,10 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { self.triggered.insert(event) self.managers.forEach { _, manager in - manager.notifyInternalEvent(event, metadata) + manager.notifyInternalEvent(event, metadata: metadata) } } } - - func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) { - notifyInternalEvent(event.type, event.metadata) - } func start() {} diff --git a/Split/Events/SplitEvent.swift b/Split/Events/SplitEvent.swift index e16628200..77bf30445 100644 --- a/Split/Events/SplitEvent.swift +++ b/Split/Events/SplitEvent.swift @@ -7,6 +7,21 @@ import Foundation +@objcMembers public class SplitEvent: NSObject { + let type: SplitEventCase + let metadata: SplitMetadata? + + @objc public init(type: SplitEventCase, metadata: SplitMetadata? = nil) { + self.type = type + self.metadata = metadata + } + + public override func isEqual(_ object: Any?) -> Bool { + guard let other = object as? SplitEvent else { return false } + return self.type == other.type + } +} + @objc public enum SplitEventCase: Int { case sdkReady case sdkReadyTimedOut @@ -15,18 +30,20 @@ import Foundation 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" } } } +// Just a key-value wrapper for extensibility. +// (Also used by SplitInternalEvent) @objc public class SplitMetadata: NSObject { var type: String = "" var value: String = "" @@ -36,19 +53,3 @@ import Foundation self.value = value } } - -@objcMembers public class SplitEvent: NSObject { - let type: SplitEventCase - let metadata: SplitMetadata? - - @objc public init(type: SplitEventCase, metadata: SplitMetadata? = nil) { - self.type = type - self.metadata = metadata - } - - public override func isEqual(_ object: Any?) -> Bool { - guard let other = object as? SplitEvent else { return false } - return self.type == other.type - } -} - diff --git a/Split/Events/SplitEventCase.swift b/Split/Events/SplitEventCase.swift index 1a6989588..557226a05 100644 --- a/Split/Events/SplitEventCase.swift +++ b/Split/Events/SplitEventCase.swift @@ -3,23 +3,10 @@ // Split // // Created by Sebastian Arrubia on 4/16/18. -// import Foundation -enum SplitInternalEventCase { - case mySegmentsUpdated - case myLargeSegmentsUpdated - case splitsUpdated - case mySegmentsLoadedFromCache - case myLargeSegmentsLoadedFromCache - case splitsLoadedFromCache - case attributesLoadedFromCache - case sdkReadyTimeoutReached - case splitKilledNotification -} - -struct SplitInternalEvent: Equatable { +struct SplitInternalEvent { let type: SplitInternalEventCase let metadata: SplitMetadata? @@ -32,3 +19,15 @@ struct SplitInternalEvent: Equatable { return lhs.type == rhs.type } } + +enum SplitInternalEventCase { + case mySegmentsUpdated + case myLargeSegmentsUpdated + case splitsUpdated + case mySegmentsLoadedFromCache + case myLargeSegmentsLoadedFromCache + case splitsLoadedFromCache + case attributesLoadedFromCache + case sdkReadyTimeoutReached + case splitKilledNotification +} diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index 7437356ea..5886cf61d 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -10,22 +10,14 @@ import Foundation protocol SplitEventsManager: AnyObject { func register(event: SplitEvent, task: SplitEventActionTask) - func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: SplitMetadata?) - func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) + func notifyInternalEvent(_ event: SplitInternalEventCase) + func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata) + func notifyInternalEvent(_ event: SplitInternalEvent) func start() func stop() func eventAlreadyTriggered(event: SplitEventCase) -> Bool } -/* This overload is intentionally kept for backwards compatibility. - It allows calling `notifyInternalEvent(.event)` without needing to pass `nil` as metadata. - Do not remove unless all usages have migrated to the new signature. */ -extension SplitEventsManager { - func notifyInternalEvent(_ event: SplitInternalEventCase) { - notifyInternalEvent(event, nil) - } -} - class DefaultSplitEventsManager: SplitEventsManager { private let readingRefreshTime: Int @@ -62,23 +54,27 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - func notifyInternalEvent(_ event: SplitInternalEventCase, _ metadata: SplitMetadata? = nil) { - notifyInternalEventWithMetadata(SplitInternalEvent(event, metadata: metadata)) + // MARK: Notifications + /* Overload kept for backwards compatibility. + It allows calling `notifyInternalEvent(.event)` without needing to pass `nil` as metadata. + Do not remove unless all usages have migrated to the new signature. */ + func notifyInternalEvent(_ event: SplitInternalEventCase) { + notifyInternalEvent(SplitInternalEvent(event, metadata: nil)) } func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata) { - notifyInternalEventWithMetadata(SplitInternalEvent(event, metadata: metadata)) + notifyInternalEvent(SplitInternalEvent(event, metadata: metadata)) } - func notifyInternalEventWithMetadata(_ event: SplitInternalEvent) { + func notifyInternalEvent(_ event: SplitInternalEvent) { processQueue.async { [weak self] in if let self = self { - Logger.v("Event \(event.type) notified - Metadata: \(event.metadata ?? nil)") self.eventsQueue.add(event) } } } + // MARK: Register func register (event: SplitEvent, task: SplitEventActionTask) { let eventName = event.type.toString() processQueue.async { [weak self] in diff --git a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift index b72736f3c..a5273a898 100644 --- a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift @@ -132,7 +132,7 @@ class BasePeriodicSyncWorker: PeriodicSyncWorker { func notifyUpdate(_ events: [SplitInternalEvent]) { events.forEach { - eventsManager.notifyInternalEventWithMetadata($0) + eventsManager.notifyInternalEvent($0) } } } diff --git a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift index bce8bf057..003b195fe 100644 --- a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift @@ -81,7 +81,7 @@ class BaseRetryableSyncWorker: RetryableSyncWorker { func notifyUpdate(_ events: [SplitInternalEvent]) { events.forEach { - eventsManager.notifyInternalEvent($0.type, $0.metadata) + eventsManager.notifyInternalEvent($0.type, metadata: $0.metadata ?? SplitMetadata(type: "", value: "")) } } diff --git a/Split/Localhost/LocalhostClientManager.swift b/Split/Localhost/LocalhostClientManager.swift index 04dfcdf4e..cf2b28917 100644 --- a/Split/Localhost/LocalhostClientManager.swift +++ b/Split/Localhost/LocalhostClientManager.swift @@ -92,8 +92,8 @@ class LocalhostClientManager: SplitClientManager { let newGroup = LocalhostComponentsGroup(client: newClient, eventsManager: newEventsManager) clients.setValue(newGroup, forKey: key.matchingKey) eventsManagerCoordinator.add(newEventsManager, forKey: key) - newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.mySegmentsUpdated, metadata: SplitMetadata(type: "Metadata forKey segments", value: ""))) - newEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.myLargeSegmentsUpdated, metadata: SplitMetadata(type: "Metadata forKey largeSegments", value: ""))) + newEventsManager.notifyInternalEvent(.mySegmentsUpdated, metadata: SplitMetadata(type: "Metadata forKey segments", value: "")) + newEventsManager.notifyInternalEvent(.myLargeSegmentsUpdated, metadata: SplitMetadata(type: "Metadata forKey largeSegments", value: "")) return newClient } diff --git a/Split/Localhost/LocalhostSynchronizer.swift b/Split/Localhost/LocalhostSynchronizer.swift index 7fe97569e..24e19cf4f 100644 --- a/Split/Localhost/LocalhostSynchronizer.swift +++ b/Split/Localhost/LocalhostSynchronizer.swift @@ -75,7 +75,7 @@ class LocalhostSynchronizer: FeatureFlagsSynchronizer { _ = self.featureFlagsStorage.update(splitChange: change) let metadata = SplitMetadata(type: "Splits Updated", value: featureFlags.values.description) - self.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.splitsUpdated, metadata: metadata)) + self.eventsManager.notifyInternalEvent(SplitInternalEvent(.splitsUpdated, metadata: metadata)) } } } diff --git a/Split/Network/Sync/ByKeyFacade.swift b/Split/Network/Sync/ByKeyFacade.swift index 9b1c06b78..777382fa1 100644 --- a/Split/Network/Sync/ByKeyFacade.swift +++ b/Split/Network/Sync/ByKeyFacade.swift @@ -78,7 +78,7 @@ class DefaultByKeyFacade: ByKeyFacade { doInAll(forMatchingKey: key) { group in group.attributesStorage.loadLocal() let metadata = SplitMetadata(type: "Metadata", value: "Attributes from cache ready") - group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.attributesLoadedFromCache, metadata: metadata)) + group.eventsManager.notifyInternalEvent(.attributesLoadedFromCache, metadata: metadata) } TimeChecker.logInterval("Time until attributes loaded from cache") } @@ -124,14 +124,14 @@ class DefaultByKeyFacade: ByKeyFacade { func notifyMySegmentsUpdated(forKey key: String) { doInAll(forMatchingKey: key) { group in let metadata = SplitMetadata(type: "Metadata", value: "Segments updated for key \(key)") - group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.mySegmentsUpdated, metadata: metadata)) + group.eventsManager.notifyInternalEvent(.mySegmentsUpdated, metadata: metadata) } } func notifyMyLargeSegmentsUpdated(forKey key: String) { doInAll(forMatchingKey: key) { group in let metadata = SplitMetadata(type: "Metadata", value: "Large segments updated for key \(key)") - group.eventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.myLargeSegmentsUpdated, metadata: metadata)) + group.eventsManager.notifyInternalEvent(.myLargeSegmentsUpdated, metadata: metadata) } } diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index 2b4aeb756..76ff75c5a 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -89,7 +89,7 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { // Trigger event let metadata = SplitMetadata(type: "Metadata", value: "Splits from cache ready") let event = SplitInternalEvent(.attributesLoadedFromCache, metadata: metadata) - self.splitEventsManager.notifyInternalEventWithMetadata(event) + self.splitEventsManager.notifyInternalEvent(event) } self.broadcasterChannel.push(event: .splitLoadedFromCache) Logger.v("Notifying Splits loaded from cache") @@ -149,7 +149,7 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { func notifyUpdated(flagList: [String]) { let metadata = SplitMetadata(type: "Updated Flags", value: flagList.description) - splitEventsManager.notifyInternalEventWithMetadata(SplitInternalEvent(.splitsUpdated, metadata: metadata)) + splitEventsManager.notifyInternalEvent(SplitInternalEvent(.splitsUpdated, metadata: metadata)) } func pause() { From c42eca60a2d647d10fe62ea59f5f177761e0e7f5 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 13 May 2025 13:58:19 -0300 Subject: [PATCH 15/20] Added legibility --- Split/Api/DefaultSplitClient.swift | 82 +++++++++++------------------- 1 file changed, 31 insertions(+), 51 deletions(-) diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index 36760589f..84a1bebb9 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -55,68 +55,48 @@ public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClie } } -// MARK: Events +// MARK: Events Listeners extension DefaultSplitClient { private func onWithMetadata(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute actionWithMetadata: @escaping SplitActionWithMetadata) { - guard let factory = clientManager?.splitFactory else { - return - } - - let task = SplitEventActionTask(action: actionWithMetadata, event: event.type, - runInBackground: runInBackground, - factory: factory, - queue: queue) - task.event = event.type + guard let factory = clientManager?.splitFactory else { return } + let task = SplitEventActionTask(action: actionWithMetadata, event: event.type, runInBackground: runInBackground, factory: factory, queue: queue) on(event: event.type, executeTask: task) } - public func on(event: SplitEventCase, executeWithMetadata action: SplitActionWithMetadata?) { - guard let action = action else { return } - onWithMetadata(event: SplitEvent(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) - } + public func on(event: SplitEventCase, executeWithMetadata action: SplitActionWithMetadata?) { + guard let action = action else { return } + onWithMetadata(event: SplitEvent(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) + } - private func on(event: SplitEventCase, 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.") + 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: SplitEvent(type: event, metadata: nil), task: task) } - private func on(event: SplitEventCase, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { - 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) - } + private func on(event: SplitEventCase, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { + 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) + } - public func on(event: SplitEventCase, execute action: @escaping SplitAction) { - on(event: event, runInBackground: false, queue: nil, execute: action) - } - - public func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) { - on(event: event, runInBackground: runInBackground, queue: nil, execute: action) - } - - public func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) { - on(event: event, runInBackground: true, queue: queue, execute: action) - } - - - + public func on(event: SplitEventCase, execute action: @escaping SplitAction) { + on(event: event, runInBackground: false, queue: nil, execute: action) + } + + public func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) { + on(event: event, runInBackground: runInBackground, queue: nil, execute: action) + } + + public func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) { + on(event: event, runInBackground: true, queue: queue, execute: action) + } } -// MARK: Treatment / Evaluation +// MARK: Treatments extension DefaultSplitClient { public func getTreatmentWithConfig(_ split: String) -> SplitResult { return treatmentManager.getTreatmentWithConfig(split, attributes: nil, evaluationOptions: nil) @@ -143,7 +123,7 @@ extension DefaultSplitClient { } } -// MARK: Treatment / Evaluation with Properties +// MARK: With Properties extension DefaultSplitClient { public func getTreatment(_ split: String, attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> String { return treatmentManager.getTreatment(split, attributes: attributes, evaluationOptions: evaluationOptions) @@ -180,7 +160,7 @@ extension DefaultSplitClient { } } -// MARK: Track Events +// MARK: Tracking extension DefaultSplitClient { public func track(trafficType: String, eventType: String) -> Bool { @@ -230,7 +210,7 @@ extension DefaultSplitClient { } } -// MARK: Persistent attributes feature +// MARK: Persistence extension DefaultSplitClient { public func setAttribute(name: String, value: Any) -> Bool { @@ -286,7 +266,7 @@ extension DefaultSplitClient { } } -// MARK: By Sets evaluation +// MARK: By FlagSets extension DefaultSplitClient { public func getTreatmentsByFlagSet(_ flagSet: String, attributes: [String: Any]?) -> [String: String] { return treatmentManager.getTreatmentsByFlagSet(flagSet: flagSet, attributes: attributes, evaluationOptions: nil) @@ -306,7 +286,7 @@ extension DefaultSplitClient { } } -// MARK: Flush / Destroy +// MARK: Lifecycle extension DefaultSplitClient { private func syncFlush() { From e4087d8880c825cf9787a5949b8b76b1af46a9b3 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 13 May 2025 14:02:36 -0300 Subject: [PATCH 16/20] Minor improvement --- Split/Api/SplitClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index 7f420c6b8..e74e9e1e7 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -9,7 +9,7 @@ import Foundation public typealias SplitAction = () -> Void -public typealias SplitActionWithMetadata = (_ data: SplitMetadata?) -> Void +public typealias SplitActionWithMetadata = (_ metadata: SplitMetadata?) -> Void @objc public protocol SplitClient { From f500d29d0e9cdf8d6a4a4616d9fd8a8db123c5c1 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 13 May 2025 14:16:13 -0300 Subject: [PATCH 17/20] File name changed --- Split/Api/SplitClient.swift | 3 --- Split/Events/SplitEventActionTask.swift | 3 +++ ...entCase.swift => SplitInternalEvent.swift} | 0 Split/Localhost/LocalhostSynchronizer.swift | 21 +++++++------------ 4 files changed, 10 insertions(+), 17 deletions(-) rename Split/Events/{SplitEventCase.swift => SplitInternalEvent.swift} (100%) diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index e74e9e1e7..4d48eb7d1 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -8,9 +8,6 @@ import Foundation -public typealias SplitAction = () -> Void -public typealias SplitActionWithMetadata = (_ metadata: SplitMetadata?) -> Void - @objc public protocol SplitClient { // MARK: Listeners for customer diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index f61cf4554..3236af6a0 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -7,6 +7,9 @@ import Foundation +public typealias SplitAction = () -> Void +public typealias SplitActionWithMetadata = (_ metadata: SplitMetadata?) -> Void + class SplitEventActionTask: SplitEventTask { private var eventHandler: SplitAction? diff --git a/Split/Events/SplitEventCase.swift b/Split/Events/SplitInternalEvent.swift similarity index 100% rename from Split/Events/SplitEventCase.swift rename to Split/Events/SplitInternalEvent.swift diff --git a/Split/Localhost/LocalhostSynchronizer.swift b/Split/Localhost/LocalhostSynchronizer.swift index 24e19cf4f..2b57f58e9 100644 --- a/Split/Localhost/LocalhostSynchronizer.swift +++ b/Split/Localhost/LocalhostSynchronizer.swift @@ -24,34 +24,27 @@ class LocalhostSynchronizer: FeatureFlagsSynchronizer { setup() } - func load() { - } + func load() {} func synchronize() { featureFlagsDataSource.start() } - func synchronize(changeNumber: Int64) { - } + func synchronize(changeNumber: Int64) {} func startPeriodicSync() { featureFlagsDataSource.start() } - func stopPeriodicSync() { - } + func stopPeriodicSync() {} - func notifyKilled() { - } + func notifyKilled() {} - func notifyUpdated(flagList: [String]) { - } + func notifyUpdated(flagList: [String]) {} - func pause() { - } + func pause() {} - func resume() { - } + func resume() {} func destroy() { featureFlagsDataSource.stop() From 988253c3e861fac40adbe29f62ab4d920b7ab654 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 13 May 2025 14:30:35 -0300 Subject: [PATCH 18/20] Fixing half of tests --- Split/Network/Sync/FeatureFlagsSynchronizer.swift | 2 +- SplitTests/Fake/SplitEventsManagerStub.swift | 6 +++--- .../Fake/Streaming/FeatureFlagsSynchronizerStub.swift | 2 +- SplitTests/Fake/Streaming/SynchronizerSpy.swift | 2 +- SplitTests/Integration/Api/FlagSetsIntegrationTest.swift | 4 ++-- SplitTests/Integration/Recorder/TrackTest.swift | 2 +- .../Integration/streaming/StreamingOccupancyTest.swift | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index 76ff75c5a..2d9c4e7e0 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -149,7 +149,7 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { func notifyUpdated(flagList: [String]) { let metadata = SplitMetadata(type: "Updated Flags", value: flagList.description) - splitEventsManager.notifyInternalEvent(SplitInternalEvent(.splitsUpdated, metadata: metadata)) + splitEventsManager.notifyInternalEvent(.splitsUpdated, metadata: metadata) } func pause() { diff --git a/SplitTests/Fake/SplitEventsManagerStub.swift b/SplitTests/Fake/SplitEventsManagerStub.swift index 1d7685dc3..74105efcc 100644 --- a/SplitTests/Fake/SplitEventsManagerStub.swift +++ b/SplitTests/Fake/SplitEventsManagerStub.swift @@ -19,7 +19,7 @@ class SplitEventsManagerStub: SplitEventsManager { var startCalled = false var stopCalled = false - func notifyInternalEvent(_ event: SplitInternalEvent) { + func notifyInternalEvent(_ event: SplitInternalEventCase) { switch event { case .mySegmentsLoadedFromCache: mySegmentsLoadedEventFiredCount+=1 @@ -40,7 +40,7 @@ class SplitEventsManagerStub: SplitEventsManager { } var registeredEvents = [SplitEvent: SplitEventTask]() - func register(event: SplitEvent, task: SplitEventTask) { + func register(event: SplitEventCase, task: SplitEventTask) { registeredEvents[event] = task } @@ -52,7 +52,7 @@ class SplitEventsManagerStub: SplitEventsManager { stopCalled = true } - func eventAlreadyTriggered(event: SplitEvent) -> Bool { + func eventAlreadyTriggered(event: SplitEventCase) -> Bool { return false } } diff --git a/SplitTests/Fake/Streaming/FeatureFlagsSynchronizerStub.swift b/SplitTests/Fake/Streaming/FeatureFlagsSynchronizerStub.swift index 6695c3cfd..d157ce347 100644 --- a/SplitTests/Fake/Streaming/FeatureFlagsSynchronizerStub.swift +++ b/SplitTests/Fake/Streaming/FeatureFlagsSynchronizerStub.swift @@ -40,7 +40,7 @@ class FeatureFlagsSynchronizerStub: FeatureFlagsSynchronizer { } var notifyUpdatedCalled = false - func notifyUpdated() { + func notifyUpdated(flagList: [String]) { notifyUpdatedCalled = true } diff --git a/SplitTests/Fake/Streaming/SynchronizerSpy.swift b/SplitTests/Fake/Streaming/SynchronizerSpy.swift index da1b1011b..16361a8e0 100644 --- a/SplitTests/Fake/Streaming/SynchronizerSpy.swift +++ b/SplitTests/Fake/Streaming/SynchronizerSpy.swift @@ -196,7 +196,7 @@ class SynchronizerSpy: Synchronizer { } var notifyFeatureFlagsUpdatedCalled = false - func notifyFeatureFlagsUpdated() { + func notifyFeatureFlagsUpdated(flagList: [String]) { notifyFeatureFlagsUpdatedCalled = true } diff --git a/SplitTests/Integration/Api/FlagSetsIntegrationTest.swift b/SplitTests/Integration/Api/FlagSetsIntegrationTest.swift index 4b5561382..bbbac9e1e 100644 --- a/SplitTests/Integration/Api/FlagSetsIntegrationTest.swift +++ b/SplitTests/Integration/Api/FlagSetsIntegrationTest.swift @@ -799,11 +799,11 @@ class FlagSetsIntegrationTests: XCTestCase { let client = factory?.client let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") - client?.on(event: SplitEvent.sdkReady) { + client?.on(event: SplitEventCase.sdkReady) { sdkReadyExpectation.fulfill() } - client?.on(event: SplitEvent.sdkReadyTimedOut) { + client?.on(event: SplitEventCase.sdkReadyTimedOut) { sdkReadyExpectation.fulfill() } diff --git a/SplitTests/Integration/Recorder/TrackTest.swift b/SplitTests/Integration/Recorder/TrackTest.swift index d5b4e7fe6..9dd55a988 100644 --- a/SplitTests/Integration/Recorder/TrackTest.swift +++ b/SplitTests/Integration/Recorder/TrackTest.swift @@ -128,7 +128,7 @@ class TrackTest: XCTestCase { var sdkReadyFired = false - client.on(event: SplitEvent.sdkReady) { + client.on(event: SplitEventCase.sdkReady) { sdkReadyFired = true sdkReady.fulfill() } diff --git a/SplitTests/Integration/streaming/StreamingOccupancyTest.swift b/SplitTests/Integration/streaming/StreamingOccupancyTest.swift index b971a6c4d..7e7e8d428 100644 --- a/SplitTests/Integration/streaming/StreamingOccupancyTest.swift +++ b/SplitTests/Integration/streaming/StreamingOccupancyTest.swift @@ -45,11 +45,11 @@ class StreamingOccupancyTest: XCTestCase { let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") - client.on(event: SplitEvent.sdkReady) { + client.on(event: SplitEventCase.sdkReady) { sdkReadyExpectation.fulfill() } - client.on(event: SplitEvent.sdkReadyTimedOut) { + client.on(event: SplitEventCase.sdkReadyTimedOut) { sdkReadyExpectation.fulfill() } From 6eaae9475551820d4d14e118adf6bfb285fb29b2 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 13 May 2025 18:09:54 -0300 Subject: [PATCH 19/20] Reverted events to original names --- Split/Api/DefaultSplitClient.swift | 18 +++--- Split/Api/FailHelpers.swift | 12 ++-- Split/Api/LocalhostSplitClient.swift | 20 +++--- Split/Api/SplitClient.swift | 8 +-- Split/Common/Structs/BlockingQueue.swift | 10 +-- Split/Engine/DefaultTreatmentManager.swift | 2 +- Split/Events/EventsManagerCoordinator.swift | 14 ++-- Split/Events/SplitEvent.swift | 10 +-- Split/Events/SplitEventActionTask.swift | 6 +- Split/Events/SplitEventTask.swift | 2 +- Split/Events/SplitEventsManager.swift | 64 +++++++++---------- Split/Events/SplitInternalEvent.swift | 10 +-- .../Refresh/PeriodicSyncWorker.swift | 6 +- .../Refresh/RetryableSegmentsSyncWorker.swift | 6 +- .../Refresh/RetryableSyncWorker.swift | 6 +- Split/Localhost/LocalhostSynchronizer.swift | 2 +- .../Sync/FeatureFlagsSynchronizer.swift | 2 +- 17 files changed, 99 insertions(+), 99 deletions(-) diff --git a/Split/Api/DefaultSplitClient.swift b/Split/Api/DefaultSplitClient.swift index 84a1bebb9..2cc5d4f71 100644 --- a/Split/Api/DefaultSplitClient.swift +++ b/Split/Api/DefaultSplitClient.swift @@ -58,40 +58,40 @@ public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClie // MARK: Events Listeners extension DefaultSplitClient { - private func onWithMetadata(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute actionWithMetadata: @escaping SplitActionWithMetadata) { + private func onWithMetadata(event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute actionWithMetadata: @escaping SplitActionWithMetadata) { guard let factory = clientManager?.splitFactory else { return } let task = SplitEventActionTask(action: actionWithMetadata, event: event.type, runInBackground: runInBackground, factory: factory, queue: queue) on(event: event.type, executeTask: task) } - public func on(event: SplitEventCase, executeWithMetadata action: SplitActionWithMetadata?) { + public func on(event: SplitEvent, executeWithMetadata action: SplitActionWithMetadata?) { guard let action = action else { return } - onWithMetadata(event: SplitEvent(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) + onWithMetadata(event: SplitEventWithMetadata(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action) } - private func on(event: SplitEventCase, executeTask task: SplitEventActionTask) { + 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: SplitEvent(type: event, metadata: nil), task: task) + eventsManager.register(event: SplitEventWithMetadata(type: event, metadata: nil), task: task) } - private func on(event: SplitEventCase, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { + private func on(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { 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) } - public func on(event: SplitEventCase, execute action: @escaping SplitAction) { + public func on(event: SplitEvent, execute action: @escaping SplitAction) { on(event: event, runInBackground: false, queue: nil, execute: action) } - public func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) { + public func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) { on(event: event, runInBackground: runInBackground, queue: nil, execute: action) } - public func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) { + public func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction) { on(event: event, runInBackground: true, queue: queue, execute: action) } } diff --git a/Split/Api/FailHelpers.swift b/Split/Api/FailHelpers.swift index bd91d69eb..e1ec94d56 100644 --- a/Split/Api/FailHelpers.swift +++ b/Split/Api/FailHelpers.swift @@ -12,7 +12,7 @@ import Foundation /// on Failed init class FailedClient: SplitClient { - func on(event: SplitEventCase, executeWithMetadata: SplitActionWithMetadata) {} + func on(event: SplitEvent, executeWithMetadata: SplitActionWithMetadata) {} func getTreatment(_ split: String) -> String { return SplitConstants.control @@ -54,17 +54,17 @@ class FailedClient: SplitClient { return [:] } - public func on(event: SplitEventCase, perform: SplitAction?) {} + public func on(event: SplitEvent, perform: SplitAction?) {} - func on(event: SplitEventCase, perform: @escaping ([String : Any]?) -> Void) {} + func on(event: SplitEvent, perform: @escaping ([String : Any]?) -> Void) {} - func on(event: SplitEventCase, execute action: @escaping SplitAction) {} + func on(event: SplitEvent, execute action: @escaping SplitAction) {} - func on(event: SplitEventCase, runInBackground: Bool, + func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) { } - func on(event: SplitEventCase, + func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction) { } diff --git a/Split/Api/LocalhostSplitClient.swift b/Split/Api/LocalhostSplitClient.swift index 5a2efc05b..0e04ffc22 100644 --- a/Split/Api/LocalhostSplitClient.swift +++ b/Split/Api/LocalhostSplitClient.swift @@ -120,33 +120,33 @@ public final class LocalhostSplitClient: NSObject, SplitClient { } // MARK: Events Listeners - public func on(event: SplitEventCase, perform: SplitAction?) { + public func on(event: SplitEvent, perform: SplitAction?) { guard let perform = perform else { return } on(event: event, execute: perform) } - public func on(event: SplitEventCase, executeWithMetadata: @escaping SplitActionWithMetadata) { - on(eventWithMetadata: SplitEvent(type: event, metadata: nil), runInBackground: false, queue: nil, execute: executeWithMetadata) + public func on(event: SplitEvent, executeWithMetadata: @escaping SplitActionWithMetadata) { + on(eventWithMetadata: SplitEventWithMetadata(type: event, metadata: nil), runInBackground: false, queue: nil, execute: executeWithMetadata) } - public func on(event: SplitEventCase, runInBackground: Bool, + public func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) { on(event: event, runInBackground: runInBackground, queue: nil, execute: action) } - public func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) { + public func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction) { on(event: event, runInBackground: true, queue: queue, execute: action) } - public func on(event: SplitEventCase, execute action: @escaping SplitAction) { + public func on(event: SplitEvent, execute action: @escaping SplitAction) { on(event: event, runInBackground: false, queue: nil, execute: action) } - private func on(event: SplitEventCase, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { - on(eventWithMetadata: SplitEvent(type: event, metadata: nil), runInBackground: runInBackground, queue: queue, execute: action) + private func on(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { + on(eventWithMetadata: SplitEventWithMetadata(type: event, metadata: nil), runInBackground: runInBackground, queue: queue, execute: action) } - private func on(eventWithMetadata event: SplitEvent, runInBackground: Bool, + private func on(eventWithMetadata event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) { guard let factory = clientManger?.splitFactory else { return } @@ -159,7 +159,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient { } } - private func on(eventWithMetadata event: SplitEvent, runInBackground: Bool, + private func on(eventWithMetadata event: SplitEventWithMetadata, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitActionWithMetadata) { guard let factory = clientManger?.splitFactory else { return } diff --git a/Split/Api/SplitClient.swift b/Split/Api/SplitClient.swift index 4d48eb7d1..86a7fdae5 100644 --- a/Split/Api/SplitClient.swift +++ b/Split/Api/SplitClient.swift @@ -11,10 +11,10 @@ import Foundation @objc public protocol SplitClient { // MARK: Listeners for customer - func on(event: SplitEventCase, execute action: @escaping SplitAction) - func on(event: SplitEventCase, executeWithMetadata: @escaping SplitActionWithMetadata) -> Void - func on(event: SplitEventCase, runInBackground: Bool, execute action: @escaping SplitAction) - func on(event: SplitEventCase, queue: DispatchQueue, execute action: @escaping SplitAction) + func on(event: SplitEvent, execute action: @escaping SplitAction) + func on(event: SplitEvent, executeWithMetadata: @escaping SplitActionWithMetadata) -> Void + func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) + func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction) // MARK: Treatments diff --git a/Split/Common/Structs/BlockingQueue.swift b/Split/Common/Structs/BlockingQueue.swift index 8fd45f67f..df5bfbb6f 100644 --- a/Split/Common/Structs/BlockingQueue.swift +++ b/Split/Common/Structs/BlockingQueue.swift @@ -73,18 +73,18 @@ class GenericBlockingQueue { // 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() - func add(_ item: SplitInternalEvent) { + let blockingQueue = GenericBlockingQueue() + func add(_ item: SplitInternalEventWithMetadata) { blockingQueue.add(item) } - func take() throws -> SplitInternalEvent { + func take() throws -> SplitInternalEventWithMetadata { let value = try blockingQueue.take() return value } diff --git a/Split/Engine/DefaultTreatmentManager.swift b/Split/Engine/DefaultTreatmentManager.swift index 8eeed9f46..d9b86bf53 100644 --- a/Split/Engine/DefaultTreatmentManager.swift +++ b/Split/Engine/DefaultTreatmentManager.swift @@ -337,7 +337,7 @@ extension DefaultTreatmentManager { } private func isSdkReady() -> Bool { - return eventsManager.eventAlreadyTriggered(event: SplitEventCase.sdkReadyFromCache) || + return eventsManager.eventAlreadyTriggered(event: SplitEvent.sdkReadyFromCache) || eventsManager.eventAlreadyTriggered(event: .sdkReady) } diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index 6cf5ff40a..71ab98282 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -17,24 +17,24 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { private var defaultManager: SplitEventsManager? private var managers = [Key: SplitEventsManager]() - private var triggered = Set() + private var triggered = Set() private let queue = DispatchQueue(label: "split-event-manager-coordinator") - private let eventsToHandle: Set = Set( + private let eventsToHandle: Set = Set( [.splitsLoadedFromCache, .splitsUpdated, .splitKilledNotification] ) // MARK: Notifications - func notifyInternalEvent(_ event: SplitInternalEventCase) { + func notifyInternalEvent(_ event: SplitInternalEvent) { notifyInternalEvent(event, metadata: SplitMetadata(type: "", value: "")) } - func notifyInternalEvent(_ event: SplitInternalEvent) { + func notifyInternalEvent(_ event: SplitInternalEventWithMetadata) { notifyInternalEvent(event.type, metadata: event.metadata ?? SplitMetadata(type: "", value: "")) } - func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata) { + func notifyInternalEvent(_ event: SplitInternalEvent, metadata: SplitMetadata) { if !eventsToHandle.contains(event) { return } @@ -60,7 +60,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { } } - func eventAlreadyTriggered(event: SplitEventCase) -> Bool { + func eventAlreadyTriggered(event: SplitEvent) -> Bool { return defaultManager?.eventAlreadyTriggered(event: event) ?? false } @@ -86,5 +86,5 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { } } - func register(event: SplitEvent, task: SplitEventActionTask) {} + func register(event: SplitEventWithMetadata, task: SplitEventActionTask) {} } diff --git a/Split/Events/SplitEvent.swift b/Split/Events/SplitEvent.swift index 77bf30445..6d1179c35 100644 --- a/Split/Events/SplitEvent.swift +++ b/Split/Events/SplitEvent.swift @@ -7,22 +7,22 @@ import Foundation -@objcMembers public class SplitEvent: NSObject { - let type: SplitEventCase +@objcMembers public class SplitEventWithMetadata: NSObject { + let type: SplitEvent let metadata: SplitMetadata? - @objc public init(type: SplitEventCase, metadata: SplitMetadata? = nil) { + @objc public init(type: SplitEvent, metadata: SplitMetadata? = nil) { self.type = type self.metadata = metadata } public override func isEqual(_ object: Any?) -> Bool { - guard let other = object as? SplitEvent else { return false } + guard let other = object as? SplitEventWithMetadata else { return false } return self.type == other.type } } -@objc public enum SplitEventCase: Int { +@objc public enum SplitEvent: Int { case sdkReady case sdkReadyTimedOut case sdkReadyFromCache diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index 3236af6a0..cfa086b86 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -15,11 +15,11 @@ class SplitEventActionTask: SplitEventTask { private var eventHandler: SplitAction? private var eventHandlerWithMetadata: SplitActionWithMetadata? private var queue: DispatchQueue? - var event: SplitEventCase + var event: SplitEvent var runInBackground: Bool = false var factory: SplitFactory - init(action: @escaping SplitActionWithMetadata, event: SplitEventCase, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { + init(action: @escaping SplitActionWithMetadata, event: SplitEvent, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { self.eventHandlerWithMetadata = action self.event = event self.runInBackground = runInBackground @@ -27,7 +27,7 @@ class SplitEventActionTask: SplitEventTask { self.factory = factory } - init(action: @escaping SplitAction, event: SplitEventCase, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { + init(action: @escaping SplitAction, event: SplitEvent, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) { self.eventHandler = action self.event = event self.runInBackground = runInBackground diff --git a/Split/Events/SplitEventTask.swift b/Split/Events/SplitEventTask.swift index aa398347c..79bf23d2d 100644 --- a/Split/Events/SplitEventTask.swift +++ b/Split/Events/SplitEventTask.swift @@ -8,7 +8,7 @@ import Foundation protocol SplitEventTask { - var event: SplitEventCase { get } + var event: SplitEvent { get } var runInBackground: Bool { get } func takeQueue() -> DispatchQueue? func run(_ metadata: SplitMetadata?) diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index 5886cf61d..0d2375fce 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -9,13 +9,13 @@ import Foundation protocol SplitEventsManager: AnyObject { - func register(event: SplitEvent, task: SplitEventActionTask) - func notifyInternalEvent(_ event: SplitInternalEventCase) - func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata) + func register(event: SplitEventWithMetadata, task: SplitEventActionTask) func notifyInternalEvent(_ event: SplitInternalEvent) + func notifyInternalEvent(_ event: SplitInternalEvent, metadata: SplitMetadata) + func notifyInternalEvent(_ event: SplitInternalEventWithMetadata) func start() func stop() - func eventAlreadyTriggered(event: SplitEventCase) -> Bool + func eventAlreadyTriggered(event: SplitEvent) -> Bool } class DefaultSplitEventsManager: SplitEventsManager { @@ -24,9 +24,9 @@ class DefaultSplitEventsManager: SplitEventsManager { private var sdkReadyTimeStart: Int64 - private var subscriptions = [SplitEventCase: [SplitEventTask]]() + private var subscriptions = [SplitEvent: [SplitEventTask]]() private var executionTimes: [String: Int] - private var triggered: [SplitInternalEvent] + private var triggered: [SplitInternalEventWithMetadata] private let processQueue: DispatchQueue private let dataAccessQueue: DispatchQueue private var isStarted: Bool @@ -40,7 +40,7 @@ class DefaultSplitEventsManager: SplitEventsManager { self.isStarted = false self.sdkReadyTimeStart = Date().unixTimestampInMiliseconds() self.readingRefreshTime = 300 - self.triggered = [SplitInternalEvent]() + self.triggered = [SplitInternalEventWithMetadata]() self.eventsQueue = DefaultInternalEventBlockingQueue() self.executionTimes = [String: Int]() registerMaxAllowedExecutionTimesPerEvent() @@ -58,15 +58,15 @@ class DefaultSplitEventsManager: SplitEventsManager { /* Overload kept for backwards compatibility. It allows calling `notifyInternalEvent(.event)` without needing to pass `nil` as metadata. Do not remove unless all usages have migrated to the new signature. */ - func notifyInternalEvent(_ event: SplitInternalEventCase) { - notifyInternalEvent(SplitInternalEvent(event, metadata: nil)) + func notifyInternalEvent(_ event: SplitInternalEvent) { + notifyInternalEvent(SplitInternalEventWithMetadata(event, metadata: nil)) } - func notifyInternalEvent(_ event: SplitInternalEventCase, metadata: SplitMetadata) { - notifyInternalEvent(SplitInternalEvent(event, metadata: metadata)) + func notifyInternalEvent(_ event: SplitInternalEvent, metadata: SplitMetadata) { + notifyInternalEvent(SplitInternalEventWithMetadata(event, metadata: metadata)) } - func notifyInternalEvent(_ event: SplitInternalEvent) { + func notifyInternalEvent(_ event: SplitInternalEventWithMetadata) { processQueue.async { [weak self] in if let self = self { self.eventsQueue.add(event) @@ -75,7 +75,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } // MARK: Register - func register (event: SplitEvent, task: SplitEventActionTask) { + func register (event: SplitEventWithMetadata, task: SplitEventActionTask) { let eventName = event.type.toString() processQueue.async { [weak self] in guard let self = self else { return } @@ -100,7 +100,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - func eventAlreadyTriggered(event: SplitEventCase) -> Bool { + func eventAlreadyTriggered(event: SplitEvent) -> Bool { var isTriggered = false processQueue.sync { isTriggered = self.isTriggered(external: event) @@ -129,10 +129,10 @@ class DefaultSplitEventsManager: SplitEventsManager { */ private func registerMaxAllowedExecutionTimesPerEvent() { - executionTimes = [ SplitEventCase.sdkReady.toString(): 1, - SplitEventCase.sdkUpdated.toString(): -1, - SplitEventCase.sdkReadyFromCache.toString(): 1, - SplitEventCase.sdkReadyTimedOut.toString(): 1] + executionTimes = [ SplitEvent.sdkReady.toString(): 1, + SplitEvent.sdkUpdated.toString(): -1, + SplitEvent.sdkReadyFromCache.toString(): 1, + SplitEvent.sdkReadyTimedOut.toString(): 1] } private func isRunning() -> Bool { @@ -143,7 +143,7 @@ class DefaultSplitEventsManager: SplitEventsManager { return isRunning } - private func takeEvent() -> SplitInternalEvent? { + private func takeEvent() -> SplitInternalEventWithMetadata? { do { return try eventsQueue.take() } catch BlockingQueueError.hasBeenStopped { @@ -163,7 +163,7 @@ class DefaultSplitEventsManager: SplitEventsManager { switch event.type { case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated: if isTriggered(external: .sdkReady) { - trigger(event: SplitEvent(type: .sdkUpdated, metadata: event.metadata)) + trigger(event: SplitEventWithMetadata(type: .sdkUpdated, metadata: event.metadata)) continue } self.triggerSdkReadyIfNeeded() @@ -174,7 +174,7 @@ class DefaultSplitEventsManager: SplitEventsManager { isTriggered(internal: .mySegmentsLoadedFromCache), isTriggered(internal: .myLargeSegmentsLoadedFromCache), isTriggered(internal: .attributesLoadedFromCache) { - trigger(event: SplitEventCase.sdkReadyFromCache) + trigger(event: SplitEvent.sdkReadyFromCache) } case .splitKilledNotification: if isTriggered(external: .sdkReady) { @@ -183,14 +183,14 @@ class DefaultSplitEventsManager: SplitEventsManager { } case .sdkReadyTimeoutReached: if !isTriggered(external: .sdkReady) { - trigger(event: SplitEventCase.sdkReadyTimedOut) + trigger(event: SplitEvent.sdkReadyTimedOut) } } } } // MARK: Helper functions. - func isTriggered(external event: SplitEventCase) -> Bool { + func isTriggered(external event: SplitEvent) -> Bool { var triggered = false dataAccessQueue.sync { if let times = executionTimes[event.toString()] { @@ -211,11 +211,11 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func trigger(event: SplitEventCase) { - trigger(event: SplitEvent(type: event, metadata: nil)) + private func trigger(event: SplitEvent) { + trigger(event: SplitEventWithMetadata(type: event, metadata: nil)) } - private func trigger(event: SplitEvent) { + private func trigger(event: SplitEventWithMetadata) { let eventName = event.type.toString() // If executionTimes is zero, maximum executions has been reached @@ -237,7 +237,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func executeTask(eventWithMetadata event: SplitEvent, task: SplitEventTask) { + private func executeTask(eventWithMetadata event: SplitEventWithMetadata, task: SplitEventTask) { let eventName = task.event.toString() @@ -258,12 +258,12 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func isTriggered(internal event: SplitInternalEvent) -> Bool { + private func isTriggered(internal event: SplitInternalEventWithMetadata) -> Bool { return triggered.filter { $0 == event }.count > 0 } - private func isTriggered(internal event: SplitInternalEventCase) -> Bool { - return isTriggered(internal: SplitInternalEvent(event, metadata: nil)) + private func isTriggered(internal event: SplitInternalEvent) -> Bool { + return isTriggered(internal: SplitInternalEventWithMetadata(event, metadata: nil)) } // MARK: Safe Data Access @@ -275,7 +275,7 @@ class DefaultSplitEventsManager: SplitEventsManager { return times } - func subscribe(task: SplitEventTask, to event: SplitEventCase) { + func subscribe(task: SplitEventTask, to event: SplitEvent) { dataAccessQueue.async { [weak self] in guard let self = self else { return } var subscriptions = self.subscriptions[event] ?? [SplitEventTask]() @@ -284,7 +284,7 @@ class DefaultSplitEventsManager: SplitEventsManager { } } - private func getSubscriptions(for event: SplitEventCase) -> [SplitEventTask]? { + private func getSubscriptions(for event: SplitEvent) -> [SplitEventTask]? { var subscriptions: [SplitEventTask]? dataAccessQueue.sync { subscriptions = self.subscriptions[event] diff --git a/Split/Events/SplitInternalEvent.swift b/Split/Events/SplitInternalEvent.swift index 557226a05..8e3c31cdc 100644 --- a/Split/Events/SplitInternalEvent.swift +++ b/Split/Events/SplitInternalEvent.swift @@ -6,21 +6,21 @@ import Foundation -struct SplitInternalEvent { - let type: SplitInternalEventCase +struct SplitInternalEventWithMetadata { + let type: SplitInternalEvent let metadata: SplitMetadata? - init(_ type: SplitInternalEventCase, metadata: SplitMetadata? = nil) { + init(_ type: SplitInternalEvent, metadata: SplitMetadata? = nil) { self.type = type self.metadata = metadata } - static func == (lhs: SplitInternalEvent, rhs: SplitInternalEvent) -> Bool { + static func == (lhs: SplitInternalEventWithMetadata, rhs: SplitInternalEventWithMetadata) -> Bool { return lhs.type == rhs.type } } -enum SplitInternalEventCase { +enum SplitInternalEvent { case mySegmentsUpdated case myLargeSegmentsUpdated case splitsUpdated diff --git a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift index a5273a898..b6c0807da 100644 --- a/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift @@ -130,7 +130,7 @@ class BasePeriodicSyncWorker: PeriodicSyncWorker { Logger.i("Fetch from remote not implemented") } - func notifyUpdate(_ events: [SplitInternalEvent]) { + func notifyUpdate(_ events: [SplitInternalEventWithMetadata]) { events.forEach { eventsManager.notifyInternalEvent($0) } @@ -179,7 +179,7 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker { } let metadata = SplitMetadata(type: "Updated Flags", value: updatedFlags.description) - notifyUpdate([SplitInternalEvent(.splitsUpdated, metadata: metadata)]) + notifyUpdate([SplitInternalEventWithMetadata(.splitsUpdated, metadata: metadata)]) } } @@ -222,7 +222,7 @@ class PeriodicMySegmentsSyncWorker: BasePeriodicSyncWorker { if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty { let metadata = SplitMetadata(type: "Updated segments", value: (result.msUpdated + result.mlsUpdated).description) - notifyUpdate([SplitInternalEvent(.mySegmentsUpdated, metadata: metadata)]) + notifyUpdate([SplitInternalEventWithMetadata(.mySegmentsUpdated, metadata: metadata)]) } } } catch { diff --git a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift index c57466ea3..14504b946 100644 --- a/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift @@ -48,12 +48,12 @@ class RetryableMySegmentsSyncWorker: BaseRetryableSyncWorker { if !isSdkReadyTriggered() { // Notifying both to trigger SDK Ready - notifyUpdate([SplitInternalEvent(.mySegmentsUpdated, metadata: msMetadata)]) + notifyUpdate([SplitInternalEventWithMetadata(.mySegmentsUpdated, metadata: msMetadata)]) let mlsMetadata = SplitMetadata(type: "Large segments updated", value: result.mlsUpdated.description) - notifyUpdate([SplitInternalEvent(.myLargeSegmentsUpdated, metadata: mlsMetadata)]) + notifyUpdate([SplitInternalEventWithMetadata(.myLargeSegmentsUpdated, metadata: mlsMetadata)]) } else if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty { - notifyUpdate([SplitInternalEvent(.mySegmentsUpdated, metadata: msMetadata)]) + notifyUpdate([SplitInternalEventWithMetadata(.mySegmentsUpdated, metadata: msMetadata)]) } return true } diff --git a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift index 003b195fe..61869a284 100644 --- a/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/RetryableSyncWorker.swift @@ -79,7 +79,7 @@ class BaseRetryableSyncWorker: RetryableSyncWorker { } } - func notifyUpdate(_ events: [SplitInternalEvent]) { + func notifyUpdate(_ events: [SplitInternalEventWithMetadata]) { events.forEach { eventsManager.notifyInternalEvent($0.type, metadata: $0.metadata ?? SplitMetadata(type: "", value: "")) } @@ -164,7 +164,7 @@ class RetryableSplitsSyncWorker: BaseRetryableSyncWorker { if result.success { if !isSdkReadyTriggered() || result.featureFlagsUpdated.count > 0 { let metadata = SplitMetadata(type: "Flags", value: result.featureFlagsUpdated.description) - notifyUpdate([SplitInternalEvent(.splitsUpdated, metadata: metadata)]) + notifyUpdate([SplitInternalEventWithMetadata(.splitsUpdated, metadata: metadata)]) } resetBackoffCounter() return true @@ -222,7 +222,7 @@ class RetryableSplitsUpdateWorker: BaseRetryableSyncWorker { if result.success { if result.featureFlagsUpdated.count > 0 { let metadata = SplitMetadata(type: "Data", value: result.featureFlagsUpdated.description) - notifyUpdate([SplitInternalEvent(.splitsUpdated, metadata: metadata)]) + notifyUpdate([SplitInternalEventWithMetadata(.splitsUpdated, metadata: metadata)]) } resetBackoffCounter() return true diff --git a/Split/Localhost/LocalhostSynchronizer.swift b/Split/Localhost/LocalhostSynchronizer.swift index 2b57f58e9..489fa6b82 100644 --- a/Split/Localhost/LocalhostSynchronizer.swift +++ b/Split/Localhost/LocalhostSynchronizer.swift @@ -68,7 +68,7 @@ class LocalhostSynchronizer: FeatureFlagsSynchronizer { _ = self.featureFlagsStorage.update(splitChange: change) let metadata = SplitMetadata(type: "Splits Updated", value: featureFlags.values.description) - self.eventsManager.notifyInternalEvent(SplitInternalEvent(.splitsUpdated, metadata: metadata)) + self.eventsManager.notifyInternalEvent(SplitInternalEventWithMetadata(.splitsUpdated, metadata: metadata)) } } } diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index 2d9c4e7e0..88b8c49ff 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -88,7 +88,7 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { // Trigger event let metadata = SplitMetadata(type: "Metadata", value: "Splits from cache ready") - let event = SplitInternalEvent(.attributesLoadedFromCache, metadata: metadata) + let event = SplitInternalEventWithMetadata(.attributesLoadedFromCache, metadata: metadata) self.splitEventsManager.notifyInternalEvent(event) } self.broadcasterChannel.push(event: .splitLoadedFromCache) From f10f324bd6025ed821f34185f0102a227e322d64 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 15 May 2025 11:09:08 -0300 Subject: [PATCH 20/20] Discarding branch --- Split/Events/EventsManagerCoordinator.swift | 2 +- Split/Events/SplitEvent.swift | 14 ++++++++++---- Split/Events/SplitEventActionTask.swift | 3 +++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Split/Events/EventsManagerCoordinator.swift b/Split/Events/EventsManagerCoordinator.swift index 71ab98282..e84dc33f5 100644 --- a/Split/Events/EventsManagerCoordinator.swift +++ b/Split/Events/EventsManagerCoordinator.swift @@ -27,7 +27,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator { // MARK: Notifications func notifyInternalEvent(_ event: SplitInternalEvent) { - notifyInternalEvent(event, metadata: SplitMetadata(type: "", value: "")) + notifyInternalEvent(event, metadata: SplitMetadata(type: .NETWORK_ERROR.toStrin(), value: "")) } func notifyInternalEvent(_ event: SplitInternalEventWithMetadata) { diff --git a/Split/Events/SplitEvent.swift b/Split/Events/SplitEvent.swift index 6d1179c35..279e1da1b 100644 --- a/Split/Events/SplitEvent.swift +++ b/Split/Events/SplitEvent.swift @@ -45,11 +45,17 @@ import Foundation // Just a key-value wrapper for extensibility. // (Also used by SplitInternalEvent) @objc public class SplitMetadata: NSObject { - var type: String = "" - var value: String = "" + var type: String + var data: String = "" - init(type: String, value: String) { + init(type: String, data: String) { self.type = type - self.value = value + self.data = data } } + +enum SplitError: Int { + case NETWORK_ERROR + case STORAGE_ERROR + case SPLIT_UPDATE +} diff --git a/Split/Events/SplitEventActionTask.swift b/Split/Events/SplitEventActionTask.swift index cfa086b86..4e1b9236f 100644 --- a/Split/Events/SplitEventActionTask.swift +++ b/Split/Events/SplitEventActionTask.swift @@ -41,7 +41,10 @@ class SplitEventActionTask: SplitEventTask { } func run(_ metadata: SplitMetadata?) { + // filter + eventHandler?() + eventHandlerWithMetadata?(metadata) } }