com-tapp-so-sdk 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,7 @@
1
1
  package com.comtappsosdk
2
2
 
3
+ import android.os.Handler
4
+ import android.os.Looper
3
5
  import android.util.Log
4
6
  import com.example.tapp.Tapp
5
7
  import com.example.tapp.models.Affiliate
@@ -23,6 +25,7 @@ import kotlinx.coroutines.CoroutineScope
23
25
  import kotlinx.coroutines.Dispatchers
24
26
  import kotlinx.coroutines.launch
25
27
  import kotlinx.coroutines.withContext
28
+ import java.util.concurrent.ConcurrentLinkedQueue
26
29
 
27
30
  @ReactModule(name = ComTappSoSdkModule.NAME)
28
31
  class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
@@ -30,16 +33,153 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
30
33
 
31
34
  companion object {
32
35
  const val NAME = "ComTappSoSdk"
36
+ private const val FLUSH_RETRY_INTERVAL_MS = 100L
37
+ private const val FLUSH_RETRY_MAX_DURATION_MS = 2000L
33
38
  }
34
39
 
35
40
  private lateinit var tapp: Tapp
41
+ private var listenerCount = 0
42
+ private val eventQueue = ConcurrentLinkedQueue<Pair<String, WritableMap?>>()
43
+ private val mainHandler = Handler(Looper.getMainLooper())
44
+ private var flushRetryStartTime: Long = 0
45
+ private var isFlushRetryScheduled = false
36
46
 
37
47
  init {
38
- Log.i(NAME, "IS_NEW_ARCHITECTURE_ENABLED: ${BuildConfig.IS_NEW_ARCHITECTURE_ENABLED}")
48
+ // Module initialized
39
49
  }
40
-
41
50
  override fun getName(): String = NAME
42
51
 
