@bigcrunch/react-native-ads 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +5 -5
  2. package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchAds.kt +434 -0
  3. package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchBannerView.kt +484 -0
  4. package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchInterstitial.kt +403 -0
  5. package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchRewarded.kt +409 -0
  6. package/android/bigcrunch-ads/com/bigcrunch/ads/adapters/GoogleAdsAdapter.kt +592 -0
  7. package/android/bigcrunch-ads/com/bigcrunch/ads/core/AdOrchestrator.kt +623 -0
  8. package/android/bigcrunch-ads/com/bigcrunch/ads/core/AnalyticsClient.kt +719 -0
  9. package/android/bigcrunch-ads/com/bigcrunch/ads/core/BidRequestClient.kt +364 -0
  10. package/android/bigcrunch-ads/com/bigcrunch/ads/core/ConfigManager.kt +301 -0
  11. package/android/bigcrunch-ads/com/bigcrunch/ads/core/DeviceContext.kt +385 -0
  12. package/android/bigcrunch-ads/com/bigcrunch/ads/core/RewardedCallback.kt +42 -0
  13. package/android/bigcrunch-ads/com/bigcrunch/ads/core/SessionManager.kt +330 -0
  14. package/android/bigcrunch-ads/com/bigcrunch/ads/internal/DeviceHelper.kt +60 -0
  15. package/android/bigcrunch-ads/com/bigcrunch/ads/internal/HttpClient.kt +114 -0
  16. package/android/bigcrunch-ads/com/bigcrunch/ads/internal/Logger.kt +71 -0
  17. package/android/bigcrunch-ads/com/bigcrunch/ads/internal/PrivacyStore.kt +125 -0
  18. package/android/bigcrunch-ads/com/bigcrunch/ads/internal/Storage.kt +88 -0
  19. package/android/bigcrunch-ads/com/bigcrunch/ads/listeners/BannerAdListener.kt +55 -0
  20. package/android/bigcrunch-ads/com/bigcrunch/ads/listeners/InterstitialAdListener.kt +55 -0
  21. package/android/bigcrunch-ads/com/bigcrunch/ads/listeners/RewardedAdListener.kt +58 -0
  22. package/android/bigcrunch-ads/com/bigcrunch/ads/models/AdEvent.kt +880 -0
  23. package/android/bigcrunch-ads/com/bigcrunch/ads/models/AppConfig.kt +90 -0
  24. package/android/bigcrunch-ads/com/bigcrunch/ads/models/DeviceData.kt +18 -0
  25. package/android/bigcrunch-ads/com/bigcrunch/ads/models/PlacementConfig.kt +70 -0
  26. package/android/bigcrunch-ads/com/bigcrunch/ads/models/SessionInfo.kt +21 -0
  27. package/android/build.gradle +22 -10
  28. package/android/settings.gradle +2 -6
  29. package/ios/BigCrunchAds/Sources/Adapters/GoogleAdsAdapter.swift +512 -0
  30. package/ios/BigCrunchAds/Sources/BigCrunchAds.swift +387 -0
  31. package/ios/BigCrunchAds/Sources/BigCrunchBannerView.swift +448 -0
  32. package/ios/BigCrunchAds/Sources/BigCrunchInterstitial.swift +412 -0
  33. package/ios/BigCrunchAds/Sources/BigCrunchRewarded.swift +523 -0
  34. package/ios/BigCrunchAds/Sources/Core/AdOrchestrator.swift +514 -0
  35. package/ios/BigCrunchAds/Sources/Core/AnalyticsClient.swift +874 -0
  36. package/ios/BigCrunchAds/Sources/Core/BidRequestClient.swift +344 -0
  37. package/ios/BigCrunchAds/Sources/Core/ConfigManager.swift +306 -0
  38. package/ios/BigCrunchAds/Sources/Core/DeviceContext.swift +284 -0
  39. package/ios/BigCrunchAds/Sources/Core/SessionManager.swift +392 -0
  40. package/ios/BigCrunchAds/Sources/Internal/HTTPClient.swift +146 -0
  41. package/ios/BigCrunchAds/Sources/Internal/Logger.swift +62 -0
  42. package/ios/BigCrunchAds/Sources/Internal/PrivacyStore.swift +129 -0
  43. package/ios/BigCrunchAds/Sources/Internal/Storage.swift +73 -0
  44. package/ios/BigCrunchAds/Sources/Models/AdEvent.swift +784 -0
  45. package/ios/BigCrunchAds/Sources/Models/AppConfig.swift +100 -0
  46. package/ios/BigCrunchAds/Sources/Models/DeviceData.swift +68 -0
  47. package/ios/BigCrunchAds/Sources/Models/PlacementConfig.swift +137 -0
  48. package/ios/BigCrunchAds/Sources/Models/SessionInfo.swift +48 -0
  49. package/ios/BigCrunchAdsModule.swift +0 -1
  50. package/ios/BigCrunchBannerViewManager.swift +0 -1
  51. package/lib/index.d.ts +1 -1
  52. package/lib/index.d.ts.map +1 -1
  53. package/lib/index.js +3 -2
  54. package/package.json +8 -2
  55. package/react-native-bigcrunch-ads.podspec +0 -1
  56. package/scripts/inject-version.js +55 -0
  57. package/src/index.ts +3 -2
