com-tapp-so-sdk 0.1.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ComTappSoSdk.podspec +6 -2
- package/LICENSE +6 -17
- package/README.md +304 -183
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/comtappsosdk/ComTappSoSdkModule.kt +148 -16
- package/lib/module/Types.js.map +1 -1
- package/lib/module/index.js +58 -14
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/Types.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/Types.ts +0 -1
- package/src/index.tsx +64 -14
package/ComTappSoSdk.podspec
CHANGED
|
@@ -17,10 +17,12 @@ Pod::Spec.new do |s|
|
|
|
17
17
|
s.platforms = { :ios => min_ios_version_supported }
|
|
18
18
|
s.source = { :git => "https://www.tapp.so/.git", :tag => "#{s.version}" }
|
|
19
19
|
|
|
20
|
-
#
|
|
21
|
-
#
|
|
20
|
+
# Keep architecture behavior explicit:
|
|
21
|
+
# - New Arch: generated specs are compiled by ReactCodegen, so exclude local generated files.
|
|
22
|
+
# - Legacy: generated files are not used.
|
|
22
23
|
if ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
|
23
24
|
s.source_files = "ios/**/*.{h,m,mm,cpp}"
|
|
25
|
+
s.exclude_files = "ios/generated/**/*"
|
|
24
26
|
else
|
|
25
27
|
s.source_files = Dir.glob("ios/**/*.{h,m,mm,cpp}").reject { |path| path.include?("generated") }
|
|
26
28
|
end
|
|
@@ -46,6 +48,8 @@ Pod::Spec.new do |s|
|
|
|
46
48
|
s.dependency "ReactCommon/turbomodule/core"
|
|
47
49
|
end
|
|
48
50
|
end
|
|
51
|
+
# TODO: pin Tapp version when it is updated
|
|
49
52
|
s.dependency "Tapp"
|
|
53
|
+
# s.dependency "Tapp", "1.1.13"
|
|
50
54
|
s.dependency "Tapp-Networking"
|
|
51
55
|
end
|
package/LICENSE
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
Copyright (c) 2025 Tapp.so
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
-
in the Software without restriction, including without limitation the rights
|
|
7
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
-
furnished to do so, subject to the following conditions:
|
|
3
|
+
Use of Tapp platform features requires an active Tapp.so account and valid API credentials from https://tapp.so.
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
copies or substantial portions of the Software.
|
|
5
|
+
Unauthorized access to Tapp services, credentials, or private backend endpoints is prohibited.
|
|
13
6
|
|
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
-
SOFTWARE.
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
8
|
+
|
|
9
|
+
For licensing or commercial inquiries, contact: support@tapp.so
|
package/README.md
CHANGED
|
@@ -1,263 +1,384 @@
|
|
|
1
|
-
#
|
|
1
|
+
# com-tapp-so-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`com-tapp-so-sdk` provides the official React Native integration for Tapp, giving iOS and Android apps a single JS/TS API for SDK initialization, deep-link attribution, tracked URL generation, and event reporting.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-

|
|
7
|
-