52
+ // Required by NativeEventEmitter
53
+ @ReactMethod
54
+ fun addListener(eventName: String) {
55
+ listenerCount++
56
+ flushEvents()
57
+ // Schedule retry in case React is not active yet
58
+ scheduleFlushRetry()
59
+ }
60
+
61
+ // Required by NativeEventEmitter
62
+ @ReactMethod
63
+ fun removeListeners(count: Int) {
64
+ listenerCount -= count
65
+ if (listenerCount < 0) {
66
+ listenerCount = 0
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Check if React Native is active. Prefers hasActiveCatalystInstance(),
72
+ * falls back to hasActiveReactInstance() for newer RN versions.
73
+ */
74
+ private fun isReactActive(): Boolean {
75
+ return try {
76
+ reactApplicationContext.hasActiveCatalystInstance()
77
+ } catch (e: Exception) {
78
+ try {
79
+ // Fallback for newer React Native versions
80
+ val method = reactApplicationContext.javaClass.getMethod("hasActiveReactInstance")
81
+ method.invoke(reactApplicationContext) as Boolean
82
+ } catch (e2: Exception) {
83
+ false
84
+ }
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Schedule flush retries every 100ms for up to 2 seconds.
90
+ * This ensures buffered events are flushed even if React wasn't active initially.
91
+ */
92
+ private fun scheduleFlushRetry() {
93
+ if (isFlushRetryScheduled) return
94
+ if (eventQueue.isEmpty()) return
95
+
96
+ isFlushRetryScheduled = true
97
+ flushRetryStartTime = System.currentTimeMillis()
98
+
99
+ mainHandler.postDelayed(object : Runnable {
100
+ override fun run() {
101
+ val elapsed = System.currentTimeMillis() - flushRetryStartTime
102
+
103
+ if (eventQueue.isEmpty() || listenerCount == 0) {
104
+ isFlushRetryScheduled = false
105
+ return
106
+ }
107
+
108
+ if (elapsed > FLUSH_RETRY_MAX_DURATION_MS) {
109
+ isFlushRetryScheduled = false
110
+ return
111
+ }
112
+
113
+ flushEvents()
114
+
115
+ // Continue retrying if queue still has items
116
+ if (!eventQueue.isEmpty()) {
117
+ mainHandler.postDelayed(this, FLUSH_RETRY_INTERVAL_MS)
118
+ } else {
119
+ isFlushRetryScheduled = false
120
+ }
121
+ }
122
+ }, FLUSH_RETRY_INTERVAL_MS)
123
+ }
124
+
125
+ private fun emitOrBuffer(eventName: String, params: WritableMap?) {
126
+ // If we have listeners and the React context is active, emit immediately
127
+ if (listenerCount > 0 && isReactActive()) {
128
+ mainHandler.post {
129
+ emitNow(eventName, params)
130
+ }
131
+ } else {
132
+ // Otherwise buffer
133
+ try {
134
+ eventQueue.add(Pair(eventName, params))
135
+ // Cap buffer size at 10 to avoid memory leaks
136
+ if (eventQueue.size > 10) {
137
+ eventQueue.poll()
138
+ }
139
+ // Start retry scheduler if not already running
140
+ if (listenerCount > 0) {
141
+ scheduleFlushRetry()
142
+ }
143
+ } catch (e: Exception) {
144
+ Log.e(NAME, "Error buffering event: ${e.message}")
145
+ }
146
+ }
147
+ }
148
+
149
+ private fun flushEvents() {
150
+ if (!isReactActive() || listenerCount == 0) {
151
+ return
152
+ }
153
+
154
+ mainHandler.post {
155
+ while (listenerCount > 0 && isReactActive() && !eventQueue.isEmpty()) {
156
+ val event = eventQueue.poll()
157
+ if (event != null) {
158
+ emitNow(event.first, event.second)
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ private fun emitNow(eventName: String, params: WritableMap?) {
165
+ try {
166
+ if (isReactActive()) {
167
+ reactApplicationContext
168
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
169
+ .emit(eventName, params)
170
+ } else {
171
+ // Re-enqueue if React became inactive
172
+ eventQueue.add(Pair(eventName, params))
173
+ scheduleFlushRetry()
174
+ }
175
+ } catch (e: Exception) {
176
+ Log.e(NAME, "Error emitting event $eventName: ${e.message}")
177
+ // Re-enqueue on error
178
+ eventQueue.add(Pair(eventName, params))
179
+ scheduleFlushRetry()
180
+ }
181
+ }
182
+
43
183
 
44
184
  @ReactMethod
45
185
  fun start(authToken: String, env: String, tappToken: String) {
@@ -298,9 +438,7 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
298
438
  putString("url", response.url)
299
439
  putString("error", response.error.ifEmpty { "" })
300
440
  }
301
- reactApplicationContext
302
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
303
- .emit("onDidFailResolvingURL", map)
441
+ emitOrBuffer("onDidFailResolvingURL", map)
304
442
  }
305
443
 
306
444
 
@@ -322,18 +460,14 @@ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
322
460
  }
323
461
  putMap("data", dataMap)
324
462
  }
325
- reactApplicationContext
326
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
327
- .emit("onDeferredLinkReceived", map)
463
+ emitOrBuffer("onDeferredLinkReceived", map)
328
464
  }
329
465
 
330
466
  private fun sendTestEvent(test: String) {
331
467
  val map: WritableMap = Arguments.createMap().apply {
332
468
  putString("test", test)
333
469
  }
334
- reactApplicationContext
335
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
336
- .emit("onTestListener", map)
470
+ emitOrBuffer("onTestListener", map)
337
471
  }
338
472
 
339
473
  private fun convertResponseToMap(response: RequestModels.TappLinkDataResponse): WritableMap {
@@ -18,28 +18,31 @@
18
18
  #endif
19
19
 
20
20
  //-------------------------------------------------------------
21
- // MARK: - Internal EventEmitter
21
+ // MARK: - Internal TappEventEmitter
22
22
  // This module sends events from native to JS.
23
23
  //-------------------------------------------------------------
24
- @interface EventEmitter : RCTEventEmitter <RCTBridgeModule>
24
+ @interface TappEventEmitter : RCTEventEmitter <RCTBridgeModule>
25
25
  + (instancetype)shared;
26
+ - (void)emitOrBuffer:(NSString *)eventName body:(id)body;
26
27
  @end
27
28
 
28
29
  static NSString * const kOnDeferredLinkReceived = @"onDeferredLinkReceived";
29
30
  static NSString * const kOnDidFailResolvingURL = @"onDidFailResolvingURL";
30
31
  static NSString * const kOnTestListener = @"onTestListener";
31
- static EventEmitter *sharedInstance = nil;
32
+ static TappEventEmitter *sharedInstance = nil;
32
33
 
33
- @implementation EventEmitter
34
+ @implementation TappEventEmitter {
35
+ BOOL _hasListeners;
36
+ NSMutableArray<NSDictionary *> *_pendingEvents;
37
+ }
34
38
 
35
- RCT_EXPORT_MODULE(EventEmitter);
39
+ RCT_EXPORT_MODULE(TappEventEmitter);
36
40
 
37
41
  - (instancetype)init {
38
42
  if ((self = [super init])) {
39
43
  sharedInstance = self;
40
- [self addListener:kOnDeferredLinkReceived];
41
- [self addListener:kOnDidFailResolvingURL];
42
- [self addListener:kOnTestListener];
44
+ _pendingEvents = [NSMutableArray new];
45
+ _hasListeners = NO;
43
46
  }
44
47
  return self;
45
48
  }
@@ -56,8 +59,38 @@ RCT_EXPORT_MODULE(EventEmitter);
56
59
  return @[kOnDeferredLinkReceived, kOnDidFailResolvingURL, kOnTestListener];
57
60
  }
58
61
 
59
- - (void)startObserving {}
60
- - (void)stopObserving {}
62
+ - (void)startObserving {
63
+ _hasListeners = YES;
64
+ [self flushEvents];
65
+ }
66
+
67
+ - (void)stopObserving {
68
+ _hasListeners = NO;
69
+ }
70
+
71
+ - (void)emitOrBuffer:(NSString *)eventName body:(id)body {
72
+ // Ensure main thread for safety, as listeners are JS-side
73
+ dispatch_async(dispatch_get_main_queue(), ^{
74
+ if (self->_hasListeners) {
75
+ [self sendEventWithName:eventName body:body];
76
+ } else {
77
+ [self->_pendingEvents addObject:@{@"name": eventName, @"body": body ?: [NSNull null]}];
78
+ // Keep buffer small (last 5 events) to avoid memory issues if listeners never attach
79
+ if (self->_pendingEvents.count > 5) {
80
+ [self->_pendingEvents removeObjectAtIndex:0];
81
+ }
82
+ }
83
+ });
84
+ }
85
+
86
+ - (void)flushEvents {
87
+ dispatch_async(dispatch_get_main_queue(), ^{
88
+ for (NSDictionary *event in self->_pendingEvents) {
89
+ [self sendEventWithName:event[@"name"] body:event[@"body"]];
90
+ }
91
+ [self->_pendingEvents removeAllObjects];
92
+ });
93
+ }
61
94
 
62
95
  @end
63
96
 
@@ -273,16 +306,97 @@ RCT_EXPORT_METHOD(simulateTestEvent) {
273
306
  NSLog(@"[ComTappSoSdk] simulateTestEvent is unsupported on iOS");
274
307
  }
275
308
 
309
+ // DEBUG-only: Simulate a deferred link event for testing
310
+ // This should only be used in development for verifying event delivery
311
+ RCT_EXPORT_METHOD(simulateDeferredLink:(NSDictionary *)payload) {
312
+ #ifdef DEBUG
313
+ // Guard against nil payload from JS
314
+ NSDictionary *safePayload = payload ?: @{};
315
+ NSLog(@"[ComTappSoSdk] simulateDeferredLink called with: %@", safePayload);
316
+
317
+ // Build result on main thread to avoid any thread issues
318
+ dispatch_async(dispatch_get_main_queue(), ^{
319
+ NSDictionary *result = @{
320
+ @"error": @(NO),
321
+ @"message": [NSNull null],
322
+ @"tappUrl": safePayload[@"tappUrl"] ?: @"tapp://simulated",
323
+ @"attrTappUrl": safePayload[@"attrTappUrl"] ?: @"tapp://simulated/attributed",
324
+ @"influencer": safePayload[@"influencer"] ?: @"test_influencer",
325
+ @"data": safePayload[@"data"] ?: @{},
326
+ @"isFirstSession": safePayload[@"isFirstSession"] ?: @(YES)
327
+ };
328
+
329
+ TappEventEmitter *emitter = [TappEventEmitter shared];
330
+ if (emitter) {
331
+ [emitter emitOrBuffer:kOnDeferredLinkReceived body:result];
332
+ NSLog(@"[ComTappSoSdk] simulateDeferredLink emitted successfully");
333
+ } else {
334
+ NSLog(@"[ComTappSoSdk] simulateDeferredLink: No emitter available");
335
+ }
336
+ });
337
+ #else
338
+ NSLog(@"[ComTappSoSdk] simulateDeferredLink is only available in DEBUG builds");
339
+ #endif
340
+ }
341
+
342
+ // DEBUG-only: Simulate a fail resolving URL event for testing
343
+ RCT_EXPORT_METHOD(simulateFailResolving:(NSString *)url errorMessage:(NSString *)errorMessage) {
344
+ #ifdef DEBUG
345
+ // Guard against nil values from JS
346
+ NSString *safeUrl = url ?: @"tapp://unknown";
347
+ NSString *safeError = errorMessage ?: @"Simulated error for testing";
348
+ NSLog(@"[ComTappSoSdk] simulateFailResolving called with url: %@, error: %@", safeUrl, safeError);
349
+
350
+ // Build result and emit on main thread
351
+ dispatch_async(dispatch_get_main_queue(), ^{
352
+ NSDictionary *result = @{
353
+ @"url": safeUrl,
354
+ @"error": safeError
355
+ };
356
+
357
+ TappEventEmitter *emitter = [TappEventEmitter shared];
358
+ if (emitter) {
359
+ [emitter emitOrBuffer:kOnDidFailResolvingURL body:result];
360
+ NSLog(@"[ComTappSoSdk] simulateFailResolving emitted successfully");
361
+ } else {
362
+ NSLog(@"[ComTappSoSdk] simulateFailResolving: No emitter available");
363
+ }
364
+ });
365
+ #else
366
+ NSLog(@"[ComTappSoSdk] simulateFailResolving is only available in DEBUG builds");
367
+ #endif
368
+ }
369
+
276
370
  // ----------------------------------------------------------------------
277
371
  // (Keep all existing methods below unchanged.)
278
372
  // ----------------------------------------------------------------------
279
373
 
280
374
 
375
+ #pragma mark - TappDelegate Methods
376
+
377
+ // Helper to get the TappEventEmitter shared instance.
378
+ // In New Architecture, we must only use the shared static instance.
379
+ // Using self.bridge or moduleForClass causes EXC_BAD_ACCESS crashes.
380
+ - (TappEventEmitter *)ensureEmitter {
381
+ TappEventEmitter *emitter = [TappEventEmitter shared];
382
+ if (!emitter) {
383
+ NSLog(@"[ComTappSoSdk] WARNING: TappEventEmitter shared instance not yet available");
384
+ }
385
+ return emitter;
386
+ }
387
+
281
388
  #pragma mark - TappDelegate Methods
282
389
 
283
390
  - (void)didOpenApplicationWith:(TappDeferredLinkData *)data {
284
391
  NSDictionary *result = [self dictionaryFromLinkData:data];
285
- [[EventEmitter shared] sendEventWithName:@"onDeferredLinkReceived" body:result];
392
+ dispatch_async(dispatch_get_main_queue(), ^{
393
+ TappEventEmitter *emitter = [self ensureEmitter];
394
+ if (emitter) {
395
+ [emitter emitOrBuffer:kOnDeferredLinkReceived body:result];
396
+ } else {
397
+ NSLog(@"[ComTappSoSdk] ERROR: TappEventEmitter unavailable, event dropped");
398
+ }
399
+ });
286
400
  }
287
401
 
288
402
  - (void)didFailResolvingURLWithUrl:(NSURL *)url error:(NSError *)error {
@@ -290,7 +404,14 @@ RCT_EXPORT_METHOD(simulateTestEvent) {
290
404
  @"url": url.absoluteString ?: @"",
291
405
  @"error": error.localizedDescription ?: @""
292
406
  };
293
- [[EventEmitter shared] sendEventWithName:@"onDidFailResolvingURL" body:result];
407
+ dispatch_async(dispatch_get_main_queue(), ^{
408
+ TappEventEmitter *emitter = [self ensureEmitter];
409
+ if (emitter) {
410
+ [emitter emitOrBuffer:kOnDidFailResolvingURL body:result];
411
+ } else {
412
+ NSLog(@"[ComTappSoSdk] ERROR: TappEventEmitter unavailable, event dropped");
413
+ }
414
+ });
294
415
  }
295
416
 
296
417
  - (NSString *)mapEventAction:(double)eventAction {
@@ -54,6 +54,22 @@ namespace facebook::react {
54
54
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "simulateTestEvent", @selector(simulateTestEvent), args, count);
55
55
  }
56
56
 
57
+ static facebook::jsi::Value __hostFunction_NativeComTappSoSdkSpecJSI_simulateDeferredLink(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
58
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "simulateDeferredLink", @selector(simulateDeferredLink:), args, count);
59
+ }
60
+
61
+ static facebook::jsi::Value __hostFunction_NativeComTappSoSdkSpecJSI_simulateFailResolving(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
62
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "simulateFailResolving", @selector(simulateFailResolving:errorMessage:), args, count);
63
+ }
64
+
65
+ static facebook::jsi::Value __hostFunction_NativeComTappSoSdkSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
66
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count);
67
+ }
68
+
69
+ static facebook::jsi::Value __hostFunction_NativeComTappSoSdkSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
70
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count);
71
+ }
72
+
57
73
  NativeComTappSoSdkSpecJSI::NativeComTappSoSdkSpecJSI(const ObjCTurboModule::InitParams &params)
