@billdog.io/react-native 1.0.0-beta.2
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/BillDogReactNative.podspec +30 -0
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/android/build.gradle +99 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
- package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
- package/dist/AdvancedPaywallCache.d.ts +70 -0
- package/dist/AdvancedPaywallCache.js +344 -0
- package/dist/BillDog.d.ts +255 -0
- package/dist/BillDog.js +918 -0
- package/dist/BillDogConfiguration.d.ts +145 -0
- package/dist/BillDogConfiguration.js +229 -0
- package/dist/ComponentRenderer.d.ts +17 -0
- package/dist/ComponentRenderer.js +886 -0
- package/dist/LayoutEngine.d.ts +101 -0
- package/dist/LayoutEngine.js +394 -0
- package/dist/LocalizationResolver.d.ts +99 -0
- package/dist/LocalizationResolver.js +266 -0
- package/dist/NativePaywallView.d.ts +36 -0
- package/dist/NativePaywallView.js +59 -0
- package/dist/PaywallCache.d.ts +74 -0
- package/dist/PaywallCache.js +293 -0
- package/dist/PaywallStateManager.d.ts +62 -0
- package/dist/PaywallStateManager.js +147 -0
- package/dist/PaywallView.d.ts +42 -0
- package/dist/PaywallView.js +275 -0
- package/dist/ProductVariableProvider.d.ts +60 -0
- package/dist/ProductVariableProvider.js +239 -0
- package/dist/VariableResolver.d.ts +120 -0
- package/dist/VariableResolver.js +379 -0
- package/dist/WebViewPaywall.d.ts +75 -0
- package/dist/WebViewPaywall.js +360 -0
- package/dist/animations/presets.d.ts +49 -0
- package/dist/animations/presets.js +367 -0
- package/dist/components/AccoladeCpsComponent.d.ts +13 -0
- package/dist/components/AccoladeCpsComponent.js +248 -0
- package/dist/components/AccordionComponent.d.ts +10 -0
- package/dist/components/AccordionComponent.js +187 -0
- package/dist/components/AnimatedWrapper.d.ts +35 -0
- package/dist/components/AnimatedWrapper.js +277 -0
- package/dist/components/CarouselComponent.d.ts +23 -0
- package/dist/components/CarouselComponent.js +484 -0
- package/dist/components/ChartCurveComponent.d.ts +13 -0
- package/dist/components/ChartCurveComponent.js +333 -0
- package/dist/components/CheckboxComponent.d.ts +9 -0
- package/dist/components/CheckboxComponent.js +172 -0
- package/dist/components/ConfettiComponent.d.ts +9 -0
- package/dist/components/ConfettiComponent.js +172 -0
- package/dist/components/CountdownComponent.d.ts +16 -0
- package/dist/components/CountdownComponent.js +414 -0
- package/dist/components/DelayedXComponent.d.ts +12 -0
- package/dist/components/DelayedXComponent.js +240 -0
- package/dist/components/DrawerModalComponent.d.ts +20 -0
- package/dist/components/DrawerModalComponent.js +286 -0
- package/dist/components/GradientView.d.ts +38 -0
- package/dist/components/GradientView.js +126 -0
- package/dist/components/HapticFeedbackComponent.d.ts +16 -0
- package/dist/components/HapticFeedbackComponent.js +64 -0
- package/dist/components/HorizontalProductsComponent.d.ts +17 -0
- package/dist/components/HorizontalProductsComponent.js +284 -0
- package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
- package/dist/components/MultiChoiceGridComponent.js +234 -0
- package/dist/components/NPSScaleComponent.d.ts +16 -0
- package/dist/components/NPSScaleComponent.js +260 -0
- package/dist/components/NavigationComponent.d.ts +26 -0
- package/dist/components/NavigationComponent.js +302 -0
- package/dist/components/OAuthButtonComponent.d.ts +20 -0
- package/dist/components/OAuthButtonComponent.js +237 -0
- package/dist/components/ProgressLoaderComponent.d.ts +13 -0
- package/dist/components/ProgressLoaderComponent.js +308 -0
- package/dist/components/ProgressStepperComponent.d.ts +18 -0
- package/dist/components/ProgressStepperComponent.js +228 -0
- package/dist/components/SegmentedPickerComponent.d.ts +10 -0
- package/dist/components/SegmentedPickerComponent.js +221 -0
- package/dist/components/ShareButtonsComponent.d.ts +21 -0
- package/dist/components/ShareButtonsComponent.js +265 -0
- package/dist/components/ShareCodeComponent.d.ts +16 -0
- package/dist/components/ShareCodeComponent.js +274 -0
- package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
- package/dist/components/SingleChoiceGridComponent.js +210 -0
- package/dist/components/SliderComponent.d.ts +9 -0
- package/dist/components/SliderComponent.js +263 -0
- package/dist/components/StarRatingInputComponent.d.ts +20 -0
- package/dist/components/StarRatingInputComponent.js +198 -0
- package/dist/components/StickerCompositeComponent.d.ts +13 -0
- package/dist/components/StickerCompositeComponent.js +136 -0
- package/dist/components/TabsComponent.d.ts +19 -0
- package/dist/components/TabsComponent.js +431 -0
- package/dist/components/TextInputComponent.d.ts +18 -0
- package/dist/components/TextInputComponent.js +227 -0
- package/dist/components/TimelineComponent.d.ts +19 -0
- package/dist/components/TimelineComponent.js +104 -0
- package/dist/components/ToggleComponent.d.ts +14 -0
- package/dist/components/ToggleComponent.js +132 -0
- package/dist/components/VerticalProductsComponent.d.ts +17 -0
- package/dist/components/VerticalProductsComponent.js +311 -0
- package/dist/dynamicValues.d.ts +36 -0
- package/dist/dynamicValues.js +142 -0
- package/dist/filters/dateFilters.d.ts +50 -0
- package/dist/filters/dateFilters.js +390 -0
- package/dist/filters/index.d.ts +18 -0
- package/dist/filters/index.js +253 -0
- package/dist/hooks/useOfferings.d.ts +76 -0
- package/dist/hooks/useOfferings.js +99 -0
- package/dist/hooks/usePaywall.d.ts +27 -0
- package/dist/hooks/usePaywall.js +114 -0
- package/dist/hooks/usePlacement.d.ts +34 -0
- package/dist/hooks/usePlacement.js +89 -0
- package/dist/hooks/useSubscription.d.ts +40 -0
- package/dist/hooks/useSubscription.js +97 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +198 -0
- package/dist/models/BillDogError.d.ts +40 -0
- package/dist/models/BillDogError.js +11 -0
- package/dist/models/CacheFetchPolicy.d.ts +8 -0
- package/dist/models/CacheFetchPolicy.js +4 -0
- package/dist/models/CustomProduct.d.ts +32 -0
- package/dist/models/CustomProduct.js +15 -0
- package/dist/models/CustomerInfo.d.ts +17 -0
- package/dist/models/CustomerInfo.js +11 -0
- package/dist/models/Delegate.d.ts +20 -0
- package/dist/models/Delegate.js +11 -0
- package/dist/models/EntitlementInfo.d.ts +19 -0
- package/dist/models/EntitlementInfo.js +2 -0
- package/dist/models/Placement.d.ts +54 -0
- package/dist/models/Placement.js +15 -0
- package/dist/models/PurchaseParams.d.ts +39 -0
- package/dist/models/PurchaseParams.js +60 -0
- package/dist/models/PurchaseResult.d.ts +30 -0
- package/dist/models/PurchaseResult.js +15 -0
- package/dist/models/RestoreResult.d.ts +30 -0
- package/dist/models/RestoreResult.js +22 -0
- package/dist/models/RestoredItem.d.ts +26 -0
- package/dist/models/RestoredItem.js +13 -0
- package/dist/modules/ABTest.d.ts +65 -0
- package/dist/modules/ABTest.js +113 -0
- package/dist/modules/Analytics.d.ts +91 -0
- package/dist/modules/Analytics.js +182 -0
- package/dist/modules/Survey.d.ts +65 -0
- package/dist/modules/Survey.js +82 -0
- package/dist/modules/VirtualCurrency.d.ts +63 -0
- package/dist/modules/VirtualCurrency.js +147 -0
- package/dist/murmur.d.ts +23 -0
- package/dist/murmur.js +104 -0
- package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
- package/dist/notifications/LiveActivitiesManager.js +106 -0
- package/dist/notifications/NotificationScheduler.d.ts +55 -0
- package/dist/notifications/NotificationScheduler.js +199 -0
- package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
- package/dist/notifications/PurchaseNotificationHandler.js +55 -0
- package/dist/notifications/useDeviceToken.d.ts +36 -0
- package/dist/notifications/useDeviceToken.js +97 -0
- package/dist/notifications/useLiveActivity.d.ts +39 -0
- package/dist/notifications/useLiveActivity.js +104 -0
- package/dist/review/ReviewAnalytics.d.ts +34 -0
- package/dist/review/ReviewAnalytics.js +73 -0
- package/dist/review/ReviewManager.d.ts +83 -0
- package/dist/review/ReviewManager.js +339 -0
- package/dist/review/ReviewTimingManager.d.ts +82 -0
- package/dist/review/ReviewTimingManager.js +224 -0
- package/dist/survey-core/types.d.ts +215 -0
- package/dist/survey-core/types.js +8 -0
- package/dist/types.d.ts +438 -0
- package/dist/types.js +158 -0
- package/dist/utils/ApiClient.d.ts +65 -0
- package/dist/utils/ApiClient.js +132 -0
- package/dist/utils/CSSConverter.d.ts +49 -0
- package/dist/utils/CSSConverter.js +470 -0
- package/dist/utils/animations.d.ts +41 -0
- package/dist/utils/animations.js +194 -0
- package/dist/utils/calendarIntegration.d.ts +36 -0
- package/dist/utils/calendarIntegration.js +89 -0
- package/dist/utils/configurationResolver.d.ts +23 -0
- package/dist/utils/configurationResolver.js +78 -0
- package/dist/utils/deviceDetection.d.ts +38 -0
- package/dist/utils/deviceDetection.js +59 -0
- package/dist/utils/hapticFeedback.d.ts +17 -0
- package/dist/utils/hapticFeedback.js +53 -0
- package/dist/utils/iconMapper.d.ts +26 -0
- package/dist/utils/iconMapper.js +113 -0
- package/dist/utils/notificationPermissions.d.ts +13 -0
- package/dist/utils/notificationPermissions.js +51 -0
- package/dist/utils/priceFormatting.d.ts +19 -0
- package/dist/utils/priceFormatting.js +99 -0
- package/dist/utils/shareSheet.d.ts +17 -0
- package/dist/utils/shareSheet.js +42 -0
- package/dist/utils/subscriptionUpgrade.d.ts +84 -0
- package/dist/utils/subscriptionUpgrade.js +100 -0
- package/dist/valueUtils.d.ts +48 -0
- package/dist/valueUtils.js +129 -0
- package/dist/xFlowTags.d.ts +18 -0
- package/dist/xFlowTags.js +385 -0
- package/ios/BillDogABTest.m +50 -0
- package/ios/BillDogABTest.swift +188 -0
- package/ios/BillDogAnalytics.m +105 -0
- package/ios/BillDogAnalytics.swift +324 -0
- package/ios/BillDogBridge.m +222 -0
- package/ios/BillDogBridge.swift +715 -0
- package/ios/BillDogLiveActivities.m +41 -0
- package/ios/BillDogLiveActivities.swift +104 -0
- package/ios/BillDogPaywallViewManager.m +39 -0
- package/ios/BillDogPaywallViewManager.swift +361 -0
- package/ios/BillDogSurvey.m +34 -0
- package/ios/BillDogSurvey.swift +257 -0
- package/ios/BillDogVirtualCurrencyBridge.m +63 -0
- package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
- package/junit.xml +25 -0
- package/package.json +61 -0
- package/react-native.config.js +13 -0
- package/src/AdvancedPaywallCache.ts +433 -0
- package/src/BillDog.ts +983 -0
- package/src/BillDogConfiguration.ts +318 -0
- package/src/ComponentRenderer.tsx +1325 -0
- package/src/LayoutEngine.ts +459 -0
- package/src/LocalizationResolver.ts +311 -0
- package/src/NativePaywallView.tsx +132 -0
- package/src/PaywallCache.ts +358 -0
- package/src/PaywallStateManager.ts +154 -0
- package/src/PaywallView.tsx +328 -0
- package/src/ProductVariableProvider.ts +282 -0
- package/src/VariableResolver.ts +516 -0
- package/src/WebViewPaywall.tsx +454 -0
- package/src/animations/presets.ts +421 -0
- package/src/components/AccoladeCpsComponent.tsx +259 -0
- package/src/components/AccordionComponent.tsx +213 -0
- package/src/components/AnimatedWrapper.tsx +316 -0
- package/src/components/CarouselComponent.tsx +615 -0
- package/src/components/ChartCurveComponent.tsx +452 -0
- package/src/components/CheckboxComponent.tsx +185 -0
- package/src/components/ConfettiComponent.tsx +178 -0
- package/src/components/CountdownComponent.tsx +454 -0
- package/src/components/DelayedXComponent.tsx +266 -0
- package/src/components/DrawerModalComponent.tsx +324 -0
- package/src/components/GradientView.tsx +129 -0
- package/src/components/HapticFeedbackComponent.tsx +39 -0
- package/src/components/HorizontalProductsComponent.tsx +329 -0
- package/src/components/MultiChoiceGridComponent.tsx +265 -0
- package/src/components/NPSScaleComponent.tsx +294 -0
- package/src/components/NavigationComponent.tsx +329 -0
- package/src/components/OAuthButtonComponent.tsx +266 -0
- package/src/components/ProgressLoaderComponent.tsx +366 -0
- package/src/components/ProgressStepperComponent.tsx +250 -0
- package/src/components/SegmentedPickerComponent.tsx +245 -0
- package/src/components/ShareButtonsComponent.tsx +299 -0
- package/src/components/ShareCodeComponent.tsx +303 -0
- package/src/components/SingleChoiceGridComponent.tsx +229 -0
- package/src/components/SliderComponent.tsx +280 -0
- package/src/components/StarRatingInputComponent.tsx +223 -0
- package/src/components/StickerCompositeComponent.tsx +168 -0
- package/src/components/TabsComponent.tsx +496 -0
- package/src/components/TextInputComponent.tsx +258 -0
- package/src/components/TimelineComponent.tsx +129 -0
- package/src/components/ToggleComponent.tsx +142 -0
- package/src/components/VerticalProductsComponent.tsx +357 -0
- package/src/dynamicValues.ts +180 -0
- package/src/filters/dateFilters.ts +411 -0
- package/src/filters/index.ts +281 -0
- package/src/hooks/useOfferings.ts +192 -0
- package/src/hooks/usePaywall.ts +154 -0
- package/src/hooks/usePlacement.ts +143 -0
- package/src/hooks/useSubscription.ts +154 -0
- package/src/index.ts +245 -0
- package/src/models/BillDogError.ts +24 -0
- package/src/models/CacheFetchPolicy.ts +13 -0
- package/src/models/CustomProduct.ts +33 -0
- package/src/models/CustomerInfo.ts +26 -0
- package/src/models/Delegate.ts +22 -0
- package/src/models/EntitlementInfo.ts +27 -0
- package/src/models/Placement.ts +50 -0
- package/src/models/PurchaseParams.ts +81 -0
- package/src/models/PurchaseResult.ts +32 -0
- package/src/models/RestoreResult.ts +27 -0
- package/src/models/RestoredItem.ts +25 -0
- package/src/modules/ABTest.ts +131 -0
- package/src/modules/Analytics.ts +227 -0
- package/src/modules/Survey.ts +126 -0
- package/src/modules/VirtualCurrency.ts +223 -0
- package/src/murmur.ts +102 -0
- package/src/notifications/LiveActivitiesManager.ts +140 -0
- package/src/notifications/NotificationScheduler.ts +292 -0
- package/src/notifications/PurchaseNotificationHandler.ts +67 -0
- package/src/notifications/useDeviceToken.ts +116 -0
- package/src/notifications/useLiveActivity.ts +125 -0
- package/src/review/ReviewAnalytics.ts +89 -0
- package/src/review/ReviewManager.ts +355 -0
- package/src/review/ReviewTimingManager.ts +265 -0
- package/src/survey-core/types.ts +244 -0
- package/src/types/ambient.d.ts +9 -0
- package/src/types.ts +680 -0
- package/src/utils/ApiClient.ts +163 -0
- package/src/utils/CSSConverter.ts +552 -0
- package/src/utils/animations.ts +232 -0
- package/src/utils/calendarIntegration.ts +113 -0
- package/src/utils/configurationResolver.ts +106 -0
- package/src/utils/deviceDetection.ts +83 -0
- package/src/utils/hapticFeedback.ts +55 -0
- package/src/utils/iconMapper.ts +102 -0
- package/src/utils/notificationPermissions.ts +48 -0
- package/src/utils/priceFormatting.ts +119 -0
- package/src/utils/shareSheet.ts +55 -0
- package/src/utils/subscriptionUpgrade.ts +183 -0
- package/src/valueUtils.ts +108 -0
- package/src/xFlowTags.ts +463 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BillDogBridge.swift
|
|
3
|
+
// @billdog.io/react-native
|
|
4
|
+
//
|
|
5
|
+
// Core BillDog React Native module — REAL native delegation to the BillDog iOS
|
|
6
|
+
// SDK (JS accesses this as `NativeModules.BillDog`; the objc name is pinned via
|
|
7
|
+
// `@objc(BillDog)`).
|
|
8
|
+
//
|
|
9
|
+
// This bridges the ~40 `native.<method>(...)` calls in `../src/BillDog.ts` to
|
|
10
|
+
// `BillDog.shared` (native BillDog iOS SDK, vended by the `BillDog` pod — see the
|
|
11
|
+
// podspec `s.dependency 'BillDog'`, Phase 0). CocoaPods compiles every BillDog
|
|
12
|
+
// subspec (Core, Paywall, …) into a SINGLE Clang module named `BillDog`, so one
|
|
13
|
+
// `import BillDog` exposes every public symbol used below (BillDog, BillDogConfig,
|
|
14
|
+
// ConsentManager, CustomerInfo, EntitlementInfo, PlacementResult, PresentationResult,
|
|
15
|
+
// RestoreResult, PurchaseResult, PurchaseParams, CacheFetchPolicy, InterfaceStyle).
|
|
16
|
+
// Because this Swift class is named `BillDogBridge` (only its objc export is
|
|
17
|
+
// `BillDog`), the bare `BillDog` identifier below unambiguously resolves to the
|
|
18
|
+
// SDK class, never to this bridge.
|
|
19
|
+
//
|
|
20
|
+
// ⚠️ VERIFICATION: there is no iOS/CocoaPods/Xcode toolchain in the authoring
|
|
21
|
+
// environment. This file is written to be internally consistent and compile-ready
|
|
22
|
+
// against the verified native signatures (cited inline as `BillDog.swift:<line>`
|
|
23
|
+
// etc.), but it REQUIRES a `pod install` + Xcode build in a host RN app to
|
|
24
|
+
// actually compile and run.
|
|
25
|
+
//
|
|
26
|
+
// Method names + argument shapes are the AUTHORITATIVE contract from
|
|
27
|
+
// `../src/BillDog.ts`. The result maps mirror the same model shapes the JS side
|
|
28
|
+
// parses (PurchaseResult / RestoreResult / CustomerInfo / PlacementResult /
|
|
29
|
+
// PresentationResult), matching `BillDogFlutterPlugin.swift`'s serialization.
|
|
30
|
+
//
|
|
31
|
+
|
|
32
|
+
import Foundation
|
|
33
|
+
import React
|
|
34
|
+
import BillDogFull
|
|
35
|
+
|
|
36
|
+
@objc(BillDog)
|
|
37
|
+
class BillDogBridge: NSObject {
|
|
38
|
+
|
|
39
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// native.configure(apiKey, options) → BillDog.shared.configure(config:)
|
|
44
|
+
/// (BillDog.swift:338). Without this the native SDK is unconfigured and every
|
|
45
|
+
/// delegating call fails; this is what makes the thin bridge usable.
|
|
46
|
+
@objc
|
|
47
|
+
func configure(_ apiKey: String,
|
|
48
|
+
options: NSDictionary?,
|
|
49
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
50
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
51
|
+
let enableLogging = (options?["enableLogging"] as? Bool) ?? true
|
|
52
|
+
let localeOverride = options?["localeOverride"] as? String
|
|
53
|
+
let config = BillDogConfig(
|
|
54
|
+
apiKey: apiKey,
|
|
55
|
+
enableLogging: enableLogging,
|
|
56
|
+
environment: .production,
|
|
57
|
+
localeOverride: localeOverride
|
|
58
|
+
)
|
|
59
|
+
BillDog.shared.configure(config: config)
|
|
60
|
+
BillDogBridge.registerOptionalModules()
|
|
61
|
+
resolve(true)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Guards `registerOptionalModules()` — `use(module:)` does not de-dupe.
|
|
65
|
+
private static var modulesRegistered = false
|
|
66
|
+
|
|
67
|
+
/// Register the optional native submodules with the core SDK.
|
|
68
|
+
///
|
|
69
|
+
/// WHY THIS EXISTS: the iOS SDK keeps an OPT-IN module registry —
|
|
70
|
+
/// `BillDog.shared.use(module:)` is the only way to populate it, and
|
|
71
|
+
/// `configure(config:)` registers nothing. Every bridge here resolves its
|
|
72
|
+
/// manager per-call out of `BillDog.shared.getModules()` (BillDog.swift:579),
|
|
73
|
+
/// so with an empty registry `BillDogAnalytics` / `BillDogABTest` resolve nil
|
|
74
|
+
/// and silently NO-OP — `track()` reports success and delivers nothing.
|
|
75
|
+
/// Confirmed on the Android twin of this bug 2026-07-21: events never reached
|
|
76
|
+
/// ClickHouse.
|
|
77
|
+
///
|
|
78
|
+
/// A native Swift host does this itself (see the iOS Inspector's
|
|
79
|
+
/// BillDogBootstrap.swift:42-56) — but an RN app has no Swift bootstrap, so the
|
|
80
|
+
/// bridge IS the host. Mirrors that reference sequence. Unlike Android, the
|
|
81
|
+
/// iOS analytics manager needs no Application context and no separate
|
|
82
|
+
/// persistence-init call (`init(directoryURL:)` defaults).
|
|
83
|
+
static func registerOptionalModules() {
|
|
84
|
+
guard !modulesRegistered else { return }
|
|
85
|
+
modulesRegistered = true
|
|
86
|
+
BillDog.shared.use(module: BillDogAnalyticsManager())
|
|
87
|
+
BillDog.shared.use(module: BillDogABTestManager())
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// PUR-364 — Whether a JS-side host PurchaseController is registered.
|
|
91
|
+
///
|
|
92
|
+
/// NOTE: full JS-PurchaseController callback bridging (invoking a JS
|
|
93
|
+
/// `purchase(productId)` from native and awaiting its typed result) is
|
|
94
|
+
/// DEFERRED. For now we only store the flag; `purchase(_:)` always routes
|
|
95
|
+
/// through the SDK's own native controller via `BillDog.shared.purchase(params:)`
|
|
96
|
+
/// (the JS facade already short-circuits to the JS controller before ever
|
|
97
|
+
/// reaching this bridge — see BillDog.ts:209 — so this flag is currently
|
|
98
|
+
/// informational only).
|
|
99
|
+
private var purchaseControllerActive = false
|
|
100
|
+
|
|
101
|
+
// MARK: - Async helpers
|
|
102
|
+
|
|
103
|
+
/// Run an async throwing block; resolve its value or reject on throw.
|
|
104
|
+
private func runThrowing(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
105
|
+
_ reject: @escaping RCTPromiseRejectBlock,
|
|
106
|
+
_ work: @escaping () async throws -> Any?) {
|
|
107
|
+
Task {
|
|
108
|
+
do {
|
|
109
|
+
let value = try await work()
|
|
110
|
+
resolve(value)
|
|
111
|
+
} catch {
|
|
112
|
+
reject("BILLDOG_ERROR", error.localizedDescription, error)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Run an async non-throwing block and resolve its value.
|
|
118
|
+
private func runValue(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
119
|
+
_ work: @escaping () async -> Any?) {
|
|
120
|
+
Task {
|
|
121
|
+
let value = await work()
|
|
122
|
+
resolve(value)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// MARK: - Purchases
|
|
127
|
+
|
|
128
|
+
/// BillDog.ts:221 — params is `{ ...PurchaseParams, source }`. We map every
|
|
129
|
+
/// PurchaseParams field onto the native `PurchaseParams.Builder`
|
|
130
|
+
/// (PurchaseParams.swift:67) and call `purchase(params:)` (BillDog.swift:1697),
|
|
131
|
+
/// which returns the canonical 5-case PurchaseResult.
|
|
132
|
+
@objc(purchase:resolver:rejecter:)
|
|
133
|
+
func purchase(_ params: NSDictionary,
|
|
134
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
135
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
136
|
+
guard let productId = params["productId"] as? String else {
|
|
137
|
+
reject("BILLDOG_ERROR", "purchase: missing productId", nil)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
// PurchaseParams.Builder — PurchaseParams.swift:67.
|
|
141
|
+
let builder = PurchaseParams.Builder(productId: productId)
|
|
142
|
+
if let promo = params["promoOfferId"] as? String { builder.with(promoOfferId: promo) }
|
|
143
|
+
if let winBack = params["winBackOfferId"] as? String { builder.with(winBackOfferId: winBack) }
|
|
144
|
+
if let token = params["appAccountToken"] as? String, let uuid = UUID(uuidString: token) {
|
|
145
|
+
builder.with(appAccountToken: uuid)
|
|
146
|
+
}
|
|
147
|
+
if let qty = params["quantity"] as? Int { builder.with(quantity: qty) }
|
|
148
|
+
if let personalized = params["isPersonalizedOffer"] as? Bool { builder.with(isPersonalizedOffer: personalized) }
|
|
149
|
+
if let mode = params["replacementMode"] as? String { builder.with(replacementMode: mode) }
|
|
150
|
+
let built = builder.build()
|
|
151
|
+
|
|
152
|
+
runThrowing(resolve, reject) {
|
|
153
|
+
// BillDog.swift:1697 — purchase(params:) async throws -> PurchaseResult.
|
|
154
|
+
let result = try await BillDog.shared.purchase(params: built)
|
|
155
|
+
return Self.serialize(result)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// BillDog.ts:266 — store the flag only (JS-controller bridging deferred; see
|
|
160
|
+
/// `purchaseControllerActive` doc above). No native equivalent needed here.
|
|
161
|
+
@objc(setPurchaseControllerActive:)
|
|
162
|
+
func setPurchaseControllerActive(_ active: Bool) {
|
|
163
|
+
purchaseControllerActive = active
|
|
164
|
+
NSLog("[BillDog] setPurchaseControllerActive(\(active)) — flag stored; JS PurchaseController bridging is deferred")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// BillDog.ts:296 — restorePurchases() async throws -> RestoreResult. BillDog.swift:1507.
|
|
168
|
+
@objc(restore:rejecter:)
|
|
169
|
+
func restore(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
170
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
171
|
+
runValue(resolve) {
|
|
172
|
+
do {
|
|
173
|
+
let result = try await BillDog.shared.restorePurchases()
|
|
174
|
+
return Self.serialize(result)
|
|
175
|
+
} catch {
|
|
176
|
+
return ["type": "failed",
|
|
177
|
+
"error": ["category": "unknown", "message": error.localizedDescription]]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// MARK: - CustomerInfo
|
|
183
|
+
|
|
184
|
+
/// BillDog.ts:320 — getCustomerInfo(fetchPolicy:) async throws -> CustomerInfo. BillDog.swift:2640.
|
|
185
|
+
@objc(getCustomerInfo:resolver:rejecter:)
|
|
186
|
+
func getCustomerInfo(_ policy: String,
|
|
187
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
188
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
189
|
+
let fetchPolicy = Self.mapCacheFetchPolicy(policy)
|
|
190
|
+
runThrowing(resolve, reject) {
|
|
191
|
+
let info = try await BillDog.shared.getCustomerInfo(fetchPolicy: fetchPolicy)
|
|
192
|
+
return Self.serialize(info)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/// BillDog.ts:792 — invalidateCustomerInfoCache() async throws. BillDog.swift:1813.
|
|
197
|
+
@objc(invalidateCustomerInfoCache:rejecter:)
|
|
198
|
+
func invalidateCustomerInfoCache(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
199
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
200
|
+
runThrowing(resolve, reject) {
|
|
201
|
+
try await BillDog.shared.invalidateCustomerInfoCache()
|
|
202
|
+
return nil
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// MARK: - Store capability surface
|
|
207
|
+
|
|
208
|
+
/// BillDog.ts:350 — static canMakePayments() async -> Bool. BillDog.swift:2616.
|
|
209
|
+
@objc(canMakePayments:rejecter:)
|
|
210
|
+
func canMakePayments(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
211
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
212
|
+
runValue(resolve) {
|
|
213
|
+
await BillDog.canMakePayments()
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/// BillDog.ts:359 — presentCodeRedemptionSheet() async throws. BillDog.swift:1656.
|
|
218
|
+
@objc(presentCodeRedemptionSheet:rejecter:)
|
|
219
|
+
func presentCodeRedemptionSheet(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
220
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
221
|
+
runThrowing(resolve, reject) {
|
|
222
|
+
try await BillDog.shared.presentCodeRedemptionSheet()
|
|
223
|
+
return nil
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// BillDog.ts:375 — NO native equivalent on BillDog.shared (searched the whole
|
|
228
|
+
/// paywall SDK: no `checkTrialOrIntroDiscountEligibility` / intro-eligibility
|
|
229
|
+
/// query API). Resolve all ids `unknown` (the JS fallback shape) + NSLog.
|
|
230
|
+
@objc(checkTrialOrIntroDiscountEligibility:resolver:rejecter:)
|
|
231
|
+
func checkTrialOrIntroDiscountEligibility(_ productIds: NSArray,
|
|
232
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
233
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
234
|
+
NSLog("[BillDog] checkTrialOrIntroDiscountEligibility — no native SDK query; returning 'unknown' for all ids")
|
|
235
|
+
var out: [String: String] = [:]
|
|
236
|
+
for case let id as String in productIds { out[id] = "unknown" }
|
|
237
|
+
resolve(out)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/// BillDog.ts:388 — the SDK exposes storefront changes only as an AsyncSequence
|
|
241
|
+
/// (StorefrontManager.swift:30) and this bridge is a plain NSObject with no
|
|
242
|
+
/// RCTEventEmitter channel, so there is no way to push updates back to JS yet.
|
|
243
|
+
/// Documented no-op (the JS facade keeps its listeners locally).
|
|
244
|
+
@objc(subscribeStorefront)
|
|
245
|
+
func subscribeStorefront() {
|
|
246
|
+
NSLog("[BillDog] subscribeStorefront — no event channel on this NSObject module; no-op (storefront push deferred)")
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// MARK: - Self-hosted proxy
|
|
250
|
+
|
|
251
|
+
/// BillDog.ts:404 — setProxyURL(_:). BillDog.swift:1185.
|
|
252
|
+
@objc(setProxyURL:)
|
|
253
|
+
func setProxyURL(_ url: String?) {
|
|
254
|
+
BillDog.shared.setProxyURL(url)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/// BillDog.ts:453 — SYNCHRONOUS read. BillDogConfig.getProxyURL() static. BillDogConfig.swift:76.
|
|
258
|
+
@objc(getProxyURL)
|
|
259
|
+
func getProxyURL() -> Any? {
|
|
260
|
+
return BillDogConfig.getProxyURL()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// MARK: - Consent
|
|
264
|
+
|
|
265
|
+
/// BillDog.ts:419 — ConsentManager.setConsentRequired(_:). ConsentManager.swift:42
|
|
266
|
+
/// (also re-exposed on BillDog.shared at BillDog.swift:460).
|
|
267
|
+
@objc(setConsentRequired:)
|
|
268
|
+
func setConsentRequired(_ required: Bool) {
|
|
269
|
+
ConsentManager.shared.setConsentRequired(required)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// BillDog.ts:428 — granular per-purpose consent. ConsentManager.setConsent(for:granted:).
|
|
273
|
+
/// ConsentManager.swift:53. Purpose enum: analytics|replay|messaging|survey (ConsentManager.swift:16).
|
|
274
|
+
@objc(setConsent:granted:)
|
|
275
|
+
func setConsent(_ purpose: String, granted: Bool) {
|
|
276
|
+
guard let p = ConsentManager.Purpose(rawValue: purpose) else {
|
|
277
|
+
NSLog("[BillDog] setConsent — unknown purpose '\(purpose)'; ignored")
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
ConsentManager.shared.setConsent(for: p, granted: granted)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/// BillDog.ts:437 — ConsentManager.setConsentGiven(_:). ConsentManager.swift:46
|
|
284
|
+
/// (also BillDog.swift:464).
|
|
285
|
+
@objc(setConsentGiven:)
|
|
286
|
+
func setConsentGiven(_ given: Bool) {
|
|
287
|
+
ConsentManager.shared.setConsentGiven(given)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/// BillDog.ts:446 — ConsentManager.consentHeaderValue() -> String?. ConsentManager.swift:75.
|
|
291
|
+
/// Sync accessor; the JS facade awaits it, so we surface it as a Promise.
|
|
292
|
+
@objc(consentHeaderValue:rejecter:)
|
|
293
|
+
func consentHeaderValue(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
294
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
295
|
+
resolve(ConsentManager.shared.consentHeaderValue())
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// MARK: - Attribution
|
|
299
|
+
|
|
300
|
+
/// BillDog.ts:484 — collectDeviceIdentifiers() async -> DeviceIdentifiers. BillDog.swift:2480.
|
|
301
|
+
/// JS calls this fire-and-forget; collect on a Task and log.
|
|
302
|
+
@objc(collectDeviceIdentifiers)
|
|
303
|
+
func collectDeviceIdentifiers() {
|
|
304
|
+
Task {
|
|
305
|
+
_ = await BillDog.shared.collectDeviceIdentifiers()
|
|
306
|
+
NSLog("[BillDog] collectDeviceIdentifiers — device identifiers collected")
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/// native.setAttribution({ key: id, … }) → the matching native provider setter
|
|
311
|
+
/// (BillDog.swift:3388+). Keys are provider attribution keys; each maps 1:1 to
|
|
312
|
+
/// a consent-gated native setter. Unknown keys are ignored. The JS facade's
|
|
313
|
+
/// named setters (setMixpanelDistinctID etc.) all funnel through here.
|
|
314
|
+
@objc
|
|
315
|
+
func setAttribution(_ data: NSDictionary,
|
|
316
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
317
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
318
|
+
let entries = (data as? [String: String]) ?? [:]
|
|
319
|
+
Task {
|
|
320
|
+
do {
|
|
321
|
+
for (key, value) in entries {
|
|
322
|
+
switch key {
|
|
323
|
+
case "cleverTapId": try await BillDog.shared.setCleverTapID(value)
|
|
324
|
+
case "mixpanelDistinctId": try await BillDog.shared.setMixpanelDistinctID(value)
|
|
325
|
+
case "onesignalId": try await BillDog.shared.setOnesignalID(value)
|
|
326
|
+
case "fbAnonymousId": try await BillDog.shared.setFBAnonymousID(value)
|
|
327
|
+
case "airshipChannelId": try await BillDog.shared.setAirshipChannelID(value)
|
|
328
|
+
case "moEngageAnonymousId": try await BillDog.shared.setMoEngageAnonymousID(value)
|
|
329
|
+
case "mparticleId": try await BillDog.shared.setMparticleID(value)
|
|
330
|
+
case "kochavaId": try await BillDog.shared.setKochavaDeviceID(value)
|
|
331
|
+
case "tenjinId": try await BillDog.shared.setTenjinAnalyticsInstallationID(value)
|
|
332
|
+
case "iterableId": try await BillDog.shared.setIterableUserID(value)
|
|
333
|
+
case "segmentId": try await BillDog.shared.setSegmentAnonymousID(value)
|
|
334
|
+
// MMP providers — dedicated native setters.
|
|
335
|
+
case "adjustId": try await BillDog.shared.setAdjustId(value)
|
|
336
|
+
case "appsflyerId": try await BillDog.shared.setAppsflyerId(value)
|
|
337
|
+
case "firebaseAppInstanceId": try await BillDog.shared.setFirebaseAppInstanceId(value)
|
|
338
|
+
default: break // unknown provider key — ignore
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
resolve(nil)
|
|
342
|
+
} catch {
|
|
343
|
+
reject("ATTRIBUTION_ERROR", error.localizedDescription, error)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// MARK: - Identity
|
|
349
|
+
|
|
350
|
+
/// BillDog.ts:513 — logIn(_:) async throws -> LogInResult. BillDog.swift:2529.
|
|
351
|
+
/// JS ignores the return value; resolve nil on success.
|
|
352
|
+
@objc(logIn:resolver:rejecter:)
|
|
353
|
+
func logIn(_ appUserId: String,
|
|
354
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
355
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
356
|
+
runThrowing(resolve, reject) {
|
|
357
|
+
_ = try await BillDog.shared.logIn(appUserId)
|
|
358
|
+
return nil
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/// BillDog.ts:536 — logOut() (synchronous on the SDK). BillDog.swift:2547.
|
|
363
|
+
@objc(logOut:rejecter:)
|
|
364
|
+
func logOut(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
365
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
366
|
+
BillDog.shared.logOut()
|
|
367
|
+
resolve(nil)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/// BillDog.ts:589 — NO native runtime toggle (sandbox derives from API-key
|
|
371
|
+
/// prefix at configure time — same finding as BillDogFlutterPlugin.swift:381).
|
|
372
|
+
@objc(setSandboxMode:)
|
|
373
|
+
func setSandboxMode(_ enabled: Bool) {
|
|
374
|
+
NSLog("[BillDog] setSandboxMode(\(enabled)) — no native runtime toggle (sandbox derives from API-key prefix); no-op")
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// MARK: - Placements
|
|
378
|
+
|
|
379
|
+
/// BillDog.ts:634 — args is `{ placementId, params }`. FULL-SCREEN PRESENT via
|
|
380
|
+
/// register(placement:params:) async throws -> PlacementResult. BillDog.swift:2899.
|
|
381
|
+
@objc(registerPlacement:resolver:rejecter:)
|
|
382
|
+
func registerPlacement(_ args: NSDictionary,
|
|
383
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
384
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
385
|
+
guard let placementId = args["placementId"] as? String else {
|
|
386
|
+
reject("BILLDOG_ERROR", "registerPlacement: missing placementId", nil)
|
|
387
|
+
return
|
|
388
|
+
}
|
|
389
|
+
let params = args["params"] as? [String: Any]
|
|
390
|
+
runValue(resolve) {
|
|
391
|
+
do {
|
|
392
|
+
let result = try await BillDog.shared.register(placement: placementId, params: params)
|
|
393
|
+
return Self.serialize(result)
|
|
394
|
+
} catch {
|
|
395
|
+
return ["type": "error", "message": error.localizedDescription]
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/// BillDog.ts:679 — SYNCHRONOUS entitlement probe. hasAccessForPlacement(_:) -> Bool.
|
|
401
|
+
/// BillDog.swift:2213.
|
|
402
|
+
@objc(hasAccessForPlacement:)
|
|
403
|
+
func hasAccessForPlacement(_ placementId: String) -> NSNumber {
|
|
404
|
+
return NSNumber(value: BillDog.shared.hasAccessForPlacement(placementId))
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/// BillDog.ts:878 — getPresentationResult(forPlacement:) async -> PresentationResult.
|
|
408
|
+
/// BillDog.swift:3319.
|
|
409
|
+
@objc(getPresentationResult:resolver:rejecter:)
|
|
410
|
+
func getPresentationResult(_ placementId: String,
|
|
411
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
412
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
413
|
+
runValue(resolve) {
|
|
414
|
+
let result = await BillDog.shared.getPresentationResult(forPlacement: placementId)
|
|
415
|
+
return Self.serialize(result)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Experiments & feature flags: the paywall-facade shim was removed. Use the
|
|
420
|
+
// standalone BillDogABTestManager (bridged via BillDogABTestModule) instead.
|
|
421
|
+
|
|
422
|
+
// MARK: - In-app messages
|
|
423
|
+
|
|
424
|
+
/// BillDog.ts:722 — NO native equivalent on BillDog.shared (same finding as
|
|
425
|
+
/// BillDogFlutterPlugin.swift:389 — in-app messages dispatch internally / via
|
|
426
|
+
/// journeys, not through a paywall-channel call). Resolve nil + NSLog.
|
|
427
|
+
@objc(showInAppMessages:resolver:rejecter:)
|
|
428
|
+
func showInAppMessages(_ placementId: String?,
|
|
429
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
430
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
431
|
+
NSLog("[BillDog] showInAppMessages(\(placementId ?? "nil")) — no native SDK method on BillDog.shared; no-op")
|
|
432
|
+
resolve(nil)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// MARK: - Deep linking
|
|
436
|
+
|
|
437
|
+
/// BillDog.ts:731 — handleDeepLink(_: URL) -> String? (non-nil route id when
|
|
438
|
+
/// handled). BillDog.swift:2876. JS wants a bool.
|
|
439
|
+
@objc(handleDeepLink:resolver:rejecter:)
|
|
440
|
+
func handleDeepLink(_ uri: String,
|
|
441
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
442
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
443
|
+
guard let url = URL(string: uri) else {
|
|
444
|
+
resolve(false)
|
|
445
|
+
return
|
|
446
|
+
}
|
|
447
|
+
resolve(BillDog.shared.handleDeepLink(url) != nil)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// MARK: - Component selection state
|
|
451
|
+
|
|
452
|
+
/// BillDog.ts:740 — SYNCHRONOUS. getSelectedProductIndex(componentId:) -> Int?
|
|
453
|
+
/// (nil is a valid response → JS null). BillDog.swift:1889.
|
|
454
|
+
@objc(getSelectedProductIndex:)
|
|
455
|
+
func getSelectedProductIndex(_ componentId: String) -> Any? {
|
|
456
|
+
if let index = BillDog.shared.getSelectedProductIndex(componentId: componentId) {
|
|
457
|
+
return NSNumber(value: index)
|
|
458
|
+
}
|
|
459
|
+
return nil
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/// BillDog.ts:747 — setSelectedProductIndex(componentId:index:). BillDog.swift:1895.
|
|
463
|
+
@objc(setSelectedProductIndex:index:)
|
|
464
|
+
func setSelectedProductIndex(_ componentId: String, index: NSNumber) {
|
|
465
|
+
BillDog.shared.setSelectedProductIndex(componentId: componentId, index: index.intValue)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// MARK: - Theming
|
|
469
|
+
|
|
470
|
+
/// BillDog.ts:755 — updateTheme(_: [String: String]?). BillDog.swift:1174.
|
|
471
|
+
@objc(updateTheme:)
|
|
472
|
+
func updateTheme(_ overrides: NSDictionary) {
|
|
473
|
+
BillDog.shared.updateTheme(overrides as? [String: String])
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/// BillDog.ts:762 — setInterfaceStyle(_: InterfaceStyle?). BillDog.swift:3333.
|
|
477
|
+
/// InterfaceStyle: light/dark/system; nil == follow system.
|
|
478
|
+
@objc(setInterfaceStyle:)
|
|
479
|
+
func setInterfaceStyle(_ style: String?) {
|
|
480
|
+
BillDog.shared.setInterfaceStyle(Self.mapInterfaceStyle(style))
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// MARK: - Subscription management
|
|
484
|
+
|
|
485
|
+
/// BillDog.ts:776 — showManageSubscriptions() async (takes no argument; Apple
|
|
486
|
+
/// resolves the active subscription — the JS `subscriptionId` is ignored, same
|
|
487
|
+
/// as BillDogFlutterPlugin.swift:344). BillDog.swift:3182.
|
|
488
|
+
@objc(showManageSubscriptions:resolver:rejecter:)
|
|
489
|
+
func showManageSubscriptions(_ subscriptionId: String?,
|
|
490
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
491
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
492
|
+
runValue(resolve) {
|
|
493
|
+
await BillDog.shared.showManageSubscriptions()
|
|
494
|
+
return nil
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// MARK: - Cache control
|
|
499
|
+
|
|
500
|
+
/// BillDog.ts:785 — clearCache() (synchronous on the SDK). BillDog.swift:1914.
|
|
501
|
+
@objc(clearCache:rejecter:)
|
|
502
|
+
func clearCache(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
503
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
504
|
+
BillDog.shared.clearCache()
|
|
505
|
+
resolve(nil)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// MARK: - User attributes
|
|
509
|
+
|
|
510
|
+
/// BillDog.ts:801 — setUserAttributes(_: [String: Any?]) async throws. BillDog.swift:3344.
|
|
511
|
+
@objc(setUserAttributes:resolver:rejecter:)
|
|
512
|
+
func setUserAttributes(_ attributes: NSDictionary,
|
|
513
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
514
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
515
|
+
let dict = (attributes as? [String: Any]) ?? [:]
|
|
516
|
+
runThrowing(resolve, reject) {
|
|
517
|
+
try await BillDog.shared.setUserAttributes(dict)
|
|
518
|
+
return nil
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/// BillDog.ts:812 — getUserAttributes() -> [String: Any] (synchronous on the
|
|
523
|
+
/// SDK; the JS facade awaits it). BillDog.swift:3358.
|
|
524
|
+
@objc(getUserAttributes:rejecter:)
|
|
525
|
+
func getUserAttributes(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
526
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
527
|
+
resolve(BillDog.shared.getUserAttributes())
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// MARK: - External-purchase observation
|
|
531
|
+
|
|
532
|
+
/// BillDog.ts:822 — observePurchaseStart(productId:). BillDog.swift:3568.
|
|
533
|
+
@objc(observePurchaseStart:)
|
|
534
|
+
func observePurchaseStart(_ productId: String) {
|
|
535
|
+
BillDog.shared.observePurchaseStart(productId: productId)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/// BillDog.ts:829 — observePurchaseResult(productId:orderId:...). BillDog.swift:3583.
|
|
539
|
+
@objc(observePurchaseResult:orderId:)
|
|
540
|
+
func observePurchaseResult(_ productId: String, orderId: String?) {
|
|
541
|
+
BillDog.shared.observePurchaseResult(productId: productId, orderId: orderId)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/// BillDog.ts:835 — observePurchaseError(productId:error:). BillDog.swift:3610.
|
|
545
|
+
/// JS passes an error string; wrap it in an NSError (same as the Flutter plugin).
|
|
546
|
+
@objc(observePurchaseError:error:)
|
|
547
|
+
func observePurchaseError(_ productId: String, error: String) {
|
|
548
|
+
let nsError = NSError(domain: "io.billdog.reactnative", code: -1,
|
|
549
|
+
userInfo: [NSLocalizedDescriptionKey: error])
|
|
550
|
+
BillDog.shared.observePurchaseError(productId: productId, error: nsError)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// MARK: - Server-side actions
|
|
554
|
+
|
|
555
|
+
/// BillDog.ts:900 — executeAction(paywallId:componentId:actionType:idempotencyKey:metadata:)
|
|
556
|
+
/// async throws -> [String: Any]. BillDog.swift:231.
|
|
557
|
+
@objc(executeAction:componentId:actionType:idempotencyKey:metadata:resolver:rejecter:)
|
|
558
|
+
func executeAction(_ paywallId: String?,
|
|
559
|
+
componentId: String,
|
|
560
|
+
actionType: String,
|
|
561
|
+
idempotencyKey: String?,
|
|
562
|
+
metadata: NSDictionary?,
|
|
563
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
564
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
565
|
+
let meta = metadata as? [String: Any]
|
|
566
|
+
runThrowing(resolve, reject) {
|
|
567
|
+
try await BillDog.shared.executeAction(paywallId: paywallId,
|
|
568
|
+
componentId: componentId,
|
|
569
|
+
actionType: actionType,
|
|
570
|
+
idempotencyKey: idempotencyKey,
|
|
571
|
+
metadata: meta)
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// MARK: - Enum mapping
|
|
576
|
+
|
|
577
|
+
/// CacheFetchPolicy (CustomerManager.swift:4); JS wire names from CacheFetchPolicy.ts.
|
|
578
|
+
private static func mapCacheFetchPolicy(_ raw: String?) -> CacheFetchPolicy {
|
|
579
|
+
switch raw {
|
|
580
|
+
case "FROM_CACHE_ONLY": return .fromCacheOnly
|
|
581
|
+
case "FETCH_CURRENT": return .fetchCurrent
|
|
582
|
+
case "NOT_STALE_CACHED_OR_CURRENT": return .notStaleCachedOrCurrent
|
|
583
|
+
default: return .cachedOrFetched // PUR-141 default (CACHED_OR_FETCHED)
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/// InterfaceStyle (InterfaceStyle.swift); JS passes 'light' | 'dark' | null.
|
|
588
|
+
private static func mapInterfaceStyle(_ raw: String?) -> InterfaceStyle? {
|
|
589
|
+
switch raw?.lowercased() {
|
|
590
|
+
case "light": return .light
|
|
591
|
+
case "dark": return .dark
|
|
592
|
+
case "system": return .system
|
|
593
|
+
default: return nil // nil == follow system
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// MARK: - Serialization (mirrors BillDogFlutterPlugin.swift; matches BillDog.ts model shapes)
|
|
598
|
+
|
|
599
|
+
/// PurchaseResult (PurchaseResult.swift:24) → PurchaseResult.ts 5-case union.
|
|
600
|
+
private static func serialize(_ r: PurchaseResult) -> [String: Any] {
|
|
601
|
+
switch r {
|
|
602
|
+
case .purchased(let transactionId, let productId, let originalTransactionId):
|
|
603
|
+
var m: [String: Any] = ["type": "purchased",
|
|
604
|
+
"transactionId": transactionId,
|
|
605
|
+
"productId": productId]
|
|
606
|
+
if let orig = originalTransactionId { m["originalTransactionId"] = orig }
|
|
607
|
+
return m
|
|
608
|
+
case .cancelled:
|
|
609
|
+
return ["type": "cancelled"]
|
|
610
|
+
case .pending:
|
|
611
|
+
return ["type": "pending"]
|
|
612
|
+
case .failed(let error):
|
|
613
|
+
return ["type": "failed",
|
|
614
|
+
"error": ["category": "unknown", "message": error.localizedDescription]]
|
|
615
|
+
case .restored(let transactionId, let productId, let originalTransactionId):
|
|
616
|
+
var m: [String: Any] = ["type": "restored",
|
|
617
|
+
"transactionId": transactionId,
|
|
618
|
+
"productId": productId]
|
|
619
|
+
if let orig = originalTransactionId { m["originalTransactionId"] = orig }
|
|
620
|
+
return m
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/// RestoreResult (PurchaseResult.swift:52) → RestoreResult.ts.
|
|
625
|
+
/// Each item carries `status` per RestoredItem.ts (per-transaction success).
|
|
626
|
+
private static func serialize(_ r: RestoreResult) -> [String: Any] {
|
|
627
|
+
switch r {
|
|
628
|
+
case .restored(let items):
|
|
629
|
+
let mapped: [[String: Any]] = items.map { p in
|
|
630
|
+
var m: [String: Any] = [
|
|
631
|
+
"productId": p.productId,
|
|
632
|
+
"transactionId": p.transactionId,
|
|
633
|
+
"status": ["type": "restored"]
|
|
634
|
+
]
|
|
635
|
+
if let orig = p.originalTransactionId { m["originalTransactionId"] = orig }
|
|
636
|
+
return m
|
|
637
|
+
}
|
|
638
|
+
return ["type": "restored", "items": mapped]
|
|
639
|
+
case .failed(let error):
|
|
640
|
+
return ["type": "failed",
|
|
641
|
+
"error": ["category": "unknown", "message": error.localizedDescription]]
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/// PlacementResult (PlacementManager) → Placement.ts PlacementResult.
|
|
646
|
+
private static func serialize(_ r: PlacementResult) -> [String: Any] {
|
|
647
|
+
switch r {
|
|
648
|
+
case .entitled:
|
|
649
|
+
return ["type": "entitled"]
|
|
650
|
+
case .purchased(let productId):
|
|
651
|
+
return ["type": "purchased", "productId": productId]
|
|
652
|
+
case .dismissed:
|
|
653
|
+
return ["type": "dismissed"]
|
|
654
|
+
case .restored:
|
|
655
|
+
return ["type": "restored"]
|
|
656
|
+
case .oauthSuccess:
|
|
657
|
+
// No JS PlacementResult case for OAuth sign-in — map to neutral `dismissed`
|
|
658
|
+
// so the caller resumes without a spurious error (parity with Flutter).
|
|
659
|
+
return ["type": "dismissed"]
|
|
660
|
+
case .error(let err):
|
|
661
|
+
return ["type": "error", "message": err.localizedDescription]
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/// PresentationResult (BillDog.swift:3844) → Placement.ts PresentationResult.
|
|
666
|
+
private static func serialize(_ r: PresentationResult) -> [String: Any] {
|
|
667
|
+
switch r {
|
|
668
|
+
case .paywall:
|
|
669
|
+
return ["type": "paywall"]
|
|
670
|
+
case .userSubscribed:
|
|
671
|
+
return ["type": "userSubscribed"]
|
|
672
|
+
case .holdout:
|
|
673
|
+
return ["type": "holdout"]
|
|
674
|
+
case .noPaywall(let reason):
|
|
675
|
+
return ["type": "noPaywall", "reason": reason]
|
|
676
|
+
case .error(let err):
|
|
677
|
+
return ["type": "error", "message": err.localizedDescription]
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/// CustomerInfo (CustomerInfo.swift:169) → CustomerInfo.ts shape.
|
|
682
|
+
private static func serialize(_ info: CustomerInfo) -> [String: Any] {
|
|
683
|
+
var m: [String: Any] = [
|
|
684
|
+
"userId": info.userId,
|
|
685
|
+
"entitlements": info.entitlements.map { serialize($0) },
|
|
686
|
+
"activeSubscriptions": info.activeSubscriptions,
|
|
687
|
+
"productEntitlementMapping": info.productEntitlementMapping,
|
|
688
|
+
"isInTrial": info.isInTrial,
|
|
689
|
+
"isInGracePeriod": info.isInGracePeriod,
|
|
690
|
+
"hasExpiredSubscription": info.hasExpiredSubscription
|
|
691
|
+
]
|
|
692
|
+
if let orig = info.originalAppUserId { m["originalAppUserId"] = orig }
|
|
693
|
+
return m
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/// EntitlementInfo (CustomerInfo.swift:77) → EntitlementInfo.ts shape.
|
|
697
|
+
private static func serialize(_ e: EntitlementInfo) -> [String: Any] {
|
|
698
|
+
var m: [String: Any] = [
|
|
699
|
+
"identifier": e.identifier,
|
|
700
|
+
"isActive": e.isActive,
|
|
701
|
+
"willRenew": e.willRenew,
|
|
702
|
+
"isLifetime": e.isLifetime,
|
|
703
|
+
"productIds": e.productIds
|
|
704
|
+
]
|
|
705
|
+
if let v = e.expiresAt { m["expiresAt"] = v }
|
|
706
|
+
if let v = e.store { m["store"] = v }
|
|
707
|
+
if let v = e.grantedAt { m["grantedAt"] = v }
|
|
708
|
+
if let v = e.displayName { m["displayName"] = v }
|
|
709
|
+
if let v = e.state { m["state"] = v }
|
|
710
|
+
if let v = e.startsAt { m["startsAt"] = v }
|
|
711
|
+
if let v = e.renewedAt { m["renewedAt"] = v }
|
|
712
|
+
if let v = e.latestProductId { m["latestProductId"] = v }
|
|
713
|
+
return m
|
|
714
|
+
}
|
|
715
|
+
}
|