Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion Split/Api/DefaultSplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Foundation
typealias DestroyHandler = () -> Void

public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClient, @unchecked Sendable {

private var storageContainer: SplitStorageContainer
private var key: Key
private let config: SplitClientConfig
Expand All @@ -17,6 +17,7 @@ public final class DefaultSplitClient: NSObject, SplitClient, TelemetrySplitClie
private var isClientDestroyed = false
private let eventsTracker: EventsTracker
private weak var clientManager: SplitClientManager?
@objc public var listener: SplitClientEventListener?

var initStopwatch: Stopwatch?

Expand Down Expand Up @@ -87,6 +88,27 @@ extension DefaultSplitClient {
}
eventsManager.register(event: event, task: task)
}

// MARK: Events Listeners with Medatadata
@objc public func addEventsListener(listener: SplitClientEventListener) {
if let l = listener.onSdkReady {
registerEvent(.sdkReady, action: l)
}

if let l = listener.onSdkReadyFromCache {
registerEvent(.sdkReadyFromCache, action: l)
}

if let l = listener.onSdkUpdate {
registerEvent(.sdkUpdated, action: l)
}
}

private func registerEvent<T: EventMetadata>(_ event: SplitEvent, action: @escaping (T) -> Void) {
guard let factory = clientManager?.splitFactory else { return }
let task = SplitEventActionTask(action: action, event: event, runInBackground: true, factory: factory, queue: nil)
eventsManager.register(event: event, task: task)
}
}

// MARK: Treatment / Evaluation
Expand Down
6 changes: 6 additions & 0 deletions Split/Api/FailHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,18 @@ class FailedClient: SplitClient {
[:]
}

// MARK: Events
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) {}

// MARK: Events Listeners with Medatadata
var listener: (any SplitClientEventListener)?
@objc public func addEventsListener(listener: SplitClientEventListener) {}

// MARK: Track
func track(trafficType: String, eventType: String) -> Bool {
false
}
Expand Down
32 changes: 28 additions & 4 deletions Split/Api/LocalhostSplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,21 @@ import Foundation
///