@@ -0,0 +1,784 @@
1
+ import Foundation
2
+
3
+ // MARK: - Legacy Event (keeping for backward compatibility)
4
+
5
+ /**
6
+ * Analytics event sent to BigCrunch backend
7
+ *
8
+ * All ad-related events (impressions, clicks, revenue) are tracked and sent
9
+ * to the analytics endpoint in this format.
10
+ */
11
+ internal struct AdEvent: Codable {
12
+ let eventType: String // "screen_view", "ad_request", "ad_impression", "ad_revenue", "ad_click", "ad_viewable"
13
+ let placementId: String?
14
+ let format: String? // "banner", "interstitial", "rewarded"
15
+ let timestamp: Int64
16
+ let sessionId: String
17
+ let deviceInfo: DeviceInfo
18
+ let revenue: RevenueData?
19
+ }
20
+
21
+ /**
22
+ * Device and app information included with every event
23
+ */
24
+ internal struct DeviceInfo: Codable {
25
+ let platform: String
26
+ let osVersion: String
27
+ let appVersion: String
28
+ let deviceId: String
29
+ }
30
+
31
+ /**
32
+ * Revenue data from ILRD (Impression-Level Revenue Data)
33
+ *
34
+ * Only present in ad_revenue events. Values are in micros (1/1,000,000 of currency unit).
35
+ */
36
+ internal struct RevenueData: Codable {
37
+ let valueMicros: Int64
38
+ let currency: String
39
+ }
40
+
41
+ // MARK: - Enhanced Analytics Events (matching web SDK data model)
42
+
43
+ /**
44
+ * Screen/Page view event - matches `/pageviews` endpoint
45
+ *
46
+ * Sent when a new screen is viewed. Contains session context and device info.
47
+ */
48
+ internal struct PageViewEvent: Codable {
49
+ // Web schema common fields
50
+ let payloadVersion: String // Must be semver format "x.y.z"
51
+ let configVersion: Int
52
+ let browserTimestamp: String // ISO 8601 with milliseconds
53
+
54
+ // Session/user context
55
+ let sessionId: String
56
+ let userId: String
57
+ let propertyId: String
58
+ let newUser: Bool
59
+
60
+ // Page context
61
+ let pageId: String
62
+ let sessionDepth: Int
63
+ let pageUrl: String // Must be valid URL or empty string (mobile uses empty)
64
+ let pageSearch: String // Query string, empty for mobile
65
+ let pageReferrer: String // Must be valid URL or empty string
66
+
67
+ // Device context (flattened)
68
+ let browser: String
69
+ let device: String
70
+ let os: String
71
+ let country: String
72
+ let region: String // Must be 2 chars or empty
73
+
74
+ // Attribution
75
+ let sessionSource: String
76
+ let sessionMedium: String
77
+ let utmSource: String
78
+ let utmMedium: String
79
+ let utmCampaign: String
80
+ let utmTerm: String
81
+ let utmContent: String
82
+
83
+ // Click IDs for attribution
84
+ let gclid: String
85
+ let fbclid: String
86
+
87
+ // Additional required fields
88
+ let acctType: String // "anonymous" for mobile
89
+ let diiSource: String // empty for mobile
90
+ let gamNetworkCode: String
91
+ let amznPubId: String // Must be valid UUID (nil UUID for empty)
92
+ let customDimensions: [String: String]
93
+
94
+ enum CodingKeys: String, CodingKey {
95
+ case payloadVersion = "payload_version"
96
+ case configVersion = "config_version"
97
+ case browserTimestamp = "browser_timestamp"
98
+ case sessionId = "session_id"
99
+ case userId = "user_id"
100
+ case propertyId = "property_id"
101
+ case newUser = "new_user"
102
+ case pageId = "page_id"
103
+ case sessionDepth = "session_depth"
104
+ case pageUrl = "page_url"
105
+ case pageSearch = "page_search"
106
+ case pageReferrer = "page_referrer"
107
+ case browser
108
+ case device
109
+ case os
110
+ case country
111
+ case region
112
+ case sessionSource = "session_source"
113
+ case sessionMedium = "session_medium"
114
+ case utmSource = "utm_source"
115
+ case utmMedium = "utm_medium"
116
+ case utmCampaign = "utm_campaign"
117
+ case utmTerm = "utm_term"
118
+ case utmContent = "utm_content"
119
+ case gclid
120
+ case fbclid
121
+ case acctType = "acct_type"
122
+ case diiSource = "dii_source"
123
+ case gamNetworkCode = "gam_network_code"
124
+ case amznPubId = "amzn_pub_id"
125
+ case customDimensions = "custom_dimensions"
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Individual impression record - nested inside ImpressionBatchEvent
131
+ *
132
+ * Represents a single ad impression with auction/bid data.
133
+ */
134
+ internal struct ImpressionRecord: Codable {
135
+ /// Placement identifier (slot_id in web SDK)
136
+ let slotId: String
137
+
138
+ /// GAM ad unit path
139
+ let gamUnit: String
140
+
141
+ /// GAM price bucket
142
+ let gamPriceBucket: String
143
+
144
+ /// Unique identifier for this impression
145
+ let impressionId: String
146
+
147
+ /// Auction ID (from Prebid) - REQUIRED, must be valid UUID
148
+ let auctionId: String
149
+
150
+ /// Refresh count for this placement
151
+ let refreshCount: Int
152
+
153
+ /// Winning bidder name
154
+ let adBidder: String
155
+
156
+ /// Ad size (e.g., "320x50")
157
+ let adSize: String
158
+
159
+ /// Bid price/revenue in CPM
160
+ let adPrice: Double
161
+
162
+ /// Floor price
163
+ let adFloorPrice: Double
164
+
165
+ /// Minimum bid to win
166
+ let minBidToWin: Double
167
+
168
+ /// Advertiser ID from GAM
169
+ let advertiserId: String
170
+
171
+ /// Campaign ID from GAM
172
+ let campaignId: String
173
+
174
+ /// Line item ID from GAM
175
+ let lineItemId: String
176
+
177
+ /// Creative ID from GAM response
178
+ let creativeId: String
179
+
180
+ /// Amazon bid data (if applicable)
181
+ let adAmznbid: String
182
+
183
+ /// Amazon price data (if applicable)
184
+ let adAmznp: String
185
+
186
+ /// Demand type (banner, video, etc.)
187
+ let adDemandType: String
188
+
189
+ /// Demand channel (e.g., "Prebid Header", "GAM Direct")
190
+ let demandChannel: String
191
+
192
+ /// Custom dimensions
193
+ let customDimensions: [String: String]
194
+
195
+ enum CodingKeys: String, CodingKey {
196
+ case slotId = "slot_id"
197
+ case gamUnit = "gam_unit"
198
+ case gamPriceBucket = "gam_price_bucket"
199
+ case impressionId = "impression_id"
200
+ case auctionId = "auction_id"
201
+ case refreshCount = "refresh_count"
202
+ case adBidder = "ad_bidder"
203
+ case adSize = "ad_size"
204
+ case adPrice = "ad_price"
205
+ case adFloorPrice = "ad_floor_price"
206
+ case minBidToWin = "min_bid_to_win"
207
+ case advertiserId = "advertiser_id"
208
+ case campaignId = "campaign_id"
209
+ case lineItemId = "line_item_id"
210
+ case creativeId = "creative_id"
211
+ case adAmznbid = "ad_amznbid"
212
+ case adAmznp = "ad_amznp"
213
+ case adDemandType = "ad_demand_type"
214
+ case demandChannel = "demand_channel"
215
+ case customDimensions = "custom_dimensions"
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Impression batch event - matches `/impressions` endpoint
221
+ *
222
+ * Contains session/page context with nested array of impression records.
223
+ * This matches the web SDK format where impressions are batched with pageview context.
224
+ */
225
+ internal struct ImpressionBatchEvent: Codable {
226
+ // Web schema common fields
227
+ let payloadVersion: String // Must be semver format "x.y.z"
228
+ let configVersion: Int
229
+ let browserTimestamp: String // ISO 8601 with milliseconds
230
+
231
+ // Session/user context
232
+ let sessionId: String
233
+ let userId: String
234
+ let propertyId: String
235
+ let newUser: Bool
236
+ let pageId: String
237
+ let sessionDepth: Int
238
+
239
+ // Page context
240
+ let pageUrl: String // Must be valid URL or empty string
241
+ let pageSearch: String
242
+ let pageReferrer: String
243
+
244
+ // Device context (flattened)
245
+ let browser: String
246
+ let device: String
247
+ let os: String
248
+ let country: String
249
+ let region: String // Must be 2 chars or empty
250
+
251
+ // Attribution
252
+ let sessionSource: String
253
+ let sessionMedium: String
254
+ let utmSource: String
255
+ let utmMedium: String
256
+ let utmCampaign: String
257
+ let utmTerm: String
258
+ let utmContent: String
259
+
260
+ // Click IDs for attribution
261
+ let gclid: String
262
+ let fbclid: String
263
+
264
+ // Additional required fields
265
+ let acctType: String
266
+ let diiSource: String
267
+ let gamNetworkCode: String
268
+ let amznPubId: String // Must be valid UUID (nil UUID for empty)
269
+ let customDimensions: [String: String]
270
+
271
+ // Nested impressions array
272
+ let impressions: [ImpressionRecord]
273
+
274
+ enum CodingKeys: String, CodingKey {
275
+ case payloadVersion = "payload_version"
276
+ case configVersion = "config_version"
277
+ case browserTimestamp = "browser_timestamp"
278
+ case sessionId = "session_id"
279
+ case userId = "user_id"
280
+ case propertyId = "property_id"
281
+ case newUser = "new_user"
282
+ case pageId = "page_id"
283
+ case sessionDepth = "session_depth"
284
+ case pageUrl = "page_url"
285
+ case pageSearch = "page_search"
286
+ case pageReferrer = "page_referrer"
287
+ case browser
288
+ case device
289
+ case os
290
+ case country
291
+ case region
292
+ case sessionSource = "session_source"
293
+ case sessionMedium = "session_medium"
294
+ case utmSource = "utm_source"
295
+ case utmMedium = "utm_medium"
296
+ case utmCampaign = "utm_campaign"
297
+ case utmTerm = "utm_term"
298
+ case utmContent = "utm_content"
299
+ case gclid
300
+ case fbclid
301
+ case acctType = "acct_type"
302
+ case diiSource = "dii_source"
303
+ case gamNetworkCode = "gam_network_code"
304
+ case amznPubId = "amzn_pub_id"
305
+ case customDimensions = "custom_dimensions"
306
+ case impressions
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Click data object nested inside ClickEvent
312
+ *
313
+ * Contains the click-specific fields per backend schema.
314
+ */
315
+ internal struct ClickData: Codable {
316
+ /// Unique click identifier
317
+ let clickId: String
318
+
319
+ /// Placement identifier (slot_id in web SDK)
320
+ let slotId: String
321
+
322
+ /// Reference to the impression that was clicked - must be valid UUID
323
+ let impressionId: String
324
+
325
+ /// Refresh count for this placement
326
+ let refreshCount: Int
327
+
328
+ /// Amazon price data (if applicable)
329
+ let adAmznp: String
330
+
331
+ /// Winning bidder name
332
+ let adBidder: String
333
+
334
+ /// Ad size (e.g., "320x50")
335
+ let adSize: String
336
+
337
+ /// Advertiser ID from GAM
338
+ let advertiserId: String
339
+
340
+ /// Campaign ID from GAM
341
+ let campaignId: String
342
+
343
+ /// Line item ID from GAM
344
+ let lineItemId: String
345
+
346
+ /// Creative ID from GAM response
347
+ let creativeId: String
348
+
349
+ /// Demand type (banner, video, etc.)
350
+ let adDemandType: String
351
+
352
+ /// Demand channel (e.g., "Prebid Header", "GAM Direct")
353
+ let demandChannel: String
354
+
355
+ /// Custom dimensions - values must be string arrays per backend schema
356
+ let customDimensions: [String: [String]]
357
+
358
+ enum CodingKeys: String, CodingKey {
359
+ case clickId = "click_id"
360
+ case slotId = "slot_id"
361
+ case impressionId = "impression_id"
362
+ case refreshCount = "refresh_count"
363
+ case adAmznp = "ad_amznp"
364
+ case adBidder = "ad_bidder"
365
+ case adSize = "ad_size"
366
+ case advertiserId = "advertiser_id"
367
+ case campaignId = "campaign_id"
368
+ case lineItemId = "line_item_id"
369
+ case creativeId = "creative_id"
370
+ case adDemandType = "ad_demand_type"
371
+ case demandChannel = "demand_channel"
372
+ case customDimensions = "custom_dimensions"
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Ad click event - matches `/clicks` endpoint
378
+ *
379
+ * Sent when a user clicks on an ad.
380
+ */
381
+ internal struct ClickEvent: Codable {
382
+ // Web schema common fields
383
+ let payloadVersion: String // Must be semver format "x.y.z"
384
+ let configVersion: Int
385
+ let browserTimestamp: String // ISO 8601 with milliseconds
386
+
387
+ // Session/user context
388
+ let sessionId: String
389
+ let userId: String
390
+ let propertyId: String
391
+ let newUser: Bool
392
+ let pageId: String
393
+ let sessionDepth: Int
394
+
395
+ // Page context
396
+ let pageUrl: String
397
+ let pageSearch: String
398
+ let pageReferrer: String
399
+
400
+ // Device context (flattened)
401
+ let browser: String
402
+ let device: String
403
+ let os: String
404
+ let country: String
405
+ let region: String // Must be 2 chars or empty
406
+
407
+ // Attribution
408
+ let sessionSource: String
409
+ let sessionMedium: String
410
+ let utmSource: String
411
+ let utmMedium: String
412
+ let utmCampaign: String
413
+ let utmTerm: String
414
+ let utmContent: String
415
+
416
+ // Click IDs
417
+ let gclid: String
418
+ let fbclid: String
419
+
420
+ // Additional required fields
421
+ let acctType: String
422
+ let diiSource: String
423
+ let gamNetworkCode: String
424
+ let amznPubId: String
425
+ let customDimensions: [String: String]
426
+
427
+ // Click-specific fields (nested click object per schema)
428
+ let click: ClickData
429
+
430
+ enum CodingKeys: String, CodingKey {
431
+ case payloadVersion = "payload_version"
432
+ case configVersion = "config_version"
433
+ case browserTimestamp = "browser_timestamp"
434
+ case sessionId = "session_id"
435
+ case userId = "user_id"
436
+ case propertyId = "property_id"
437
+ case newUser = "new_user"
438
+ case pageId = "page_id"
439
+ case sessionDepth = "session_depth"
440
+ case pageUrl = "page_url"
441
+ case pageSearch = "page_search"
442
+ case pageReferrer = "page_referrer"
443
+ case browser
444
+ case device
445
+ case os
446
+ case country
447
+ case region
448
+ case sessionSource = "session_source"
449
+ case sessionMedium = "session_medium"
450
+ case utmSource = "utm_source"
451
+ case utmMedium = "utm_medium"
452
+ case utmCampaign = "utm_campaign"
453
+ case utmTerm = "utm_term"
454
+ case utmContent = "utm_content"
455
+ case gclid
456
+ case fbclid
457
+ case acctType = "acct_type"
458
+ case diiSource = "dii_source"
459
+ case gamNetworkCode = "gam_network_code"
460
+ case amznPubId = "amzn_pub_id"
461
+ case customDimensions = "custom_dimensions"
462
+ case click
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Viewability data object nested inside ViewabilityEvent
468
+ *
469
+ * Contains the per-slot viewability metrics.
470
+ */
471
+ internal struct ViewabilityData: Codable {
472
+ /// Placement identifier (slot_id in web SDK)
473
+ let slotId: String
474
+
475
+ /// Reference to the impression - must be valid UUID
476
+ let impressionId: String
477
+
478
+ /// Refresh count for this placement
479
+ let refreshCount: Int
480
+
481
+ /// Amazon price data (if applicable)
482
+ let adAmznp: String
483
+
484
+ /// Winning bidder name
485
+ let adBidder: String
486
+
487
+ /// Ad size (e.g., "320x50")
488
+ let adSize: String
489
+
490
+ /// Advertiser ID from GAM
491
+ let advertiserId: String
492
+
493
+ /// Campaign ID from GAM
494
+ let campaignId: String
495
+
496
+ /// Line item ID from GAM
497
+ let lineItemId: String
498
+
499
+ /// Creative ID from GAM response
500
+ let creativeId: String
501
+
502
+ /// Demand type (banner, video, etc.)
503
+ let adDemandType: String
504
+
505
+ /// Demand channel (e.g., "Prebid Header", "GAM Direct")
506
+ let demandChannel: String
507
+
508
+ /// Custom dimensions
509
+ let customDimensions: [String: [String]]
510
+
511
+ enum CodingKeys: String, CodingKey {
512
+ case slotId = "slot_id"
513
+ case impressionId = "impression_id"
514
+ case refreshCount = "refresh_count"
515
+ case adAmznp = "ad_amznp"
516
+ case adBidder = "ad_bidder"
517
+ case adSize = "ad_size"
518
+ case advertiserId = "advertiser_id"
519
+ case campaignId = "campaign_id"
520
+ case lineItemId = "line_item_id"
521
+ case creativeId = "creative_id"
522
+ case adDemandType = "ad_demand_type"
523
+ case demandChannel = "demand_channel"
524
+ case customDimensions = "custom_dimensions"
525
+ }
526
+ }
527
+
528
+ /**
529
+ * Viewability event - matches `/viewability` endpoint
530
+ *
531
+ * Sent when an ad meets viewability thresholds (e.g., 50% visible for 1 second).
532
+ * Contains session/page context with nested viewability array.
533
+ */
534
+ internal struct ViewabilityEvent: Codable {
535
+ // Web schema common fields
536
+ let payloadVersion: String // Must be semver format "x.y.z"
537
+ let configVersion: Int
538
+ let browserTimestamp: String // ISO 8601 with milliseconds
539
+
540
+ // Session/user context
541
+ let sessionId: String
542
+ let userId: String
543
+ let propertyId: String
544
+ let newUser: Bool
545
+ let pageId: String
546
+ let sessionDepth: Int
547
+
548
+ // Page context
549
+ let pageUrl: String
550
+ let pageSearch: String
551
+ let pageReferrer: String
552
+
553
+ // Device context (flattened)
554
+ let browser: String
555
+ let device: String
556
+ let os: String
557
+ let country: String
558
+ let region: String // Must be 2 chars or empty
559
+
560
+ // Attribution
561
+ let sessionSource: String
562
+ let sessionMedium: String
563
+ let utmSource: String
564
+ let utmMedium: String
565
+ let utmCampaign: String
566
+ let utmTerm: String
567
+ let utmContent: String
568
+
569
+ // Click IDs
570
+ let gclid: String
571
+ let fbclid: String
572
+
573
+ // Additional required fields
574
+ let acctType: String
575
+ let diiSource: String
576
+ let gamNetworkCode: String
577
+ let amznPubId: String
578
+
579
+ // Nested viewability array (per backend schema)
580
+ let viewability: [ViewabilityData]
581
+
582
+ enum CodingKeys: String, CodingKey {
583
+ case payloadVersion = "payload_version"
584
+ case configVersion = "config_version"
585
+ case browserTimestamp = "browser_timestamp"
586
+ case sessionId = "session_id"
587
+ case userId = "user_id"
588
+ case propertyId = "property_id"
589
+ case newUser = "new_user"
590
+ case pageId = "page_id"
591
+ case sessionDepth = "session_depth"
592
+ case pageUrl = "page_url"
593
+ case pageSearch = "page_search"
594
+ case pageReferrer = "page_referrer"
595
+ case browser
596
+ case device
597
+ case os
598
+ case country
599
+ case region
600
+ case sessionSource = "session_source"
601
+ case sessionMedium = "session_medium"
602
+ case utmSource = "utm_source"
603
+ case utmMedium = "utm_medium"
604
+ case utmCampaign = "utm_campaign"
605
+ case utmTerm = "utm_term"
606
+ case utmContent = "utm_content"
607
+ case gclid
608
+ case fbclid
609
+ case acctType = "acct_type"
610
+ case diiSource = "dii_source"
611
+ case gamNetworkCode = "gam_network_code"
612
+ case amznPubId = "amzn_pub_id"
613
+ case viewability
614
+ }
615
+ }
616
+
617
+ /**
618
+ * Engagement event - matches `/engagement` endpoint
619
+ *
620
+ * Tracks user engagement with content/screens.
621
+ */
622
+ internal struct EngagementEvent: Codable {
623
+ // Web schema common fields
624
+ let payloadVersion: String // Must be semver format "x.y.z"
625
+ let configVersion: Int
626
+ let browserTimestamp: String // ISO 8601 with milliseconds
627
+
628
+ // Session/user context
629
+ let sessionId: String
630
+ let userId: String
631
+ let propertyId: String
632
+ let newUser: Bool
633
+ let pageId: String
634
+ let sessionDepth: Int
635
+
636
+ // Page context
637
+ let pageUrl: String
638
+ let pageSearch: String
639
+ let pageReferrer: String
640
+
641
+ // Device context (flattened)
642
+ let browser: String
643
+ let device: String
644
+ let os: String
645
+ let country: String
646
+ let region: String // Must be 2 chars or empty
647
+
648
+ // Attribution
649
+ let sessionSource: String
650
+ let sessionMedium: String
651
+ let utmSource: String
652
+ let utmMedium: String
653
+ let utmCampaign: String
654
+ let utmTerm: String
655
+ let utmContent: String
656
+
657
+ // Click IDs
658
+ let gclid: String
659
+ let fbclid: String
660
+
661
+ // Additional required fields
662
+ let acctType: String
663
+ let diiSource: String
664
+ let gamNetworkCode: String
665
+ let amznPubId: String
666
+
667
+ /// Custom dimensions - values must be string arrays per backend schema
668
+ let customDimensions: [String: [String]]
669
+
670
+ // Engagement-specific fields
671
+ /// Time actively engaged in seconds
672
+ let engagedTime: Int
673
+
674
+ /// Total time on page/screen in seconds
675
+ let timeOnPage: Int
676
+
677
+ /// Maximum scroll depth percentage (0-100)
678
+ let scrollDepth: Int
679
+
680
+ enum CodingKeys: String, CodingKey {
681
+ case payloadVersion = "payload_version"
682
+ case configVersion = "config_version"
683
+ case browserTimestamp = "browser_timestamp"
684
+ case sessionId = "session_id"
685
+ case userId = "user_id"
686
+ case propertyId = "property_id"
687
+ case newUser = "new_user"
688
+ case pageId = "page_id"
689
+ case sessionDepth = "session_depth"
690
+ case pageUrl = "page_url"
691
+ case pageSearch = "page_search"
692
+ case pageReferrer = "page_referrer"
693
+ case browser
694
+ case device
695
+ case os
696
+ case country
697
+ case region
698
+ case sessionSource = "session_source"
699
+ case sessionMedium = "session_medium"
700
+ case utmSource = "utm_source"
701
+ case utmMedium = "utm_medium"
702
+ case utmCampaign = "utm_campaign"
703
+ case utmTerm = "utm_term"
704
+ case utmContent = "utm_content"
705
+ case gclid
706
+ case fbclid
707
+ case acctType = "acct_type"
708
+ case diiSource = "dii_source"
709
+ case gamNetworkCode = "gam_network_code"
710
+ case amznPubId = "amzn_pub_id"
711
+ case customDimensions = "custom_dimensions"
712
+ case engagedTime = "engaged_time"
713
+ case timeOnPage = "time_on_page"
714
+ case scrollDepth = "scroll_depth"
715
+ }
716
+ }
717
+
718
+ // MARK: - Auction Data
719
+
720
+ /**
721
+ * Auction data from Prebid response
722
+ *
723
+ * Captures bidding information for analytics.
724
+ */
725
+ internal struct AuctionData {
726
+ /// Prebid auction ID
727
+ let auctionId: String?
728
+
729
+ /// Winning bidder name
730
+ let bidder: String?
731
+
732
+ /// Winning bid price in CPM
733
+ let bidPriceCpm: Double?
734
+
735
+ /// Creative ID from winning bid
736
+ let creativeId: String?
737
+
738
+ /// Empty auction data
739
+ static let empty = AuctionData(auctionId: nil, bidder: nil, bidPriceCpm: nil, creativeId: nil)
740
+ }
741
+
742
+ // MARK: - Impression Context
743
+
744
+ /**
745
+ * Context for tracking an ad impression
746
+ *
747
+ * Aggregates all data needed to track an impression through its lifecycle.
748
+ */
749
+ internal struct ImpressionContext {
750
+ /// Unique impression identifier
751
+ let impressionId: String
752
+
753
+ /// Placement configuration
754
+ let placementId: String
755
+ let gamAdUnit: String
756
+ let format: String
757
+
758
+ /// Size (for banners)
759
+ let width: Int?
760
+ let height: Int?
761
+
762
+ /// Auction data (populated after Prebid response)
763
+ var auctionData: AuctionData
764
+
765
+ /// Timestamp when impression was created
766
+ let createdAt: Int64
767
+
768
+ init(
769
+ placementId: String,
770
+ gamAdUnit: String,
771
+ format: String,
772
+ width: Int? = nil,
773
+ height: Int? = nil
774
+ ) {
775
+ self.impressionId = UUID().uuidString
776
+ self.placementId = placementId
777
+ self.gamAdUnit = gamAdUnit
778
+ self.format = format
779
+ self.width = width
780
+ self.height = height
781
+ self.auctionData = .empty
782
+ self.createdAt = Int64(Date().timeIntervalSince1970 * 1000)
783
+ }
784
+ }