58
74
  : ObjCTurboModule(params) {
59
75
 
@@ -77,5 +93,17 @@ namespace facebook::react {
77
93
 
78
94
  methodMap_["simulateTestEvent"] = MethodMetadata {0, __hostFunction_NativeComTappSoSdkSpecJSI_simulateTestEvent};
79
95
 
96
+
97
+ methodMap_["simulateDeferredLink"] = MethodMetadata {1, __hostFunction_NativeComTappSoSdkSpecJSI_simulateDeferredLink};
98
+
99
+
100
+ methodMap_["simulateFailResolving"] = MethodMetadata {2, __hostFunction_NativeComTappSoSdkSpecJSI_simulateFailResolving};
101
+
102
+
103
+ methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeComTappSoSdkSpecJSI_addListener};
104
+
105
+
106
+ methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeComTappSoSdkSpecJSI_removeListeners};
107
+
80
108
  }
81
109
  } // namespace facebook::react
@@ -58,6 +58,11 @@ NS_ASSUME_NONNULL_BEGIN
58
58
  - (void)fetchOriginLinkData:(RCTPromiseResolveBlock)resolve
59
59
  reject:(RCTPromiseRejectBlock)reject;
60
60
  - (void)simulateTestEvent;
61
+ - (void)simulateDeferredLink:(NSDictionary *)payload;
62
+ - (void)simulateFailResolving:(NSString *)url
63
+ errorMessage:(NSString *)errorMessage;
64
+ - (void)addListener:(NSString *)eventName;
65
+ - (void)removeListeners:(double)count;
61
66
 
