@bigcrunch/react-native-ads 0.4.0 → 0.6.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/README.md +5 -5
- package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchAds.kt +434 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchBannerView.kt +484 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchInterstitial.kt +403 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/BigCrunchRewarded.kt +409 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/adapters/GoogleAdsAdapter.kt +592 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/AdOrchestrator.kt +623 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/AnalyticsClient.kt +719 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/BidRequestClient.kt +364 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/ConfigManager.kt +300 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/DeviceContext.kt +385 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/RewardedCallback.kt +42 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/core/SessionManager.kt +330 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/internal/DeviceHelper.kt +60 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/internal/HttpClient.kt +114 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/internal/Logger.kt +71 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/internal/PrivacyStore.kt +125 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/internal/Storage.kt +88 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/listeners/BannerAdListener.kt +55 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/listeners/InterstitialAdListener.kt +55 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/listeners/RewardedAdListener.kt +58 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/models/AdEvent.kt +880 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/models/AppConfig.kt +87 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/models/DeviceData.kt +18 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/models/PlacementConfig.kt +70 -0
- package/android/bigcrunch-ads/com/bigcrunch/ads/models/SessionInfo.kt +21 -0
- package/android/build.gradle +22 -10
- package/android/settings.gradle +2 -6
- package/android/src/main/java/com/bigcrunch/ads/react/BigCrunchAdsModule.kt +8 -2
- package/ios/BigCrunchAds/Sources/Adapters/GoogleAdsAdapter.swift +512 -0
- package/ios/BigCrunchAds/Sources/BigCrunchAds.swift +387 -0
- package/ios/BigCrunchAds/Sources/BigCrunchBannerView.swift +448 -0
- package/ios/BigCrunchAds/Sources/BigCrunchInterstitial.swift +412 -0
- package/ios/BigCrunchAds/Sources/BigCrunchRewarded.swift +523 -0
- package/ios/BigCrunchAds/Sources/Core/AdOrchestrator.swift +514 -0
- package/ios/BigCrunchAds/Sources/Core/AnalyticsClient.swift +874 -0
- package/ios/BigCrunchAds/Sources/Core/BidRequestClient.swift +344 -0
- package/ios/BigCrunchAds/Sources/Core/ConfigManager.swift +305 -0
- package/ios/BigCrunchAds/Sources/Core/DeviceContext.swift +284 -0
- package/ios/BigCrunchAds/Sources/Core/SessionManager.swift +392 -0
- package/ios/BigCrunchAds/Sources/Internal/HTTPClient.swift +146 -0
- package/ios/BigCrunchAds/Sources/Internal/Logger.swift +62 -0
- package/ios/BigCrunchAds/Sources/Internal/PrivacyStore.swift +129 -0
- package/ios/BigCrunchAds/Sources/Internal/Storage.swift +73 -0
- package/ios/BigCrunchAds/Sources/Models/AdEvent.swift +784 -0
- package/ios/BigCrunchAds/Sources/Models/AppConfig.swift +97 -0
- package/ios/BigCrunchAds/Sources/Models/DeviceData.swift +68 -0
- package/ios/BigCrunchAds/Sources/Models/PlacementConfig.swift +137 -0
- package/ios/BigCrunchAds/Sources/Models/SessionInfo.swift +48 -0
- package/ios/BigCrunchAdsModule.swift +37 -9
- package/ios/BigCrunchBannerViewManager.swift +0 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- package/package.json +7 -1
- package/react-native-bigcrunch-ads.podspec +0 -1
- package/scripts/inject-version.js +55 -0
- package/src/index.ts +3 -2
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import GoogleMobileAds
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BigCrunch Rewarded Ads - Static API for rewarded video ads
|
|
6
|
+
*
|
|
7
|
+
* Rewarded ads are full-screen ads that reward users for watching video content.
|
|
8
|
+
* Users opt-in to watch the ad in exchange for in-app rewards.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```swift
|
|
12
|
+
* // 1. Preload the ad (do this early, e.g., when entering a screen)
|
|
13
|
+
* BigCrunchRewarded.preload(placementId: "rewarded_video") { result in
|
|
14
|
+
* switch result {
|
|
15
|
+
* case .success:
|
|
16
|
+
* print("Ad ready to show")
|
|
17
|
+
* case .failure(let error):
|
|
18
|
+
* print("Failed to load: \(error)")
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* // 2. Check if ready (optional)
|
|
23
|
+
* if BigCrunchRewarded.isReady(placementId: "rewarded_video") {
|
|
24
|
+
* // Ad is ready to show
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* // 3. Show when user opts in
|
|
28
|
+
* BigCrunchRewarded.show(
|
|
29
|
+
* from: self,
|
|
30
|
+
* placementId: "rewarded_video",
|
|
31
|
+
* delegate: self
|
|
32
|
+
* )
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Implement BigCrunchRewardedDelegate for events:
|
|
36
|
+
* ```swift
|
|
37
|
+
* extension ViewController: BigCrunchRewardedDelegate {
|
|
38
|
+
* func rewardedDidShow(placementId: String) { }
|
|
39
|
+
* func rewardedDidDismiss(placementId: String) { }
|
|
40
|
+
* func rewardedDidClick(placementId: String) { }
|
|
41
|
+
* func rewardedDidFail(placementId: String, error: String) { }
|
|
42
|
+
* func rewardedDidEarnReward(placementId: String, type: String, amount: Int) {
|
|
43
|
+
* // Grant the reward to the user
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
public final class BigCrunchRewarded {
|
|
49
|
+
|
|
50
|
+
private static let TAG = "BigCrunchRewarded"
|
|
51
|
+
|
|
52
|
+
// Cache for preloaded rewarded ads
|
|
53
|
+
private static var rewardedCache: [String: RewardedAd] = [:]
|
|
54
|
+
private static let cacheLock = NSLock()
|
|
55
|
+
|
|
56
|
+
// Store delegates to prevent deallocation during show
|
|
57
|
+
private static var activeDelegates: [String: RewardedDelegateWrapper] = [:]
|
|
58
|
+
private static let delegateLock = NSLock()
|
|
59
|
+
|
|
60
|
+
// Private initializer to prevent instantiation
|
|
61
|
+
private init() {}
|
|
62
|
+
|
|
63
|
+
// MARK: - Legacy Callback Protocol
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Legacy callback protocol for rewarded ad events
|
|
67
|
+
*/
|
|
68
|
+
public protocol Callback {
|
|
69
|
+
func onUserEarnedReward(type: String, amount: Int)
|
|
70
|
+
func onAdDismissed()
|
|
71
|
+
func onAdFailed(error: String)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// MARK: - Preload Methods
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Preload a rewarded ad for the given placement
|
|
78
|
+
*
|
|
79
|
+
* Call this early (e.g., when entering a screen) to give the ad time to load.
|
|
80
|
+
*
|
|
81
|
+
* - Parameters:
|
|
82
|
+
* - placementId: The placement ID from BigCrunch dashboard
|
|
83
|
+
* - completion: Optional completion handler called when preload finishes
|
|
84
|
+
*/
|
|
85
|
+
public static func preload(
|
|
86
|
+
placementId: String,
|
|
87
|
+
completion: ((Result<Void, Error>) -> Void)? = nil
|
|
88
|
+
) {
|
|
89
|
+
BCLogger.debug("\(TAG): Preloading rewarded: \(placementId)")
|
|
90
|
+
|
|
91
|
+
BigCrunchAds.requireInitialized()
|
|
92
|
+
|
|
93
|
+
guard !placementId.isEmpty else {
|
|
94
|
+
BCLogger.error("\(TAG): Invalid placementId: cannot be empty")
|
|
95
|
+
completion?(.failure(BigCrunchError.invalidPlacement("placementId cannot be empty")))
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check if already cached
|
|
100
|
+
cacheLock.lock()
|
|
101
|
+
if rewardedCache[placementId] != nil {
|
|
102
|
+
cacheLock.unlock()
|
|
103
|
+
BCLogger.debug("\(TAG): Rewarded already cached: \(placementId)")
|
|
104
|
+
completion?(.success(()))
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
cacheLock.unlock()
|
|
108
|
+
|
|
109
|
+
// Get placement config
|
|
110
|
+
let configManager = BigCrunchAds.getConfigManager()
|
|
111
|
+
guard let placement = configManager.getPlacement(placementId) else {
|
|
112
|
+
BCLogger.error("\(TAG): Placement not found: \(placementId)")
|
|
113
|
+
completion?(.failure(BigCrunchError.invalidPlacement("Placement not found: \(placementId)")))
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
guard placement.format == "rewarded" else {
|
|
118
|
+
BCLogger.error("\(TAG): Invalid format for rewarded: \(placement.format)")
|
|
119
|
+
completion?(.failure(BigCrunchError.invalidPlacement("Invalid placement format: \(placement.format)")))
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Track ad request
|
|
124
|
+
let analyticsClient = BigCrunchAds.getAnalyticsClient()
|
|
125
|
+
analyticsClient.trackAdRequest(placementId: placementId, format: placement.format)
|
|
126
|
+
|
|
127
|
+
// Load the rewarded ad
|
|
128
|
+
Task {
|
|
129
|
+
await loadRewardedAsync(
|
|
130
|
+
placement: placement,
|
|
131
|
+
configManager: configManager,
|
|
132
|
+
analyticsClient: analyticsClient,
|
|
133
|
+
completion: completion
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Preload a rewarded ad (simplified version without completion)
|
|
140
|
+
*
|
|
141
|
+
* - Parameter placementId: The placement ID from BigCrunch dashboard
|
|
142
|
+
*/
|
|
143
|
+
public static func preload(placementId: String) {
|
|
144
|
+
preload(placementId: placementId, completion: nil)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private static func loadRewardedAsync(
|
|
148
|
+
placement: PlacementConfig,
|
|
149
|
+
configManager: ConfigManager,
|
|
150
|
+
analyticsClient: AnalyticsClient,
|
|
151
|
+
completion: ((Result<Void, Error>) -> Void)?
|
|
152
|
+
) async {
|
|
153
|
+
// Get GAM network code from config
|
|
154
|
+
guard let gamNetworkCode = configManager.getGamNetworkCode() else {
|
|
155
|
+
BCLogger.error("\(TAG): GAM network code not available")
|
|
156
|
+
await MainActor.run {
|
|
157
|
+
completion?(.failure(BigCrunchError.loadFailed("Config not loaded")))
|
|
158
|
+
}
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Build ad unit ID
|
|
163
|
+
let adUnitID: String
|
|
164
|
+
if gamNetworkCode.isEmpty {
|
|
165
|
+
adUnitID = placement.gamAdUnit
|
|
166
|
+
} else {
|
|
167
|
+
adUnitID = "/\(gamNetworkCode)/\(placement.gamAdUnit)"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Create ad request
|
|
171
|
+
let request = Request()
|
|
172
|
+
|
|
173
|
+
// Fetch S2S demand
|
|
174
|
+
if let bidRequestClient = BigCrunchAds.getBidRequestClient() {
|
|
175
|
+
let targeting = await bidRequestClient.fetchDemand(placement: placement)
|
|
176
|
+
if let targeting = targeting, !targeting.isEmpty {
|
|
177
|
+
var customTargeting = request.customTargeting ?? [:]
|
|
178
|
+
for (key, value) in targeting {
|
|
179
|
+
customTargeting[key] = value
|
|
180
|
+
}
|
|
181
|
+
request.customTargeting = customTargeting
|
|
182
|
+
BCLogger.debug("\(TAG): S2S demand fetched for: \(placement.placementId) (\(targeting.count) keys)")
|
|
183
|
+
} else {
|
|
184
|
+
BCLogger.warning("\(TAG): S2S demand fetch returned no targeting, continuing with Google only")
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Load Google rewarded ad
|
|
189
|
+
await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
|
|
190
|
+
RewardedAd.load(
|
|
191
|
+
with: adUnitID,
|
|
192
|
+
request: request
|
|
193
|
+
) { rewardedAd, error in
|
|
194
|
+
if let error = error {
|
|
195
|
+
BCLogger.warning("\(TAG): Rewarded ad failed to load: \(error.localizedDescription)")
|
|
196
|
+
Task { @MainActor in
|
|
197
|
+
completion?(.failure(BigCrunchError.loadFailed(error.localizedDescription)))
|
|
198
|
+
}
|
|
199
|
+
continuation.resume()
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
guard let rewardedAd = rewardedAd else {
|
|
204
|
+
BCLogger.warning("\(TAG): Rewarded ad loaded but was nil")
|
|
205
|
+
Task { @MainActor in
|
|
206
|
+
completion?(.failure(BigCrunchError.loadFailed("Rewarded ad was nil")))
|
|
207
|
+
}
|
|
208
|
+
continuation.resume()
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
BCLogger.debug("\(TAG): Rewarded ad loaded: \(placement.placementId)")
|
|
213
|
+
|
|
214
|
+
// Set up paid event handler for ILRD
|
|
215
|
+
rewardedAd.paidEventHandler = { adValue in
|
|
216
|
+
BCLogger.debug("\(TAG): Rewarded paid event: \(adValue.value) \(adValue.currencyCode)")
|
|
217
|
+
let valueMicros = adValue.value.multiplying(by: NSDecimalNumber(value: 1_000_000))
|
|
218
|
+
analyticsClient.trackAdRevenue(
|
|
219
|
+
placementId: placement.placementId,
|
|
220
|
+
format: placement.format,
|
|
221
|
+
valueMicros: valueMicros.int64Value,
|
|
222
|
+
currency: adValue.currencyCode
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Cache the ad
|
|
227
|
+
cacheLock.lock()
|
|
228
|
+
rewardedCache[placement.placementId] = rewardedAd
|
|
229
|
+
cacheLock.unlock()
|
|
230
|
+
|
|
231
|
+
Task { @MainActor in
|
|
232
|
+
completion?(.success(()))
|
|
233
|
+
}
|
|
234
|
+
continuation.resume()
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// MARK: - Show Methods
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Show a preloaded rewarded ad
|
|
243
|
+
*
|
|
244
|
+
* The ad must be preloaded first using `preload(placementId:)`. Check `isReady(placementId:)`
|
|
245
|
+
* before calling this to ensure an ad is available.
|
|
246
|
+
*
|
|
247
|
+
* - Parameters:
|
|
248
|
+
* - viewController: The view controller to present the ad from
|
|
249
|
+
* - placementId: The placement ID
|
|
250
|
+
* - delegate: Optional delegate for ad events
|
|
251
|
+
* - Returns: true if an ad was available and will be shown, false otherwise
|
|
252
|
+
*/
|
|
253
|
+
@discardableResult
|
|
254
|
+
public static func show(
|
|
255
|
+
from viewController: UIViewController,
|
|
256
|
+
placementId: String,
|
|
257
|
+
delegate: BigCrunchRewardedDelegate? = nil
|
|
258
|
+
) -> Bool {
|
|
259
|
+
BCLogger.debug("\(TAG): Showing rewarded: \(placementId)")
|
|
260
|
+
|
|
261
|
+
BigCrunchAds.requireInitialized()
|
|
262
|
+
|
|
263
|
+
guard !placementId.isEmpty else {
|
|
264
|
+
BCLogger.error("\(TAG): Invalid placementId: cannot be empty")
|
|
265
|
+
delegate?.rewardedDidFail(placementId: placementId, error: "Invalid placementId")
|
|
266
|
+
return false
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Get cached rewarded ad
|
|
270
|
+
cacheLock.lock()
|
|
271
|
+
guard let rewardedAd = rewardedCache.removeValue(forKey: placementId) else {
|
|
272
|
+
cacheLock.unlock()
|
|
273
|
+
BCLogger.warning("\(TAG): No preloaded rewarded for: \(placementId)")
|
|
274
|
+
delegate?.rewardedDidFail(placementId: placementId, error: "No preloaded ad available")
|
|
275
|
+
return false
|
|
276
|
+
}
|
|
277
|
+
cacheLock.unlock()
|
|
278
|
+
|
|
279
|
+
// Get placement config for analytics
|
|
280
|
+
let configManager = BigCrunchAds.getConfigManager()
|
|
281
|
+
guard let placement = configManager.getPlacement(placementId) else {
|
|
282
|
+
BCLogger.error("\(TAG): Placement not found: \(placementId)")
|
|
283
|
+
delegate?.rewardedDidFail(placementId: placementId, error: "Placement not found")
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Create and store delegate wrapper
|
|
288
|
+
let analyticsClient = BigCrunchAds.getAnalyticsClient()
|
|
289
|
+
let wrapper = RewardedDelegateWrapper(
|
|
290
|
+
placementId: placementId,
|
|
291
|
+
placementConfig: placement,
|
|
292
|
+
analyticsClient: analyticsClient,
|
|
293
|
+
delegate: delegate
|
|
294
|
+
) { pid in
|
|
295
|
+
// Cleanup delegate wrapper after ad is dismissed or fails
|
|
296
|
+
delegateLock.lock()
|
|
297
|
+
activeDelegates.removeValue(forKey: pid)
|
|
298
|
+
delegateLock.unlock()
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
delegateLock.lock()
|
|
302
|
+
activeDelegates[placementId] = wrapper
|
|
303
|
+
delegateLock.unlock()
|
|
304
|
+
|
|
305
|
+
rewardedAd.fullScreenContentDelegate = wrapper
|
|
306
|
+
|
|
307
|
+
// Present the ad
|
|
308
|
+
rewardedAd.present(from: viewController) {
|
|
309
|
+
// User earned reward
|
|
310
|
+
let reward = rewardedAd.adReward
|
|
311
|
+
BCLogger.debug("\(TAG): User earned reward: \(reward.amount) \(reward.type)")
|
|
312
|
+
delegate?.rewardedDidEarnReward(
|
|
313
|
+
placementId: placementId,
|
|
314
|
+
type: reward.type,
|
|
315
|
+
amount: reward.amount.intValue
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return true
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Show a preloaded rewarded ad (legacy API)
|
|
324
|
+
*
|
|
325
|
+
* - Parameters:
|
|
326
|
+
* - viewController: The view controller to present the ad from
|
|
327
|
+
* - placementId: The placement ID
|
|
328
|
+
* - callback: Legacy callback for ad events
|
|
329
|
+
*/
|
|
330
|
+
public static func show(
|
|
331
|
+
viewController: UIViewController,
|
|
332
|
+
placementId: String,
|
|
333
|
+
callback: Callback? = nil
|
|
334
|
+
) {
|
|
335
|
+
let delegate: BigCrunchRewardedDelegate? = callback.map { cb in
|
|
336
|
+
LegacyRewardedCallbackAdapter(callback: cb)
|
|
337
|
+
}
|
|
338
|
+
show(from: viewController, placementId: placementId, delegate: delegate)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// MARK: - Query Methods
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Check if a rewarded ad is ready to show
|
|
345
|
+
*
|
|
346
|
+
* - Parameter placementId: The placement ID
|
|
347
|
+
* - Returns: true if an ad is preloaded and ready to show
|
|
348
|
+
*/
|
|
349
|
+
public static func isReady(placementId: String) -> Bool {
|
|
350
|
+
cacheLock.lock()
|
|
351
|
+
defer { cacheLock.unlock() }
|
|
352
|
+
return rewardedCache[placementId] != nil
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Clear all cached rewarded ads
|
|
357
|
+
*
|
|
358
|
+
* Call this to free up memory when ads are no longer needed.
|
|
359
|
+
*/
|
|
360
|
+
public static func clearCache() {
|
|
361
|
+
BCLogger.debug("\(TAG): Clearing rewarded cache")
|
|
362
|
+
cacheLock.lock()
|
|
363
|
+
rewardedCache.removeAll()
|
|
364
|
+
cacheLock.unlock()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// MARK: - Testing Support
|
|
368
|
+
|
|
369
|
+
internal static func resetForTesting() {
|
|
370
|
+
cacheLock.lock()
|
|
371
|
+
rewardedCache.removeAll()
|
|
372
|
+
cacheLock.unlock()
|
|
373
|
+
|
|
374
|
+
delegateLock.lock()
|
|
375
|
+
activeDelegates.removeAll()
|
|
376
|
+
delegateLock.unlock()
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// MARK: - Delegate Protocol
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Delegate protocol for BigCrunchRewarded events
|
|
384
|
+
*/
|
|
385
|
+
public protocol BigCrunchRewardedDelegate: AnyObject {
|
|
386
|
+
/**
|
|
387
|
+
* Called when the rewarded ad is displayed on screen
|
|
388
|
+
*/
|
|
389
|
+
func rewardedDidShow(placementId: String)
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Called when the rewarded ad is dismissed by the user
|
|
393
|
+
*/
|
|
394
|
+
func rewardedDidDismiss(placementId: String)
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Called when the user clicks on the rewarded ad
|
|
398
|
+
*/
|
|
399
|
+
func rewardedDidClick(placementId: String)
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Called when the rewarded ad fails to show
|
|
403
|
+
*/
|
|
404
|
+
func rewardedDidFail(placementId: String, error: String)
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Called when the user earns a reward
|
|
408
|
+
*
|
|
409
|
+
* - Parameters:
|
|
410
|
+
* - placementId: The placement ID
|
|
411
|
+
* - type: The reward type (e.g., "coins", "points")
|
|
412
|
+
* - amount: The reward amount
|
|
413
|
+
*/
|
|
414
|
+
func rewardedDidEarnReward(placementId: String, type: String, amount: Int)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// MARK: - Default Implementations
|
|
418
|
+
|
|
419
|
+
public extension BigCrunchRewardedDelegate {
|
|
420
|
+
func rewardedDidShow(placementId: String) {}
|
|
421
|
+
func rewardedDidDismiss(placementId: String) {}
|
|
422
|
+
func rewardedDidClick(placementId: String) {}
|
|
423
|
+
func rewardedDidFail(placementId: String, error: String) {}
|
|
424
|
+
func rewardedDidEarnReward(placementId: String, type: String, amount: Int) {}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// MARK: - Private Helper Classes
|
|
428
|
+
|
|
429
|
+
private class RewardedDelegateWrapper: NSObject, FullScreenContentDelegate {
|
|
430
|
+
let placementId: String
|
|
431
|
+
let placementConfig: PlacementConfig
|
|
432
|
+
let analyticsClient: AnalyticsClient
|
|
433
|
+
weak var delegate: BigCrunchRewardedDelegate?
|
|
434
|
+
let cleanup: (String) -> Void
|
|
435
|
+
|
|
436
|
+
init(
|
|
437
|
+
placementId: String,
|
|
438
|
+
placementConfig: PlacementConfig,
|
|
439
|
+
analyticsClient: AnalyticsClient,
|
|
440
|
+
delegate: BigCrunchRewardedDelegate?,
|
|
441
|
+
cleanup: @escaping (String) -> Void
|
|
442
|
+
) {
|
|
443
|
+
self.placementId = placementId
|
|
444
|
+
self.placementConfig = placementConfig
|
|
445
|
+
self.analyticsClient = analyticsClient
|
|
446
|
+
self.delegate = delegate
|
|
447
|
+
self.cleanup = cleanup
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
|
|
451
|
+
BCLogger.debug("BigCrunchRewarded: Rewarded ad shown: \(placementId)")
|
|
452
|
+
|
|
453
|
+
// Extract GAM metadata from ResponseInfo
|
|
454
|
+
if let rewardedAd = ad as? RewardedAd {
|
|
455
|
+
let responseInfo = rewardedAd.responseInfo
|
|
456
|
+
let adMetadata = GoogleAdsAdapter.extractAdMetadata(from: responseInfo)
|
|
457
|
+
|
|
458
|
+
analyticsClient.trackAdImpression(
|
|
459
|
+
placementId: placementConfig.placementId,
|
|
460
|
+
format: placementConfig.format,
|
|
461
|
+
advertiserId: adMetadata["advertiser_id"] as? String,
|
|
462
|
+
campaignId: adMetadata["campaign_id"] as? String,
|
|
463
|
+
lineItemId: adMetadata["line_item_id"] as? String,
|
|
464
|
+
creativeId: adMetadata["creative_id"] as? String
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
delegate?.rewardedDidShow(placementId: placementId)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
|
|
472
|
+
BCLogger.debug("BigCrunchRewarded: Rewarded ad dismissed: \(placementId)")
|
|
473
|
+
delegate?.rewardedDidDismiss(placementId: placementId)
|
|
474
|
+
cleanup(placementId)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
|
|
478
|
+
BCLogger.warning("BigCrunchRewarded: Rewarded ad failed to show: \(error.localizedDescription)")
|
|
479
|
+
delegate?.rewardedDidFail(placementId: placementId, error: error.localizedDescription)
|
|
480
|
+
cleanup(placementId)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
func adDidRecordClick(_ ad: FullScreenPresentingAd) {
|
|
484
|
+
BCLogger.debug("BigCrunchRewarded: Rewarded ad clicked: \(placementId)")
|
|
485
|
+
analyticsClient.trackAdClick(
|
|
486
|
+
placementId: placementConfig.placementId,
|
|
487
|
+
format: placementConfig.format
|
|
488
|
+
)
|
|
489
|
+
delegate?.rewardedDidClick(placementId: placementId)
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
func adDidRecordImpression(_ ad: FullScreenPresentingAd) {
|
|
493
|
+
BCLogger.debug("BigCrunchRewarded: Rewarded ad impression: \(placementId)")
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private class LegacyRewardedCallbackAdapter: BigCrunchRewardedDelegate {
|
|
498
|
+
let callback: BigCrunchRewarded.Callback
|
|
499
|
+
|
|
500
|
+
init(callback: BigCrunchRewarded.Callback) {
|
|
501
|
+
self.callback = callback
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
func rewardedDidShow(placementId: String) {
|
|
505
|
+
// Not in legacy callback
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
func rewardedDidDismiss(placementId: String) {
|
|
509
|
+
callback.onAdDismissed()
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
func rewardedDidClick(placementId: String) {
|
|
513
|
+
// Not in legacy callback
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
func rewardedDidFail(placementId: String, error: String) {
|
|
517
|
+
callback.onAdFailed(error: error)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
func rewardedDidEarnReward(placementId: String, type: String, amount: Int) {
|
|
521
|
+
callback.onUserEarnedReward(type: type, amount: amount)
|
|
522
|
+
}
|
|
523
|
+
}
|