cloudx-react-native 2.0.0-beta.1
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/LICENSE +57 -0
- package/README.md +1258 -0
- package/android/build.gradle +78 -0
- package/android/gradle.properties +7 -0
- package/android/src/main/AndroidManifest.xml +7 -0
- package/android/src/main/java/com/cloudxreactnative/CloudXAdViewEvent.kt +15 -0
- package/android/src/main/java/com/cloudxreactnative/CloudXBannerViewManagerImpl.kt +472 -0
- package/android/src/main/java/com/cloudxreactnative/CloudXReactNativeModuleImpl.kt +861 -0
- package/android/src/newarch/com/cloudxreactnative/CloudXBannerViewManager.kt +90 -0
- package/android/src/newarch/com/cloudxreactnative/CloudXReactNativeModule.kt +73 -0
- package/android/src/newarch/com/cloudxreactnative/CloudXReactNativePackage.kt +37 -0
- package/android/src/oldarch/com/cloudxreactnative/CloudXBannerViewManager.kt +76 -0
- package/android/src/oldarch/com/cloudxreactnative/CloudXReactNativeModule.kt +73 -0
- package/android/src/oldarch/com/cloudxreactnative/CloudXReactNativePackage.kt +16 -0
- package/cloudx-react-native.podspec +46 -0
- package/ios/AppTrackingTransparency.h +13 -0
- package/ios/AppTrackingTransparency.mm +59 -0
- package/ios/RNCloudXBannerView.h +51 -0
- package/ios/RNCloudXBannerView.mm +633 -0
- package/ios/RNCloudXBannerViewManager.h +11 -0
- package/ios/RNCloudXBannerViewManager.mm +60 -0
- package/ios/RNCloudXModule.h +30 -0
- package/ios/RNCloudXModule.mm +1088 -0
- package/lib/commonjs/AdView.js +151 -0
- package/lib/commonjs/AdView.js.map +1 -0
- package/lib/commonjs/AppTrackingTransparency.js +62 -0
- package/lib/commonjs/AppTrackingTransparency.js.map +1 -0
- package/lib/commonjs/BannerAd.js +117 -0
- package/lib/commonjs/BannerAd.js.map +1 -0
- package/lib/commonjs/CloudX.js +49 -0
- package/lib/commonjs/CloudX.js.map +1 -0
- package/lib/commonjs/ErrorCode.js +116 -0
- package/lib/commonjs/ErrorCode.js.map +1 -0
- package/lib/commonjs/EventEmitter.js +44 -0
- package/lib/commonjs/EventEmitter.js.map +1 -0
- package/lib/commonjs/InterstitialAd.js +94 -0
- package/lib/commonjs/InterstitialAd.js.map +1 -0
- package/lib/commonjs/MRecAd.js +117 -0
- package/lib/commonjs/MRecAd.js.map +1 -0
- package/lib/commonjs/RewardedAd.js +103 -0
- package/lib/commonjs/RewardedAd.js.map +1 -0
- package/lib/commonjs/hooks.js +239 -0
- package/lib/commonjs/hooks.js.map +1 -0
- package/lib/commonjs/index.js +144 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/specs/CloudXAdViewNativeComponent.js +26 -0
- package/lib/commonjs/specs/CloudXAdViewNativeComponent.js.map +1 -0
- package/lib/commonjs/specs/NativeCloudXModule.js +18 -0
- package/lib/commonjs/specs/NativeCloudXModule.js.map +1 -0
- package/lib/commonjs/types/AdEvent.js +6 -0
- package/lib/commonjs/types/AdEvent.js.map +1 -0
- package/lib/commonjs/types/AdInfo.js +6 -0
- package/lib/commonjs/types/AdInfo.js.map +1 -0
- package/lib/commonjs/types/BannerAd.js +6 -0
- package/lib/commonjs/types/BannerAd.js.map +1 -0
- package/lib/commonjs/types/CloudX.js +6 -0
- package/lib/commonjs/types/CloudX.js.map +1 -0
- package/lib/commonjs/types/Configuration.js +2 -0
- package/lib/commonjs/types/Configuration.js.map +1 -0
- package/lib/commonjs/types/FullscreenAd.js +6 -0
- package/lib/commonjs/types/FullscreenAd.js.map +1 -0
- package/lib/commonjs/types/RewardedAd.js +6 -0
- package/lib/commonjs/types/RewardedAd.js.map +1 -0
- package/lib/commonjs/types/index.js +2 -0
- package/lib/commonjs/types/index.js.map +1 -0
- package/lib/module/AdView.js +144 -0
- package/lib/module/AdView.js.map +1 -0
- package/lib/module/AppTrackingTransparency.js +55 -0
- package/lib/module/AppTrackingTransparency.js.map +1 -0
- package/lib/module/BannerAd.js +110 -0
- package/lib/module/BannerAd.js.map +1 -0
- package/lib/module/CloudX.js +42 -0
- package/lib/module/CloudX.js.map +1 -0
- package/lib/module/ErrorCode.js +110 -0
- package/lib/module/ErrorCode.js.map +1 -0
- package/lib/module/EventEmitter.js +35 -0
- package/lib/module/EventEmitter.js.map +1 -0
- package/lib/module/InterstitialAd.js +87 -0
- package/lib/module/InterstitialAd.js.map +1 -0
- package/lib/module/MRecAd.js +110 -0
- package/lib/module/MRecAd.js.map +1 -0
- package/lib/module/RewardedAd.js +96 -0
- package/lib/module/RewardedAd.js.map +1 -0
- package/lib/module/hooks.js +230 -0
- package/lib/module/hooks.js.map +1 -0
- package/lib/module/index.js +33 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/CloudXAdViewNativeComponent.js +20 -0
- package/lib/module/specs/CloudXAdViewNativeComponent.js.map +1 -0
- package/lib/module/specs/NativeCloudXModule.js +14 -0
- package/lib/module/specs/NativeCloudXModule.js.map +1 -0
- package/lib/module/types/AdEvent.js +2 -0
- package/lib/module/types/AdEvent.js.map +1 -0
- package/lib/module/types/AdInfo.js +2 -0
- package/lib/module/types/AdInfo.js.map +1 -0
- package/lib/module/types/BannerAd.js +2 -0
- package/lib/module/types/BannerAd.js.map +1 -0
- package/lib/module/types/CloudX.js +2 -0
- package/lib/module/types/CloudX.js.map +1 -0
- package/lib/module/types/Configuration.js +2 -0
- package/lib/module/types/Configuration.js.map +1 -0
- package/lib/module/types/FullscreenAd.js +2 -0
- package/lib/module/types/FullscreenAd.js.map +1 -0
- package/lib/module/types/RewardedAd.js +2 -0
- package/lib/module/types/RewardedAd.js.map +1 -0
- package/lib/module/types/index.js +2 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/typescript/src/AdView.d.ts +69 -0
- package/lib/typescript/src/AdView.d.ts.map +1 -0
- package/lib/typescript/src/AppTrackingTransparency.d.ts +28 -0
- package/lib/typescript/src/AppTrackingTransparency.d.ts.map +1 -0
- package/lib/typescript/src/BannerAd.d.ts +4 -0
- package/lib/typescript/src/BannerAd.d.ts.map +1 -0
- package/lib/typescript/src/CloudX.d.ts +4 -0
- package/lib/typescript/src/CloudX.d.ts.map +1 -0
- package/lib/typescript/src/ErrorCode.d.ts +102 -0
- package/lib/typescript/src/ErrorCode.d.ts.map +1 -0
- package/lib/typescript/src/EventEmitter.d.ts +16 -0
- package/lib/typescript/src/EventEmitter.d.ts.map +1 -0
- package/lib/typescript/src/InterstitialAd.d.ts +4 -0
- package/lib/typescript/src/InterstitialAd.d.ts.map +1 -0
- package/lib/typescript/src/MRecAd.d.ts +4 -0
- package/lib/typescript/src/MRecAd.d.ts.map +1 -0
- package/lib/typescript/src/RewardedAd.d.ts +4 -0
- package/lib/typescript/src/RewardedAd.d.ts.map +1 -0
- package/lib/typescript/src/hooks.d.ts +50 -0
- package/lib/typescript/src/hooks.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +23 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts +61 -0
- package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeCloudXModule.d.ts +106 -0
- package/lib/typescript/src/specs/NativeCloudXModule.d.ts.map +1 -0
- package/lib/typescript/src/types/AdEvent.d.ts +10 -0
- package/lib/typescript/src/types/AdEvent.d.ts.map +1 -0
- package/lib/typescript/src/types/AdInfo.d.ts +48 -0
- package/lib/typescript/src/types/AdInfo.d.ts.map +1 -0
- package/lib/typescript/src/types/BannerAd.d.ts +39 -0
- package/lib/typescript/src/types/BannerAd.d.ts.map +1 -0
- package/lib/typescript/src/types/CloudX.d.ts +25 -0
- package/lib/typescript/src/types/CloudX.d.ts.map +1 -0
- package/lib/typescript/src/types/Configuration.d.ts +10 -0
- package/lib/typescript/src/types/Configuration.d.ts.map +1 -0
- package/lib/typescript/src/types/FullscreenAd.d.ts +29 -0
- package/lib/typescript/src/types/FullscreenAd.d.ts.map +1 -0
- package/lib/typescript/src/types/RewardedAd.d.ts +10 -0
- package/lib/typescript/src/types/RewardedAd.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +8 -0
- package/lib/typescript/src/types/index.d.ts.map +1 -0
- package/package.json +78 -0
- package/src/AdView.tsx +213 -0
- package/src/AppTrackingTransparency.ts +56 -0
- package/src/BannerAd.ts +133 -0
- package/src/CloudX.ts +55 -0
- package/src/ErrorCode.ts +157 -0
- package/src/EventEmitter.ts +38 -0
- package/src/InterstitialAd.ts +103 -0
- package/src/MRecAd.ts +133 -0
- package/src/RewardedAd.ts +113 -0
- package/src/hooks.ts +292 -0
- package/src/index.ts +50 -0
- package/src/specs/CloudXAdViewNativeComponent.ts +82 -0
- package/src/specs/NativeCloudXModule.ts +162 -0
- package/src/types/AdEvent.ts +11 -0
- package/src/types/AdInfo.ts +60 -0
- package/src/types/BannerAd.ts +59 -0
- package/src/types/CloudX.ts +33 -0
- package/src/types/Configuration.ts +10 -0
- package/src/types/FullscreenAd.ts +39 -0
- package/src/types/RewardedAd.ts +10 -0
- package/src/types/index.ts +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,1258 @@
|
|
|
1
|
+
# CloudX React Native SDK
|
|
2
|
+
|
|
3
|
+
A React Native wrapper for the CloudX native SDKs, providing a comprehensive solution for programmatic advertising with full privacy compliance support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Full SDK Integration**: Complete wrapper for CloudX Core SDK
|
|
8
|
+
- **Cross-Platform Ready**: iOS support (Android coming soon)
|
|
9
|
+
- **All Ad Types**: Banner, Interstitial, Rewarded, Native, and MREC ads
|
|
10
|
+
- **Privacy**: IAB privacy compliance is handled by the native SDK automatically
|
|
11
|
+
- **Event System**: React Native event emitter pattern for ad lifecycle management
|
|
12
|
+
- **Error Handling**: Comprehensive error handling with detailed error codes
|
|
13
|
+
- **Type Safety**: Full TypeScript support with complete type definitions
|
|
14
|
+
- **React Hooks**: Custom hooks for seamless React integration
|
|
15
|
+
- **Modern Patterns**: Follows React Native 0.70+ best practices
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install cloudx-react-native
|
|
21
|
+
# or
|
|
22
|
+
yarn add cloudx-react-native
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### iOS Setup
|
|
26
|
+
|
|
27
|
+
1. Add to your `Podfile`:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# CloudX Core SDK
|
|
31
|
+
pod 'CloudXCore'
|
|
32
|
+
|
|
33
|
+
# Optional: CloudX Adapters
|
|
34
|
+
pod 'CloudXMetaAdapter'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
2. Install pods:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd ios && pod install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
3. Add required permissions to `Info.plist`:
|
|
44
|
+
|
|
45
|
+
```xml
|
|
46
|
+
<key>NSUserTrackingUsageDescription</key>
|
|
47
|
+
<string>This app uses tracking to provide personalized ads and better user experience.</string>
|
|
48
|
+
|
|
49
|
+
<key>SKAdNetworkItems</key>
|
|
50
|
+
<array>
|
|
51
|
+
<!-- Add your SKAdNetwork IDs -->
|
|
52
|
+
</array>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Android Setup
|
|
56
|
+
|
|
57
|
+
> ⚠️ **Android support coming soon**
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### 1. Initialize the SDK
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import CloudXSDK from 'cloudx-react-native';
|
|
67
|
+
|
|
68
|
+
// Initialize CloudX SDK (testMode: false = production, true = test ads)
|
|
69
|
+
const result = await CloudXSDK.initialize({
|
|
70
|
+
appKey: 'YOUR_APP_KEY',
|
|
71
|
+
testMode: false, // false = production, true = test ads
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (result.success) {
|
|
75
|
+
console.log('CloudX SDK initialized successfully');
|
|
76
|
+
} else {
|
|
77
|
+
console.error('Failed to initialize CloudX SDK:', result.message);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// Check if initialized
|
|
82
|
+
const isInitialized = await CloudXSDK.isInitialized();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Test Mode
|
|
86
|
+
|
|
87
|
+
The SDK supports an optional `testMode` parameter:
|
|
88
|
+
|
|
89
|
+
- **Production (`testMode: false`)**: Real ads with actual billing. Use for production releases.
|
|
90
|
+
- **Test Mode (`testMode: true`)**: Test ads without billing. Use for development and QA testing.
|
|
91
|
+
|
|
92
|
+
When `testMode` is enabled:
|
|
93
|
+
- Bid requests include `test=1` flag per OpenRTB spec
|
|
94
|
+
- Adapter SDKs are configured for test mode (e.g., Meta test ads)
|
|
95
|
+
- No real monetization occurs
|
|
96
|
+
|
|
97
|
+
### 2. Using React Hooks (Recommended)
|
|
98
|
+
|
|
99
|
+
```jsx
|
|
100
|
+
import { useCloudXInterstitial, useCloudXRewarded } from 'cloudx-react-native';
|
|
101
|
+
|
|
102
|
+
function GameOverScreen() {
|
|
103
|
+
const interstitial = useCloudXInterstitial({
|
|
104
|
+
placement: 'game-over-interstitial',
|
|
105
|
+
adId: 'interstitial-1',
|
|
106
|
+
onLoaded: (ad) => console.log('Interstitial ready:', ad.placementName),
|
|
107
|
+
onFailed: (error) => console.error('Failed to load:', error),
|
|
108
|
+
onShown: (ad) => console.log('Interstitial shown'),
|
|
109
|
+
onClosed: (ad) => console.log('Interstitial closed'),
|
|
110
|
+
onRevenuePaid: (ad) => console.log('Revenue:', ad.revenue, ad.currency),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<View>
|
|
115
|
+
<Button
|
|
116
|
+
title="Show Interstitial"
|
|
117
|
+
onPress={() => interstitial.show()}
|
|
118
|
+
disabled={!interstitial.isReady || interstitial.isLoading}
|
|
119
|
+
/>
|
|
120
|
+
{interstitial.isLoading && <ActivityIndicator />}
|
|
121
|
+
</View>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 3. Banner Ads (Component)
|
|
127
|
+
|
|
128
|
+
```jsx
|
|
129
|
+
import { CloudXBannerView, BannerSizes } from 'cloudx-react-native';
|
|
130
|
+
|
|
131
|
+
function HomeScreen() {
|
|
132
|
+
return (
|
|
133
|
+
<View style={styles.container}>
|
|
134
|
+
<Text>Your Content Here</Text>
|
|
135
|
+
|
|
136
|
+
<CloudXBannerView
|
|
137
|
+
placement="home-banner"
|
|
138
|
+
adId="banner-1"
|
|
139
|
+
bannerSize={BannerSizes.BANNER}
|
|
140
|
+
autoRefresh={true}
|
|
141
|
+
refreshInterval={30000}
|
|
142
|
+
onAdLoaded={(event) => console.log('Banner loaded:', event.ad)}
|
|
143
|
+
onAdFailedToLoad={(event) => console.error('Banner failed:', event.error)}
|
|
144
|
+
onAdImpression={(event) => trackImpression(event.ad)}
|
|
145
|
+
onRevenuePaid={(event) => trackRevenue(event.ad)}
|
|
146
|
+
style={styles.banner}
|
|
147
|
+
/>
|
|
148
|
+
</View>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const styles = StyleSheet.create({
|
|
153
|
+
banner: {
|
|
154
|
+
width: 320,
|
|
155
|
+
height: 50,
|
|
156
|
+
marginVertical: 10,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## API Reference
|
|
164
|
+
|
|
165
|
+
### SDK Initialization
|
|
166
|
+
|
|
167
|
+
#### `initialize(appKey: string | InitOptions): Promise<InitResult>`
|
|
168
|
+
|
|
169
|
+
Initialize the CloudX SDK. Must be called before using any other SDK methods.
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
// Production initialization
|
|
173
|
+
const result = await CloudXSDK.initialize({
|
|
174
|
+
appKey: 'YOUR_APP_KEY',
|
|
175
|
+
testMode: false, // false = production, true = test ads
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// result = { success: boolean, message?: string }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**InitOptions:**
|
|
182
|
+
- `appKey` (string, required): Your CloudX app key
|
|
183
|
+
- `testMode` (boolean, optional): Enable test mode (false = production, true = test ads)
|
|
184
|
+
|
|
185
|
+
**Note:** To set a hashed user ID for targeting, call `CloudXSDK.setHashedUserID()` separately before or after initialization.
|
|
186
|
+
|
|
187
|
+
#### `isInitialized(): Promise<boolean>`
|
|
188
|
+
|
|
189
|
+
Check if the SDK has been initialized.
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
const initialized = await CloudXSDK.isInitialized();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `getVersion(): Promise<string>`
|
|
196
|
+
|
|
197
|
+
Get the CloudX SDK version.
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
const version = await CloudXSDK.getVersion();
|
|
201
|
+
console.log('CloudX SDK version:', version);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### `setLoggingEnabled(enabled: boolean): Promise<void>`
|
|
205
|
+
|
|
206
|
+
Enable or disable verbose SDK logging (useful for debugging).
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// Enable logging in development
|
|
210
|
+
if (__DEV__) {
|
|
211
|
+
await CloudXSDK.setLoggingEnabled(true);
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### `setEnvironment(environment: 'dev' | 'staging' | 'production'): Promise<void>`
|
|
216
|
+
|
|
217
|
+
Set the SDK environment. Must be called **before** `initialize()`.
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
await CloudXSDK.setEnvironment('staging');
|
|
221
|
+
await CloudXSDK.initialize('YOUR_APP_KEY');
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## User Targeting
|
|
227
|
+
|
|
228
|
+
### Hashed User ID
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
// Set hashed user ID for targeting
|
|
232
|
+
await CloudXSDK.setHashedUserID('hashed-email-or-user-id');
|
|
233
|
+
|
|
234
|
+
// Get current user ID
|
|
235
|
+
const userId = await CloudXSDK.getUserID();
|
|
236
|
+
|
|
237
|
+
// Clear user ID
|
|
238
|
+
await CloudXSDK.setUserID(null);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### User-Level Targeting
|
|
242
|
+
|
|
243
|
+
User-level key-values are **cleared automatically** when privacy regulations require removing personal data.
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
import { CloudX } from 'cloudx-react-native';
|
|
247
|
+
|
|
248
|
+
// User-specific targeting (privacy-sensitive)
|
|
249
|
+
CloudX.setUserKeyValue('age', '25');
|
|
250
|
+
CloudX.setUserKeyValue('interests', 'gaming');
|
|
251
|
+
CloudX.setUserKeyValue('subscription', 'premium');
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### App-Level Targeting
|
|
255
|
+
|
|
256
|
+
App-level key-values are **NOT affected** by privacy regulations and persist across privacy changes.
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
// App-specific targeting (persistent)
|
|
260
|
+
CloudX.setAppKeyValue('app_version', '1.2.0');
|
|
261
|
+
CloudX.setAppKeyValue('build_type', 'release');
|
|
262
|
+
CloudX.setAppKeyValue('theme', 'dark');
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Clear Targeting
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
// Clear all targeting key-values
|
|
269
|
+
CloudX.clearAllKeyValues();
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Banner Ads
|
|
275
|
+
|
|
276
|
+
### Using BannerView Component (Recommended)
|
|
277
|
+
|
|
278
|
+
```jsx
|
|
279
|
+
import { CloudXBannerView, BannerSizes } from 'cloudx-react-native';
|
|
280
|
+
|
|
281
|
+
function MyComponent() {
|
|
282
|
+
return (
|
|
283
|
+
<CloudXBannerView
|
|
284
|
+
// Required
|
|
285
|
+
placement="banner-placement"
|
|
286
|
+
adId="banner-1"
|
|
287
|
+
|
|
288
|
+
// Optional
|
|
289
|
+
bannerSize={BannerSizes.BANNER} // BANNER, MREC, LEADERBOARD
|
|
290
|
+
autoRefresh={true}
|
|
291
|
+
refreshInterval={30000} // milliseconds
|
|
292
|
+
|
|
293
|
+
// Event callbacks
|
|
294
|
+
onAdLoaded={(event) => console.log('Banner loaded:', event.ad)}
|
|
295
|
+
onAdFailedToLoad={(event) => console.error('Failed:', event.error)}
|
|
296
|
+
onAdShown={(event) => console.log('Banner shown')}
|
|
297
|
+
onAdImpression={(event) => console.log('Impression tracked')}
|
|
298
|
+
onAdClicked={(event) => console.log('Banner clicked')}
|
|
299
|
+
onAdExpanded={(event) => console.log('Banner expanded')}
|
|
300
|
+
onAdCollapsed={(event) => console.log('Banner collapsed')}
|
|
301
|
+
onRevenuePaid={(event) => console.log('Revenue:', event.ad.revenue)}
|
|
302
|
+
|
|
303
|
+
// Styling
|
|
304
|
+
style={{ width: 320, height: 50 }}
|
|
305
|
+
/>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Using Custom Hook
|
|
311
|
+
|
|
312
|
+
```jsx
|
|
313
|
+
import { useCloudXBanner } from 'cloudx-react-native';
|
|
314
|
+
|
|
315
|
+
function MyComponent() {
|
|
316
|
+
const banner = useCloudXBanner({
|
|
317
|
+
placement: 'banner-placement',
|
|
318
|
+
adId: 'banner-1',
|
|
319
|
+
bannerSize: BannerSizes.BANNER,
|
|
320
|
+
autoRefresh: true,
|
|
321
|
+
onLoaded: (ad) => console.log('Banner ready'),
|
|
322
|
+
onRevenuePaid: (ad) => trackRevenue(ad),
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
return (
|
|
326
|
+
<View>
|
|
327
|
+
{banner.render({ style: styles.banner })}
|
|
328
|
+
{banner.isLoading && <ActivityIndicator />}
|
|
329
|
+
</View>
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Imperative API
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
// Create banner
|
|
338
|
+
await CloudXSDK.createBanner({
|
|
339
|
+
placement: 'banner-placement',
|
|
340
|
+
adId: 'banner-1',
|
|
341
|
+
bannerSize: BannerSizes.BANNER,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Load banner
|
|
345
|
+
await CloudXSDK.loadBanner({ adId: 'banner-1' });
|
|
346
|
+
|
|
347
|
+
// Show banner (if hidden)
|
|
348
|
+
await CloudXSDK.showBanner({ adId: 'banner-1' });
|
|
349
|
+
|
|
350
|
+
// Hide banner (keeps it loaded)
|
|
351
|
+
await CloudXSDK.hideBanner({ adId: 'banner-1' });
|
|
352
|
+
|
|
353
|
+
// Auto-refresh control
|
|
354
|
+
await CloudXSDK.startAutoRefresh({ adId: 'banner-1' });
|
|
355
|
+
await CloudXSDK.stopAutoRefresh({ adId: 'banner-1' });
|
|
356
|
+
|
|
357
|
+
// Destroy banner (cleanup)
|
|
358
|
+
await CloudXSDK.destroyAd({ adId: 'banner-1' });
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Banner Sizes
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
export const BannerSizes = {
|
|
365
|
+
BANNER: 'BANNER', // 320x50
|
|
366
|
+
MREC: 'MREC', // 300x250
|
|
367
|
+
LEADERBOARD: 'LEADERBOARD', // 728x90
|
|
368
|
+
};
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Interstitial Ads
|
|
374
|
+
|
|
375
|
+
### Using React Hook (Recommended)
|
|
376
|
+
|
|
377
|
+
```jsx
|
|
378
|
+
import { useCloudXInterstitial } from 'cloudx-react-native';
|
|
379
|
+
|
|
380
|
+
function GameOverScreen() {
|
|
381
|
+
const interstitial = useCloudXInterstitial({
|
|
382
|
+
placement: 'game-over-interstitial',
|
|
383
|
+
adId: 'interstitial-1',
|
|
384
|
+
|
|
385
|
+
// Event callbacks
|
|
386
|
+
onLoaded: (ad) => {
|
|
387
|
+
console.log('Interstitial ready:', ad.placementName);
|
|
388
|
+
setAdReady(true);
|
|
389
|
+
},
|
|
390
|
+
onFailedToLoad: (error) => {
|
|
391
|
+
console.error('Failed to load:', error);
|
|
392
|
+
},
|
|
393
|
+
onShown: (ad) => {
|
|
394
|
+
console.log('Interstitial shown');
|
|
395
|
+
pauseGame();
|
|
396
|
+
},
|
|
397
|
+
onFailedToShow: (error) => {
|
|
398
|
+
console.error('Failed to show:', error);
|
|
399
|
+
},
|
|
400
|
+
onImpression: (ad) => {
|
|
401
|
+
console.log('Impression tracked:', ad.revenue);
|
|
402
|
+
},
|
|
403
|
+
onClosed: (ad) => {
|
|
404
|
+
console.log('Interstitial closed');
|
|
405
|
+
resumeGame();
|
|
406
|
+
},
|
|
407
|
+
onClicked: (ad) => {
|
|
408
|
+
console.log('Interstitial clicked');
|
|
409
|
+
},
|
|
410
|
+
onRevenuePaid: (ad) => {
|
|
411
|
+
console.log('Revenue:', ad.revenue, ad.currency);
|
|
412
|
+
trackRevenue(ad);
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
useEffect(() => {
|
|
417
|
+
// Auto-load on mount
|
|
418
|
+
interstitial.load();
|
|
419
|
+
|
|
420
|
+
// Cleanup on unmount
|
|
421
|
+
return () => {
|
|
422
|
+
interstitial.destroy();
|
|
423
|
+
};
|
|
424
|
+
}, []);
|
|
425
|
+
|
|
426
|
+
return (
|
|
427
|
+
<View>
|
|
428
|
+
<Text>Game Over!</Text>
|
|
429
|
+
<Button
|
|
430
|
+
title="Continue (Watch Ad)"
|
|
431
|
+
onPress={() => interstitial.show()}
|
|
432
|
+
disabled={!interstitial.isReady || interstitial.isShowing}
|
|
433
|
+
/>
|
|
434
|
+
{interstitial.isLoading && <ActivityIndicator />}
|
|
435
|
+
</View>
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Imperative API
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
// Create interstitial
|
|
444
|
+
await CloudXSDK.createInterstitial({
|
|
445
|
+
placement: 'interstitial-placement',
|
|
446
|
+
adId: 'interstitial-1',
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// Load interstitial
|
|
450
|
+
await CloudXSDK.loadInterstitial({ adId: 'interstitial-1' });
|
|
451
|
+
|
|
452
|
+
// Check if ready
|
|
453
|
+
const isReady = await CloudXSDK.isInterstitialReady({ adId: 'interstitial-1' });
|
|
454
|
+
|
|
455
|
+
// Show interstitial
|
|
456
|
+
if (isReady) {
|
|
457
|
+
await CloudXSDK.showInterstitial({ adId: 'interstitial-1' });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Destroy when done
|
|
461
|
+
await CloudXSDK.destroyAd({ adId: 'interstitial-1' });
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Rewarded Ads
|
|
467
|
+
|
|
468
|
+
### Using React Hook (Recommended)
|
|
469
|
+
|
|
470
|
+
```jsx
|
|
471
|
+
import { useCloudXRewarded } from 'cloudx-react-native';
|
|
472
|
+
|
|
473
|
+
function RewardButton() {
|
|
474
|
+
const rewarded = useCloudXRewarded({
|
|
475
|
+
placement: 'extra-lives-rewarded',
|
|
476
|
+
adId: 'rewarded-1',
|
|
477
|
+
|
|
478
|
+
onLoaded: (ad) => console.log('Rewarded ad ready'),
|
|
479
|
+
onFailedToLoad: (error) => console.error('Failed:', error),
|
|
480
|
+
onShown: (ad) => console.log('Showing rewarded ad'),
|
|
481
|
+
onRewardEarned: (ad) => {
|
|
482
|
+
console.log('🎉 User earned reward!');
|
|
483
|
+
grantExtraLife();
|
|
484
|
+
},
|
|
485
|
+
onVideoStarted: (ad) => console.log('Video started'),
|
|
486
|
+
onVideoCompleted: (ad) => console.log('Video completed'),
|
|
487
|
+
onClosed: (ad) => console.log('Rewarded ad closed'),
|
|
488
|
+
onRevenuePaid: (ad) => trackRevenue(ad),
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
useEffect(() => {
|
|
492
|
+
rewarded.load();
|
|
493
|
+
return () => rewarded.destroy();
|
|
494
|
+
}, []);
|
|
495
|
+
|
|
496
|
+
return (
|
|
497
|
+
<Button
|
|
498
|
+
title="Watch Ad for Extra Life"
|
|
499
|
+
onPress={() => rewarded.show()}
|
|
500
|
+
disabled={!rewarded.isReady}
|
|
501
|
+
/>
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Imperative API
|
|
507
|
+
|
|
508
|
+
```javascript
|
|
509
|
+
// Create rewarded ad
|
|
510
|
+
await CloudXSDK.createRewarded({
|
|
511
|
+
placement: 'rewarded-placement',
|
|
512
|
+
adId: 'rewarded-1',
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// Load rewarded ad
|
|
516
|
+
await CloudXSDK.loadRewarded({ adId: 'rewarded-1' });
|
|
517
|
+
|
|
518
|
+
// Check if ready
|
|
519
|
+
const isReady = await CloudXSDK.isRewardedReady({ adId: 'rewarded-1' });
|
|
520
|
+
|
|
521
|
+
// Show rewarded ad
|
|
522
|
+
if (isReady) {
|
|
523
|
+
await CloudXSDK.showRewarded({ adId: 'rewarded-1' });
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Listen for reward event
|
|
527
|
+
const listener = CloudXSDK.addEventListener(
|
|
528
|
+
CloudXEventTypes.REWARD_EARNED,
|
|
529
|
+
(event) => {
|
|
530
|
+
if (event.ad.adId === 'rewarded-1') {
|
|
531
|
+
console.log('User earned reward!');
|
|
532
|
+
grantReward();
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
// Clean up
|
|
538
|
+
listener.remove();
|
|
539
|
+
await CloudXSDK.destroyAd({ adId: 'rewarded-1' });
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Native Ads
|
|
545
|
+
|
|
546
|
+
### Using React Hook (Recommended)
|
|
547
|
+
|
|
548
|
+
```jsx
|
|
549
|
+
import { useCloudXNative } from 'cloudx-react-native';
|
|
550
|
+
|
|
551
|
+
function FeedItemAd({ index }) {
|
|
552
|
+
const nativeAd = useCloudXNative({
|
|
553
|
+
placement: 'feed-native',
|
|
554
|
+
adId: `native-${index}`,
|
|
555
|
+
|
|
556
|
+
onLoaded: (ad) => {
|
|
557
|
+
console.log('Native ad loaded:', ad.assets);
|
|
558
|
+
},
|
|
559
|
+
onFailedToLoad: (error) => console.error('Failed:', error),
|
|
560
|
+
onImpression: (ad) => trackImpression(ad),
|
|
561
|
+
onClicked: (ad) => console.log('Native ad clicked'),
|
|
562
|
+
onRevenuePaid: (ad) => trackRevenue(ad),
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
useEffect(() => {
|
|
566
|
+
nativeAd.load();
|
|
567
|
+
return () => nativeAd.destroy();
|
|
568
|
+
}, []);
|
|
569
|
+
|
|
570
|
+
if (!nativeAd.isLoaded || !nativeAd.assets) {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return (
|
|
575
|
+
<View style={styles.nativeAd}>
|
|
576
|
+
{nativeAd.assets.iconUrl && (
|
|
577
|
+
<Image source={{ uri: nativeAd.assets.iconUrl }} style={styles.icon} />
|
|
578
|
+
)}
|
|
579
|
+
<View style={styles.content}>
|
|
580
|
+
<Text style={styles.title}>{nativeAd.assets.title}</Text>
|
|
581
|
+
<Text style={styles.body}>{nativeAd.assets.body}</Text>
|
|
582
|
+
{nativeAd.assets.callToAction && (
|
|
583
|
+
<TouchableOpacity
|
|
584
|
+
style={styles.cta}
|
|
585
|
+
onPress={() => nativeAd.click()}
|
|
586
|
+
>
|
|
587
|
+
<Text style={styles.ctaText}>{nativeAd.assets.callToAction}</Text>
|
|
588
|
+
</TouchableOpacity>
|
|
589
|
+
)}
|
|
590
|
+
</View>
|
|
591
|
+
<Text style={styles.adBadge}>Ad</Text>
|
|
592
|
+
</View>
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### Imperative API
|
|
598
|
+
|
|
599
|
+
```javascript
|
|
600
|
+
// Create native ad
|
|
601
|
+
await CloudXSDK.createNative({
|
|
602
|
+
placement: 'native-placement',
|
|
603
|
+
adId: 'native-1',
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// Load native ad
|
|
607
|
+
await CloudXSDK.loadNative({ adId: 'native-1' });
|
|
608
|
+
|
|
609
|
+
// Get native ad assets
|
|
610
|
+
const assets = await CloudXSDK.getNativeAdAssets({ adId: 'native-1' });
|
|
611
|
+
// assets = {
|
|
612
|
+
// title: string,
|
|
613
|
+
// body: string,
|
|
614
|
+
// callToAction: string,
|
|
615
|
+
// iconUrl: string,
|
|
616
|
+
// imageUrl: string,
|
|
617
|
+
// starRating: number,
|
|
618
|
+
// advertiser: string,
|
|
619
|
+
// }
|
|
620
|
+
|
|
621
|
+
// Register view for impression tracking
|
|
622
|
+
await CloudXSDK.registerNativeAdView({ adId: 'native-1' });
|
|
623
|
+
|
|
624
|
+
// Track click
|
|
625
|
+
await CloudXSDK.trackNativeAdClick({ adId: 'native-1' });
|
|
626
|
+
|
|
627
|
+
// Destroy
|
|
628
|
+
await CloudXSDK.destroyAd({ adId: 'native-1' });
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
## MREC Ads
|
|
634
|
+
|
|
635
|
+
MREC (Medium Rectangle) ads are 300x250 banner ads commonly used in content feeds.
|
|
636
|
+
|
|
637
|
+
### Using Component
|
|
638
|
+
|
|
639
|
+
```jsx
|
|
640
|
+
import { CloudXBannerView, BannerSizes } from 'cloudx-react-native';
|
|
641
|
+
|
|
642
|
+
function ContentFeed() {
|
|
643
|
+
return (
|
|
644
|
+
<ScrollView>
|
|
645
|
+
<Article />
|
|
646
|
+
<Article />
|
|
647
|
+
|
|
648
|
+
{/* MREC ad between content */}
|
|
649
|
+
<CloudXBannerView
|
|
650
|
+
placement="feed-mrec"
|
|
651
|
+
adId="mrec-1"
|
|
652
|
+
bannerSize={BannerSizes.MREC}
|
|
653
|
+
autoRefresh={true}
|
|
654
|
+
onAdLoaded={(event) => console.log('MREC loaded')}
|
|
655
|
+
onRevenuePaid={(event) => trackRevenue(event.ad)}
|
|
656
|
+
style={{ width: 300, height: 250, alignSelf: 'center' }}
|
|
657
|
+
/>
|
|
658
|
+
|
|
659
|
+
<Article />
|
|
660
|
+
<Article />
|
|
661
|
+
</ScrollView>
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## Event Handling
|
|
669
|
+
|
|
670
|
+
### Event Types
|
|
671
|
+
|
|
672
|
+
```javascript
|
|
673
|
+
import { CloudXEventTypes } from 'cloudx-react-native';
|
|
674
|
+
|
|
675
|
+
// Banner Events
|
|
676
|
+
CloudXEventTypes.BANNER_LOADED
|
|
677
|
+
CloudXEventTypes.BANNER_FAILED_TO_LOAD
|
|
678
|
+
CloudXEventTypes.BANNER_SHOWN
|
|
679
|
+
CloudXEventTypes.BANNER_FAILED_TO_SHOW
|
|
680
|
+
CloudXEventTypes.BANNER_HIDDEN
|
|
681
|
+
CloudXEventTypes.BANNER_CLICKED
|
|
682
|
+
CloudXEventTypes.BANNER_IMPRESSION
|
|
683
|
+
CloudXEventTypes.BANNER_EXPANDED
|
|
684
|
+
CloudXEventTypes.BANNER_COLLAPSED
|
|
685
|
+
CloudXEventTypes.BANNER_REVENUE_PAID
|
|
686
|
+
|
|
687
|
+
// Interstitial Events
|
|
688
|
+
CloudXEventTypes.INTERSTITIAL_LOADED
|
|
689
|
+
CloudXEventTypes.INTERSTITIAL_FAILED_TO_LOAD
|
|
690
|
+
CloudXEventTypes.INTERSTITIAL_SHOWN
|
|
691
|
+
CloudXEventTypes.INTERSTITIAL_FAILED_TO_SHOW
|
|
692
|
+
CloudXEventTypes.INTERSTITIAL_CLOSED
|
|
693
|
+
CloudXEventTypes.INTERSTITIAL_CLICKED
|
|
694
|
+
CloudXEventTypes.INTERSTITIAL_IMPRESSION
|
|
695
|
+
CloudXEventTypes.INTERSTITIAL_CLOSED_BY_USER
|
|
696
|
+
CloudXEventTypes.INTERSTITIAL_REVENUE_PAID
|
|
697
|
+
|
|
698
|
+
// Rewarded Events
|
|
699
|
+
CloudXEventTypes.REWARDED_LOADED
|
|
700
|
+
CloudXEventTypes.REWARDED_FAILED_TO_LOAD
|
|
701
|
+
CloudXEventTypes.REWARDED_SHOWN
|
|
702
|
+
CloudXEventTypes.REWARDED_FAILED_TO_SHOW
|
|
703
|
+
CloudXEventTypes.REWARDED_CLOSED
|
|
704
|
+
CloudXEventTypes.REWARDED_CLICKED
|
|
705
|
+
CloudXEventTypes.REWARDED_IMPRESSION
|
|
706
|
+
CloudXEventTypes.REWARDED_CLOSED_BY_USER
|
|
707
|
+
CloudXEventTypes.REWARDED_REVENUE_PAID
|
|
708
|
+
CloudXEventTypes.REWARD_EARNED
|
|
709
|
+
CloudXEventTypes.REWARDED_VIDEO_STARTED
|
|
710
|
+
CloudXEventTypes.REWARDED_VIDEO_COMPLETED
|
|
711
|
+
|
|
712
|
+
// Native Events
|
|
713
|
+
CloudXEventTypes.NATIVE_LOADED
|
|
714
|
+
CloudXEventTypes.NATIVE_FAILED_TO_LOAD
|
|
715
|
+
CloudXEventTypes.NATIVE_SHOWN
|
|
716
|
+
CloudXEventTypes.NATIVE_CLICKED
|
|
717
|
+
CloudXEventTypes.NATIVE_IMPRESSION
|
|
718
|
+
CloudXEventTypes.NATIVE_REVENUE_PAID
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### Using Event Emitter
|
|
722
|
+
|
|
723
|
+
```javascript
|
|
724
|
+
import CloudXSDK, { CloudXEventTypes } from 'cloudx-react-native';
|
|
725
|
+
|
|
726
|
+
// Add listener
|
|
727
|
+
const listener = CloudXSDK.addEventListener(
|
|
728
|
+
CloudXEventTypes.INTERSTITIAL_LOADED,
|
|
729
|
+
(event) => {
|
|
730
|
+
console.log('Ad loaded:', event.ad.adId);
|
|
731
|
+
console.log('Placement:', event.ad.placementName);
|
|
732
|
+
}
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
// Remove specific listener
|
|
736
|
+
listener.remove();
|
|
737
|
+
|
|
738
|
+
// Remove all listeners for an event
|
|
739
|
+
CloudXSDK.removeAllListeners(CloudXEventTypes.INTERSTITIAL_LOADED);
|
|
740
|
+
|
|
741
|
+
// Remove all listeners
|
|
742
|
+
CloudXSDK.removeAllListeners();
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### Event Payload
|
|
746
|
+
|
|
747
|
+
All events receive a payload with the following structure:
|
|
748
|
+
|
|
749
|
+
```typescript
|
|
750
|
+
interface CloudXAdEvent {
|
|
751
|
+
ad?: CloudXAd;
|
|
752
|
+
error?: string;
|
|
753
|
+
timestamp: number;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
interface CloudXAd {
|
|
757
|
+
adId: string;
|
|
758
|
+
placementName: string;
|
|
759
|
+
adType: 'banner' | 'interstitial' | 'rewarded' | 'native' | 'mrec';
|
|
760
|
+
revenue?: number;
|
|
761
|
+
currency?: string;
|
|
762
|
+
network?: string;
|
|
763
|
+
lineItemId?: string;
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Using with React
|
|
768
|
+
|
|
769
|
+
```jsx
|
|
770
|
+
import { useEffect } from 'react';
|
|
771
|
+
import CloudXSDK, { CloudXEventTypes } from 'cloudx-react-native';
|
|
772
|
+
|
|
773
|
+
function MyComponent() {
|
|
774
|
+
useEffect(() => {
|
|
775
|
+
const listener = CloudXSDK.addEventListener(
|
|
776
|
+
CloudXEventTypes.INTERSTITIAL_LOADED,
|
|
777
|
+
handleInterstitialLoaded
|
|
778
|
+
);
|
|
779
|
+
|
|
780
|
+
return () => {
|
|
781
|
+
listener.remove(); // Cleanup on unmount
|
|
782
|
+
};
|
|
783
|
+
}, []);
|
|
784
|
+
|
|
785
|
+
const handleInterstitialLoaded = (event) => {
|
|
786
|
+
console.log('Interstitial ready:', event.ad);
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
return <View>...</View>;
|
|
790
|
+
}
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
---
|
|
794
|
+
|
|
795
|
+
## Error Handling
|
|
796
|
+
|
|
797
|
+
### Error Codes
|
|
798
|
+
|
|
799
|
+
```javascript
|
|
800
|
+
// Common error codes
|
|
801
|
+
const ErrorCodes = {
|
|
802
|
+
NO_FILL: 'NO_FILL', // No ad available
|
|
803
|
+
TIMEOUT: 'TIMEOUT', // Request timed out
|
|
804
|
+
NETWORK_ERROR: 'NETWORK_ERROR', // Network issue
|
|
805
|
+
INVALID_PLACEMENT: 'INVALID_PLACEMENT', // Placement not configured
|
|
806
|
+
SDK_NOT_INITIALIZED: 'SDK_NOT_INITIALIZED', // Must initialize first
|
|
807
|
+
AD_NOT_READY: 'AD_NOT_READY', // Ad not loaded yet
|
|
808
|
+
AD_ALREADY_SHOWN: 'AD_ALREADY_SHOWN', // Ad already displayed
|
|
809
|
+
INVALID_AD_ID: 'INVALID_AD_ID', // Ad ID not found
|
|
810
|
+
};
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### Handling Errors
|
|
814
|
+
|
|
815
|
+
```javascript
|
|
816
|
+
// Try-catch for async methods
|
|
817
|
+
try {
|
|
818
|
+
await CloudXSDK.loadInterstitial({ adId: 'interstitial-1' });
|
|
819
|
+
} catch (error) {
|
|
820
|
+
if (error.code === 'NO_FILL') {
|
|
821
|
+
console.log('No ad available, continue without ad');
|
|
822
|
+
} else if (error.code === 'NETWORK_ERROR') {
|
|
823
|
+
console.log('Network error, retry later');
|
|
824
|
+
setTimeout(() => retry(), 5000);
|
|
825
|
+
} else {
|
|
826
|
+
console.error('Unexpected error:', error);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Error events
|
|
831
|
+
CloudXSDK.addEventListener(
|
|
832
|
+
CloudXEventTypes.INTERSTITIAL_FAILED_TO_LOAD,
|
|
833
|
+
(event) => {
|
|
834
|
+
console.error('Failed to load ad:', event.error);
|
|
835
|
+
|
|
836
|
+
// Handle specific errors
|
|
837
|
+
switch (event.error) {
|
|
838
|
+
case 'NO_FILL':
|
|
839
|
+
// Show alternative content
|
|
840
|
+
break;
|
|
841
|
+
case 'TIMEOUT':
|
|
842
|
+
// Retry with longer timeout
|
|
843
|
+
break;
|
|
844
|
+
default:
|
|
845
|
+
// Generic error handling
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
);
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### Best Practices
|
|
852
|
+
|
|
853
|
+
```javascript
|
|
854
|
+
async function showInterstitialSafely(adId: string) {
|
|
855
|
+
try {
|
|
856
|
+
// Check if SDK is initialized
|
|
857
|
+
const initialized = await CloudXSDK.isInitialized();
|
|
858
|
+
if (!initialized) {
|
|
859
|
+
throw new Error('SDK not initialized');
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// Check if ad is ready
|
|
863
|
+
const isReady = await CloudXSDK.isInterstitialReady({ adId });
|
|
864
|
+
if (!isReady) {
|
|
865
|
+
console.log('Ad not ready, loading...');
|
|
866
|
+
await CloudXSDK.loadInterstitial({ adId });
|
|
867
|
+
// Wait for load event before showing
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Show the ad
|
|
872
|
+
await CloudXSDK.showInterstitial({ adId });
|
|
873
|
+
} catch (error) {
|
|
874
|
+
console.error('Error showing interstitial:', error);
|
|
875
|
+
// Continue app flow without ad
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
## Advanced Usage
|
|
883
|
+
|
|
884
|
+
### Multiple Ad Instances
|
|
885
|
+
|
|
886
|
+
```javascript
|
|
887
|
+
// Create multiple instances of the same placement
|
|
888
|
+
await CloudXSDK.createInterstitial({
|
|
889
|
+
placement: 'level-complete',
|
|
890
|
+
adId: 'level-1-interstitial',
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
await CloudXSDK.createInterstitial({
|
|
894
|
+
placement: 'level-complete',
|
|
895
|
+
adId: 'level-2-interstitial',
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
// Each instance is managed independently
|
|
899
|
+
await CloudXSDK.loadInterstitial({ adId: 'level-1-interstitial' });
|
|
900
|
+
await CloudXSDK.loadInterstitial({ adId: 'level-2-interstitial' });
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
### Preloading Ads
|
|
904
|
+
|
|
905
|
+
```javascript
|
|
906
|
+
// Preload ads early for better UX
|
|
907
|
+
async function preloadAds() {
|
|
908
|
+
// Preload interstitial
|
|
909
|
+
await CloudXSDK.createInterstitial({
|
|
910
|
+
placement: 'game-over',
|
|
911
|
+
adId: 'preloaded-interstitial',
|
|
912
|
+
});
|
|
913
|
+
await CloudXSDK.loadInterstitial({ adId: 'preloaded-interstitial' });
|
|
914
|
+
|
|
915
|
+
// Preload rewarded
|
|
916
|
+
await CloudXSDK.createRewarded({
|
|
917
|
+
placement: 'extra-life',
|
|
918
|
+
adId: 'preloaded-rewarded',
|
|
919
|
+
});
|
|
920
|
+
await CloudXSDK.loadRewarded({ adId: 'preloaded-rewarded' });
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Call during app initialization or loading screens
|
|
924
|
+
useEffect(() => {
|
|
925
|
+
preloadAds();
|
|
926
|
+
}, []);
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
### Ad Frequency Capping
|
|
930
|
+
|
|
931
|
+
```javascript
|
|
932
|
+
// Implement your own frequency capping
|
|
933
|
+
class AdFrequencyCapper {
|
|
934
|
+
constructor(maxAdsPerHour = 5) {
|
|
935
|
+
this.maxAdsPerHour = maxAdsPerHour;
|
|
936
|
+
this.adTimestamps = [];
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
canShowAd() {
|
|
940
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
941
|
+
this.adTimestamps = this.adTimestamps.filter(ts => ts > oneHourAgo);
|
|
942
|
+
return this.adTimestamps.length < this.maxAdsPerHour;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
recordAdShown() {
|
|
946
|
+
this.adTimestamps.push(Date.now());
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const frequencyCapper = new AdFrequencyCapper(5);
|
|
951
|
+
|
|
952
|
+
async function showInterstitialWithCapping(adId: string) {
|
|
953
|
+
if (!frequencyCapper.canShowAd()) {
|
|
954
|
+
console.log('Ad frequency cap reached');
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
try {
|
|
959
|
+
await CloudXSDK.showInterstitial({ adId });
|
|
960
|
+
frequencyCapper.recordAdShown();
|
|
961
|
+
} catch (error) {
|
|
962
|
+
console.error('Failed to show ad:', error);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
### Revenue Tracking
|
|
968
|
+
|
|
969
|
+
```javascript
|
|
970
|
+
// Track revenue from all ad types
|
|
971
|
+
CloudXSDK.addEventListener(
|
|
972
|
+
CloudXEventTypes.INTERSTITIAL_REVENUE_PAID,
|
|
973
|
+
(event) => trackRevenue(event.ad)
|
|
974
|
+
);
|
|
975
|
+
|
|
976
|
+
CloudXSDK.addEventListener(
|
|
977
|
+
CloudXEventTypes.REWARDED_REVENUE_PAID,
|
|
978
|
+
(event) => trackRevenue(event.ad)
|
|
979
|
+
);
|
|
980
|
+
|
|
981
|
+
CloudXSDK.addEventListener(
|
|
982
|
+
CloudXEventTypes.BANNER_REVENUE_PAID,
|
|
983
|
+
(event) => trackRevenue(event.ad)
|
|
984
|
+
);
|
|
985
|
+
|
|
986
|
+
function trackRevenue(ad: CloudXAd) {
|
|
987
|
+
if (ad.revenue && ad.currency) {
|
|
988
|
+
// Send to analytics
|
|
989
|
+
Analytics.logRevenue({
|
|
990
|
+
amount: ad.revenue,
|
|
991
|
+
currency: ad.currency,
|
|
992
|
+
adNetwork: ad.network,
|
|
993
|
+
adType: ad.adType,
|
|
994
|
+
placement: ad.placementName,
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
console.log(
|
|
998
|
+
`💰 Revenue: ${ad.currency} ${ad.revenue} from ${ad.network}`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
---
|
|
1005
|
+
|
|
1006
|
+
## React Hooks API
|
|
1007
|
+
|
|
1008
|
+
### useCloudXInterstitial
|
|
1009
|
+
|
|
1010
|
+
```typescript
|
|
1011
|
+
interface UseInterstitialOptions {
|
|
1012
|
+
placement: string;
|
|
1013
|
+
adId: string;
|
|
1014
|
+
onLoaded?: (ad: CloudXAd) => void;
|
|
1015
|
+
onFailedToLoad?: (error: string) => void;
|
|
1016
|
+
onShown?: (ad: CloudXAd) => void;
|
|
1017
|
+
onFailedToShow?: (error: string) => void;
|
|
1018
|
+
onClosed?: (ad: CloudXAd) => void;
|
|
1019
|
+
onClicked?: (ad: CloudXAd) => void;
|
|
1020
|
+
onImpression?: (ad: CloudXAd) => void;
|
|
1021
|
+
onRevenuePaid?: (ad: CloudXAd) => void;
|
|
1022
|
+
autoLoad?: boolean; // Default: false
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
interface UseInterstitialResult {
|
|
1026
|
+
isReady: boolean;
|
|
1027
|
+
isLoading: boolean;
|
|
1028
|
+
isShowing: boolean;
|
|
1029
|
+
error: string | null;
|
|
1030
|
+
load: () => Promise<void>;
|
|
1031
|
+
show: () => Promise<void>;
|
|
1032
|
+
destroy: () => Promise<void>;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const interstitial = useCloudXInterstitial(options);
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
### useCloudXRewarded
|
|
1039
|
+
|
|
1040
|
+
```typescript
|
|
1041
|
+
interface UseRewardedOptions extends UseInterstitialOptions {
|
|
1042
|
+
onRewardEarned?: (ad: CloudXAd) => void;
|
|
1043
|
+
onVideoStarted?: (ad: CloudXAd) => void;
|
|
1044
|
+
onVideoCompleted?: (ad: CloudXAd) => void;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
interface UseRewardedResult extends UseInterstitialResult {
|
|
1048
|
+
// Same as interstitial
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const rewarded = useCloudXRewarded(options);
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
### useCloudXBanner
|
|
1055
|
+
|
|
1056
|
+
```typescript
|
|
1057
|
+
interface UseBannerOptions {
|
|
1058
|
+
placement: string;
|
|
1059
|
+
adId: string;
|
|
1060
|
+
bannerSize?: BannerSize;
|
|
1061
|
+
autoRefresh?: boolean;
|
|
1062
|
+
refreshInterval?: number;
|
|
1063
|
+
onLoaded?: (ad: CloudXAd) => void;
|
|
1064
|
+
onFailedToLoad?: (error: string) => void;
|
|
1065
|
+
onShown?: (ad: CloudXAd) => void;
|
|
1066
|
+
onImpression?: (ad: CloudXAd) => void;
|
|
1067
|
+
onClicked?: (ad: CloudXAd) => void;
|
|
1068
|
+
onRevenuePaid?: (ad: CloudXAd) => void;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
interface UseBannerResult {
|
|
1072
|
+
isLoaded: boolean;
|
|
1073
|
+
isVisible: boolean;
|
|
1074
|
+
error: string | null;
|
|
1075
|
+
render: (props?: ViewProps) => React.ReactElement;
|
|
1076
|
+
show: () => Promise<void>;
|
|
1077
|
+
hide: () => Promise<void>;
|
|
1078
|
+
destroy: () => Promise<void>;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const banner = useCloudXBanner(options);
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
### useCloudXNative
|
|
1085
|
+
|
|
1086
|
+
```typescript
|
|
1087
|
+
interface UseNativeOptions {
|
|
1088
|
+
placement: string;
|
|
1089
|
+
adId: string;
|
|
1090
|
+
onLoaded?: (ad: CloudXAd) => void;
|
|
1091
|
+
onFailedToLoad?: (error: string) => void;
|
|
1092
|
+
onImpression?: (ad: CloudXAd) => void;
|
|
1093
|
+
onClicked?: (ad: CloudXAd) => void;
|
|
1094
|
+
onRevenuePaid?: (ad: CloudXAd) => void;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
interface UseNativeResult {
|
|
1098
|
+
isLoaded: boolean;
|
|
1099
|
+
assets: NativeAdAssets | null;
|
|
1100
|
+
error: string | null;
|
|
1101
|
+
load: () => Promise<void>;
|
|
1102
|
+
click: () => Promise<void>;
|
|
1103
|
+
destroy: () => Promise<void>;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
const nativeAd = useCloudXNative(options);
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
---
|
|
1110
|
+
|
|
1111
|
+
## TypeScript Support
|
|
1112
|
+
|
|
1113
|
+
The SDK is fully typed with TypeScript definitions included.
|
|
1114
|
+
|
|
1115
|
+
```typescript
|
|
1116
|
+
import CloudXSDK, {
|
|
1117
|
+
CloudXAd,
|
|
1118
|
+
CloudXAdEvent,
|
|
1119
|
+
CloudXEventTypes,
|
|
1120
|
+
BannerSizes,
|
|
1121
|
+
CloudXBannerView,
|
|
1122
|
+
useCloudXInterstitial,
|
|
1123
|
+
useCloudXRewarded,
|
|
1124
|
+
useCloudXBanner,
|
|
1125
|
+
useCloudXNative,
|
|
1126
|
+
} from 'cloudx-react-native';
|
|
1127
|
+
|
|
1128
|
+
// All types are exported and fully documented
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
---
|
|
1132
|
+
|
|
1133
|
+
## Platform Support
|
|
1134
|
+
|
|
1135
|
+
| Platform | Status | Minimum Version |
|
|
1136
|
+
|----------|--------|-----------------|
|
|
1137
|
+
| iOS | ✅ Supported | iOS 14.0+ |
|
|
1138
|
+
| Android | 🚧 Coming Soon | API 21+ (Android 5.0+) |
|
|
1139
|
+
|
|
1140
|
+
### Requirements
|
|
1141
|
+
|
|
1142
|
+
#### iOS
|
|
1143
|
+
- iOS 14.0 or higher
|
|
1144
|
+
- CocoaPods
|
|
1145
|
+
- Xcode 13.0+
|
|
1146
|
+
|
|
1147
|
+
#### Android
|
|
1148
|
+
- Android API 21 (Android 5.0) or higher
|
|
1149
|
+
- Gradle 8.0+
|
|
1150
|
+
- Kotlin 1.8+
|
|
1151
|
+
|
|
1152
|
+
#### React Native
|
|
1153
|
+
- React Native 0.70+
|
|
1154
|
+
- React 18.0+
|
|
1155
|
+
- TypeScript 4.5+ (optional but recommended)
|
|
1156
|
+
|
|
1157
|
+
---
|
|
1158
|
+
|
|
1159
|
+
## Troubleshooting
|
|
1160
|
+
|
|
1161
|
+
### SDK Not Initializing
|
|
1162
|
+
|
|
1163
|
+
```javascript
|
|
1164
|
+
// Make sure to await initialization with explicit testMode
|
|
1165
|
+
const result = await CloudXSDK.initialize({
|
|
1166
|
+
appKey: 'YOUR_APP_KEY',
|
|
1167
|
+
testMode: false, // Explicit for production
|
|
1168
|
+
});
|
|
1169
|
+
if (!result.success) {
|
|
1170
|
+
console.error('Initialization failed:', result.message);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// Check iOS setup
|
|
1174
|
+
// 1. Verify CloudXCore pod is installed
|
|
1175
|
+
// 2. Run `pod install` in ios/ directory
|
|
1176
|
+
// 3. Check Info.plist has required permissions
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
### Ads Not Loading
|
|
1180
|
+
|
|
1181
|
+
```javascript
|
|
1182
|
+
// 1. Check SDK is initialized
|
|
1183
|
+
const initialized = await CloudXSDK.isInitialized();
|
|
1184
|
+
|
|
1185
|
+
// 2. Enable logging to see detailed errors
|
|
1186
|
+
await CloudXSDK.setLoggingEnabled(true);
|
|
1187
|
+
|
|
1188
|
+
// 3. Check placement is configured in CloudX dashboard
|
|
1189
|
+
|
|
1190
|
+
// 4. Verify network connectivity
|
|
1191
|
+
|
|
1192
|
+
// 5. Check for error events
|
|
1193
|
+
CloudXSDK.addEventListener(
|
|
1194
|
+
CloudXEventTypes.INTERSTITIAL_FAILED_TO_LOAD,
|
|
1195
|
+
(event) => console.error('Load error:', event.error)
|
|
1196
|
+
);
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
### Memory Leaks
|
|
1200
|
+
|
|
1201
|
+
```javascript
|
|
1202
|
+
// Always destroy ads when done
|
|
1203
|
+
useEffect(() => {
|
|
1204
|
+
return () => {
|
|
1205
|
+
CloudXSDK.destroyAd({ adId: 'my-ad' });
|
|
1206
|
+
};
|
|
1207
|
+
}, []);
|
|
1208
|
+
|
|
1209
|
+
// Remove event listeners
|
|
1210
|
+
useEffect(() => {
|
|
1211
|
+
const listener = CloudXSDK.addEventListener(...);
|
|
1212
|
+
return () => listener.remove();
|
|
1213
|
+
}, []);
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
---
|
|
1217
|
+
|
|
1218
|
+
## Examples
|
|
1219
|
+
|
|
1220
|
+
Complete example application is available in the [example/](example/) directory.
|
|
1221
|
+
|
|
1222
|
+
```bash
|
|
1223
|
+
# Run example app
|
|
1224
|
+
cd example
|
|
1225
|
+
npm install
|
|
1226
|
+
cd ios && pod install && cd ..
|
|
1227
|
+
npm run ios
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
## Documentation
|
|
1233
|
+
|
|
1234
|
+
- **[SDK Audit](SDK_AUDIT.md)**: Detailed comparison with Flutter SDK and implementation roadmap
|
|
1235
|
+
- **[API Reference](https://docs.cloudx.io/react-native)**: Complete API documentation
|
|
1236
|
+
- **[iOS Integration Guide](https://docs.cloudx.io/ios)**: CloudX iOS SDK documentation
|
|
1237
|
+
|
|
1238
|
+
---
|
|
1239
|
+
|
|
1240
|
+
## Support
|
|
1241
|
+
|
|
1242
|
+
For technical support and questions:
|
|
1243
|
+
|
|
1244
|
+
- 📧 **Email**: support@cloudx.io
|
|
1245
|
+
- 🐛 **Issues**: [GitHub Issues](https://github.com/cloudx-io/cloudx-react-native/issues)
|
|
1246
|
+
- 📚 **Docs**: [docs.cloudx.io](https://docs.cloudx.io)
|
|
1247
|
+
|
|
1248
|
+
---
|
|
1249
|
+
|
|
1250
|
+
## License
|
|
1251
|
+
|
|
1252
|
+
This project is licensed under the Elastic License 2.0 - see the [LICENSE](LICENSE) file for details.
|
|
1253
|
+
|
|
1254
|
+
---
|
|
1255
|
+
|
|
1256
|
+
## Changelog
|
|
1257
|
+
|
|
1258
|
+
See [RELEASES](https://github.com/cloudx-io/cloudx-react-native/releases) for detailed changelog and version history.
|