@bigcrunch/react-native-ads 0.1.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 (52) hide show
  1. package/README.md +417 -0
  2. package/android/build.gradle +70 -0
  3. package/android/settings.gradle +6 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/com/bigcrunch/ads/react/BigCrunchAdsModule.kt +653 -0
  6. package/android/src/main/java/com/bigcrunch/ads/react/BigCrunchAdsPackage.kt +20 -0
  7. package/android/src/main/java/com/bigcrunch/ads/react/BigCrunchBannerViewManager.kt +296 -0
  8. package/ios/BigCrunchAdsModule.swift +588 -0
  9. package/ios/BigCrunchBannerViewManager.swift +270 -0
  10. package/ios/react-native-bigcrunch-ads-Bridging-Header.h +8 -0
  11. package/lib/BigCrunchAds.d.ts +168 -0
  12. package/lib/BigCrunchAds.d.ts.map +1 -0
  13. package/lib/BigCrunchAds.js +241 -0
  14. package/lib/BigCrunchBannerView.d.ts +21 -0
  15. package/lib/BigCrunchBannerView.d.ts.map +1 -0
  16. package/lib/BigCrunchBannerView.js +176 -0
  17. package/lib/BigCrunchInterstitial.d.ts +66 -0
  18. package/lib/BigCrunchInterstitial.d.ts.map +1 -0
  19. package/lib/BigCrunchInterstitial.js +222 -0
  20. package/lib/BigCrunchRewarded.d.ts +85 -0
  21. package/lib/BigCrunchRewarded.d.ts.map +1 -0
  22. package/lib/BigCrunchRewarded.js +257 -0
  23. package/lib/NativeBigCrunchAds.d.ts +71 -0
  24. package/lib/NativeBigCrunchAds.d.ts.map +1 -0
  25. package/lib/NativeBigCrunchAds.js +54 -0
  26. package/lib/index.d.ts +28 -0
  27. package/lib/index.d.ts.map +1 -0
  28. package/lib/index.js +32 -0
  29. package/lib/types/ads.d.ts +101 -0
  30. package/lib/types/ads.d.ts.map +1 -0
  31. package/lib/types/ads.js +4 -0
  32. package/lib/types/config.d.ts +137 -0
  33. package/lib/types/config.d.ts.map +1 -0
  34. package/lib/types/config.js +4 -0
  35. package/lib/types/events.d.ts +306 -0
  36. package/lib/types/events.d.ts.map +1 -0
  37. package/lib/types/events.js +4 -0
  38. package/lib/types/index.d.ts +175 -0
  39. package/lib/types/index.d.ts.map +1 -0
  40. package/lib/types/index.js +23 -0
  41. package/package.json +88 -0
  42. package/react-native-bigcrunch-ads.podspec +27 -0
  43. package/src/BigCrunchAds.ts +298 -0
  44. package/src/BigCrunchBannerView.tsx +262 -0
  45. package/src/BigCrunchInterstitial.ts +266 -0
  46. package/src/BigCrunchRewarded.ts +307 -0
  47. package/src/NativeBigCrunchAds.ts +120 -0
  48. package/src/index.ts +71 -0
  49. package/src/types/ads.ts +112 -0
  50. package/src/types/config.ts +145 -0
  51. package/src/types/events.ts +337 -0
  52. package/src/types/index.ts +193 -0
