@bigcrunch/react-native-ads 0.11.0 → 0.14.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.
- package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchAds.kt +21 -4
- package/android/bigcrunch-ads/com/bigcrunch/ads/adapters/GoogleAdsAdapter.kt +8 -1
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/AdOrchestrator.kt +37 -12
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/AnalyticsClient.kt +213 -46
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/BidRequestClient.kt +52 -17
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/DeviceContext.kt +1 -1
- package/android/bigcrunch-ads/com/bigcrunch/ads/models/AdEvent.kt +81 -2
- package/android/bigcrunch-ads/com/bigcrunch/ads/models/PlacementConfig.kt +4 -1
- package/android/src/main/java/com/bigcrunch/ads/react/BigCrunchAdsModule.kt +57 -2
- package/ios/BigCrunchAds/Sources/Adapters/GoogleAdsAdapter.swift +7 -0
- package/ios/BigCrunchAds/Sources/BigCrunchAds.swift +29 -6
- package/ios/BigCrunchAds/Sources/BigCrunchRewarded.swift +10 -2
- package/ios/BigCrunchAds/Sources/Core/AdOrchestrator.swift +20 -4
- package/ios/BigCrunchAds/Sources/Core/AnalyticsClient.swift +193 -84
- package/ios/BigCrunchAds/Sources/Core/BidRequestClient.swift +54 -17
- package/ios/BigCrunchAds/Sources/Core/DeviceContext.swift +1 -1
- package/ios/BigCrunchAds/Sources/Models/AdEvent.swift +111 -3
- package/ios/BigCrunchAds/Sources/Models/PlacementConfig.swift +4 -1
- package/ios/BigCrunchAdsModule.m +6 -0
- package/ios/BigCrunchAdsModule.swift +39 -2
- package/ios/BigCrunchBannerViewManager.swift +1 -0
- package/lib/BigCrunchAds.d.ts +33 -2
- package/lib/BigCrunchAds.d.ts.map +1 -1
- package/lib/BigCrunchAds.js +35 -2
- package/lib/NativeBigCrunchAds.d.ts +3 -2
- package/lib/NativeBigCrunchAds.d.ts.map +1 -1
- package/lib/types/index.d.ts +40 -0
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/react-native-bigcrunch-ads.podspec +1 -1
- package/src/BigCrunchAds.ts +41 -2
- package/src/NativeBigCrunchAds.ts +5 -2
- package/src/types/index.ts +43 -0
|
@@ -4,6 +4,7 @@ import com.bigcrunch.ads.BigCrunchAds
|
|
|
4
4
|
import com.bigcrunch.ads.internal.BCLogger
|
|
5
5
|
import com.bigcrunch.ads.internal.HttpClient
|
|
6
6
|
import com.bigcrunch.ads.internal.PrivacyStore
|
|
7
|
+
import com.bigcrunch.ads.models.AuctionData
|
|
7
8
|
import com.bigcrunch.ads.models.PlacementConfig
|
|
8
9
|
import com.bigcrunch.ads.models.S2SConfig
|
|
9
10
|
import kotlinx.coroutines.*
|
|
@@ -37,9 +38,15 @@ internal class BidRequestClient(
|
|
|
37
38
|
private const val BATCH_DELAY_MS = 100L
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
/** Bid response containing both targeting KVPs and auction metadata */
|
|
42
|
+
internal data class BidResponse(
|
|
43
|
+
val targeting: Map<String, String>,
|
|
44
|
+
val auctionData: AuctionData
|
|
45
|
+
)
|
|
46
|
+
|
|
40
47
|
private class PendingRequest(
|
|
41
48
|
val placement: PlacementConfig,
|
|
42
|
-
val deferred: CompletableDeferred<
|
|
49
|
+
val deferred: CompletableDeferred<BidResponse?>
|
|
43
50
|
)
|
|
44
51
|
|
|
45
52
|
// MARK: - Public API
|
|
@@ -53,13 +60,13 @@ internal class BidRequestClient(
|
|
|
53
60
|
* @param placement The placement to fetch demand for
|
|
54
61
|
* @return Targeting KVPs to apply to GAM request, or null
|
|
55
62
|
*/
|
|
56
|
-
suspend fun fetchDemand(placement: PlacementConfig):
|
|
63
|
+
suspend fun fetchDemand(placement: PlacementConfig): BidResponse? {
|
|
57
64
|
if (!s2sConfig.enabled) {
|
|
58
65
|
BCLogger.d(TAG, "S2S is disabled, skipping bid request")
|
|
59
66
|
return null
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
val deferred = CompletableDeferred<
|
|
69
|
+
val deferred = CompletableDeferred<BidResponse?>()
|
|
63
70
|
|
|
64
71
|
mutex.withLock {
|
|
65
72
|
pendingRequests.add(PendingRequest(placement, deferred))
|
|
@@ -94,8 +101,8 @@ internal class BidRequestClient(
|
|
|
94
101
|
|
|
95
102
|
// Distribute results to each pending deferred
|
|
96
103
|
for (pending in batch) {
|
|
97
|
-
val
|
|
98
|
-
pending.deferred.complete(
|
|
104
|
+
val bidResponse = results[pending.placement.placementId]
|
|
105
|
+
pending.deferred.complete(bidResponse)
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
|
|
@@ -103,7 +110,7 @@ internal class BidRequestClient(
|
|
|
103
110
|
|
|
104
111
|
private suspend fun executeBidRequest(
|
|
105
112
|
placements: List<PlacementConfig>
|
|
106
|
-
): Map<String,
|
|
113
|
+
): Map<String, BidResponse> {
|
|
107
114
|
val requestBody = buildRequestJSON(placements)
|
|
108
115
|
val jsonString = requestBody.toString()
|
|
109
116
|
|
|
@@ -118,7 +125,7 @@ internal class BidRequestClient(
|
|
|
118
125
|
return when {
|
|
119
126
|
result.isSuccess -> {
|
|
120
127
|
val responseJson = result.getOrNull()!!
|
|
121
|
-
parseResponse(responseJson)
|
|
128
|
+
parseResponse(responseJson, placements)
|
|
122
129
|
}
|
|
123
130
|
else -> {
|
|
124
131
|
BCLogger.e(TAG, "Bid request failed: ${result.exceptionOrNull()?.message}")
|
|
@@ -302,16 +309,29 @@ internal class BidRequestClient(
|
|
|
302
309
|
*
|
|
303
310
|
* Groups bids by impid, picks winner (highest price), copies ext.targeting.
|
|
304
311
|
*/
|
|
305
|
-
private fun parseResponse(jsonString: String
|
|
312
|
+
private fun parseResponse(jsonString: String, placements: List<PlacementConfig>): Map<String, BidResponse> {
|
|
306
313
|
return try {
|
|
307
314
|
val json = JSONObject(jsonString)
|
|
308
315
|
val seatbids = json.optJSONArray("seatbid") ?: return emptyMap()
|
|
309
316
|
|
|
317
|
+
val auctionId = json.optString("id", "")
|
|
318
|
+
|
|
319
|
+
// Build floor price lookup from placement configs
|
|
320
|
+
val floorPrices = placements.associate { it.placementId to it.floorPrice }
|
|
321
|
+
|
|
310
322
|
// Collect all bids grouped by impid
|
|
311
|
-
|
|
323
|
+
data class BidInfo(
|
|
324
|
+
val price: Double,
|
|
325
|
+
val targeting: Map<String, String>,
|
|
326
|
+
val seat: String,
|
|
327
|
+
val creativeId: String?
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
val bidsByImpId = mutableMapOf<String, MutableList<BidInfo>>()
|
|
312
331
|
|
|
313
332
|
for (i in 0 until seatbids.length()) {
|
|
314
333
|
val seatbid = seatbids.getJSONObject(i)
|
|
334
|
+
val seat = seatbid.optString("seat", "")
|
|
315
335
|
val bids = seatbid.optJSONArray("bid") ?: continue
|
|
316
336
|
|
|
317
337
|
for (j in 0 until bids.length()) {
|
|
@@ -320,6 +340,7 @@ internal class BidRequestClient(
|
|
|
320
340
|
val price = bid.optDouble("price", 0.0)
|
|
321
341
|
val ext = bid.optJSONObject("ext") ?: continue
|
|
322
342
|
val targetingJson = ext.optJSONObject("targeting") ?: continue
|
|
343
|
+
val crid = bid.optString("crid", null)
|
|
323
344
|
|
|
324
345
|
val targeting = mutableMapOf<String, String>()
|
|
325
346
|
val keys = targetingJson.keys()
|
|
@@ -330,19 +351,33 @@ internal class BidRequestClient(
|
|
|
330
351
|
|
|
331
352
|
if (impid.isNotEmpty()) {
|
|
332
353
|
bidsByImpId.getOrPut(impid) { mutableListOf() }
|
|
333
|
-
.add(
|
|
354
|
+
.add(BidInfo(price, targeting, seat, crid))
|
|
334
355
|
}
|
|
335
356
|
}
|
|
336
357
|
}
|
|
337
358
|
|
|
338
|
-
// Pick winner (highest price) for each impid
|
|
339
|
-
val results = mutableMapOf<String,
|
|
359
|
+
// Pick winner (highest price) for each impid and compute auction metadata
|
|
360
|
+
val results = mutableMapOf<String, BidResponse>()
|
|
340
361
|
for ((impid, bids) in bidsByImpId) {
|
|
341
|
-
val
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
362
|
+
val sortedBids = bids.sortedByDescending { it.price }
|
|
363
|
+
val winner = sortedBids.first()
|
|
364
|
+
|
|
365
|
+
val secondHighestBid = if (sortedBids.size > 1) sortedBids[1].price else null
|
|
366
|
+
val floorPrice = floorPrices[impid]
|
|
367
|
+
|
|
368
|
+
val auctionData = AuctionData(
|
|
369
|
+
auctionId = auctionId.ifEmpty { null },
|
|
370
|
+
bidder = winner.seat,
|
|
371
|
+
bidPriceCpm = winner.price,
|
|
372
|
+
creativeId = winner.creativeId,
|
|
373
|
+
gamPriceBucket = winner.targeting["hb_pb"],
|
|
374
|
+
floorPrice = floorPrice,
|
|
375
|
+
secondHighestBid = secondHighestBid,
|
|
376
|
+
demandChannel = "S2S"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
results[impid] = BidResponse(winner.targeting, auctionData)
|
|
380
|
+
BCLogger.d(TAG, "Winner for $impid: $${winner.price}")
|
|
346
381
|
}
|
|
347
382
|
|
|
348
383
|
BCLogger.i(TAG, "Parsed ${results.size} winning bids")
|
|
@@ -28,7 +28,7 @@ internal class DeviceContext private constructor(context: Context) {
|
|
|
28
28
|
|
|
29
29
|
companion object {
|
|
30
30
|
private const val TAG = "DeviceContext"
|
|
31
|
-
internal const val SDK_VERSION = "0.
|
|
31
|
+
internal const val SDK_VERSION = "0.14.0"
|
|
32
32
|
|
|
33
33
|
@Volatile
|
|
34
34
|
private var instance: DeviceContext? = null
|
|
@@ -69,6 +69,69 @@ internal data class RevenueData(
|
|
|
69
69
|
val currency: String
|
|
70
70
|
)
|
|
71
71
|
|
|
72
|
+
// MARK: - Screen View Options
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Options for customizing screen view tracking
|
|
76
|
+
*
|
|
77
|
+
* Allows app developers to provide page URL, content metadata,
|
|
78
|
+
* and custom dimensions for analytics events.
|
|
79
|
+
*/
|
|
80
|
+
data class ScreenViewOptions(
|
|
81
|
+
/** Override the auto-generated page URL (must be a valid URL) */
|
|
82
|
+
val pageUrl: String? = null,
|
|
83
|
+
|
|
84
|
+
/** Content metadata for this screen/page */
|
|
85
|
+
val pageMeta: PageMetaData? = null,
|
|
86
|
+
|
|
87
|
+
/** Custom key-value dimensions attached to analytics events */
|
|
88
|
+
val customDimensions: Map<String, String>? = null
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Content metadata for a screen/page view
|
|
93
|
+
*
|
|
94
|
+
* Matches the `page_meta_data` fields in the analytics schema.
|
|
95
|
+
*/
|
|
96
|
+
@JsonClass(generateAdapter = true)
|
|
97
|
+
data class PageMetaData(
|
|
98
|
+
/** Canonical page URL */
|
|
99
|
+
@Json(name = "url")
|
|
100
|
+
val url: String? = null,
|
|
101
|
+
|
|
102
|
+
/** Content author */
|
|
103
|
+
@Json(name = "author")
|
|
104
|
+
val author: String? = null,
|
|
105
|
+
|
|
106
|
+
/** Page/screen title */
|
|
107
|
+
@Json(name = "title")
|
|
108
|
+
val title: String? = null,
|
|
109
|
+
|
|
110
|
+
/** Featured image URL */
|
|
111
|
+
@Json(name = "thumbnailUrl")
|
|
112
|
+
val thumbnailUrl: String? = null,
|
|
113
|
+
|
|
114
|
+
/** Content section/category */
|
|
115
|
+
@Json(name = "articleSection")
|
|
116
|
+
val articleSection: String? = null,
|
|
117
|
+
|
|
118
|
+
/** Comma-separated keywords */
|
|
119
|
+
@Json(name = "keywords")
|
|
120
|
+
val keywords: String? = null,
|
|
121
|
+
|
|
122
|
+
/** Content creation date (ISO 8601) */
|
|
123
|
+
@Json(name = "dateCreated")
|
|
124
|
+
val dateCreated: String? = null,
|
|
125
|
+
|
|
126
|
+
/** Content last modified date (ISO 8601) */
|
|
127
|
+
@Json(name = "dateModified")
|
|
128
|
+
val dateModified: String? = null,
|
|
129
|
+
|
|
130
|
+
/** Content publication date (ISO 8601) */
|
|
131
|
+
@Json(name = "datePublished")
|
|
132
|
+
val datePublished: String? = null
|
|
133
|
+
)
|
|
134
|
+
|
|
72
135
|
// MARK: - Enhanced Analytics Events (matching web SDK data model)
|
|
73
136
|
|
|
74
137
|
/**
|
|
@@ -178,7 +241,11 @@ internal data class PageViewEvent(
|
|
|
178
241
|
|
|
179
242
|
// Custom dimensions
|
|
180
243
|
@Json(name = "custom_dimensions")
|
|
181
|
-
val customDimensions: Map<String, String> = emptyMap()
|
|
244
|
+
val customDimensions: Map<String, String> = emptyMap(),
|
|
245
|
+
|
|
246
|
+
// Page metadata (only for pageview events)
|
|
247
|
+
@Json(name = "page_meta_data")
|
|
248
|
+
val pageMetaData: PageMetaData? = null
|
|
182
249
|
)
|
|
183
250
|
|
|
184
251
|
/**
|
|
@@ -845,7 +912,19 @@ internal data class AuctionData(
|
|
|
845
912
|
val bidPriceCpm: Double? = null,
|
|
846
913
|
|
|
847
914
|
/** Creative ID from winning bid */
|
|
848
|
-
val creativeId: String? = null
|
|
915
|
+
val creativeId: String? = null,
|
|
916
|
+
|
|
917
|
+
/** GAM price bucket (hb_pb value from targeting KVPs) */
|
|
918
|
+
val gamPriceBucket: String? = null,
|
|
919
|
+
|
|
920
|
+
/** Floor price sent to bidders */
|
|
921
|
+
val floorPrice: Double? = null,
|
|
922
|
+
|
|
923
|
+
/** Second-highest bid price (for computing min_bid_to_win) */
|
|
924
|
+
val secondHighestBid: Double? = null,
|
|
925
|
+
|
|
926
|
+
/** Demand channel ("S2S", "Google Ad Exchange", etc.) */
|
|
927
|
+
val demandChannel: String? = null
|
|
849
928
|
) {
|
|
850
929
|
companion object {
|
|
851
930
|
val EMPTY = AuctionData()
|
|
@@ -95,10 +95,52 @@ class BigCrunchAdsModule(reactContext: ReactApplicationContext) :
|
|
|
95
95
|
* Track screen view
|
|
96
96
|
*/
|
|
97
97
|
@ReactMethod
|
|
98
|
-
fun trackScreenView(screenName: String, promise: Promise) {
|
|
98
|
+
fun trackScreenView(screenName: String, options: ReadableMap?, promise: Promise) {
|
|
99
99
|
scope.launch {
|
|
100
100
|
try {
|
|
101
|
-
|
|
101
|
+
var screenViewOptions: ScreenViewOptions? = null
|
|
102
|
+
|
|
103
|
+
if (options != null) {
|
|
104
|
+
var pageMeta: PageMetaData? = null
|
|
105
|
+
if (options.hasKey("pageMeta")) {
|
|
106
|
+
val metaMap = options.getMap("pageMeta")
|
|
107
|
+
if (metaMap != null) {
|
|
108
|
+
pageMeta = PageMetaData(
|
|
109
|
+
url = metaMap.getString("url"),
|
|
110
|
+
author = metaMap.getString("author"),
|
|
111
|
+
title = metaMap.getString("title"),
|
|
112
|
+
thumbnailUrl = metaMap.getString("thumbnailUrl"),
|
|
113
|
+
articleSection = metaMap.getString("articleSection"),
|
|
114
|
+
keywords = metaMap.getString("keywords"),
|
|
115
|
+
dateCreated = metaMap.getString("dateCreated"),
|
|
116
|
+
dateModified = metaMap.getString("dateModified"),
|
|
117
|
+
datePublished = metaMap.getString("datePublished")
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var customDimensions: Map<String, String>? = null
|
|
123
|
+
if (options.hasKey("customDimensions")) {
|
|
124
|
+
val dimMap = options.getMap("customDimensions")
|
|
125
|
+
if (dimMap != null) {
|
|
126
|
+
val dims = mutableMapOf<String, String>()
|
|
127
|
+
val iterator = dimMap.keySetIterator()
|
|
128
|
+
while (iterator.hasNextKey()) {
|
|
129
|
+
val key = iterator.nextKey()
|
|
130
|
+
dimMap.getString(key)?.let { dims[key] = it }
|
|
131
|
+
}
|
|
132
|
+
customDimensions = dims
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
screenViewOptions = ScreenViewOptions(
|
|
137
|
+
pageUrl = if (options.hasKey("pageUrl")) options.getString("pageUrl") else null,
|
|
138
|
+
pageMeta = pageMeta,
|
|
139
|
+
customDimensions = customDimensions
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
BigCrunchAds.trackScreenView(screenName, screenViewOptions)
|
|
102
144
|
promise.resolve(null)
|
|
103
145
|
} catch (e: Exception) {
|
|
104
146
|
promise.reject("TRACK_ERROR", "Failed to track screen view: ${e.message}", e)
|
|
@@ -501,6 +543,19 @@ class BigCrunchAdsModule(reactContext: ReactApplicationContext) :
|
|
|
501
543
|
}
|
|
502
544
|
}
|
|
503
545
|
|
|
546
|
+
/**
|
|
547
|
+
* Set account type
|
|
548
|
+
*/
|
|
549
|
+
@ReactMethod
|
|
550
|
+
fun setAccountType(accountType: String, promise: Promise) {
|
|
551
|
+
try {
|
|
552
|
+
BigCrunchAds.setAccountType(accountType)
|
|
553
|
+
promise.resolve(null)
|
|
554
|
+
} catch (e: Exception) {
|
|
555
|
+
promise.reject("ACCOUNT_ERROR", "Failed to set account type: ${e.message}", e)
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
504
559
|
/**
|
|
505
560
|
* Set debug mode
|
|
506
561
|
*/
|
|
@@ -361,9 +361,15 @@ private class BannerDelegateWrapper: NSObject, GoogleMobileAds.BannerViewDelegat
|
|
|
361
361
|
let responseInfo = bannerView.responseInfo
|
|
362
362
|
let adMetadata = GoogleAdsAdapter.extractAdMetadata(from: responseInfo)
|
|
363
363
|
|
|
364
|
+
// Extract ad size from the loaded banner
|
|
365
|
+
let adSize = bannerView.adSize
|
|
366
|
+
let adSizeString = "\(Int(adSize.size.width))x\(Int(adSize.size.height))"
|
|
367
|
+
|
|
364
368
|
analyticsClient.trackAdImpression(
|
|
365
369
|
placementId: placementConfig.placementId,
|
|
366
370
|
format: placementConfig.format,
|
|
371
|
+
gamAdUnit: placementConfig.gamAdUnit,
|
|
372
|
+
adSize: adSizeString,
|
|
367
373
|
refreshCount: refreshCount,
|
|
368
374
|
advertiserId: adMetadata["advertiser_id"] as? String,
|
|
369
375
|
campaignId: adMetadata["campaign_id"] as? String,
|
|
@@ -426,6 +432,7 @@ private class InterstitialDelegateWrapper: NSObject, GoogleMobileAds.FullScreenC
|
|
|
426
432
|
analyticsClient.trackAdImpression(
|
|
427
433
|
placementId: placementConfig.placementId,
|
|
428
434
|
format: placementConfig.format,
|
|
435
|
+
gamAdUnit: placementConfig.gamAdUnit,
|
|
429
436
|
advertiserId: adMetadata["advertiser_id"] as? String,
|
|
430
437
|
campaignId: adMetadata["campaign_id"] as? String,
|
|
431
438
|
lineItemId: adMetadata["line_item_id"] as? String,
|
|
@@ -156,14 +156,16 @@ public final class BigCrunchAds {
|
|
|
156
156
|
/**
|
|
157
157
|
* Track screen view for analytics
|
|
158
158
|
*
|
|
159
|
-
* -
|
|
159
|
+
* - Parameters:
|
|
160
|
+
* - screenName: Name of the screen being viewed
|
|
161
|
+
* - options: Optional overrides for page URL, content metadata, and custom dimensions
|
|
160
162
|
*/
|
|
161
|
-
public static func trackScreen(_ screenName: String) {
|
|
163
|
+
public static func trackScreen(_ screenName: String, options: ScreenViewOptions? = nil) {
|
|
162
164
|
guard _isInitialized else {
|
|
163
165
|
BCLogger.warning("trackScreen called before initialization, ignoring")
|
|
164
166
|
return
|
|
165
167
|
}
|
|
166
|
-
analyticsClient.trackScreenView(screenName)
|
|
168
|
+
analyticsClient.trackScreenView(screenName, options: options)
|
|
167
169
|
SessionManager.shared.startPageView()
|
|
168
170
|
}
|
|
169
171
|
|
|
@@ -247,6 +249,25 @@ public final class BigCrunchAds {
|
|
|
247
249
|
)
|
|
248
250
|
}
|
|
249
251
|
|
|
252
|
+
// MARK: - Account Type
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Set the account type for the current user
|
|
256
|
+
*
|
|
257
|
+
* Included in all analytics events. Defaults to "guest" if not set.
|
|
258
|
+
* Valid values: "guest", "logged_in", "paid", "subscriber", "free"
|
|
259
|
+
*
|
|
260
|
+
* - Parameter accountType: The user's account type
|
|
261
|
+
*/
|
|
262
|
+
public static func setAccountType(_ accountType: String) {
|
|
263
|
+
guard _isInitialized else {
|
|
264
|
+
BCLogger.warning("setAccountType called before initialization, ignoring")
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
analyticsClient.setAccountType(accountType)
|
|
268
|
+
BCLogger.debug("Account type set to: \(accountType)")
|
|
269
|
+
}
|
|
270
|
+
|
|
250
271
|
// MARK: - Privacy Compliance
|
|
251
272
|
|
|
252
273
|
/**
|
|
@@ -352,10 +373,12 @@ public final class BigCrunchAds {
|
|
|
352
373
|
/**
|
|
353
374
|
* Track screen view for analytics (alias for Android API compatibility)
|
|
354
375
|
*
|
|
355
|
-
* -
|
|
376
|
+
* - Parameters:
|
|
377
|
+
* - screenName: Name of the screen being viewed
|
|
378
|
+
* - options: Optional overrides for page URL, content metadata, and custom dimensions
|
|
356
379
|
*/
|
|
357
|
-
public static func trackScreenView(_ screenName: String) {
|
|
358
|
-
trackScreen(screenName)
|
|
380
|
+
public static func trackScreenView(_ screenName: String, options: ScreenViewOptions? = nil) {
|
|
381
|
+
trackScreen(screenName, options: options)
|
|
359
382
|
}
|
|
360
383
|
|
|
361
384
|
// MARK: - Internal
|
|
@@ -172,8 +172,8 @@ public final class BigCrunchRewarded {
|
|
|
172
172
|
|
|
173
173
|
// Fetch S2S demand
|
|
174
174
|
if let bidRequestClient = BigCrunchAds.getBidRequestClient() {
|
|
175
|
-
let
|
|
176
|
-
if let targeting = targeting, !targeting.isEmpty {
|
|
175
|
+
let bidResponse = await bidRequestClient.fetchDemand(placement: placement)
|
|
176
|
+
if let targeting = bidResponse?.targeting, !targeting.isEmpty {
|
|
177
177
|
var customTargeting = request.customTargeting ?? [:]
|
|
178
178
|
for (key, value) in targeting {
|
|
179
179
|
customTargeting[key] = value
|
|
@@ -183,6 +183,14 @@ public final class BigCrunchRewarded {
|
|
|
183
183
|
} else {
|
|
184
184
|
BCLogger.warning("\(TAG): S2S demand fetch returned no targeting, continuing with Google only")
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
// Store auction data for analytics
|
|
188
|
+
let auctionData = bidResponse?.auctionData ?? AuctionData(
|
|
189
|
+
auctionId: nil, bidder: "google_ad_exchange", bidPriceCpm: nil, creativeId: nil,
|
|
190
|
+
gamPriceBucket: nil, floorPrice: placement.floorPrice, secondHighestBid: nil,
|
|
191
|
+
demandChannel: "Google Ad Exchange"
|
|
192
|
+
)
|
|
193
|
+
BigCrunchAds.getAnalyticsClient().setAuctionData(placementId: placement.placementId, auctionData: auctionData)
|
|
186
194
|
}
|
|
187
195
|
|
|
188
196
|
// Load Google rewarded ad
|
|
@@ -160,8 +160,8 @@ internal class AdOrchestrator {
|
|
|
160
160
|
let adRequest = GoogleMobileAds.Request()
|
|
161
161
|
|
|
162
162
|
// 3. Fetch S2S demand (even if it fails, continue with Google)
|
|
163
|
-
let
|
|
164
|
-
if let targeting = targeting, !targeting.isEmpty {
|
|
163
|
+
let bidResponse = await bidRequestClient.fetchDemand(placement: placement)
|
|
164
|
+
if let targeting = bidResponse?.targeting, !targeting.isEmpty {
|
|
165
165
|
var customTargeting = adRequest.customTargeting ?? [:]
|
|
166
166
|
for (key, value) in targeting {
|
|
167
167
|
customTargeting[key] = value
|
|
@@ -172,6 +172,14 @@ internal class AdOrchestrator {
|
|
|
172
172
|
BCLogger.warning("\(AdOrchestrator.TAG): S2S demand fetch returned no targeting, continuing with Google only")
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
// Store auction data for analytics (or default to GAM if no S2S)
|
|
176
|
+
let auctionData = bidResponse?.auctionData ?? AuctionData(
|
|
177
|
+
auctionId: nil, bidder: "google_ad_exchange", bidPriceCpm: nil, creativeId: nil,
|
|
178
|
+
gamPriceBucket: nil, floorPrice: placement.floorPrice, secondHighestBid: nil,
|
|
179
|
+
demandChannel: "Google Ad Exchange"
|
|
180
|
+
)
|
|
181
|
+
analyticsClient.setAuctionData(placementId: placement.placementId, auctionData: auctionData)
|
|
182
|
+
|
|
175
183
|
// 4. Load Google ad with the (possibly enriched) ad request
|
|
176
184
|
let delegateWrapper = BannerDelegateAdapterWrapper(
|
|
177
185
|
placementId: placement.placementId,
|
|
@@ -284,8 +292,8 @@ internal class AdOrchestrator {
|
|
|
284
292
|
let adRequest = GoogleMobileAds.Request()
|
|
285
293
|
|
|
286
294
|
// 3. Fetch S2S demand
|
|
287
|
-
let
|
|
288
|
-
if let targeting = targeting, !targeting.isEmpty {
|
|
295
|
+
let bidResponse = await bidRequestClient.fetchDemand(placement: placement)
|
|
296
|
+
if let targeting = bidResponse?.targeting, !targeting.isEmpty {
|
|
289
297
|
var customTargeting = adRequest.customTargeting ?? [:]
|
|
290
298
|
for (key, value) in targeting {
|
|
291
299
|
customTargeting[key] = value
|
|
@@ -296,6 +304,14 @@ internal class AdOrchestrator {
|
|
|
296
304
|
BCLogger.warning("\(AdOrchestrator.TAG): S2S demand fetch returned no targeting, continuing with Google only")
|
|
297
305
|
}
|
|
298
306
|
|
|
307
|
+
// Store auction data for analytics (or default to GAM if no S2S)
|
|
308
|
+
let auctionData = bidResponse?.auctionData ?? AuctionData(
|
|
309
|
+
auctionId: nil, bidder: "google_ad_exchange", bidPriceCpm: nil, creativeId: nil,
|
|
310
|
+
gamPriceBucket: nil, floorPrice: placement.floorPrice, secondHighestBid: nil,
|
|
311
|
+
demandChannel: "Google Ad Exchange"
|
|
312
|
+
)
|
|
313
|
+
analyticsClient.setAuctionData(placementId: placement.placementId, auctionData: auctionData)
|
|
314
|
+
|
|
299
315
|
// Check if we should use test ad units
|
|
300
316
|
let useTestAds = configManager.shouldUseTestAds()
|
|
301
317
|
|