public final class LocalhostSplitClient: NSObject, SplitClient {

private let splitsStorage: SplitsStorage
private let mySegmentsStorage = EmptyMySegmentsStorage()

private let eventsManager: SplitEventsManager?
private var evaluator: Evaluator
private let key: Key
weak var clientManger: SplitClientManager?
weak var clientManager: SplitClientManager?
@objc public var listener: SplitClientEventListener?

init(key: Key, splitsStorage: SplitsStorage, clientManager: SplitClientManager?, eventsManager: SplitEventsManager? = nil, evaluator: Evaluator) {
self.eventsManager = eventsManager
self.key = key
self.splitsStorage = splitsStorage
self.clientManger = clientManager
self.clientManager = clientManager

self.evaluator = evaluator
super.init()
Expand Down Expand Up @@ -117,6 +118,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient {
return results
}

// MARK: Events
public func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) {
on(event: event, runInBackground: runInBackground, queue: nil, execute: action)
}
Expand All @@ -131,7 +133,7 @@ public final class LocalhostSplitClient: NSObject, SplitClient {

private func on(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) {

guard let factory = clientManger?.splitFactory else { return }
guard let factory = clientManager?.splitFactory else { return }
if let eventsManager = self.eventsManager {
let task = SplitEventActionTask(action: action, event: event,
runInBackground: runInBackground,
Expand All @@ -140,7 +142,29 @@ public final class LocalhostSplitClient: NSObject, SplitClient {
eventsManager.register(event: event, task: task)
}
}

// MARK: Events Listeners with Medatadata
@objc public func addEventsListener(listener: SplitClientEventListener) {
if let l = listener.onSdkReady {
registerEvent(.sdkReady, action: l)
}

if let l = listener.onSdkReadyFromCache {
registerEvent(.sdkReadyFromCache, action: l)
}

if let l = listener.onSdkUpdate {
registerEvent(.sdkUpdated, action: l)
}
}

private func registerEvent<T: EventMetadata>(_ event: SplitEvent, action: @escaping (T) -> Void) {
guard let factory = clientManager?.splitFactory else { return }
let task = SplitEventActionTask(action: action, event: event, runInBackground: true, factory: factory, queue: nil)
eventsManager?.register(event: event, task: task)
}

// MARK: Track
public func track(trafficType: String, eventType: String) -> Bool {
true
}
Expand Down
16 changes: 15 additions & 1 deletion Split/Api/SplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@
import Foundation

public typealias SplitAction = () -> Void
public typealias SplitActionWithMetadata = (EventMetadata) -> Void
public typealias SplitActionWithMetadata<T: EventMetadata> = (T) -> Void

@objc public protocol SplitClientEventListener: AnyObject {
@objc(onSdkReady:)
optional func onSdkReady(_ metadata: SdkReadyMetadata)
@objc(onSdkReadyFromCache:)
optional func onSdkReadyFromCache(_ metadata: SdkReadyFromCacheMetadata)
@objc(onSdkUpdate:)
optional func onSdkUpdate(_ metadata: SdkUpdateMetadata)
}

@objc public protocol SplitClient {

Expand Down Expand Up @@ -37,6 +46,11 @@ public typealias SplitActionWithMetadata = (EventMetadata) -> Void
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)

// MARK: Events with Metadata
@objc var listener: SplitClientEventListener? { get set }
@objc(addEventsListener:)
func addEventsListener(listener: SplitClientEventListener)

// MARK: Track feature
func track(trafficType: String, eventType: String) -> Bool
Expand Down
23 changes: 16 additions & 7 deletions Split/Events/SplitEventActionTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@ import Foundation
class SplitEventActionTask: SplitEventTask, @unchecked Sendable {

private var eventHandler: SplitAction?
private var eventHandlerWithMetadata: SplitActionWithMetadata?
private var eventHandlerWithMetadata: SplitActionWithMetadata<EventMetadata>?
private var queue: DispatchQueue?
var event: SplitEvent
var runInBackground: Bool = false
var factory: SplitFactory

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

self.event = event
self.runInBackground = runInBackground
self.queue = queue
self.factory = factory

// Metadata: "swap" for concrete type and ensure type is correct for this event
self.eventHandlerWithMetadata = { metadata in
guard let typed = metadata as? T else {
Logger.e("Wrong metadata type for this event (\(event.toString())).")
return
}
action(typed)
}
}

init(action: @escaping SplitAction, event: SplitEvent, runInBackground: Bool = false, factory: SplitFactory, queue: DispatchQueue? = nil) {
Expand Down
2 changes: 1 addition & 1 deletion Split/Events/SplitMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import Foundation

@objc public protocol EventMetadata: Sendable {}
@objc public protocol EventMetadata: Sendable, NSObjectProtocol {}

// MARK: UPDATE
/// Represents the type of SDK update that triggered a metadata callback.
Expand Down
14 changes: 7 additions & 7 deletions SplitTests/Fake/InternalSplitClientStub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import Foundation
@testable import Split

class InternalSplitClientStub: InternalSplitClient, @unchecked Sendable {

var splitsStorage: SplitsStorage?
var mySegmentsStorage: MySegmentsStorage?
var myLargeSegmentsStorage: MySegmentsStorage?
var listener: SplitClientEventListener?

init(splitsStorage: SplitsStorage?,
mySegmentsStorage: MySegmentsStorage?,
Expand Down Expand Up @@ -95,14 +96,13 @@ class InternalSplitClientStub: InternalSplitClient, @unchecked Sendable {
return ["": SplitResult(treatment: SplitConstants.control)]
}

func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction) {
}
func on(event: SplitEvent, queue: DispatchQueue, execute action: @escaping SplitAction) {}

func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) {
}
func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) {}

func on(event: SplitEvent, execute action: @escaping SplitAction) {
}
func on(event: SplitEvent, execute action: @escaping SplitAction) {}

func addEventsListener(listener: SplitClientEventListener) {}

func track(trafficType: String, eventType: String) -> Bool {
return true
Expand Down
5 changes: 5 additions & 0 deletions SplitTests/Fake/SplitClientStub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import Foundation

class SplitClientStub: SplitClient, @unchecked Sendable {

@objc public var listener: SplitClientEventListener?

func getTreatment(_ split: String, attributes: [String : Any]?) -> String {
return SplitConstants.control
}
Expand Down Expand Up @@ -95,6 +97,9 @@ class SplitClientStub: SplitClient, @unchecked Sendable {
func on(event: SplitEvent, runInBackground: Bool, queue: DispatchQueue?, execute action: @escaping SplitAction) {
}

func addEventsListener(listener: SplitClientEventListener) {
}

func track(trafficType: String, eventType: String) -> Bool {
return true
}
Expand Down
65 changes: 63 additions & 2 deletions SplitTests/SplitEventsManagerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,74 @@ class SplitEventsManagerTest: XCTestCase, @unchecked Sendable {
eventManager.stop()
}

func testSdkReadyWithMetadata() {
let taskExp = XCTestExpectation()

let timestamp: Int64 = 1000
let freshInstall = true

let metadata = SdkReadyMetadata(lastUpdateTimestamp: timestamp, isInitialCacheLoad: freshInstall)

let handler: SplitActionWithMetadata<SdkReadyMetadata> = { handlerMetadata in
XCTAssertEqual(handlerMetadata.lastUpdateTimestamp, timestamp)
XCTAssertEqual(handlerMetadata.isInitialCacheLoad, freshInstall)
taskExp.fulfill()
}
let task = SplitEventActionTask(action: handler,
event: .sdkReady,
runInBackground: false,
factory: SplitFactoryStub(apiKey: IntegrationHelper.dummyApiKey),
queue: nil)

// Run & test
task.run(metadata)
wait(for: [taskExp], timeout: 1)
}

func testSdkUpdateWithMetadata() {
let taskExp = XCTestExpectation()

let type: SdkUpdateMetadataType = .FLAGS_UPDATE
let names = ["Flag1", "FLAG2"]

let metadata = SdkUpdateMetadata(type: type, names: names)

let handler: SplitActionWithMetadata<SdkUpdateMetadata> = { handlerMetadata in
XCTAssertEqual(handlerMetadata.type, type)
XCTAssertEqual(handlerMetadata.names, names)
taskExp.fulfill()
}
let task = SplitEventActionTask(action: handler, event: .sdkReady, runInBackground: false, factory: SplitFactoryStub(apiKey: IntegrationHelper.dummyApiKey), queue: nil)

// Run & test
task.run(metadata)
wait(for: [taskExp], timeout: 1)
}

func testSdkReadyFromCacheWithMetadata() {
let taskExp = XCTestExpectation()

let freshInstall = true
let metadata = SdkReadyFromCacheMetadata(isInitialCacheLoad: freshInstall)

let handler: SplitActionWithMetadata<SdkReadyFromCacheMetadata> = { handlerMetadata in
XCTAssertEqual(handlerMetadata.isInitialCacheLoad, freshInstall)
taskExp.fulfill()
}
let task = SplitEventActionTask(action: handler, event: .sdkReady, runInBackground: false, factory: SplitFactoryStub(apiKey: IntegrationHelper.dummyApiKey), queue: nil)

// Run & test
task.run(metadata)
wait(for: [taskExp], timeout: 1)
}

// MARK: Helpers
func currentTimestamp() -> Int {
return Int(Date().unixTimestamp())
}

func sdkTask(exp: XCTestExpectation) -> TestTask {
return TestTask(exp: exp)
TestTask(exp: exp)
}
}

Expand All @@ -321,7 +382,7 @@ class TestTask: SplitEventActionTask, @unchecked Sendable {
var taskTriggered = false
let label: String
var exp: XCTestExpectation?
init(exp: XCTestExpectation?, label: String = "", action: SplitActionWithMetadata? = nil, metadata: EventMetadata? = nil) {
init(exp: XCTestExpectation?, label: String = "", action: SplitActionWithMetadata<EventMetadata>? = nil, metadata: EventMetadata? = nil) {
self.exp = exp
self.label = label
super.init(action: action ?? { _ in }, event: .sdkReady, factory: SplitFactoryStub(apiKey: IntegrationHelper.dummyApiKey))
Expand Down
Loading