|
|
5
|
+
Use this package to decide whether incoming links should be processed by Tapp, retrieve resolved link payloads, and subscribe to deferred-link callbacks from the native SDKs.
|
|
8
6
|
|
|
9
|
-
##
|
|
7
|
+
## Prerequisites
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
## Features
|
|
19
|
-
|
|
20
|
-
- 🚀 **Easy Initialization**: Single configuration step for both platforms.
|
|
21
|
-
- 📊 **Event Tracking**: 40+ predefined event types plus support for custom events.
|
|
22
|
-
- 🔗 **Deep Linking**: Robust support for direct and deferred deep linking.
|
|
23
|
-
- 🌐 **Affiliate URL Generation**: Create tracking links programmatically.
|
|
24
|
-
- ⚡ **TurboModules Support**: Ready for React Native's New Architecture.
|
|
25
|
-
|
|
26
|
-
## Requirements
|
|
27
|
-
|
|
28
|
-
- **React Native**: >= 0.70.0
|
|
29
|
-
- **iOS**: 13.0+
|
|
30
|
-
- **Android**: 5.0+ (API Level 21+)
|
|
31
|
-
- **TypeScript**: 5.0+ (recommended)
|
|
9
|
+
- React Native `>=0.70.0`
|
|
10
|
+
- Tapp credentials for your app:
|
|
11
|
+
- `authToken`
|
|
12
|
+
- `tappToken`
|
|
13
|
+
- Android app with `minSdk 24` and `compileSdk 34+`
|
|
14
|
+
- iOS app with deployment target `13.0+`
|
|
32
15
|
|
|
33
16
|
## Installation
|
|
34
17
|
|
|
35
|
-
1
|
|
18
|
+
### 1) Add dependency
|
|
36
19
|
|
|
37
|
-
```
|
|
20
|
+
```bash
|
|
38
21
|
yarn add com-tapp-so-sdk
|
|
39
22
|
# or
|
|
40
23
|
npm install com-tapp-so-sdk
|
|
41
24
|
```
|
|
42
25
|
|
|
43
|
-
2
|
|
26
|
+
### 2) Android setup (required)
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
This SDK depends on Tapp Android artifacts published through JitPack.
|
|
29
|
+
|
|
30
|
+
Add JitPack to your Android repositories:
|
|
31
|
+
|
|
32
|
+
```gradle
|
|
33
|
+
// android/settings.gradle (or project-level repositories)
|
|
34
|
+
dependencyResolutionManagement {
|
|
35
|
+
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
36
|
+
repositories {
|
|
37
|
+
google()
|
|
38
|
+
mavenCentral()
|
|
39
|
+
maven { url 'https://jitpack.io' }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
47
42
|
```
|
|
48
43
|
|
|
49
|
-
|
|
44
|
+
Ensure your Android app build config is compatible:
|
|
50
45
|
|
|
51
|
-
|
|
46
|
+
```gradle
|
|
47
|
+
// android/app/build.gradle
|
|
48
|
+
android {
|
|
49
|
+
compileSdkVersion 34
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
defaultConfig {
|
|
52
|
+
minSdkVersion 24
|
|
53
|
+
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
compileOptions {
|
|
56
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
57
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
58
|
+
}
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
kotlinOptions {
|
|
61
|
+
jvmTarget = '1.8'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
start({
|
|
64
|
-
authToken: 'YOUR_AUTH_TOKEN',
|
|
65
|
-
tappToken: 'YOUR_TAPP_TOKEN',
|
|
66
|
-
env: 'SANDBOX', // or 'PRODUCTION'
|
|
67
|
-
});
|
|
68
|
-
}, []);
|
|
66
|
+
### 3) iOS setup
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
Set iOS deployment target to `13.0+`:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
# ios/Podfile
|
|
72
|
+
platform :ios, '13.0'
|
|
72
73
|
```
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
Install and refresh pods:
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
```bash
|
|
78
|
+
cd ios
|
|
79
|
+
pod repo update
|
|
80
|
+
pod install
|
|
81
|
+
```
|
|
77
82
|
|
|
78
|
-
|
|
83
|
+
## Deep Link Setup
|
|
79
84
|
|
|
80
|
-
|
|
85
|
+
To enable incoming link handling, complete both setup tracks:
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
1. Configure your app in the Tapp dashboard.
|
|
88
|
+
2. Enable Universal Links / App Links in your native project.
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
**Note:** After your application is configured in the Tapp dashboard, Tapp manages the association files required for verification: `apple-app-site-association` (iOS) and `assetlinks.json` (Android). You do not host these files yourself.
|
|
91
|
+
|
|
92
|
+
### 1. Configure your application in the Tapp dashboard
|
|
93
|
+
|
|
94
|
+
General fields:
|
|
95
|
+
|
|
96
|
+
- Application Name
|
|
97
|
+
- Android App Identifier
|
|
98
|
+
- iOS Bundle Identifier
|
|
99
|
+
- Apple App Store ID
|
|
100
|
+
|
|
101
|
+
When Android deep linking is enabled:
|
|
102
|
+
|
|
103
|
+
- Enable Android Deep Linking
|
|
104
|
+
- SHA-256 Certificate Fingerprint
|
|
105
|
+
- Android App Scheme
|
|
106
|
+
|
|
107
|
+
When iOS universal linking is enabled:
|
|
108
|
+
|
|
109
|
+
- Enable iOS Universal Linking
|
|
110
|
+
- App ID Prefix
|
|
111
|
+
- iOS App Scheme
|
|
112
|
+
|
|
113
|
+
These values must match your real app configuration:
|
|
114
|
+
|
|
115
|
+
- **Android App Identifier** -> Android application ID (for example `com.example.app`)
|
|
116
|
+
- **SHA-256 Certificate Fingerprint** -> signing certificate fingerprint used for the distributed build
|
|
117
|
+
- **Android App Scheme** -> custom scheme configured in your Android manifest (if used)
|
|
118
|
+
- **iOS Bundle Identifier** -> bundle identifier configured in Xcode
|
|
119
|
+
- **Apple App Store ID** -> published App Store app ID, when applicable
|
|
120
|
+
- **App ID Prefix** -> Apple Team ID / App ID prefix used with Associated Domains
|
|
121
|
+
- **iOS App Scheme** -> URL Types scheme configured in Xcode (if used)
|
|
122
|
+
|
|
123
|
+
### 2. Enable iOS Universal Links
|
|
124
|
+
|
|
125
|
+
In Xcode, open your app target and enable **Associated Domains**.
|
|
126
|
+
|
|
127
|
+
Add your Tapp-managed link domain:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
applinks:your-tapp-link-domain.com
|
|
97
131
|
```
|
|
98
132
|
|
|
99
|
-
|
|
133
|
+
Ensure your iOS Bundle Identifier and App ID Prefix match dashboard values.
|
|
100
134
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
135
|
+
If custom URL schemes are part of your flow, ensure iOS App Scheme matches URL Types.
|
|
136
|
+
|
|
137
|
+
For reliable Universal Link validation, test on a physical device.
|
|
138
|
+
|
|
139
|
+
After dashboard setup, Tapp serves the required `apple-app-site-association` file for your configured link domain.
|
|
140
|
+
|
|
141
|
+
### 3. Enable Android App Links
|
|
142
|
+
|
|
143
|
+
Add an intent filter to the receiving activity (usually the launcher activity) in `android/app/src/main/AndroidManifest.xml`:
|
|
144
|
+
|
|
145
|
+
```xml
|
|
146
|
+
<intent-filter android:autoVerify="true">
|
|
147
|
+
<action android:name="android.intent.action.VIEW" />
|
|
148
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
149
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
150
|
+
|
|
151
|
+
<data
|
|
152
|
+
android:scheme="https"
|
|
153
|
+
android:host="your-tapp-link-domain.com" />
|
|
154
|
+
</intent-filter>
|
|
111
155
|
```
|
|
112
156
|
|
|
113
|
-
|
|
157
|
+
If needed, restrict matching using attributes such as `android:pathPrefix`.
|
|
158
|
+
|
|
159
|
+
Ensure these values align with dashboard configuration:
|
|
114
160
|
|
|
115
|
-
|
|
161
|
+
- Android App Identifier matches your Android application ID
|
|
162
|
+
- SHA-256 Certificate Fingerprint matches the certificate used to sign your app
|
|
163
|
+
- Android App Scheme matches manifest URL-scheme configuration (if used)
|
|
116
164
|
|
|
117
|
-
|
|
165
|
+
After dashboard setup, Tapp serves `assetlinks.json` for your configured link domain.
|
|
166
|
+
|
|
167
|
+
### 4. Use the SDK link APIs
|
|
168
|
+
|
|
169
|
+
Use `shouldProcess` before resolving incoming links, then fetch link data when applicable:
|
|
118
170
|
|
|
119
171
|
```tsx
|
|
120
|
-
import { fetchOriginLinkData } from 'com-tapp-so-sdk';
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
console.log('Attribution Data:', result.data);
|
|
127
|
-
console.log('Campaign:', result.data?.campaign);
|
|
128
|
-
console.log('Is First Session:', result.isFirstSession);
|
|
129
|
-
} else {
|
|
130
|
-
// Note: This is NOT a fatal error, just means no attribution data was found or config is empty
|
|
131
|
-
console.log('Attribution Info:', result.message);
|
|
172
|
+
import { fetchLinkData, fetchOriginLinkData, shouldProcess } from 'com-tapp-so-sdk';
|
|
173
|
+
|
|
174
|
+
async function handleIncomingLink(incomingUrl: string) {
|
|
175
|
+
if (await shouldProcess(incomingUrl)) {
|
|
176
|
+
const data = await fetchLinkData(incomingUrl);
|
|
177
|
+
console.log('Resolved link data:', data);
|
|
132
178
|
}
|
|
133
|
-
|
|
179
|
+
|
|
180
|
+
const originData = await fetchOriginLinkData();
|
|
181
|
+
console.log('Origin link data:', originData);
|
|
182
|
+
}
|
|
134
183
|
```
|
|
135
184
|
|
|
136
|
-
|
|
185
|
+
### Integration Checklist
|
|
186
|
+
|
|
187
|
+
- [ ] Add package dependency
|
|
188
|
+
- [ ] Add JitPack to Android repositories
|
|
189
|
+
- [ ] Confirm Android `minSdk 24` and `compileSdk 34+`
|
|
190
|
+
- [ ] Confirm iOS deployment target `13.0+`
|
|
191
|
+
- [ ] Configure dashboard application identifiers and link fields
|
|
192
|
+
- [ ] Enable iOS Associated Domains and Android App Links
|
|
193
|
+
- [ ] Initialize SDK once during app startup
|
|
194
|
+
- [ ] Run smoke checks from **Verify Integration**
|
|
195
|
+
|
|
196
|
+
## Minimal Integration
|
|
197
|
+
|
|
198
|
+
Use this as a minimum working startup flow.
|
|
137
199
|
|
|
138
200
|
```tsx
|
|
139
|
-
import {
|
|
140
|
-
import {
|
|
141
|
-
|
|
142
|
-
// In your deep link handler
|
|
143
|
-
const handleUrl = async (url: string) => {
|
|
144
|
-
// shouldProcess is async to support both architectures safely
|
|
145
|
-
const canProcess = await shouldProcess(url);
|
|
146
|
-
|
|
147
|
-
if (canProcess) {
|
|
148
|
-
const linkData = await fetchLinkData(url);
|
|
149
|
-
if (!linkData.error) {
|
|
150
|
-
console.log('Tapp Deep Link Data:', linkData);
|
|
151
|
-
console.log('Target Content:', linkData.tappUrl);
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
// Handle standard deep link
|
|
155
|
-
}
|
|
156
|
-
};
|
|
201
|
+
import { useEffect } from 'react';
|
|
202
|
+
import { start } from 'com-tapp-so-sdk';
|
|
157
203
|
|
|
158
|
-
|
|
159
|
-
|
|
204
|
+
export default function App() {
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
start({
|
|
207
|
+
authToken: 'YOUR_AUTH_TOKEN',
|
|
208
|
+
env: 'SANDBOX',
|
|
209
|
+
tappToken: 'YOUR_TAPP_TOKEN',
|
|
210
|
+
});
|
|
211
|
+
}, []);
|
|
212
|
+
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
160
215
|
```
|
|
161
216
|
|
|
162
|
-
|
|
217
|
+
## Verify Integration
|
|
163
218
|
|
|
164
|
-
|
|
219
|
+
After `start(...)`, run a short smoke test:
|
|
165
220
|
|
|
166
221
|
```tsx
|
|
167
|
-
import {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
177
|
-
console.log('Share this link:', inviteLink);
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error('Failed to generate link:', error);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
222
|
+
import { fetchOriginLinkData, shouldProcess } from 'com-tapp-so-sdk';
|
|
223
|
+
|
|
224
|
+
async function verifyTappSdk() {
|
|
225
|
+
const shouldHandle = await shouldProcess('https://example.com');
|
|
226
|
+
const origin = await fetchOriginLinkData();
|
|
227
|
+
|
|
228
|
+
console.log('shouldProcess(example):', shouldHandle);
|
|
229
|
+
console.log('origin link data:', origin);
|
|
230
|
+
}
|
|
182
231
|
```
|
|
183
232
|
|
|
184
|
-
|
|
233
|
+
Expected results:
|
|
185
234
|
|
|
186
|
-
|
|
235
|
+
- `shouldProcess(...)` returns `true` or `false` without throwing
|
|
236
|
+
- `fetchOriginLinkData()` resolves without throwing
|
|
187
237
|
|
|
188
|
-
|
|
238
|
+
## Recommended App Bootstrap Pattern
|
|
189
239
|
|
|
190
|
-
-
|
|
191
|
-
- `tappToken`: Your unique App token.
|
|
192
|
-
- `env`: `'PRODUCTION'` | `'SANDBOX'`.
|
|
240
|
+
Use this pattern for production-friendly startup: initialize once, attach listeners once, optionally process a cold-start link, and expose loading/ready/error state.
|
|
193
241
|
|
|
194
|
-
|
|
242
|
+
```tsx
|
|
243
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
244
|
+
import {
|
|
245
|
+
addDeferredLinkListener,
|
|
246
|
+
addDidFailResolvingURLListener,
|
|
247
|
+
fetchLinkData,
|
|
248
|
+
shouldProcess,
|
|
249
|
+
start,
|
|
250
|
+
} from 'com-tapp-so-sdk';
|
|
251
|
+
|
|
252
|
+
type BootstrapState = 'idle' | 'loading' | 'ready' | 'error';
|
|
253
|
+
|
|
254
|
+
export function useTappBootstrap(initialLink?: string) {
|
|
255
|
+
const [state, setState] = useState<BootstrapState>('idle');
|
|
256
|
+
const [lastError, setLastError] = useState<unknown>(null);
|
|
257
|
+
const startedRef = useRef(false);
|
|
258
|
+
|
|
259
|
+
const initialize = useCallback(async () => {
|
|
260
|
+
if (startedRef.current) {
|
|
261
|
+
setState('ready');
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
195
264
|
|
|
196
|
-
|
|
265
|
+
setState('loading');
|
|
266
|
+
try {
|
|
267
|
+
start({
|
|
268
|
+
authToken: 'YOUR_AUTH_TOKEN',
|
|
269
|
+
env: 'PRODUCTION',
|
|
270
|
+
tappToken: 'YOUR_TAPP_TOKEN',
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const deferredSub = addDeferredLinkListener((event) => {
|
|
274
|
+
console.log('Deferred link:', event);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const failSub = addDidFailResolvingURLListener((event) => {
|
|
278
|
+
console.log('Deferred-link resolve failed:', event);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
if (initialLink && (await shouldProcess(initialLink))) {
|
|
282
|
+
const data = await fetchLinkData(initialLink);
|
|
283
|
+
console.log('Cold-start link data:', data);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
startedRef.current = true;
|
|
287
|
+
setState('ready');
|
|
288
|
+
|
|
289
|
+
return () => {
|
|
290
|
+
deferredSub.remove();
|
|
291
|
+
failSub.remove();
|
|
292
|
+
};
|
|
293
|
+
} catch (err) {
|
|
294
|
+
setLastError(err);
|
|
295
|
+
setState('error');
|
|
296
|
+
throw err;
|
|
297
|
+
}
|
|
298
|
+
}, [initialLink]);
|
|
197
299
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
let cleanup: (() => void) | void;
|
|
302
|
+
initialize().then((c) => {
|
|
303
|
+
cleanup = c;
|
|
304
|
+
});
|
|
201
305
|
|
|
202
|
-
|
|
306
|
+
return () => {
|
|
307
|
+
if (cleanup) cleanup();
|
|
308
|
+
};
|
|
309
|
+
}, [initialize]);
|
|
203
310
|
|
|
204
|
-
|
|
311
|
+
return useMemo(() => ({ state, lastError }), [state, lastError]);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
205
314
|
|
|
206
|
-
|
|
207
|
-
- `error`: `boolean` — Indicates if the SDK encountered an issue (e.g. network error).
|
|
208
|
-
- `message`: `string | null` — Error description or info message.
|
|
209
|
-
- `tappUrl`: `string | null` — The deep link URL.
|
|
210
|
-
- `attrTappUrl`: `string | null` — The attributed URL.
|
|
211
|
-
- `influencer`: `string | null` — The influencer associated with the link.
|
|
212
|
-
- `data`: `{ [key: string]: string }` — Custom link data (never null, empty object if none).
|
|
213
|
-
- `isFirstSession`: `boolean` — `true` if this is the first app launch.
|
|
315
|
+
### Where to use this pattern
|
|
214
316
|
|
|
215
|
-
|
|
317
|
+
Use this bootstrap hook in your app root (the first mounted screen/component after app startup), so Tapp initialization and listener attachment happen immediately.
|
|
216
318
|
|
|
217
|
-
|
|
319
|
+
```tsx
|
|
320
|
+
import { Text, View } from 'react-native';
|
|
321
|
+
import { useTappBootstrap } from './useTappBootstrap';
|
|
218
322
|
|
|
219
|
-
|
|
323
|
+
export default function App() {
|
|
324
|
+
const { state, lastError } = useTappBootstrap();
|
|
220
325
|
|
|
221
|
-
|
|
326
|
+
if (state === 'loading' || state === 'idle') {
|
|
327
|
+
return <Text>Initializing...</Text>;
|
|
328
|
+
}
|
|
222
329
|
|
|
223
|
-
|
|
224
|
-
|
|
330
|
+
if (state === 'error') {
|
|
331
|
+
return <Text>{String(lastError)}</Text>;
|
|
332
|
+
}
|
|
225
333
|
|
|
226
|
-
|
|
334
|
+
return (
|
|
335
|
+
<View>
|
|
336
|
+
<Text>App Ready</Text>
|
|
337
|
+
</View>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
227
341
|
|
|
228
|
-
|
|
342
|
+
## Public API Summary
|
|
229
343
|
|
|
230
|
-
|
|
344
|
+
- `start(config)` initializes the SDK
|
|
345
|
+
- `url(influencer, adGroup?, creative?, data?)` generates a tracking URL
|
|
346
|
+
- `handleTappEvent(event)` reports predefined or custom events
|
|
347
|
+
- `shouldProcess(deepLink)` checks if a link should be handled by Tapp
|
|
348
|
+
- `fetchLinkData(deepLink)` resolves attribution payload for an incoming link
|
|
349
|
+
- `fetchOriginLinkData()` returns deferred/origin attribution payload
|
|
350
|
+
- `addDeferredLinkListener(listener)` subscribes to deferred deep link events
|
|
351
|
+
- `addDidFailResolvingURLListener(listener)` subscribes to resolve-failure events
|
|
231
352
|
|
|
232
|
-
|
|
233
|
-
- **Cross-Platform Consistency**: The response shapes for attribution data are identical on iOS and Android.
|
|
234
|
-
- **Null Safety**: Optional string fields (`tappUrl`, `influencer`) are returned as `null` (not `undefined` or empty strings) when no value is present, allowing for strict type checking.
|
|
353
|
+
## Debug Simulation Notes
|
|
235
354
|
|
|
236
|
-
|
|
355
|
+
- `simulateTestEvent()` is intentionally disabled and kept only for backward compatibility.
|
|
356
|
+
- `simulateDeferredLink(...)` and `simulateFailResolving(...)` are debug helpers and should never be used in production logic.
|
|
237
357
|
|
|
238
|
-
|
|
358
|
+
## Troubleshooting
|
|
239
359
|
|
|
240
|
-
|
|
241
|
-
- **Android**: Supports TurboModules via C++ bindings.
|
|
242
|
-
- **iOS**: Fully implemented in Objective-C++ / Swift with TurboModule support enabled.
|
|
360
|
+
### iOS build fails
|
|
243
361
|
|
|
244
|
-
|
|
362
|
+
- Confirm deployment target is `13.0+`
|
|
363
|
+
- Reinstall pods:
|
|
245
364
|
|
|
246
|
-
|
|
365
|
+
```bash
|
|
366
|
+
cd ios
|
|
367
|
+
rm -rf Pods Podfile.lock
|
|
368
|
+
pod install
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Android cannot resolve Tapp dependencies
|
|
247
372
|
|
|
248
|
-
|
|
249
|
-
-
|
|
250
|
-
- Try cleaning pods: `cd ios && rm -rf Pods Podfile.lock && pod install`.
|
|
251
|
-
- Ensure you open the `.xcworkspace` file, not the `.xcodeproj`.
|
|
373
|
+
- Confirm `maven { url 'https://jitpack.io' }` exists in app-level dependency repositories
|
|
374
|
+
- Sync Gradle and rebuild
|
|
252
375
|
|
|
253
|
-
|
|
254
|
-
- Ensure `start()` is called immediately upon app launch.
|
|
255
|
-
- Check that your `tappToken` matches the package name defined in the Tapp Dashboard.
|
|
376
|
+
### Events or attribution data do not appear
|
|
256
377
|
|
|
257
|
-
|
|
258
|
-
-
|
|
259
|
-
- Verify
|
|
378
|
+
- Ensure `start(...)` runs once during app startup
|
|
379
|
+
- Verify dashboard credentials (`authToken`, `tappToken`, env)
|
|
380
|
+
- Verify deep link domain and app identifiers are consistent between dashboard and app
|
|
260
381
|
|
|
261
382
|
## License
|
|
262
383
|
|
|
263
|
-
|
|
384
|
+
See [LICENSE](./LICENSE) for full licensing and usage terms.
|
package/android/build.gradle
CHANGED
|
@@ -107,7 +107,7 @@ dependencies {
|
|
|
107
107
|
implementation "com.facebook.react:react-android"
|
|
108
108
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:${getExtOrDefault('kotlinVersion')}"
|
|
109
109
|
|
|
110
|
-
implementation("com.github.
|
|
110
|
+
implementation("com.github.Tapp-so.Tapp-Android:Tapp-Native:1.1.4")
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// namespace "com.comtappsosdk"
|
|
@@ -11,6 +11,7 @@ import com.example.tapp.services.network.RequestModels
|
|
|
11
11
|
import com.example.tapp.utils.Logger
|
|
12
12
|
import com.example.tapp.utils.TappConfiguration
|
|
13
13
|
import com.facebook.react.bridge.Arguments
|
|
14
|
+
import com.facebook.react.bridge.LifecycleEventListener
|
|
14
15
|
import com.facebook.react.bridge.Promise
|
|
15
16
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
16
17
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
@@ -18,6 +19,7 @@ import com.facebook.react.bridge.ReactMethod
|
|
|
18
19
|
import com.facebook.react.bridge.ReadableMap
|
|
19
20
|
import com.facebook.react.bridge.ReadableType
|
|
20
21
|
import com.facebook.react.bridge.WritableMap
|
|
22
|
+
import com.facebook.react.common.LifecycleState
|
|
21
23
|
import com.facebook.react.module.annotations.ReactModule
|
|
22
24
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
23
25
|
import com.facebook.react.turbomodule.core.interfaces.TurboModule
|
|
@@ -29,26 +31,57 @@ import java.util.concurrent.ConcurrentLinkedQueue
|
|
|
29
31
|
|
|
30
32
|
@ReactModule(name = ComTappSoSdkModule.NAME)
|
|
31
33
|
class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
32
|
-
ReactContextBaseJavaModule(reactContext), TurboModule{
|
|
34
|
+
ReactContextBaseJavaModule(reactContext), TurboModule, LifecycleEventListener {
|
|
33
35
|
|
|
34
36
|
companion object {
|
|
35
37
|
const val NAME = "ComTappSoSdk"
|
|
36
38
|
private const val FLUSH_RETRY_INTERVAL_MS = 100L
|
|
37
|
-
private const val FLUSH_RETRY_MAX_DURATION_MS = 2000L
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
private lateinit var tapp: Tapp
|
|
41
42
|
private var listenerCount = 0
|
|
42
43
|
private val eventQueue = ConcurrentLinkedQueue<Pair<String, WritableMap?>>()
|
|
43
44
|
private val mainHandler = Handler(Looper.getMainLooper())
|
|
44
|
-
private var flushRetryStartTime: Long = 0
|
|
45
45
|
private var isFlushRetryScheduled = false
|
|
46
|
+
private var isHostResumed = false
|
|
46
47
|
|
|
47
48
|
init {
|
|
48
49
|
Logger.logInfo("[$NAME] IS_NEW_ARCHITECTURE_ENABLED: ${BuildConfig.IS_NEW_ARCHITECTURE_ENABLED}")
|
|
49
50
|
}
|
|
50
51
|
override fun getName(): String = NAME
|
|
51
52
|
|
|
53
|
+
override fun initialize() {
|
|
54
|
+
super.initialize()
|
|
55
|
+
reactApplicationContext.addLifecycleEventListener(this)
|
|
56
|
+
isHostResumed = reactApplicationContext.lifecycleState == LifecycleState.RESUMED
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
override fun invalidate() {
|
|
60
|
+
reactApplicationContext.removeLifecycleEventListener(this)
|
|
61
|
+
mainHandler.removeCallbacksAndMessages(null)
|
|
62
|
+
eventQueue.clear()
|
|
63
|
+
listenerCount = 0
|
|
64
|
+
isFlushRetryScheduled = false
|
|
65
|
+
isHostResumed = false
|
|
66
|
+
super.invalidate()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override fun onHostResume() {
|
|
70
|
+
isHostResumed = true
|
|
71
|
+
if (listenerCount > 0) {
|
|
72
|
+
flushEvents()
|
|
73
|
+
scheduleFlushRetry()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override fun onHostPause() {
|
|
78
|
+
isHostResumed = false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override fun onHostDestroy() {
|
|
82
|
+
isHostResumed = false
|
|
83
|
+
}
|
|
84
|
+
|
|
52
85
|
// Required by NativeEventEmitter
|
|
53
86
|
@ReactMethod
|
|
54
87
|
fun addListener(eventName: String) {
|
|
@@ -60,8 +93,9 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
60
93
|
|
|
61
94
|
// Required by NativeEventEmitter
|
|
62
95
|
@ReactMethod
|
|
63
|
-
fun removeListeners(count:
|
|
64
|
-
|
|
96
|
+
fun removeListeners(count: Double) {
|
|
97
|
+
// RN passes JS numbers as Double in Turbo host functions.
|
|
98
|
+
listenerCount -= count.toInt()
|
|
65
99
|
if (listenerCount < 0) {
|
|
66
100
|
listenerCount = 0
|
|
67
101
|
}
|
|
@@ -86,26 +120,23 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
86
120
|
}
|
|
87
121
|
|
|
88
122
|
/**
|
|
89
|
-
* Schedule flush retries every 100ms
|
|
90
|
-
* This
|
|
123
|
+
* Schedule flush retries every 100ms while host is resumed and listeners exist.
|
|
124
|
+
* This keeps buffered events reliable across cold starts and React activation delays.
|
|
91
125
|
*/
|
|
92
126
|
private fun scheduleFlushRetry() {
|
|
93
127
|
if (isFlushRetryScheduled) return
|
|
94
128
|
if (eventQueue.isEmpty()) return
|
|
95
129
|
|
|
96
130
|
isFlushRetryScheduled = true
|
|
97
|
-
flushRetryStartTime = System.currentTimeMillis()
|
|
98
131
|
|
|
99
132
|
mainHandler.postDelayed(object : Runnable {
|
|
100
133
|
override fun run() {
|
|
101
|
-
val elapsed = System.currentTimeMillis() - flushRetryStartTime
|
|
102
|
-
|
|
103
134
|
if (eventQueue.isEmpty() || listenerCount == 0) {
|
|
104
135
|
isFlushRetryScheduled = false
|
|
105
136
|
return
|
|
106
137
|
}
|
|
107
138
|
|
|
108
|
-
if (
|
|
139
|
+
if (!isHostResumed) {
|
|
109
140
|
isFlushRetryScheduled = false
|
|
110
141
|
return
|
|
111
142
|
}
|
|
@@ -126,6 +157,13 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
126
157
|
// If we have listeners and the React context is active, emit immediately
|
|
127
158
|
if (listenerCount > 0 && isReactActive()) {
|
|
128
159
|
mainHandler.post {
|
|
160
|
+
// Drain older queued events first to preserve order and avoid stuck buffered state.
|
|
161
|
+
while (listenerCount > 0 && isReactActive() && !eventQueue.isEmpty()) {
|
|
162
|
+
val event = eventQueue.poll()
|
|
163
|
+
if (event != null) {
|
|
164
|
+
emitNow(event.first, event.second)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
129
167
|
emitNow(eventName, params)
|
|
130
168
|
}
|
|
131
169
|
} else {
|
|
@@ -376,12 +414,22 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
376
414
|
}
|
|
377
415
|
} else {
|
|
378
416
|
withContext(Dispatchers.Main) {
|
|
379
|
-
promise?.
|
|
417
|
+
promise?.resolve(
|
|
418
|
+
createLinkDataErrorMap(
|
|
419
|
+
message = "No link data response",
|
|
420
|
+
deepLink = deepLink
|
|
421
|
+
)
|
|
422
|
+
)
|
|
380
423
|
}
|
|
381
424
|
}
|
|
382
425
|
} catch (e: Exception) {
|
|
383
426
|
withContext(Dispatchers.Main) {
|
|
384
|
-
promise?.
|
|
427
|
+
promise?.resolve(
|
|
428
|
+
createLinkDataErrorMap(
|
|
429
|
+
message = e.message ?: "An unexpected error occurred",
|
|
430
|
+
deepLink = deepLink
|
|
431
|
+
)
|
|
432
|
+
)
|
|
385
433
|
}
|
|
386
434
|
}
|
|
387
435
|
}
|
|
@@ -417,12 +465,14 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
417
465
|
}
|
|
418
466
|
} else {
|
|
419
467
|
withContext(Dispatchers.Main) {
|
|
420
|
-
promise?.
|
|
468
|
+
promise?.resolve(createLinkDataErrorMap(message = "No link data returned"))
|
|
421
469
|
}
|
|
422
470
|
}
|
|
423
471
|
} catch (e: Exception) {
|
|
424
472
|
withContext(Dispatchers.Main) {
|
|
425
|
-
promise?.
|
|
473
|
+
promise?.resolve(
|
|
474
|
+
createLinkDataErrorMap(message = e.message ?: "An unexpected error occurred")
|
|
475
|
+
)
|
|
426
476
|
}
|
|
427
477
|
}
|
|
428
478
|
}
|
|
@@ -433,6 +483,74 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
433
483
|
tapp.simulateTestEvent()
|
|
434
484
|
}
|
|
435
485
|
|
|
486
|
+
// DEBUG-only: Simulate a deferred link event for listener verification
|
|
487
|
+
@ReactMethod
|
|
488
|
+
fun simulateDeferredLink(payload: ReadableMap?) {
|
|
489
|
+
if (!BuildConfig.DEBUG) {
|
|
490
|
+
Log.w(NAME, "simulateDeferredLink is only available in DEBUG builds")
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
val safePayload = payload
|
|
495
|
+
val tappUrl = safePayload?.getString("tappUrl") ?: "tapp://simulated"
|
|
496
|
+
val attrTappUrl = safePayload?.getString("attrTappUrl") ?: "tapp://simulated/attributed"
|
|
497
|
+
val influencer = safePayload?.getString("influencer") ?: "test_influencer"
|
|
498
|
+
val isFirstSession = if (
|
|
499
|
+
safePayload?.hasKey("isFirstSession") == true && !safePayload.isNull("isFirstSession")
|
|
500
|
+
) {
|
|
501
|
+
safePayload.getBoolean("isFirstSession")
|
|
502
|
+
} else {
|
|
503
|
+
true
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
val dataMap = Arguments.createMap()
|
|
507
|
+
if (safePayload?.hasKey("data") == true && !safePayload.isNull("data")) {
|
|
508
|
+
val rawData = safePayload.getMap("data")
|
|
509
|
+
if (rawData != null) {
|
|
510
|
+
val iterator = rawData.keySetIterator()
|
|
511
|
+
while (iterator.hasNextKey()) {
|
|
512
|
+
val key = iterator.nextKey()
|
|
513
|
+
when (rawData.getType(key)) {
|
|
514
|
+
ReadableType.String -> dataMap.putString(key, rawData.getString(key))
|
|
515
|
+
ReadableType.Number -> dataMap.putString(key, rawData.getDouble(key).toString())
|
|
516
|
+
ReadableType.Boolean -> dataMap.putString(key, rawData.getBoolean(key).toString())
|
|
517
|
+
ReadableType.Null -> dataMap.putString(key, null)
|
|
518
|
+
ReadableType.Map, ReadableType.Array -> {
|
|
519
|
+
Log.w(NAME, "simulateDeferredLink dropped nested data key: $key")
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
val result: WritableMap = Arguments.createMap().apply {
|
|
527
|
+
putBoolean("error", false)
|
|
528
|
+
putString("message", null)
|
|
529
|
+
putString("tappUrl", tappUrl)
|
|
530
|
+
putString("attrTappUrl", attrTappUrl)
|
|
531
|
+
putString("influencer", influencer)
|
|
532
|
+
putBoolean("isFirstSession", isFirstSession)
|
|
533
|
+
putString("deepLink", tappUrl)
|
|
534
|
+
putMap("data", dataMap)
|
|
535
|
+
}
|
|
536
|
+
emitOrBuffer("onDeferredLinkReceived", result)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// DEBUG-only: Simulate a fail resolving URL event for listener verification
|
|
540
|
+
@ReactMethod
|
|
541
|
+
fun simulateFailResolving(url: String?, errorMessage: String?) {
|
|
542
|
+
if (!BuildConfig.DEBUG) {
|
|
543
|
+
Log.w(NAME, "simulateFailResolving is only available in DEBUG builds")
|
|
544
|
+
return
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
val result: WritableMap = Arguments.createMap().apply {
|
|
548
|
+
putString("url", url ?: "tapp://unknown")
|
|
549
|
+
putString("error", errorMessage ?: "Simulated error for testing")
|
|
550
|
+
}
|
|
551
|
+
emitOrBuffer("onDidFailResolvingURL", result)
|
|
552
|
+
}
|
|
553
|
+
|
|
436
554
|
private fun didFailResolvingURL(response: RequestModels.FailResolvingUrlResponse) {
|
|
437
555
|
val map: WritableMap = Arguments.createMap().apply {
|
|
438
556
|
putString("url", response.url)
|
|
@@ -444,7 +562,6 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
444
562
|
|
|
445
563
|
|
|
446
564
|
// --- helper methods for emitting deferred link events ---
|
|
447
|
-
//TODO::ADD LINK DATA ON RETURN
|
|
448
565
|
private fun sendDeferredLinkEvent(response: RequestModels.TappLinkDataResponse) {
|
|
449
566
|
val map: WritableMap = Arguments.createMap().apply {
|
|
450
567
|
putBoolean("error", response.error)
|
|
@@ -485,4 +602,19 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
|
|
|
485
602
|
map.putMap("data", dataMap)
|
|
486
603
|
return map
|
|
487
604
|
}
|
|
605
|
+
|
|
606
|
+
private fun createLinkDataErrorMap(message: String, deepLink: String? = null): WritableMap {
|
|
607
|
+
val map = Arguments.createMap()
|
|
608
|
+
map.putBoolean("error", true)
|
|
609
|
+
map.putString("message", message)
|
|
610
|
+
map.putString("tappUrl", null)
|
|
611
|
+
map.putString("attrTappUrl", null)
|
|
612
|
+
map.putString("influencer", null)
|
|
613
|
+
map.putBoolean("isFirstSession", false)
|
|
614
|
+
if (deepLink != null) {
|
|
615
|
+
map.putString("deepLink", deepLink)
|
|
616
|
+
}
|
|
617
|
+
map.putMap("data", Arguments.createMap())
|
|
618
|
+
return map
|
|
619
|
+
}
|
|
488
620
|
}
|
package/lib/module/Types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["EventAction"],"sourceRoot":"../../src","sources":["Types.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"names":["EventAction"],"sourceRoot":"../../src","sources":["Types.ts"],"mappings":";;AAUA,WAAYA,WAAW,0BAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW;EAAXA,WAAW,CAAXA,WAAW,4BAyCT;EAAA,OAzCFA,WAAW;AAAA","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -8,6 +8,27 @@ import { EventAction } from './Types';
|
|
|
8
8
|
const unsupportedPlatformMessage = methodName => {
|
|
9
9
|
console.log(`[${Platform.OS}] Method "${methodName}" is not supported.`);
|
|
10
10
|
};
|
|
11
|
+
const getErrorCode = error => {
|
|
12
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
13
|
+
const code = error.code;
|
|
14
|
+
if (typeof code === 'string') {
|
|
15
|
+
return code;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
};
|
|
20
|
+
const getErrorMessage = (error, fallback = 'An unexpected error occurred') => {
|
|
21
|
+
if (error instanceof Error && error.message) {
|
|
22
|
+
return error.message;
|
|
23
|
+
}
|
|
24
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
25
|
+
const message = error.message;
|
|
26
|
+
if (typeof message === 'string' && message.length > 0) {
|
|
27
|
+
return message;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return fallback;
|
|
31
|
+
};
|
|
11
32
|
export function start(config) {
|
|
12
33
|
if (Platform.OS === 'web') {
|
|
13
34
|
unsupportedPlatformMessage('start');
|
|
@@ -42,7 +63,22 @@ export function fetchLinkData(deepLink) {
|
|
|
42
63
|
if (Platform.OS === 'web') {
|
|
43
64
|
unsupportedPlatformMessage('fetchLinkData');
|
|
44
65
|
}
|
|
45
|
-
return ComTappSoSdk.fetchLinkData(deepLink)
|
|
66
|
+
return ComTappSoSdk.fetchLinkData(deepLink).catch(error => {
|
|
67
|
+
// Keep programmer-input errors explicit while normalizing runtime/sdk failures.
|
|
68
|
+
if (getErrorCode(error) === 'INVALID_URL') {
|
|
69
|
+
return Promise.reject(error);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
error: true,
|
|
73
|
+
message: getErrorMessage(error),
|
|
74
|
+
tappUrl: undefined,
|
|
75
|
+
attrTappUrl: undefined,
|
|
76
|
+
influencer: undefined,
|
|
77
|
+
data: {},
|
|
78
|
+
isFirstSession: false,
|
|
79
|
+
deepLink
|
|
80
|
+
};
|
|
81
|
+
});
|
|
46
82
|
}
|
|
47
83
|
export function shouldProcess(deepLink) {
|
|
48
84
|
if (Platform.OS === 'web') {
|
|
@@ -54,30 +90,38 @@ export function fetchOriginLinkData() {
|
|
|
54
90
|
if (Platform.OS === 'web') {
|
|
55
91
|
unsupportedPlatformMessage('fetchOriginLinkData');
|
|
56
92
|
}
|
|
57
|
-
return ComTappSoSdk.fetchOriginLinkData()
|
|
93
|
+
return ComTappSoSdk.fetchOriginLinkData().catch(error => {
|
|
94
|
+
return {
|
|
95
|
+
error: true,
|
|
96
|
+
message: getErrorMessage(error),
|
|
97
|
+
tappUrl: undefined,
|
|
98
|
+
attrTappUrl: undefined,
|
|
99
|
+
influencer: undefined,
|
|
100
|
+
data: {},
|
|
101
|
+
isFirstSession: false
|
|
102
|
+
};
|
|
103
|
+
});
|
|
58
104
|
}
|
|
59
105
|
export function simulateTestEvent() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
return ComTappSoSdk.simulateTestEvent();
|
|
106
|
+
// Kept for backward compatibility, but intentionally disabled.
|
|
107
|
+
unsupportedPlatformMessage('simulateTestEvent');
|
|
64
108
|
}
|
|
65
109
|
|
|
66
|
-
// DEBUG-only: Simulate a deferred link event for iOS testing
|
|
67
|
-
// Only works in DEBUG builds on
|
|
110
|
+
// DEBUG-only: Simulate a deferred link event for iOS/Android testing
|
|
111
|
+
// Only works in DEBUG builds on native platforms
|
|
68
112
|
export function simulateDeferredLink(payload) {
|
|
69
|
-
if (Platform.OS !== 'ios') {
|
|
70
|
-
console.log('[simulateDeferredLink] Only supported on iOS');
|
|
113
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
114
|
+
console.log('[simulateDeferredLink] Only supported on iOS/Android');
|
|
71
115
|
return;
|
|
72
116
|
}
|
|
73
117
|
ComTappSoSdk.simulateDeferredLink(payload || {});
|
|
74
118
|
}
|
|
75
119
|
|
|
76
|
-
// DEBUG-only: Simulate a fail resolving URL event for iOS testing
|
|
77
|
-
// Only works in DEBUG builds on
|
|
120
|
+
// DEBUG-only: Simulate a fail resolving URL event for iOS/Android testing
|
|
121
|
+
// Only works in DEBUG builds on native platforms
|
|
78
122
|
export function simulateFailResolving(url, error) {
|
|
79
|
-
if (Platform.OS !== 'ios') {
|
|
80
|
-
console.log('[simulateFailResolving] Only supported on iOS');
|
|
123
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
124
|
+
console.log('[simulateFailResolving] Only supported on iOS/Android');
|
|
81
125
|
return;
|
|
82
126
|
}
|
|
83
127
|
ComTappSoSdk.simulateFailResolving(url, error);
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["ComTappSoSdk","Platform","NativeModules","EventAction","unsupportedPlatformMessage","methodName","console","log","OS","start","config","TappEventEmitter","authToken","env","tappToken","url","influencer","adGroup","creative","data","handleTappEvent","event","value","customValue","eventAction","CUSTOM","
|
|
1
|
+
{"version":3,"names":["ComTappSoSdk","Platform","NativeModules","EventAction","unsupportedPlatformMessage","methodName","console","log","OS","getErrorCode","error","code","undefined","getErrorMessage","fallback","Error","message","length","start","config","TappEventEmitter","authToken","env","tappToken","url","influencer","adGroup","creative","data","handleTappEvent","event","value","customValue","eventAction","CUSTOM","metadata","fetchLinkData","deepLink","catch","Promise","reject","tappUrl","attrTappUrl","isFirstSession","shouldProcess","fetchOriginLinkData","simulateTestEvent","simulateDeferredLink","payload","simulateFailResolving"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Types.d.ts","sourceRoot":"","sources":["../../../src/Types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,eAAe,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Types.d.ts","sourceRoot":"","sources":["../../../src/Types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,oBAAoB,EAC1B,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,oBAAoB,EAC1B,MAAM,SAAS,CAAC;AAiCjB,wBAAgB,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAY9C;AAED,wBAAgB,GAAG,CACjB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBrE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAqB7E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhE;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAenE;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAID,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACjC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,IAAI,CAMP;AAID,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAMtE;AAED,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "com-tapp-so-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "react native tapp sdk",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"engines": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"url": "git+https://github.com/tapp-so/Tapp-RN"
|
|
56
56
|
},
|
|
57
57
|
"author": "nick@tapp.so <nick@tapp.so> (https://www.tapp.so/)",
|
|
58
|
-
"license": "
|
|
58
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
59
59
|
"bugs": {
|
|
60
60
|
"url": "https://github.com/tapp-so/Tapp-RN/issues"
|
|
61
61
|
},
|
package/src/Types.ts
CHANGED
package/src/index.tsx
CHANGED
|
@@ -12,6 +12,32 @@ const unsupportedPlatformMessage = (methodName: string) => {
|
|
|
12
12
|
console.log(`[${Platform.OS}] Method "${methodName}" is not supported.`);
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
const getErrorCode = (error: unknown): string | undefined => {
|
|
16
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
17
|
+
const code = (error as { code?: unknown }).code;
|
|
18
|
+
if (typeof code === 'string') {
|
|
19
|
+
return code;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getErrorMessage = (
|
|
26
|
+
error: unknown,
|
|
27
|
+
fallback: string = 'An unexpected error occurred'
|
|
28
|
+
): string => {
|
|
29
|
+
if (error instanceof Error && error.message) {
|
|
30
|
+
return error.message;
|
|
31
|
+
}
|
|
32
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
33
|
+
const message = (error as { message?: unknown }).message;
|
|
34
|
+
if (typeof message === 'string' && message.length > 0) {
|
|
35
|
+
return message;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return fallback;
|
|
39
|
+
};
|
|
40
|
+
|
|
15
41
|
export function start(config: InitConfig): void {
|
|
16
42
|
if (Platform.OS === 'web') {
|
|
17
43
|
unsupportedPlatformMessage('start');
|
|
@@ -60,7 +86,23 @@ export function fetchLinkData(deepLink: string): Promise<TappLinkDataResponse> {
|
|
|
60
86
|
if (Platform.OS === 'web') {
|
|
61
87
|
unsupportedPlatformMessage('fetchLinkData');
|
|
62
88
|
}
|
|
63
|
-
return ComTappSoSdk.fetchLinkData(deepLink)
|
|
89
|
+
return ComTappSoSdk.fetchLinkData(deepLink).catch((error: unknown) => {
|
|
90
|
+
// Keep programmer-input errors explicit while normalizing runtime/sdk failures.
|
|
91
|
+
if (getErrorCode(error) === 'INVALID_URL') {
|
|
92
|
+
return Promise.reject(error);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
error: true,
|
|
97
|
+
message: getErrorMessage(error),
|
|
98
|
+
tappUrl: undefined,
|
|
99
|
+
attrTappUrl: undefined,
|
|
100
|
+
influencer: undefined,
|
|
101
|
+
data: {},
|
|
102
|
+
isFirstSession: false,
|
|
103
|
+
deepLink,
|
|
104
|
+
};
|
|
105
|
+
});
|
|
64
106
|
}
|
|
65
107
|
|
|
66
108
|
export function shouldProcess(deepLink: string): Promise<boolean> {
|
|
@@ -74,18 +116,26 @@ export function fetchOriginLinkData(): Promise<TappLinkDataResponse> {
|
|
|
74
116
|
if (Platform.OS === 'web') {
|
|
75
117
|
unsupportedPlatformMessage('fetchOriginLinkData');
|
|
76
118
|
}
|
|
77
|
-
return ComTappSoSdk.fetchOriginLinkData()
|
|
119
|
+
return ComTappSoSdk.fetchOriginLinkData().catch((error: unknown) => {
|
|
120
|
+
return {
|
|
121
|
+
error: true,
|
|
122
|
+
message: getErrorMessage(error),
|
|
123
|
+
tappUrl: undefined,
|
|
124
|
+
attrTappUrl: undefined,
|
|
125
|
+
influencer: undefined,
|
|
126
|
+
data: {},
|
|
127
|
+
isFirstSession: false,
|
|
128
|
+
};
|
|
129
|
+
});
|
|
78
130
|
}
|
|
79
131
|
|
|
80
132
|
export function simulateTestEvent(): void {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
return ComTappSoSdk.simulateTestEvent();
|
|
133
|
+
// Kept for backward compatibility, but intentionally disabled.
|
|
134
|
+
unsupportedPlatformMessage('simulateTestEvent');
|
|
85
135
|
}
|
|
86
136
|
|
|
87
|
-
// DEBUG-only: Simulate a deferred link event for iOS testing
|
|
88
|
-
// Only works in DEBUG builds on
|
|
137
|
+
// DEBUG-only: Simulate a deferred link event for iOS/Android testing
|
|
138
|
+
// Only works in DEBUG builds on native platforms
|
|
89
139
|
export function simulateDeferredLink(payload?: {
|
|
90
140
|
tappUrl?: string;
|
|
91
141
|
attrTappUrl?: string;
|
|
@@ -93,18 +143,18 @@ export function simulateDeferredLink(payload?: {
|
|
|
93
143
|
data?: { [key: string]: string };
|
|
94
144
|
isFirstSession?: boolean;
|
|
95
145
|
}): void {
|
|
96
|
-
if (Platform.OS !== 'ios') {
|
|
97
|
-
console.log('[simulateDeferredLink] Only supported on iOS');
|
|
146
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
147
|
+
console.log('[simulateDeferredLink] Only supported on iOS/Android');
|
|
98
148
|
return;
|
|
99
149
|
}
|
|
100
150
|
ComTappSoSdk.simulateDeferredLink(payload || {});
|
|
101
151
|
}
|
|
102
152
|
|
|
103
|
-
// DEBUG-only: Simulate a fail resolving URL event for iOS testing
|
|
104
|
-
// Only works in DEBUG builds on
|
|
153
|
+
// DEBUG-only: Simulate a fail resolving URL event for iOS/Android testing
|
|
154
|
+
// Only works in DEBUG builds on native platforms
|
|
105
155
|
export function simulateFailResolving(url: string, error: string): void {
|
|
106
|
-
if (Platform.OS !== 'ios') {
|
|
107
|
-
console.log('[simulateFailResolving] Only supported on iOS');
|
|
156
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
157
|
+
console.log('[simulateFailResolving] Only supported on iOS/Android');
|
|
108
158
|
return;
|
|
109
159
|
}
|
|
110
160
|
ComTappSoSdk.simulateFailResolving(url, error);
|