EventDispatcher.swift
9.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
//
// EventDispatcher.swift
// SwiftWarplyFramework
//
// Created by Warply on 10/06/2025.
// Copyright © 2025 Warply. All rights reserved.
//
import Foundation
// MARK: - Event Protocol
/// Base protocol for all Warply events
public protocol WarplyEvent {
/// Unique identifier for the event type
var name: String { get }
/// Timestamp when the event was created
var timestamp: Date { get }
/// Optional data payload for the event
var data: Any? { get }
}
// MARK: - Event Subscription
/// Represents a subscription to an event
public final class EventSubscription {
let id: UUID
let eventName: String
weak var dispatcher: EventDispatcher?
init(id: UUID, eventName: String, dispatcher: EventDispatcher) {
self.id = id
self.eventName = eventName
self.dispatcher = dispatcher
}
/// Unsubscribe from the event
public func unsubscribe() {
dispatcher?.unsubscribe(self)
}
deinit {
unsubscribe()
}
}
// MARK: - Event Dispatcher
/// Modern Swift event dispatcher to replace SwiftEventBus
public final class EventDispatcher {
// MARK: - Singleton
public static let shared = EventDispatcher()
// MARK: - Private Properties
private var subscribers: [String: [UUID: (Any) -> Void]] = [:]
private let queue = DispatchQueue(label: "com.warply.eventdispatcher", attributes: .concurrent)
// MARK: - Initialization
private init() {}
// MARK: - Public Methods
/// Post an event to all subscribers
/// - Parameter event: The event to post
public func post<T: WarplyEvent>(_ event: T) {
queue.async(flags: .barrier) {
let eventName = event.name
if let eventSubscribers = self.subscribers[eventName] {
DispatchQueue.main.async {
for handler in eventSubscribers.values {
handler(event)
}
}
}
}
}
/// Post an event with string name and optional sender (for backward compatibility)
/// - Parameters:
/// - eventName: Name of the event
/// - sender: Optional sender object
public func post(_ eventName: String, sender: Any? = nil) {
let event = GenericWarplyEvent(name: eventName, data: sender)
post(event)
}
/// Subscribe to events of a specific type
/// - Parameters:
/// - eventType: The type of event to subscribe to
/// - handler: The handler to call when the event is posted
/// - Returns: An EventSubscription that can be used to unsubscribe
@discardableResult
public func subscribe<T: WarplyEvent>(_ eventType: T.Type, handler: @escaping (T) -> Void) -> EventSubscription {
let eventName = String(describing: eventType)
return subscribe(eventName) { event in
if let typedEvent = event as? T {
handler(typedEvent)
}
}
}
/// Subscribe to events by name (for backward compatibility)
/// - Parameters:
/// - eventName: Name of the event to subscribe to
/// - handler: The handler to call when the event is posted
/// - Returns: An EventSubscription that can be used to unsubscribe
@discardableResult
public func subscribe(_ eventName: String, handler: @escaping (Any) -> Void) -> EventSubscription {
let subscriptionId = UUID()
queue.async(flags: .barrier) {
if self.subscribers[eventName] == nil {
self.subscribers[eventName] = [:]
}
self.subscribers[eventName]?[subscriptionId] = handler
}
return EventSubscription(id: subscriptionId, eventName: eventName, dispatcher: self)
}
/// Unsubscribe from an event
/// - Parameter subscription: The subscription to remove
public func unsubscribe(_ subscription: EventSubscription) {
queue.async(flags: .barrier) {
self.subscribers[subscription.eventName]?.removeValue(forKey: subscription.id)
// Clean up empty event arrays
if self.subscribers[subscription.eventName]?.isEmpty == true {
self.subscribers.removeValue(forKey: subscription.eventName)
}
}
}
/// Remove all subscribers for a specific event name
/// - Parameter eventName: The event name to clear
public func removeAllSubscribers(for eventName: String) {
queue.async(flags: .barrier) {
self.subscribers.removeValue(forKey: eventName)
}
}
/// Remove all subscribers
public func removeAllSubscribers() {
queue.async(flags: .barrier) {
self.subscribers.removeAll()
}
}
}
// MARK: - Specific Event Types
/// Generic event for backward compatibility with string-based events
public struct GenericWarplyEvent: WarplyEvent {
public let name: String
public let timestamp: Date
public let data: Any?
public init(name: String, data: Any? = nil) {
self.name = name
self.timestamp = Date()
self.data = data
}
}
/// Dynatrace analytics event
public struct DynatraceEvent: WarplyEvent {
public let name: String = "dynatrace"
public let timestamp: Date
public let data: Any?
public init(data: Any? = nil) {
self.timestamp = Date()
self.data = data
}
}
/// Campaigns retrieved event
public struct CampaignsRetrievedEvent: WarplyEvent {
public let name: String = "campaigns_retrieved"
public let timestamp: Date
public let data: Any?
public init(data: Any? = nil) {
self.timestamp = Date()
self.data = data
}
}
/// Market pass details fetched event
public struct MarketPassDetailsEvent: WarplyEvent {
public let name: String = "market_pass_details_fetched"
public let timestamp: Date
public let data: Any?
public init(data: Any? = nil) {
self.timestamp = Date()
self.data = data
}
}
/// CCMS campaigns retrieved event
public struct CCMSRetrievedEvent: WarplyEvent {
public let name: String = "ccms_retrieved"
public let timestamp: Date
public let data: Any?
public init(data: Any? = nil) {
self.timestamp = Date()
self.data = data
}
}
/// Seasonal campaigns retrieved event
public struct SeasonalsRetrievedEvent: WarplyEvent {
public let name: String = "seasonals_retrieved"
public let timestamp: Date
public let data: Any?
public init(data: Any? = nil) {
self.timestamp = Date()
self.data = data
}
}
/// Coupons fetched event
public struct CouponsRetrievedEvent: WarplyEvent {
public let name: String = "coupons_fetched"
public let timestamp: Date
public let data: Any?
public init(data: Any? = nil) {
self.timestamp = Date()
self.data = data
}
}
/// Event posted when a Warply push notification is received
/// Equivalent of old ObjC `NB_PushReceived` analytics event
public struct PushNotificationReceivedEvent: WarplyEvent {
public let name: String = "push_notification_received"
public let timestamp: Date
public let data: Any?
/// The session UUID from the push notification payload
public let sessionUuid: String?
public init(data: Any? = nil, sessionUuid: String? = nil) {
self.timestamp = Date()
self.data = data
self.sessionUuid = sessionUuid
}
}
/// Event posted when a user interacts with (engages) a Warply push notification
/// Equivalent of old ObjC `NB_PushAck` analytics event
public struct PushNotificationEngagedEvent: WarplyEvent {
public let name: String = "push_notification_engaged"
public let timestamp: Date
public let data: Any?
/// The session UUID from the push notification payload
public let sessionUuid: String?
public init(data: Any? = nil, sessionUuid: String? = nil) {
self.timestamp = Date()
self.data = data
self.sessionUuid = sessionUuid
}
}
// MARK: - Convenience Extensions
extension EventDispatcher {
/// Post a dynatrace event
/// - Parameter sender: The event data
public func postDynatraceEvent(sender: Any? = nil) {
post(DynatraceEvent(data: sender))
}
/// Post a campaigns retrieved event
/// - Parameter sender: The event data
public func postCampaignsRetrievedEvent(sender: Any? = nil) {
post(CampaignsRetrievedEvent(data: sender))
}
/// Post a market pass details event
/// - Parameter sender: The event data
public func postMarketPassDetailsEvent(sender: Any? = nil) {
post(MarketPassDetailsEvent(data: sender))
}
/// Post a CCMS retrieved event
/// - Parameter sender: The event data
public func postCCMSRetrievedEvent(sender: Any? = nil) {
post(CCMSRetrievedEvent(data: sender))
}
/// Post a seasonals retrieved event
/// - Parameter sender: The event data
public func postSeasonalsRetrievedEvent(sender: Any? = nil) {
post(SeasonalsRetrievedEvent(data: sender))
}
/// Post a coupons retrieved event
/// - Parameter sender: The event data
public func postCouponsRetrievedEvent(sender: Any? = nil) {
post(CouponsRetrievedEvent(data: sender))
}
/// Post a push notification received event
/// - Parameters:
/// - sender: The event data (typically the raw push payload)
/// - sessionUuid: The session UUID from the push notification
public func postPushNotificationReceivedEvent(sender: Any? = nil, sessionUuid: String? = nil) {
post(PushNotificationReceivedEvent(data: sender, sessionUuid: sessionUuid))
}
/// Post a push notification engaged event
/// - Parameters:
/// - sender: The event data (typically the raw push payload)
/// - sessionUuid: The session UUID from the push notification
public func postPushNotificationEngagedEvent(sender: Any? = nil, sessionUuid: String? = nil) {
post(PushNotificationEngagedEvent(data: sender, sessionUuid: sessionUuid))
}
}