@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,514 @@
1
+ import Foundation
2
+ import UIKit
3
+ import GoogleMobileAds
4
+
5
+ /**
6
+ * Callback protocol for banner ad events
7
+ */
8
+ public protocol BannerCallback: AnyObject {
9
+ func onAdLoaded()
10
+ func onAdFailedToLoad(error: String)
11
+ func onAdClicked()
12
+ func onAdImpression()
13
+ }
14
+
15
+ /**
16
+ * Callback protocol for interstitial ad events
17
+ */
18
+ public protocol InterstitialCallback: AnyObject {
19
+ func onAdLoaded()
20
+ func onAdFailedToLoad(error: String)
21
+ func onAdShowed()
22
+ func onAdDismissed()
23
+ func onAdClicked()
24
+ }
25
+
26
+ /**
27
+ * AdOrchestrator - Central orchestration for ad loading lifecycle
28
+ *
29
+ * Coordinates the flow between ConfigManager, BidRequestClient, GoogleAdsAdapter,
30
+ * and AnalyticsClient to load and display ads.
31
+ *
32
+ * Flow:
33
+ * 1. Get PlacementConfig from ConfigManager
34
+ * 2. Track ad request via AnalyticsClient
35
+ * 3. Fetch S2S demand via BidRequestClient
36
+ * 4. Load Google ad via GoogleAdsAdapter (with targeting from S2S)
37
+ * 5. Wire up all callbacks for analytics
38
+ */
39
+ internal class AdOrchestrator {
40
+
41
+ private static let TAG = "AdOrchestrator"
42
+
43
+ private let configManager: ConfigManager
44
+ private let analyticsClient: AnalyticsClient
45
+ private let bidRequestClient: BidRequestClient
46
+ private let googleAdsAdapter: GoogleAdsAdapter
47
+
48
+ // Cache for preloaded interstitial ads
49
+ private var interstitialCache: [String: GoogleMobileAds.InterstitialAd] = [:]
50
+ private let interstitialLock = NSLock()
51
+
52
+ // Track active banner views for cleanup
53
+ private var activeBanners: [String: GoogleMobileAds.BannerView] = [:]
54
+ private let bannerLock = NSLock()
55
+
56
+ // Store callback wrappers to prevent deallocation
57
+ private var bannerCallbackWrappers: [String: BannerCallbackWrapper] = [:]
58
+ private var interstitialCallbackWrappers: [String: InterstitialCallbackWrapper] = [:]
59
+
60
+ init(
61
+ configManager: ConfigManager,
62
+ analyticsClient: AnalyticsClient,
63
+ bidRequestClient: BidRequestClient,
64
+ googleAdsAdapter: GoogleAdsAdapter
65
+ ) {
66
+ self.configManager = configManager
67
+ self.analyticsClient = analyticsClient
68
+ self.bidRequestClient = bidRequestClient
69
+ self.googleAdsAdapter = googleAdsAdapter
70
+ }
71
+
72
+ /**
73
+ * Load a banner ad
74
+ *
75
+ * - Parameters:
76
+ * - placementId: The placement ID from the BigCrunch dashboard
77
+ * - rootViewController: The view controller for presenting ad click actions
78
+ * - callback: Callback for ad events
79
+ * - adSizeOverride: Optional size override (takes precedence over backend config)
80
+ * - bannerViewSetter: Closure to set the banner view on the container
81
+ * - Returns: The GoogleMobileAds.BannerView if loading was initiated, nil if placement not found
82
+ */
83
+ func loadBannerAd(
84
+ placementId: String,
85
+ rootViewController: UIViewController,
86
+ callback: BannerCallback,
87
+ refreshCount: Int = 0,
88
+ adSizeOverride: AdSize? = nil,
89
+ bannerViewSetter: ((GoogleMobileAds.BannerView) -> Void)? = nil
90
+ ) -> GoogleMobileAds.BannerView? {
91
+ BCLogger.debug("\(AdOrchestrator.TAG): Loading banner ad: \(placementId) (refreshCount: \(refreshCount))")
92
+
93
+ // 1. Get placement config
94
+ guard let placement = configManager.getPlacement(placementId) else {
95
+ BCLogger.error("\(AdOrchestrator.TAG): Placement not found: \(placementId)")
96
+ callback.onAdFailedToLoad(error: "Placement not found: \(placementId)")
97
+ return nil
98
+ }
99
+
100
+ guard placement.format == "banner" else {
101
+ BCLogger.error("\(AdOrchestrator.TAG): Invalid format for banner: \(placement.format)")
102
+ callback.onAdFailedToLoad(error: "Invalid placement format: \(placement.format)")
103
+ return nil
104
+ }
105
+
106
+ // 2. Track ad request
107
+ analyticsClient.trackAdRequest(placementId: placementId, format: placement.format)
108
+
109
+ // 3. Create callback wrapper and store it
110
+ let callbackWrapper = BannerCallbackWrapper(
111
+ placementId: placementId,
112
+ callback: callback
113
+ )
114
+ bannerCallbackWrappers[placementId] = callbackWrapper
115
+
116
+ // 4. Start async ad loading
117
+ Task {
118
+ await loadBannerAsync(
119
+ placement: placement,
120
+ rootViewController: rootViewController,
121
+ callbackWrapper: callbackWrapper,
122
+ refreshCount: refreshCount,
123
+ adSizeOverride: adSizeOverride,
124
+ bannerViewSetter: bannerViewSetter
125
+ )
126
+ }
127
+
128
+ // Return nil initially - the banner view will be created by GoogleAdsAdapter
129
+ return nil
130
+ }
131
+
132
+ private func loadBannerAsync(
133
+ placement: PlacementConfig,
134
+ rootViewController: UIViewController,
135
+ callbackWrapper: BannerCallbackWrapper,
136
+ refreshCount: Int,
137
+ adSizeOverride: AdSize?,
138
+ bannerViewSetter: ((GoogleMobileAds.BannerView) -> Void)?
139
+ ) async {
140
+ // Get GAM network code from config
141
+ guard let gamNetworkCode = configManager.getGamNetworkCode() else {
142
+ BCLogger.error("\(AdOrchestrator.TAG): GAM network code not available")
143
+ callbackWrapper.callback?.onAdFailedToLoad(error: "Config not loaded")
144
+ return
145
+ }
146
+
147
+ // Resolve effective size for S2S bid request (adaptive → concrete dimensions)
148
+ let effectiveSize = adSizeOverride ?? placement.sizes?.first
149
+ let resolvedSize: AdSize?
150
+ if let eff = effectiveSize, eff.isAdaptive {
151
+ let width: CGFloat = eff.width > 0 ? CGFloat(eff.width) : UIScreen.main.bounds.width
152
+ let googleAdSize = GoogleMobileAds.currentOrientationAnchoredAdaptiveBanner(width: width)
153
+ BCLogger.debug("\(AdOrchestrator.TAG): Resolved adaptive size to \(Int(googleAdSize.size.width))x\(Int(googleAdSize.size.height))")
154
+ resolvedSize = AdSize(width: Int(googleAdSize.size.width), height: Int(googleAdSize.size.height))
155
+ } else {
156
+ resolvedSize = effectiveSize
157
+ }
158
+
159
+ // Create ad request
160
+ let adRequest = GoogleMobileAds.Request()
161
+
162
+ // 3. Fetch S2S demand (even if it fails, continue with Google)
163
+ let targeting = await bidRequestClient.fetchDemand(placement: placement)
164
+ if let targeting = targeting, !targeting.isEmpty {
165
+ var customTargeting = adRequest.customTargeting ?? [:]
166
+ for (key, value) in targeting {
167
+ customTargeting[key] = value
168
+ }
169
+ adRequest.customTargeting = customTargeting
170
+ BCLogger.debug("\(AdOrchestrator.TAG): S2S demand fetched for: \(placement.placementId) (\(targeting.count) keys)")
171
+ } else {
172
+ BCLogger.warning("\(AdOrchestrator.TAG): S2S demand fetch returned no targeting, continuing with Google only")
173
+ }
174
+
175
+ // 4. Load Google ad with the (possibly enriched) ad request
176
+ let delegateWrapper = BannerDelegateAdapterWrapper(
177
+ placementId: placement.placementId,
178
+ callbackWrapper: callbackWrapper
179
+ )
180
+
181
+ // Check if we should use test ad units
182
+ let useTestAds = configManager.shouldUseTestAds()
183
+
184
+ let bannerView = await googleAdsAdapter.loadBannerAd(
185
+ placementConfig: placement,
186
+ gamNetworkCode: gamNetworkCode,
187
+ request: adRequest,
188
+ rootViewController: rootViewController,
189
+ delegate: delegateWrapper,
190
+ useTestAds: useTestAds,
191
+ adSizeOverride: adSizeOverride,
192
+ refreshCount: refreshCount
193
+ )
194
+
195
+ // Set the banner view on the container (on main thread)
196
+ await MainActor.run {
197
+ bannerViewSetter?(bannerView)
198
+ }
199
+
200
+ // Track the active banner for cleanup
201
+ bannerLock.lock()
202
+ activeBanners[placement.placementId] = bannerView
203
+ bannerLock.unlock()
204
+
205
+ // Store the delegate wrapper to prevent deallocation
206
+ callbackWrapper.delegateWrapper = delegateWrapper
207
+ }
208
+
209
+ /**
210
+ * Destroy a banner ad
211
+ *
212
+ * - Parameter placementId: The placement ID of the banner to destroy
213
+ */
214
+ func destroyBannerAd(placementId: String) {
215
+ BCLogger.debug("\(AdOrchestrator.TAG): Destroying banner ad: \(placementId)")
216
+
217
+ bannerLock.lock()
218
+ if let bannerView = activeBanners.removeValue(forKey: placementId) {
219
+ googleAdsAdapter.destroyBannerAd(bannerView)
220
+ }
221
+ bannerLock.unlock()
222
+
223
+ // Clean up callback wrapper
224
+ bannerCallbackWrappers.removeValue(forKey: placementId)
225
+ }
226
+
227
+ /**
228
+ * Preload an interstitial ad
229
+ *
230
+ * - Parameters:
231
+ * - placementId: The placement ID from the BigCrunch dashboard
232
+ * - callback: Callback for ad events
233
+ */
234
+ func preloadInterstitialAd(
235
+ placementId: String,
236
+ callback: InterstitialCallback
237
+ ) {
238
+ BCLogger.debug("\(AdOrchestrator.TAG): Preloading interstitial ad: \(placementId)")
239
+
240
+ // 1. Get placement config
241
+ guard let placement = configManager.getPlacement(placementId) else {
242
+ BCLogger.error("\(AdOrchestrator.TAG): Placement not found: \(placementId)")
243
+ callback.onAdFailedToLoad(error: "Placement not found: \(placementId)")
244
+ return
245
+ }
246
+
247
+ guard placement.format == "interstitial" else {
248
+ BCLogger.error("\(AdOrchestrator.TAG): Invalid format for interstitial: \(placement.format)")
249
+ callback.onAdFailedToLoad(error: "Invalid placement format: \(placement.format)")
250
+ return
251
+ }
252
+
253
+ // 2. Track ad request
254
+ analyticsClient.trackAdRequest(placementId: placementId, format: placement.format)
255
+
256
+ // 3. Start async ad loading
257
+ Task {
258
+ await preloadInterstitialAsync(placement: placement, callback: callback)
259
+ }
260
+ }
261
+
262
+ private func preloadInterstitialAsync(
263
+ placement: PlacementConfig,
264
+ callback: InterstitialCallback
265
+ ) async {
266
+ interstitialLock.lock()
267
+ // Check if already cached
268
+ if interstitialCache[placement.placementId] != nil {
269
+ interstitialLock.unlock()
270
+ BCLogger.debug("\(AdOrchestrator.TAG): Interstitial already cached: \(placement.placementId)")
271
+ callback.onAdLoaded()
272
+ return
273
+ }
274
+ interstitialLock.unlock()
275
+
276
+ // Get GAM network code from config
277
+ guard let gamNetworkCode = configManager.getGamNetworkCode() else {
278
+ BCLogger.error("\(AdOrchestrator.TAG): GAM network code not available")
279
+ callback.onAdFailedToLoad(error: "Config not loaded")
280
+ return
281
+ }
282
+
283
+ // Create ad request
284
+ let adRequest = GoogleMobileAds.Request()
285
+
286
+ // 3. Fetch S2S demand
287
+ let targeting = await bidRequestClient.fetchDemand(placement: placement)
288
+ if let targeting = targeting, !targeting.isEmpty {
289
+ var customTargeting = adRequest.customTargeting ?? [:]
290
+ for (key, value) in targeting {
291
+ customTargeting[key] = value
292
+ }
293
+ adRequest.customTargeting = customTargeting
294
+ BCLogger.debug("\(AdOrchestrator.TAG): S2S demand fetched for: \(placement.placementId) (\(targeting.count) keys)")
295
+ } else {
296
+ BCLogger.warning("\(AdOrchestrator.TAG): S2S demand fetch returned no targeting, continuing with Google only")
297
+ }
298
+
299
+ // Check if we should use test ad units
300
+ let useTestAds = configManager.shouldUseTestAds()
301
+
302
+ // 4. Load Google interstitial ad
303
+ let result = await googleAdsAdapter.loadInterstitialAd(
304
+ placementConfig: placement,
305
+ gamNetworkCode: gamNetworkCode,
306
+ request: adRequest,
307
+ useTestAds: useTestAds
308
+ )
309
+
310
+ switch result {
311
+ case .success(let interstitialAd):
312
+ interstitialLock.lock()
313
+ interstitialCache[placement.placementId] = interstitialAd
314
+ interstitialLock.unlock()
315
+ BCLogger.debug("\(AdOrchestrator.TAG): Interstitial ad preloaded: \(placement.placementId)")
316
+ callback.onAdLoaded()
317
+
318
+ case .failure(let error):
319
+ BCLogger.warning("\(AdOrchestrator.TAG): Interstitial ad failed to load: \(placement.placementId) - \(error.localizedDescription)")
320
+ callback.onAdFailedToLoad(error: error.localizedDescription)
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Show an interstitial ad
326
+ *
327
+ * - Parameters:
328
+ * - viewController: The view controller to present the interstitial from
329
+ * - placementId: The placement ID from the BigCrunch dashboard
330
+ * - callback: Callback for ad events
331
+ * - Returns: true if an ad was available and shown, false otherwise
332
+ */
333
+ func showInterstitialAd(
334
+ from viewController: UIViewController,
335
+ placementId: String,
336
+ callback: InterstitialCallback
337
+ ) -> Bool {
338
+ BCLogger.debug("\(AdOrchestrator.TAG): Showing interstitial ad: \(placementId)")
339
+
340
+ // Get placement config
341
+ guard let placement = configManager.getPlacement(placementId) else {
342
+ BCLogger.error("\(AdOrchestrator.TAG): Placement not found: \(placementId)")
343
+ callback.onAdFailedToLoad(error: "Placement not found: \(placementId)")
344
+ return false
345
+ }
346
+
347
+ // Get cached interstitial
348
+ interstitialLock.lock()
349
+ guard let interstitialAd = interstitialCache.removeValue(forKey: placementId) else {
350
+ interstitialLock.unlock()
351
+ BCLogger.warning("\(AdOrchestrator.TAG): No preloaded interstitial for: \(placementId)")
352
+ callback.onAdFailedToLoad(error: "No preloaded ad available")
353
+ return false
354
+ }
355
+ interstitialLock.unlock()
356
+
357
+ // Create callback wrapper and store it
358
+ let callbackWrapper = InterstitialCallbackWrapper(
359
+ placementId: placementId,
360
+ callback: callback
361
+ )
362
+ interstitialCallbackWrappers[placementId] = callbackWrapper
363
+
364
+ // Create adapter delegate wrapper
365
+ let delegateWrapper = InterstitialDelegateAdapterWrapper(
366
+ placementId: placementId,
367
+ callbackWrapper: callbackWrapper
368
+ )
369
+ callbackWrapper.delegateWrapper = delegateWrapper
370
+
371
+ // Show the ad
372
+ googleAdsAdapter.showInterstitialAd(
373
+ interstitialAd,
374
+ from: viewController,
375
+ placementConfig: placement,
376
+ delegate: delegateWrapper
377
+ )
378
+
379
+ return true
380
+ }
381
+
382
+ /**
383
+ * Check if an interstitial ad is ready to show
384
+ *
385
+ * - Parameter placementId: The placement ID to check
386
+ * - Returns: true if an ad is preloaded and ready
387
+ */
388
+ func isInterstitialReady(placementId: String) -> Bool {
389
+ interstitialLock.lock()
390
+ defer { interstitialLock.unlock() }
391
+ return interstitialCache[placementId] != nil
392
+ }
393
+
394
+ /**
395
+ * Clear all cached ads
396
+ */
397
+ func clearCache() {
398
+ BCLogger.debug("\(AdOrchestrator.TAG): Clearing ad cache")
399
+
400
+ interstitialLock.lock()
401
+ interstitialCache.removeAll()
402
+ interstitialLock.unlock()
403
+
404
+ // Destroy all active banners
405
+ bannerLock.lock()
406
+ for bannerView in activeBanners.values {
407
+ googleAdsAdapter.destroyBannerAd(bannerView)
408
+ }
409
+ activeBanners.removeAll()
410
+ bannerLock.unlock()
411
+
412
+ // Clear callback wrappers
413
+ bannerCallbackWrappers.removeAll()
414
+ interstitialCallbackWrappers.removeAll()
415
+ }
416
+ }
417
+
418
+ // MARK: - Callback Wrapper Classes
419
+
420
+ /**
421
+ * Wrapper to hold banner callback and prevent deallocation
422
+ */
423
+ private class BannerCallbackWrapper {
424
+ let placementId: String
425
+ weak var callback: BannerCallback?
426
+ var delegateWrapper: BannerDelegateAdapterWrapper?
427
+
428
+ init(placementId: String, callback: BannerCallback) {
429
+ self.placementId = placementId
430
+ self.callback = callback
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Wrapper to hold interstitial callback and prevent deallocation
436
+ */
437
+ private class InterstitialCallbackWrapper {
438
+ let placementId: String
439
+ weak var callback: InterstitialCallback?
440
+ var delegateWrapper: InterstitialDelegateAdapterWrapper?
441
+
442
+ init(placementId: String, callback: InterstitialCallback) {
443
+ self.placementId = placementId
444
+ self.callback = callback
445
+ }
446
+ }
447
+
448
+ // MARK: - Delegate Adapter Wrappers
449
+
450
+ /**
451
+ * Adapts BannerAdDelegate to BannerCallback
452
+ */
453
+ private class BannerDelegateAdapterWrapper: BannerAdDelegate {
454
+ let placementId: String
455
+ weak var callbackWrapper: BannerCallbackWrapper?
456
+
457
+ init(placementId: String, callbackWrapper: BannerCallbackWrapper) {
458
+ self.placementId = placementId
459
+ self.callbackWrapper = callbackWrapper
460
+ }
461
+
462
+ func bannerAdDidLoad(_ bannerView: GoogleMobileAds.BannerView) {
463
+ callbackWrapper?.callback?.onAdLoaded()
464
+ }
465
+
466
+ func bannerAd(_ bannerView: GoogleMobileAds.BannerView, didFailToLoadWithError error: Error) {
467
+ callbackWrapper?.callback?.onAdFailedToLoad(error: error.localizedDescription)
468
+ }
469
+
470
+ func bannerAdDidRecordClick(_ bannerView: GoogleMobileAds.BannerView) {
471
+ callbackWrapper?.callback?.onAdClicked()
472
+ }
473
+
474
+ func bannerAdDidRecordImpression(_ bannerView: GoogleMobileAds.BannerView) {
475
+ callbackWrapper?.callback?.onAdImpression()
476
+ }
477
+ }
478
+
479
+ /**
480
+ * Adapts InterstitialAdDelegate to InterstitialCallback
481
+ */
482
+ private class InterstitialDelegateAdapterWrapper: InterstitialAdDelegate {
483
+ let placementId: String
484
+ weak var callbackWrapper: InterstitialCallbackWrapper?
485
+
486
+ init(placementId: String, callbackWrapper: InterstitialCallbackWrapper) {
487
+ self.placementId = placementId
488
+ self.callbackWrapper = callbackWrapper
489
+ }
490
+
491
+ func interstitialAdDidLoad(_ interstitialAd: GoogleMobileAds.InterstitialAd) {
492
+ callbackWrapper?.callback?.onAdLoaded()
493
+ }
494
+
495
+ func interstitialAd(didFailToLoadWithError error: Error) {
496
+ callbackWrapper?.callback?.onAdFailedToLoad(error: error.localizedDescription)
497
+ }
498
+
499
+ func interstitialAdDidPresent(_ interstitialAd: GoogleMobileAds.InterstitialAd) {
500
+ callbackWrapper?.callback?.onAdShowed()
501
+ }
502
+
503
+ func interstitialAdDidDismiss(_ interstitialAd: GoogleMobileAds.InterstitialAd) {
504
+ callbackWrapper?.callback?.onAdDismissed()
505
+ }
506
+
507
+ func interstitialAdDidRecordClick(_ interstitialAd: GoogleMobileAds.InterstitialAd) {
508
+ callbackWrapper?.callback?.onAdClicked()
509
+ }
510
+
511
+ func interstitialAdDidRecordImpression(_ interstitialAd: GoogleMobileAds.InterstitialAd) {
512
+ // Impression tracked by GoogleAdsAdapter
513
+ }
514
+ }