@@ -0,0 +1,588 @@
1
+ import Foundation
2
+ import BigCrunchAds
3
+ #if canImport(React)
4
+ import React
5
+ #endif
6
+
7
+ // MARK: - Interstitial Delegate
8
+
9
+ class InterstitialDelegate: NSObject, BigCrunchInterstitialDelegate {
10
+ weak var module: BigCrunchAdsModule?
11
+ let placementId: String
12
+
13
+ init(module: BigCrunchAdsModule, placementId: String) {
14
+ self.module = module
15
+ self.placementId = placementId
16
+ }
17
+
18
+ func interstitialDidShow(placementId: String) {
19
+ if module?.hasListeners == true {
20
+ module?.sendEvent(withName: "BigCrunchInterstitialAdOpened", body: [
21
+ "placementId": placementId
22
+ ])
23
+ // Also emit impression event
24
+ module?.sendEvent(withName: "BigCrunchInterstitialAdImpression", body: [
25
+ "placementId": placementId
26
+ ])
27
+ }
28
+ }
29
+
30
+ func interstitialDidDismiss(placementId: String) {
31
+ if module?.hasListeners == true {
32
+ module?.sendEvent(withName: "BigCrunchInterstitialAdClosed", body: [
33
+ "placementId": placementId
34
+ ])
35
+ }
36
+ // Clear from cache
37
+ module?.interstitialCache.removeValue(forKey: placementId)
38
+ }
39
+
40
+ func interstitialDidClick(placementId: String) {
41
+ if module?.hasListeners == true {
42
+ module?.sendEvent(withName: "BigCrunchInterstitialAdClicked", body: [
43
+ "placementId": placementId
44
+ ])
45
+ }
46
+ }
47
+
48
+ func interstitialDidFail(placementId: String, error: String) {
49
+ if module?.hasListeners == true {
50
+ module?.sendEvent(withName: "BigCrunchInterstitialAdFailedToShow", body: [
51
+ "placementId": placementId,
52
+ "errorCode": "SHOW_FAILED",
53
+ "errorMessage": error
54
+ ])
55
+ }
56
+ }
57
+ }
58
+
59
+ // MARK: - Rewarded Delegate
60
+
61
+ class RewardedDelegate: NSObject, BigCrunchRewardedDelegate {
62
+ weak var module: BigCrunchAdsModule?
63
+ let placementId: String
64
+
65
+ init(module: BigCrunchAdsModule, placementId: String) {
66
+ self.module = module
67
+ self.placementId = placementId
68
+ }
69
+
70
+ func rewardedDidShow(placementId: String) {
71
+ if module?.hasListeners == true {
72
+ module?.sendEvent(withName: "BigCrunchRewardedAdOpened", body: [
73
+ "placementId": placementId
74
+ ])
75
+ // Also emit impression event
76
+ module?.sendEvent(withName: "BigCrunchRewardedAdImpression", body: [
77
+ "placementId": placementId
78
+ ])
79
+ }
80
+ }
81
+
82
+ func rewardedDidDismiss(placementId: String) {
83
+ if module?.hasListeners == true {
84
+ module?.sendEvent(withName: "BigCrunchRewardedAdClosed", body: [
85
+ "placementId": placementId
86
+ ])
87
+ }
88
+ // Clear from cache and delegate
89
+ module?.rewardedCache.removeValue(forKey: placementId)
90
+ module?.rewardedDelegates.removeValue(forKey: placementId)
91
+ }
92
+
93
+ func rewardedDidClick(placementId: String) {
94
+ if module?.hasListeners == true {
95
+ module?.sendEvent(withName: "BigCrunchRewardedAdClicked", body: [
96
+ "placementId": placementId
97
+ ])
98
+ }
99
+ }
100
+
101
+ func rewardedDidEarnReward(placementId: String, type: String, amount: Int) {
102
+ if module?.hasListeners == true {
103
+ module?.sendEvent(withName: "BigCrunchRewardedAdEarned", body: [
104
+ "placementId": placementId,
105
+ "type": type,
106
+ "amount": amount
107
+ ])
108
+ }
109
+ }
110
+
111
+ func rewardedDidFail(placementId: String, error: String) {
112
+ if module?.hasListeners == true {
113
+ module?.sendEvent(withName: "BigCrunchRewardedAdFailedToShow", body: [
114
+ "placementId": placementId,
115
+ "errorCode": "SHOW_FAILED",
116
+ "errorMessage": error
117
+ ])
118
+ }
119
+ // Clean up delegate on failure
120
+ module?.rewardedDelegates.removeValue(forKey: placementId)
121
+ }
122
+ }
123
+
124
+ @objc(BigCrunchAdsModule)
125
+ class BigCrunchAdsModule: RCTEventEmitter {
126
+
127
+ // Made internal for delegate access
128
+ var hasListeners = false
129
+ var interstitialCache: [String: Any] = [:]
130
+ var rewardedCache: [String: Any] = [:]
131
+ private var interstitialDelegates: [String: InterstitialDelegate] = [:]
132
+ var rewardedDelegates: [String: RewardedDelegate] = [:]
133
+
134
+ override static func moduleName() -> String! {
135
+ return "BigCrunchAdsModule"
136
+ }
137
+
138
+ override static func requiresMainQueueSetup() -> Bool {
139
+ return true
140
+ }
141
+
142
+ override func supportedEvents() -> [String]! {
143
+ return [
144
+ // Banner events - matching TypeScript NativeEventNames
145
+ "BigCrunchBannerAdLoaded",
146
+ "BigCrunchBannerAdFailedToLoad",
147
+ "BigCrunchBannerAdImpression",
148
+ "BigCrunchBannerAdClicked",
149
+ "BigCrunchBannerAdOpened",
150
+ "BigCrunchBannerAdClosed",
151
+ "BigCrunchBannerAdRevenue",
152
+ "BigCrunchBannerAdViewable",
153
+
154
+ // Interstitial events - matching TypeScript NativeEventNames
155
+ "BigCrunchInterstitialAdLoaded",
156
+ "BigCrunchInterstitialAdFailedToLoad",
157
+ "BigCrunchInterstitialAdOpened",
158
+ "BigCrunchInterstitialAdImpression",
159
+ "BigCrunchInterstitialAdClicked",
160
+ "BigCrunchInterstitialAdClosed",
161
+ "BigCrunchInterstitialAdFailedToShow",
162
+ "BigCrunchInterstitialAdRevenue",
163
+
164
+ // Rewarded events - matching TypeScript NativeEventNames
165
+ "BigCrunchRewardedAdLoaded",
166
+ "BigCrunchRewardedAdFailedToLoad",
167
+ "BigCrunchRewardedAdOpened",
168
+ "BigCrunchRewardedAdImpression",
169
+ "BigCrunchRewardedAdClicked",
170
+ "BigCrunchRewardedAdClosed",
171
+ "BigCrunchRewardedAdEarned",
172
+ "BigCrunchRewardedAdFailedToShow",
173
+ "BigCrunchRewardedAdRevenue",
174
+
175
+ // Configuration events
176
+ "BigCrunchConfigUpdated",
177
+ "BigCrunchConfigFailed",
178
+
179
+ // Session events
180
+ "BigCrunchSessionStarted",
181
+ "BigCrunchSessionEnded"
182
+ ]
183
+ }
184
+
185
+ override func startObserving() {
186
+ hasListeners = true
187
+ }
188
+
189
+ override func stopObserving() {
190
+ hasListeners = false
191
+ }
192
+
193
+ // MARK: - Initialization
194
+
195
+ @objc(initialize:resolver:rejecter:)
196
+ func initialize(options: NSDictionary,
197
+ resolver: @escaping RCTPromiseResolveBlock,
198
+ rejecter: @escaping RCTPromiseRejectBlock) {
199
+ guard let propertyId = options["propertyId"] as? String,
200
+ let apiKey = options["apiKey"] as? String else {
201
+ rejecter("INIT_ERROR", "propertyId and apiKey are required", nil)
202
+ return
203
+ }
204
+
205
+ let environment = options["environment"] as? String ?? "production"
206
+ let debug = options["debug"] as? Bool ?? false
207
+ let useMockConfig = options["useMockConfig"] as? Bool ?? false
208
+
209
+ DispatchQueue.main.async {
210
+ // Map environment strings to SDK enum - "sandbox" maps to .staging
211
+ let env: BigCrunchEnv = environment == "sandbox" ? .staging : .prod
212
+
213
+ BigCrunchAds.initialize(
214
+ propertyId: propertyId,
215
+ apiKey: apiKey,
216
+ env: env,
217
+ useMockConfig: useMockConfig
218
+ )
219
+
220
+ // Track that we've initialized
221
+ BigCrunchAdsModule.isSDKInitialized = true
222
+
223
+ // Note: debug mode is handled by SDK based on env (.staging enables verbose logging)
224
+ resolver(nil)
225
+ }
226
+ }
227
+
228
+ private static var isSDKInitialized = false
229
+
230
+ @objc(isInitialized:rejecter:)
231
+ func isInitialized(resolver: RCTPromiseResolveBlock,
232
+ rejecter: RCTPromiseRejectBlock) {
233
+ // iOS SDK doesn't expose isInitialized() publicly yet
234
+ // Return true if we've called initialize (tracked locally)
235
+ resolver(BigCrunchAdsModule.isSDKInitialized)
236
+ }
237
+
238
+ // MARK: - Screen Tracking
239
+
240
+ @objc(trackScreenView:resolver:rejecter:)
241
+ func trackScreenView(screenName: String,
242
+ resolver: @escaping RCTPromiseResolveBlock,
243
+ rejecter: @escaping RCTPromiseRejectBlock) {
244
+ DispatchQueue.main.async {
245
+ BigCrunchAds.trackScreen(screenName)
246
+ resolver(nil)
247
+ }
248
+ }
249
+
250
+ // MARK: - Configuration
251
+
252
+ @objc(getAppConfig:rejecter:)
253
+ func getAppConfig(resolver: @escaping RCTPromiseResolveBlock,
254
+ rejecter: @escaping RCTPromiseRejectBlock) {
255
+ if let config = BigCrunchAds.getAppConfig() {
256
+ // Convert AppConfig to dictionary
257
+ let configDict: [String: Any] = [
258
+ "propertyId": config.propertyId,
259
+ "appName": config.appName,
260
+ "environment": config.environment,
261
+ "gamNetworkCode": config.gamNetworkCode,
262
+ "prebid": [
263
+ "serverUrl": config.prebid.serverUrl,
264
+ "timeoutMs": config.prebid.timeoutMs
265
+ ],
266
+ "placements": config.placements.map { placement in
267
+ var placementDict: [String: Any] = [
268
+ "placementId": placement.placementId,
269
+ "format": placement.format,
270
+ "gamAdUnit": placement.gamAdUnit
271
+ ]
272
+ if let sizes = placement.sizes {
273
+ placementDict["sizes"] = sizes.map { ["width": $0.width, "height": $0.height] }
274
+ }
275
+ if let prebidConfigId = placement.prebidConfigId {
276
+ placementDict["prebidConfigId"] = prebidConfigId
277
+ }
278
+ return placementDict
279
+ }
280
+ ]
281
+ resolver(configDict)
282
+ } else {
283
+ resolver(nil)
284
+ }
285
+ }
286
+
287
+ @objc(refreshConfig:rejecter:)
288
+ func refreshConfig(resolver: @escaping RCTPromiseResolveBlock,
289
+ rejecter: @escaping RCTPromiseRejectBlock) {
290
+ Task {
291
+ await BigCrunchAds.refreshConfig()
292
+ DispatchQueue.main.async {
293
+ resolver(nil)
294
+ }
295
+ }
296
+ }
297
+
298
+ // MARK: - Interstitial Ads
299
+
300
+ @objc(loadInterstitial:resolver:rejecter:)
301
+ func loadInterstitial(options: NSDictionary,
302
+ resolver: @escaping RCTPromiseResolveBlock,
303
+ rejecter: @escaping RCTPromiseRejectBlock) {
304
+ guard let placementId = options["placementId"] as? String else {
305
+ rejecter("LOAD_ERROR", "placementId is required", nil)
306
+ return
307
+ }
308
+
309
+ let customTargeting = options["customTargeting"] as? [String: String]
310
+
311
+ // Use preload with completion handler to emit events
312
+ BigCrunchInterstitial.preload(placementId: placementId) { [weak self] result in
313
+ switch result {
314
+ case .success:
315
+ // Store in cache and emit loaded event
316
+ self?.interstitialCache[placementId] = true
317
+
318
+ if self?.hasListeners == true {
319
+ self?.sendEvent(withName: "BigCrunchInterstitialAdLoaded", body: [
320
+ "placementId": placementId
321
+ ])
322
+ }
323
+ resolver(nil)
324
+
325
+ case .failure(let error):
326
+ // Emit failed event
327
+ if self?.hasListeners == true {
328
+ self?.sendEvent(withName: "BigCrunchInterstitialAdFailedToLoad", body: [
329
+ "placementId": placementId,
330
+ "errorCode": "LOAD_FAILED",
331
+ "errorMessage": error.localizedDescription
332
+ ])
333
+ }
334
+ rejecter("LOAD_ERROR", "Failed to load interstitial: \(error.localizedDescription)", error)
335
+ }
336
+ }
337
+ }
338
+
339
+ @objc(showInterstitial:resolver:rejecter:)
340
+ func showInterstitial(placementId: String,
341
+ resolver: @escaping RCTPromiseResolveBlock,
342
+ rejecter: @escaping RCTPromiseRejectBlock) {
343
+ DispatchQueue.main.async { [weak self] in
344
+ guard let self = self else { return }
345
+ guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else {
346
+ rejecter("NO_VIEWCONTROLLER", "No root view controller available", nil)
347
+ return
348
+ }
349
+
350
+ // Create and store delegate
351
+ let delegate = InterstitialDelegate(module: self, placementId: placementId)
352
+ self.interstitialDelegates[placementId] = delegate
353
+
354
+ // Show with delegate
355
+ let success = BigCrunchInterstitial.show(
356
+ from: rootViewController,
357
+ placementId: placementId,
358
+ delegate: delegate
359
+ )
360
+
361
+ if success {
362
+ resolver(nil)
363
+ } else {
364
+ // Clean up delegate if show failed immediately
365
+ self.interstitialDelegates.removeValue(forKey: placementId)
366
+ rejecter("SHOW_ERROR", "Failed to show interstitial: Ad not loaded", nil)
367
+ }
368
+ }
369
+ }
370
+
371
+ @objc(isInterstitialLoaded:resolver:rejecter:)
372
+ func isInterstitialLoaded(placementId: String,
373
+ resolver: RCTPromiseResolveBlock,
374
+ rejecter: RCTPromiseRejectBlock) {
375
+ resolver(BigCrunchInterstitial.isReady(placementId: placementId))
376
+ }
377
+
378
+ @objc(destroyInterstitial:resolver:rejecter:)
379
+ func destroyInterstitial(placementId: String,
380
+ resolver: RCTPromiseResolveBlock,
381
+ rejecter: RCTPromiseRejectBlock) {
382
+ // iOS SDK uses clearCache() instead of per-placement destroy
383
+ // Just clear our local cache for now
384
+ interstitialCache.removeValue(forKey: placementId)
385
+ resolver(nil)
386
+ }
387
+
388
+ // MARK: - Rewarded Ads
389
+
390
+ @objc(loadRewarded:resolver:rejecter:)
391
+ func loadRewarded(options: NSDictionary,
392
+ resolver: @escaping RCTPromiseResolveBlock,
393
+ rejecter: @escaping RCTPromiseRejectBlock) {
394
+ guard let placementId = options["placementId"] as? String else {
395
+ rejecter("LOAD_ERROR", "placementId is required", nil)
396
+ return
397
+ }
398
+
399
+ // customTargeting can be used in future for custom Prebid targeting
400
+ // let customTargeting = options["customTargeting"] as? [String: String]
401
+
402
+ BigCrunchRewarded.preload(placementId: placementId) { [weak self] result in
403
+ switch result {
404
+ case .success:
405
+ // Mark as loaded in our cache
406
+ self?.rewardedCache[placementId] = true
407
+ if self?.hasListeners == true {
408
+ self?.sendEvent(withName: "BigCrunchRewardedAdLoaded", body: [
409
+ "placementId": placementId
410
+ ])
411
+ }
412
+ resolver(nil)
413
+ case .failure(let error):
414
+ if self?.hasListeners == true {
415
+ self?.sendEvent(withName: "BigCrunchRewardedAdFailedToLoad", body: [
416
+ "placementId": placementId,
417
+ "errorCode": "LOAD_FAILED",
418
+ "errorMessage": error.localizedDescription
419
+ ])
420
+ }
421
+ rejecter("LOAD_ERROR", error.localizedDescription, error)
422
+ }
423
+ }
424
+ }
425
+
426
+ @objc(showRewarded:resolver:rejecter:)
427
+ func showRewarded(placementId: String,
428
+ resolver: @escaping RCTPromiseResolveBlock,
429
+ rejecter: @escaping RCTPromiseRejectBlock) {
430
+ DispatchQueue.main.async { [weak self] in
431
+ guard let self = self else {
432
+ rejecter("SHOW_ERROR", "Module deallocated", nil)
433
+ return
434
+ }
435
+
436
+ guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else {
437
+ rejecter("SHOW_ERROR", "No root view controller available", nil)
438
+ return
439
+ }
440
+
441
+ // Create and store delegate
442
+ let delegate = RewardedDelegate(module: self, placementId: placementId)
443
+ self.rewardedDelegates[placementId] = delegate
444
+
445
+ // Show with delegate
446
+ let success = BigCrunchRewarded.show(
447
+ from: rootViewController,
448
+ placementId: placementId,
449
+ delegate: delegate
450
+ )
451
+
452
+ if success {
453
+ resolver(nil)
454
+ } else {
455
+ // Clean up delegate if show failed immediately
456
+ self.rewardedDelegates.removeValue(forKey: placementId)
457
+ rejecter("SHOW_ERROR", "Failed to show rewarded: Ad not loaded", nil)
458
+ }
459
+ }
460
+ }
461
+
462
+ @objc(isRewardedLoaded:resolver:rejecter:)
463
+ func isRewardedLoaded(placementId: String,
464
+ resolver: RCTPromiseResolveBlock,
465
+ rejecter: RCTPromiseRejectBlock) {
466
+ resolver(BigCrunchRewarded.isReady(placementId: placementId))
467
+ }
468
+
469
+ @objc(destroyRewarded:resolver:rejecter:)
470
+ func destroyRewarded(placementId: String,
471
+ resolver: RCTPromiseResolveBlock,
472
+ rejecter: RCTPromiseRejectBlock) {
473
+ // iOS SDK doesn't have per-placement destroy for rewarded
474
+ // Just clear our local cache for now
475
+ rewardedCache.removeValue(forKey: placementId)
476
+ resolver(nil)
477
+ }
478
+
479
+ // MARK: - Session Management
480
+
481
+ @objc(getSessionInfo:rejecter:)
482
+ func getSessionInfo(resolver: @escaping RCTPromiseResolveBlock,
483
+ rejecter: @escaping RCTPromiseRejectBlock) {
484
+ let info = BigCrunchAds.getSessionInfo()
485
+ let sessionInfo: [String: Any] = [
486
+ "sessionId": info.sessionId,
487
+ "userId": info.userId,
488
+ "startTime": info.startTime,
489
+ "screenViewCount": info.screenViewCount,
490
+ "adRequestCount": info.adRequestCount,
491
+ "adImpressionCount": info.adImpressionCount,
492
+ "totalRevenueMicros": info.totalRevenueMicros
493
+ ]
494
+ resolver(sessionInfo)
495
+ }
496
+
497
+ @objc(startNewSession:rejecter:)
498
+ func startNewSession(resolver: @escaping RCTPromiseResolveBlock,
499
+ rejecter: @escaping RCTPromiseRejectBlock) {
500
+ BigCrunchAds.startNewSession()
501
+ resolver(nil)
502
+ }
503
+
504
+ // MARK: - Device Context
505
+
506
+ @objc(getDeviceContext:rejecter:)
507
+ func getDeviceContext(resolver: @escaping RCTPromiseResolveBlock,
508
+ rejecter: @escaping RCTPromiseRejectBlock) {
509
+ let data = BigCrunchAds.getDeviceData()
510
+ let deviceContext: [String: Any] = [
511
+ "os": "iOS",
512
+ "osVersion": data.osVersion,
513
+ "deviceModel": data.deviceModel,
514
+ "screenWidth": data.screenWidth,
515
+ "screenHeight": data.screenHeight,
516
+ "screenDensity": UIScreen.main.scale,
517
+ "appVersion": data.appVersion,
518
+ "sdkVersion": "0.1.0",
519
+ "locale": "\(data.language)_\(data.country)",
520
+ "timezone": TimeZone.current.identifier,
521
+ "isTablet": data.isTablet,
522
+ "deviceId": data.deviceId
523
+ ]
524
+ resolver(deviceContext)
525
+ }
526
+
527
+ // MARK: - Privacy
528
+
529
+ @objc(setGdprConsent:resolver:rejecter:)
530
+ func setGdprConsent(consent: String,
531
+ resolver: RCTPromiseResolveBlock,
532
+ rejecter: RCTPromiseRejectBlock) {
533
+ BigCrunchAds.setGdprConsent(consent)
534
+ resolver(nil)
535
+ }
536
+
537
+ @objc(setCcpaString:resolver:rejecter:)
538
+ func setCcpaString(ccpaString: String,
539
+ resolver: RCTPromiseResolveBlock,
540
+ rejecter: RCTPromiseRejectBlock) {
541
+ BigCrunchAds.setCcpaString(ccpaString)
542
+ resolver(nil)
543
+ }
544
+
545
+ @objc(setCoppaCompliant:resolver:rejecter:)
546
+ func setCoppaCompliant(isCompliant: Bool,
547
+ resolver: RCTPromiseResolveBlock,
548
+ rejecter: RCTPromiseRejectBlock) {
549
+ BigCrunchAds.setCoppaCompliant(isCompliant)
550
+ resolver(nil)
551
+ }
552
+
553
+ // MARK: - Debug & Testing
554
+
555
+ @objc(setDebugMode:resolver:rejecter:)
556
+ func setDebugMode(enabled: Bool,
557
+ resolver: RCTPromiseResolveBlock,
558
+ rejecter: RCTPromiseRejectBlock) {
559
+ BigCrunchAds.setDebugMode(enabled)
560
+ resolver(nil)
561
+ }
562
+
563
+ @objc(addTestDevice:resolver:rejecter:)
564
+ func addTestDevice(deviceId: String,
565
+ resolver: RCTPromiseResolveBlock,
566
+ rejecter: RCTPromiseRejectBlock) {
567
+ BigCrunchAds.addTestDevice(deviceId)
568
+ resolver(nil)
569
+ }
570
+
571
+ @objc(removeTestDevice:resolver:rejecter:)
572
+ func removeTestDevice(deviceId: String,
573
+ resolver: RCTPromiseResolveBlock,
574
+ rejecter: RCTPromiseRejectBlock) {
575
+ BigCrunchAds.removeTestDevice(deviceId)
576
+ resolver(nil)
577
+ }
578
+
579
+ @objc(getTestDevices:rejecter:)
580
+ func getTestDevices(resolver: RCTPromiseResolveBlock,
581
+ rejecter: RCTPromiseRejectBlock) {
582
+ resolver(BigCrunchAds.getTestDevices())
583
+ }
584
+
585
+ // MARK: - Helper Methods
586
+ // Note: convertAppConfigToDictionary and convertPlacementConfigToDictionary
587
+ // are kept for future use when iOS SDK exposes getAppConfig
588
+ }