62
67
  @end
63
68
 
@@ -60,6 +60,35 @@ static jsi::Value __hostFunction_NativeComTappSoSdkCxxSpecJSI_simulateTestEvent(
60
60
  );
61
61
  return jsi::Value::undefined();
62
62
  }
63
+ static jsi::Value __hostFunction_NativeComTappSoSdkCxxSpecJSI_simulateDeferredLink(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
64
+ static_cast<NativeComTappSoSdkCxxSpecJSI *>(&turboModule)->simulateDeferredLink(
65
+ rt,
66
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt)
67
+ );
68
+ return jsi::Value::undefined();
69
+ }
70
+ static jsi::Value __hostFunction_NativeComTappSoSdkCxxSpecJSI_simulateFailResolving(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
71
+ static_cast<NativeComTappSoSdkCxxSpecJSI *>(&turboModule)->simulateFailResolving(
72
+ rt,
73
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt),
74
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asString(rt)
75
+ );
76
+ return jsi::Value::undefined();
77
+ }
78
+ static jsi::Value __hostFunction_NativeComTappSoSdkCxxSpecJSI_addListener(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
79
+ static_cast<NativeComTappSoSdkCxxSpecJSI *>(&turboModule)->addListener(
80
+ rt,
81
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
82
+ );
83
+ return jsi::Value::undefined();
84
+ }
85
+ static jsi::Value __hostFunction_NativeComTappSoSdkCxxSpecJSI_removeListeners(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
86
+ static_cast<NativeComTappSoSdkCxxSpecJSI *>(&turboModule)->removeListeners(
87
+ rt,
88
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber()
89
+ );
90
+ return jsi::Value::undefined();
91
+ }
63
92
 
64
93
  NativeComTappSoSdkCxxSpecJSI::NativeComTappSoSdkCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
65
94
  : TurboModule("ComTappSoSdk", jsInvoker) {
@@ -70,6 +99,10 @@ NativeComTappSoSdkCxxSpecJSI::NativeComTappSoSdkCxxSpecJSI(std::shared_ptr<CallI
70
99
  methodMap_["shouldProcess"] = MethodMetadata {1, __hostFunction_NativeComTappSoSdkCxxSpecJSI_shouldProcess};
71
100
  methodMap_["fetchOriginLinkData"] = MethodMetadata {0, __hostFunction_NativeComTappSoSdkCxxSpecJSI_fetchOriginLinkData};
72
101
  methodMap_["simulateTestEvent"] = MethodMetadata {0, __hostFunction_NativeComTappSoSdkCxxSpecJSI_simulateTestEvent};
102
+ methodMap_["simulateDeferredLink"] = MethodMetadata {1, __hostFunction_NativeComTappSoSdkCxxSpecJSI_simulateDeferredLink};
103
+ methodMap_["simulateFailResolving"] = MethodMetadata {2, __hostFunction_NativeComTappSoSdkCxxSpecJSI_simulateFailResolving};
104
+ methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeComTappSoSdkCxxSpecJSI_addListener};
105
+ methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeComTappSoSdkCxxSpecJSI_removeListeners};
73
106
  }
74
107
 
75
108
 
