Skip to content

[FME-4223] Events - ActionTask #684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
21 changes: 15 additions & 6 deletions Split/Api/DefaultSplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,23 @@ 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, " +
"which has already fired and won’t be emitted again. The callback won’t be executed.")
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: SplitEvent, executeWithMetadata action: SplitActionWithMetadata?) {
guard let action = action else { return }
onWithMetadata(event: SplitEventWithMetadata(type: event, metadata: nil), runInBackground: true, queue: nil, execute: action)
}

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

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

func on(event: SplitEvent, executeWithMetadata: @escaping SplitActionWithMetadata) {}

func track(trafficType: String, eventType: String) -> Bool {
return false
Expand Down
32 changes: 25 additions & 7 deletions Split/Api/LocalhostSplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,25 +121,43 @@ public final class LocalhostSplitClient: NSObject, SplitClient {
return results
}

public func on(event: SplitEvent, runInBackground: Bool,
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: SplitEvent, executeWithMetadata: @escaping SplitActionWithMetadata) {
on(eventWithMetadata: SplitEventWithMetadata(type: event, metadata: nil), runInBackground: false, queue: nil, execute: executeWithMetadata)
}

public func on(event: SplitEvent, runInBackground: Bool, execute action: @escaping SplitAction) {
on(event: event, runInBackground: runInBackground, queue: nil, 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)
}

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

public func on(event: SplitEvent, execute action: @escaping SplitAction) {
on(event: event, runInBackground: false, queue: nil, execute: action)
private func on(eventWithMetadata event: SplitEventWithMetadata, 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.type,
runInBackground: runInBackground,
factory: factory,
queue: queue)
eventsManager.register(event: event, task: task)
}
}

private func on(event: SplitEvent, runInBackground: Bool,
queue: DispatchQueue?, execute action: @escaping SplitAction) {
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,
let task = SplitEventActionTask(action: action, event: event.type,
runInBackground: runInBackground,
factory: factory,
queue: queue)
Expand Down
3 changes: 1 addition & 2 deletions Split/Api/SplitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

import Foundation

public typealias SplitAction = () -> Void

@objc public protocol SplitClient {

// MARK: Evaluation feature
Expand All @@ -34,6 +32,7 @@ public typealias SplitAction = () -> Void
func getTreatmentsWithConfig(splits: [String], attributes: [String: Any]?, evaluationOptions: EvaluationOptions?) -> [String: SplitResult]

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)

Expand Down
14 changes: 9 additions & 5 deletions Split/Common/Structs/BlockingQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,23 @@ class GenericBlockingQueue<Item> {

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

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

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

func take() throws -> SplitInternalEvent {
let value = try blockingQueue.take()
func take() throws -> SplitInternalEventWithMetadata {
let value = try blockingQueue.take()
return value
}

Expand Down
9 changes: 7 additions & 2 deletions Split/Events/EventsManagerCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator {
)

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

func notifyInternalEvent(_ event: SplitInternalEvent, metadata: EventMetadata? = nil) {
if !eventsToHandle.contains(event) {
return
}
Expand All @@ -33,7 +37,7 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator {

self.triggered.insert(event)
self.managers.forEach { _, manager in
manager.notifyInternalEvent(event)
manager.notifyInternalEvent(event, metadata: metadata)
}
}
}
Expand Down Expand Up @@ -76,5 +80,6 @@ class MainSplitEventsManager: SplitEventsManagerCoordinator {
}
}

func register(event: SplitEvent, task: SplitEventTask) {}
func register(event: SplitEvent, task: SplitEventActionTask) {}
func register(event: SplitEventWithMetadata, task: SplitEventActionTask) {}
}
27 changes: 20 additions & 7 deletions Split/Events/SplitEventActionTask.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
//
// SplitEventActionTask.swift
// Split
//
// Created by Javier L. Avrudsky on 7/6/18.
//

import Foundation

public typealias SplitAction = () -> Void
public typealias SplitActionWithMetadata = (_ metadata: EventMetadata?) -> Void

class SplitEventActionTask: SplitEventTask {

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

var event: SplitEvent
var runInBackground: Bool = false
var factory: SplitFactory
Expand All @@ -27,13 +28,25 @@ class SplitEventActionTask: SplitEventTask {
self.queue = queue
self.factory = factory
}

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

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

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

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

protocol SplitEventsManager: AnyObject {
func register(event: SplitEvent, task: SplitEventTask)
func register(event: SplitEvent, task: SplitEventActionTask)
func register(event: SplitEventWithMetadata, task: SplitEventActionTask)
func notifyInternalEvent(_ event: SplitInternalEvent)
func notifyInternalEvent(_ event: SplitInternalEvent, metadata: EventMetadata?)
func start()
func stop()
func eventAlreadyTriggered(event: SplitEvent) -> Bool
Expand All @@ -23,7 +25,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
Expand All @@ -35,7 +37,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()
Expand All @@ -48,28 +50,39 @@ class DefaultSplitEventsManager: SplitEventsManager {
}
}
}

func notifyInternalEvent(_ event: SplitInternalEvent, metadata: EventMetadata? = nil) {
let event = SplitInternalEventWithMetadata(event, metadata: metadata)

func notifyInternalEvent(_ event: SplitInternalEvent) {
processQueue.async { [weak self] in
if let self = self {
Logger.v("Event \(event) notified")
self.eventsQueue.add(event)
}
}
}

func register(event: SplitEvent, task: SplitEventTask) {
let eventName = event.toString()
// Method kept for backwards compatibility. Allows notifying an event without metadata.
func notifyInternalEvent(_ event: SplitInternalEvent) {
notifyInternalEvent(event, metadata: nil)
}

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)
return
}
self.subscribe(task: task, to: event)
self.subscribe(task: task, to: event.type)
}
}

// Method kept for backwards compatibility. Allows registering an event without metadata.
func register(event: SplitEvent, task: SplitEventActionTask) {
register(event: SplitEventWithMetadata(type: event, metadata: nil), task: task)
}

func start() {
dataAccessQueue.sync {
Expand Down Expand Up @@ -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 {
Expand All @@ -145,7 +158,7 @@ class DefaultSplitEventsManager: SplitEventsManager {
return
}
self.triggered.append(event)
switch event {
switch event.type {
case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated:
if isTriggered(external: .sdkReady) {
trigger(event: .sdkUpdated)
Expand Down Expand Up @@ -201,7 +214,11 @@ class DefaultSplitEventsManager: SplitEventsManager {
}

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()

// If executionTimes is zero, maximum executions has been reached
if executionTimes(for: eventName) == 0 {
Expand All @@ -215,14 +232,14 @@ 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)
}
}
}

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

let eventName = task.event.toString()

Expand All @@ -232,21 +249,25 @@ class DefaultSplitEventsManager: SplitEventsManager {
let queue = task.takeQueue() ?? DispatchQueue.general
queue.async {
TimeChecker.logInterval("Running \(eventName) in Background queue \(queue)")
task.run()
task.run(event.metadata)
}
return
}

DispatchQueue.main.async {
TimeChecker.logInterval("Running event on main: \(eventName)")
// UI Updates
task.run()
task.run(event.metadata)
}
}

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(event, metadata: nil))
}

// MARK: Safe Data Access
func executionTimes(for eventName: String) -> Int? {
Expand Down
Loading
Loading