@@ -27,6 +27,10 @@ public:
27
27
  virtual jsi::Value shouldProcess(jsi::Runtime &rt, jsi::String deepLink) = 0;
28
28
  virtual jsi::Value fetchOriginLinkData(jsi::Runtime &rt) = 0;
29
29
  virtual void simulateTestEvent(jsi::Runtime &rt) = 0;
30
+ virtual void simulateDeferredLink(jsi::Runtime &rt, jsi::Object payload) = 0;
31
+ virtual void simulateFailResolving(jsi::Runtime &rt, jsi::String url, jsi::String errorMessage) = 0;
32
+ virtual void addListener(jsi::Runtime &rt, jsi::String eventName) = 0;
33
+ virtual void removeListeners(jsi::Runtime &rt, double count) = 0;
30
34
 
31
35
  };
32
36
 
@@ -113,6 +117,38 @@ private:
113
117
  return bridging::callFromJs<void>(
114
118
  rt, &T::simulateTestEvent, jsInvoker_, instance_);
115
119
  }
120
+ void simulateDeferredLink(jsi::Runtime &rt, jsi::Object payload) override {
121
+ static_assert(
122
+ bridging::getParameterCount(&T::simulateDeferredLink) == 2,
123
+ "Expected simulateDeferredLink(...) to have 2 parameters");
124
+
125
+ return bridging::callFromJs<void>(
126
+ rt, &T::simulateDeferredLink, jsInvoker_, instance_, std::move(payload));
127
+ }
128
+ void simulateFailResolving(jsi::Runtime &rt, jsi::String url, jsi::String errorMessage) override {
129
+ static_assert(
130
+ bridging::getParameterCount(&T::simulateFailResolving) == 3,
131
+ "Expected simulateFailResolving(...) to have 3 parameters");
132
+
133
+ return bridging::callFromJs<void>(
134
+ rt, &T::simulateFailResolving, jsInvoker_, instance_, std::move(url), std::move(errorMessage));
135
+ }
136
+ void addListener(jsi::Runtime &rt, jsi::String eventName) override {
137
+ static_assert(
138
+ bridging::getParameterCount(&T::addListener) == 2,
139
+ "Expected addListener(...) to have 2 parameters");
140
+
141
+ return bridging::callFromJs<void>(
142
+ rt, &T::addListener, jsInvoker_, instance_, std::move(eventName));
143
+ }
144
+ void removeListeners(jsi::Runtime &rt, double count) override {
145
+ static_assert(
146
+ bridging::getParameterCount(&T::removeListeners) == 2,
147
+ "Expected removeListeners(...) to have 2 parameters");
148
+
149
+ return bridging::callFromJs<void>(
150
+ rt, &T::removeListeners, jsInvoker_, instance_, std::move(count));
151
+ }
116
152
 
117
153
  private:
118
154
  friend class NativeComTappSoSdkCxxSpec;
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeComTappSoSdk.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AA6CpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,cAAc,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeComTappSoSdk.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAqDpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,cAAc,CAAC","ignoreList":[]}
@@ -1,10 +1,26 @@
1
1
  "use strict";
2
2
 
3
- import { NativeEventEmitter, NativeModules } from 'react-native';
3
+ import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
4
4
  const {
5
- EventEmitter
5
+ TappEventEmitter,
6
+ ComTappSoSdk
6
7
  } = NativeModules;
7
- const eventEmitter = new NativeEventEmitter(EventEmitter);
8
+ import ComTappSoSdkTurbo from './NativeComTappSoSdk';
9
+ let eventEmitterModule;
10
+ if (Platform.OS === 'ios') {
11
+ eventEmitterModule = TappEventEmitter;
12
+ } else if (Platform.OS === 'android') {
13
+ // Policy: Prefer NativeModules.ComTappSoSdk because it is consistently wired
14
+ if (ComTappSoSdk && typeof ComTappSoSdk.addListener === 'function') {
15
+ eventEmitterModule = ComTappSoSdk;
16
+ } else if (ComTappSoSdkTurbo && typeof ComTappSoSdkTurbo.addListener === 'function') {
17
+ eventEmitterModule = ComTappSoSdkTurbo;
18
+ }
19
+ }
20
+ if (!eventEmitterModule) {
21
+ console.warn(`[TappSDK] Event emitter module is undefined for platform: ${Platform.OS}. Events will not be received.`);
22
+ }
23
+ const eventEmitter = new NativeEventEmitter(eventEmitterModule);
8
24
  export function addDeferredLinkListener(listener) {
9
25
  return eventEmitter.addListener('onDeferredLinkReceived', listener);
10
26
  }
@@ -1 +1 @@
1
- {"version":3,"names":["NativeEventEmitter","NativeModules","EventEmitter","eventEmitter","addDeferredLinkListener","listener","addListener","addDidFailResolvingURLListener","addTestListener"],"sourceRoot":"../../src","sources":["events.ts"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,aAAa,QAAQ,cAAc;AAGhE,MAAM;EAAEC;AAAa,CAAC,GAAGD,aAAa;AACtC,MAAME,YAAY,GAAG,IAAIH,kBAAkB,CAACE,YAAY,CAAC;AAEzD,OAAO,SAASE,uBAAuBA,CACrCC,QAAkD,EAClD;EACA,OAAOF,YAAY,CAACG,WAAW,CAAC,wBAAwB,EAAED,QAAQ,CAAC;AACrE;AAEA,OAAO,SAASE,8BAA8BA,CAC5CF,QAA4D,EAC5D;EACA,OAAOF,YAAY,CAACG,WAAW,CAAC,uBAAuB,EAAED,QAAQ,CAAC;AACpE;AAEA,OAAO,SAASG,eAAeA,CAACH,QAA0C,EAAE;EAC1E,OAAOF,YAAY,CAACG,WAAW,CAAC,gBAAgB,EAAED,QAAQ,CAAC;AAC7D","ignoreList":[]}
1
+ {"version":3,"names":["NativeEventEmitter","NativeModules","Platform","TappEventEmitter","ComTappSoSdk","ComTappSoSdkTurbo","eventEmitterModule","OS","addListener","console","warn","eventEmitter","addDeferredLinkListener","listener","addDidFailResolvingURLListener","addTestListener"],"sourceRoot":"../../src","sources":["events.ts"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAG1E,MAAM;EAAEC,gBAAgB;EAAEC;AAAa,CAAC,GAAGH,aAAa;AACxD,OAAOI,iBAAiB,MAAM,sBAAsB;AAEpD,IAAIC,kBAAuB;AAE3B,IAAIJ,QAAQ,CAACK,EAAE,KAAK,KAAK,EAAE;EACzBD,kBAAkB,GAAGH,gBAAgB;AACvC,CAAC,MAAM,IAAID,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;EACpC;EACA,IAAIH,YAAY,IAAI,OAAOA,YAAY,CAACI,WAAW,KAAK,UAAU,EAAE;IAClEF,kBAAkB,GAAGF,YAAY;EACnC,CAAC,MAAM,IACLC,iBAAiB,IACjB,OAAOA,iBAAiB,CAACG,WAAW,KAAK,UAAU,EACnD;IACAF,kBAAkB,GAAGD,iBAAiB;EACxC;AACF;AAEA,IAAI,CAACC,kBAAkB,EAAE;EACvBG,OAAO,CAACC,IAAI,CACV,6DAA6DR,QAAQ,CAACK,EAAE,gCAC1E,CAAC;AACH;AAEA,MAAMI,YAAY,GAAG,IAAIX,kBAAkB,CAACM,kBAAkB,CAAC;AAE/D,OAAO,SAASM,uBAAuBA,CACrCC,QAAkD,EAClD;EACA,OAAOF,YAAY,CAACH,WAAW,CAAC,wBAAwB,EAAEK,QAAQ,CAAC;AACrE;AAEA,OAAO,SAASC,8BAA8BA,CAC5CD,QAA4D,EAC5D;EACA,OAAOF,YAAY,CAACH,WAAW,CAAC,uBAAuB,EAAEK,QAAQ,CAAC;AACpE;AAEA,OAAO,SAASE,eAAeA,CAACF,QAA0C,EAAE;EAC1E,OAAOF,YAAY,CAACH,WAAW,CAAC,gBAAgB,EAAEK,QAAQ,CAAC;AAC7D","ignoreList":[]}
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  import ComTappSoSdk from './NativeComTappSoSdk';
4
- import { Platform } from 'react-native';
4
+ import { Platform, NativeModules } from 'react-native';
5
5
  import { EventAction } from './Types';
6
6
 
7
7
  // Helper to handle unsupported platforms
@@ -12,6 +12,15 @@ export function start(config) {
12
12
  if (Platform.OS === 'web') {
13
13
  unsupportedPlatformMessage('start');
14
14
  }
15
+ // Force-init the event emitter module to prevent race conditions
16
+ if (Platform.OS === 'ios') {
17
+ const {
18
+ TappEventEmitter
19
+ } = NativeModules;
20
+ if (TappEventEmitter) {
21
+ /* no-op access to trigger init */
22
+ }
23
+ }
15
24
  ComTappSoSdk.start(config.authToken, config.env, config.tappToken);
16
25
  }
17
26
  export function url(influencer, adGroup, creative, data) {
@@ -53,6 +62,26 @@ export function simulateTestEvent() {
53
62
  }
54
63
  return ComTappSoSdk.simulateTestEvent();
55
64
  }
65
+
66
+ // DEBUG-only: Simulate a deferred link event for iOS testing
67
+ // Only works in DEBUG builds on iOS
68
+ export function simulateDeferredLink(payload) {
69
+ if (Platform.OS !== 'ios') {
70
+ console.log('[simulateDeferredLink] Only supported on iOS');
71
+ return;
72
+ }
73
+ ComTappSoSdk.simulateDeferredLink(payload || {});
74
+ }
75
+
76
+ // DEBUG-only: Simulate a fail resolving URL event for iOS testing
77
+ // Only works in DEBUG builds on iOS
78
+ export function simulateFailResolving(url, error) {
79
+ if (Platform.OS !== 'ios') {
80
+ console.log('[simulateFailResolving] Only supported on iOS');
81
+ return;
82
+ }
83
+ ComTappSoSdk.simulateFailResolving(url, error);
84
+ }
56
85
  export * from './Types';
57
86
  export * from './events';
58
87
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["ComTappSoSdk","Platform","EventAction","unsupportedPlatformMessage","methodName","console","log","OS","start","config","authToken","env","tappToken","url","influencer","adGroup","creative","data","handleTappEvent","event","value","customValue","eventAction","CUSTOM","undefined","metadata","fetchLinkData","deepLink","shouldProcess","fetchOriginLinkData","simulateTestEvent"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,YAAY,MAAM,sBAAsB;AAC/C,SAASC,QAAQ,QAAQ,cAAc;AACvC,SACEC,WAAW,QAIN,SAAS;;AAEhB;AACA,MAAMC,0BAA0B,GAAIC,UAAkB,IAAK;EACzDC,OAAO,CAACC,GAAG,CAAC,IAAIL,QAAQ,CAACM,EAAE,aAAaH,UAAU,qBAAqB,CAAC;AAC1E,CAAC;AAED,OAAO,SAASI,KAAKA,CAACC,MAAkB,EAAQ;EAC9C,IAAIR,QAAQ,CAACM,EAAE,KAAK,KAAK,EAAE;IACzBJ,0BAA0B,CAAC,OAAO,CAAC;EACrC;EACAH,YAAY,CAACQ,KAAK,CAACC,MAAM,CAACC,SAAS,EAAED,MAAM,CAACE,GAAG,EAAEF,MAAM,CAACG,SAAS,CAAC;AACpE;AAEA,OAAO,SAASC,GAAGA,CACjBC,UAAkB,EAClBC,OAAgB,EAChBC,QAAiB,EACjBC,IAAgC,EACf;EACjB,IAAIhB,QAAQ,CAACM,EAAE,KAAK,KAAK,EAAE;IACzBJ,0BAA0B,CAAC,KAAK,CAAC;EACnC;EACA,OAAOH,YAAY,CAACa,GAAG,CAACC,UAAU,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,IAAI,CAAC;AAC9D;AAEA,OAAO,SAASC,eAAeA,CAACC,KAAoB,EAAmB;EACrE,IAAIlB,QAAQ,CAACM,EAAE,KAAK,KAAK,EAAE;IACzBJ,0BAA0B,CAAC,iBAAiB,CAAC;EAC/C;EAEA,MAAMiB,KAAK,GAAGD,KAAK,CAACE,WAAW,IAAI,4BAA4B;EAC/D,MAAMA,WAAW,GACfF,KAAK,CAACG,WAAW,KAAKpB,WAAW,CAACqB,MAAM,GAAGH,KAAK,GAAGI,SAAS;EAE9D,MAAMC,QAAQ,GAAGN,KAAK,CAACM,QAAQ;EAE/B,OAAOzB,YAAY,CAACkB,eAAe,CACjCC,KAAK,CAACG,WAAW,EACjBD,WAAW,EACXI,QACF,CAAC;AACH;AAEA,OAAO,SAASC,aAAaA,CAACC,QAAgB,EAAiC;EAC7E,IAAI1B,QAAQ,CAACM,EAAE,KAAK,KAAK,EAAE;IACzBJ,0BAA0B,CAAC,eAAe,CAAC;EAC7C;EACA,OAAOH,YAAY,CAAC0B,aAAa,CAACC,QAAQ,CAAC;AAC7C;AAEA,OAAO,SAASC,aAAaA,CAACD,QAAgB,EAAoB;EAChE,IAAI1B,QAAQ,CAACM,EAAE,KAAK,KAAK,EAAE;IACzBJ,0BAA0B,CAAC,eAAe,CAAC;EAC7C;EACA,OAAOH,YAAY,CAAC4B,aAAa,CAACD,QAAQ,CAAC;AAC7C;AAEA,OAAO,SAASE,mBAAmBA,CAAA,EAAkC;EACnE,IAAI5B,QAAQ,CAACM,EAAE,KAAK,KAAK,EAAE;IACzBJ,0BAA0B,CAAC,qBAAqB,CAAC;EACnD;EACA,OAAOH,YAAY,CAAC6B,mBAAmB,CAAC,CAAC;AAC3C;AAEA,OAAO,SAASC,iBAAiBA,CAAA,EAAS;EACxC,IAAI7B,QAAQ,CAACM,EAAE,KAAK,KAAK,IAAIN,QAAQ,CAACM,EAAE,KAAK,KAAK,EAAE;IAClDJ,0BAA0B,CAAC,mBAAmB,CAAC;EACjD;EACA,OAAOH,YAAY,CAAC8B,iBAAiB,CAAC,CAAC;AACzC;AAEA,cAAc,SAAS;AACvB,cAAc,UAAU","ignoreList":[]}
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","undefined","metadata","fetchLinkData","deepLink","shouldProcess","fetchOriginLinkData","simulateTestEvent","simulateDeferredLink","payload","simulateFailResolving","error"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -31,6 +31,10 @@ export interface Spec extends TurboModule {
31
31
  isFirstSession?: boolean;
32
32
  }>;
33
33
  simulateTestEvent(): void;
34
+ simulateDeferredLink(payload: UnsafeObject): void;
35
+ simulateFailResolving(url: string, errorMessage: string): void;
36
+ addListener(eventName: string): void;
37
+ removeListeners(count: number): void;
34
38
  }
35
39
  declare const _default: Spec;
36
40
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeComTappSoSdk.d.ts","sourceRoot":"","sources":["../../../src/NativeComTappSoSdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAE9E,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/D,GAAG,CACD,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAC/B,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACvC,KAAK,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACjC,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IAEH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAElD,mBAAmB,IAAI,OAAO,CAAC;QAC7B,KAAK,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACjC,cAAc,CAAC,EAAE,OAAO,CAAC;KAC1B,CAAC,CAAC;IAEH,iBAAiB,IAAI,IAAI,CAAC;CAC3B;;AAED,wBAAsE"}
1
+ {"version":3,"file":"NativeComTappSoSdk.d.ts","sourceRoot":"","sources":["../../../src/NativeComTappSoSdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAE9E,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/D,GAAG,CACD,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAC/B,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACvC,KAAK,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACjC,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IAEH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAElD,mBAAmB,IAAI,OAAO,CAAC;QAC7B,KAAK,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACjC,cAAc,CAAC,EAAE,OAAO,CAAC;KAC1B,CAAC,CAAC;IAEH,iBAAiB,IAAI,IAAI,CAAC;IAI1B,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAClD,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/D,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAAsE"}
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAKpD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,IAAI,8CAGnD;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,8CAG7D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,8CAEzE"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AA6BpD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,IAAI,8CAGnD;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,8CAG7D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,8CAEzE"}
@@ -8,6 +8,16 @@ export declare function fetchLinkData(deepLink: string): Promise<TappLinkDataRes
8
8
  export declare function shouldProcess(deepLink: string): Promise<boolean>;
9
9
  export declare function fetchOriginLinkData(): Promise<TappLinkDataResponse>;
10
10
  export declare function simulateTestEvent(): void;
11
+ export declare function simulateDeferredLink(payload?: {
12
+ tappUrl?: string;
13
+ attrTappUrl?: string;
14
+ influencer?: string;
15
+ data?: {
16
+ [key: string]: string;
17
+ };
18
+ isFirstSession?: boolean;
19
+ }): void;
20
+ export declare function simulateFailResolving(url: string, error: string): void;
11
21
  export * from './Types';
12
22
  export * from './events';
13
23
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAOjB,wBAAgB,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAK9C;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,CAK7E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhE;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAKnE;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC;AAED,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,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;AAOjB,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,CAK7E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhE;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAKnE;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC;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": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "react native tapp sdk",
5
5
  "sideEffects": false,
6
6
  "engines": {
@@ -166,4 +166,4 @@
166
166
  "type": "turbo-module",
167
167
  "version": "0.54.8"
168
168
  }
169
- }
169
+ }
@@ -41,6 +41,14 @@ export interface Spec extends TurboModule {
41
41
  }>;
42
42
 
43
43
  simulateTestEvent(): void;
44
+
45
+ // DEBUG-only: iOS simulation methods for testing event delivery
46
+ // Using UnsafeObject to match NSDictionary* in Obj-C TurboModule
47
+ simulateDeferredLink(payload: UnsafeObject): void;
48
+ simulateFailResolving(url: string, errorMessage: string): void;
49
+
50
+ addListener(eventName: string): void;
51
+ removeListeners(count: number): void;
44
52
  }
45
53
 
46
54
  export default TurboModuleRegistry.getEnforcing<Spec>('ComTappSoSdk');
package/src/events.ts CHANGED
@@ -1,8 +1,32 @@
1
- import { NativeEventEmitter, NativeModules } from 'react-native';
1
+ import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
2
2
  import type { TappLinkDataResponse } from './Types';
3
3
 
4
- const { EventEmitter } = NativeModules;
5
- const eventEmitter = new NativeEventEmitter(EventEmitter);
4
+ const { TappEventEmitter, ComTappSoSdk } = NativeModules;
5
+ import ComTappSoSdkTurbo from './NativeComTappSoSdk';
6
+
7
+ let eventEmitterModule: any;
8
+
9
+ if (Platform.OS === 'ios') {
10
+ eventEmitterModule = TappEventEmitter;
11
+ } else if (Platform.OS === 'android') {
12
+ // Policy: Prefer NativeModules.ComTappSoSdk because it is consistently wired
13
+ if (ComTappSoSdk && typeof ComTappSoSdk.addListener === 'function') {
14
+ eventEmitterModule = ComTappSoSdk;
15
+ } else if (
16
+ ComTappSoSdkTurbo &&
17
+ typeof ComTappSoSdkTurbo.addListener === 'function'
18
+ ) {
19
+ eventEmitterModule = ComTappSoSdkTurbo;
20
+ }
21
+ }
22
+
23
+ if (!eventEmitterModule) {
24
+ console.warn(
25
+ `[TappSDK] Event emitter module is undefined for platform: ${Platform.OS}. Events will not be received.`
26
+ );
27
+ }
28
+
29
+ const eventEmitter = new NativeEventEmitter(eventEmitterModule);
6
30
 
7
31
  export function addDeferredLinkListener(
8
32
  listener: (response: TappLinkDataResponse) => void
package/src/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import ComTappSoSdk from './NativeComTappSoSdk';
2
- import { Platform } from 'react-native';
2
+ import { Platform, NativeModules } from 'react-native';
3
3
  import {
4
4
  EventAction,
5
5
  type InitConfig,
@@ -16,6 +16,13 @@ export function start(config: InitConfig): void {
16
16
  if (Platform.OS === 'web') {
17
17
  unsupportedPlatformMessage('start');
18
18
  }
19
+ // Force-init the event emitter module to prevent race conditions
20
+ if (Platform.OS === 'ios') {
21
+ const { TappEventEmitter } = NativeModules;
22
+ if (TappEventEmitter) {
23
+ /* no-op access to trigger init */
24
+ }
25
+ }
19
26
  ComTappSoSdk.start(config.authToken, config.env, config.tappToken);
20
27
  }
21
28
 
@@ -77,5 +84,31 @@ export function simulateTestEvent(): void {
77
84
  return ComTappSoSdk.simulateTestEvent();
78
85
  }
79
86
 
87
+ // DEBUG-only: Simulate a deferred link event for iOS testing
88
+ // Only works in DEBUG builds on iOS
89
+ export function simulateDeferredLink(payload?: {
90
+ tappUrl?: string;
91
+ attrTappUrl?: string;
92
+ influencer?: string;
93
+ data?: { [key: string]: string };
94
+ isFirstSession?: boolean;
95
+ }): void {
96
+ if (Platform.OS !== 'ios') {
97
+ console.log('[simulateDeferredLink] Only supported on iOS');
98
+ return;
99
+ }
100
+ ComTappSoSdk.simulateDeferredLink(payload || {});
101
+ }
102
+
103
+ // DEBUG-only: Simulate a fail resolving URL event for iOS testing
104
+ // Only works in DEBUG builds on iOS
105
+ export function simulateFailResolving(url: string, error: string): void {
106
+ if (Platform.OS !== 'ios') {
107
+ console.log('[simulateFailResolving] Only supported on iOS');
108
+ return;
109
+ }
110
+ ComTappSoSdk.simulateFailResolving(url, error);
111
+ }
112
+
80
113
  export * from './Types';
81
114
  export * from './events';