@amplytools/react-native-amply-sdk 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,18 +55,58 @@ static ASDKCampaignResult* AmplyCampaignResultFromString(NSString *result) {
55
55
  return ASDKCampaignResult.unavailable;
56
56
  }
57
57
 
58
- @interface Amply : NativeAmplyModuleSpecBase <NativeAmplyModuleSpec, ASDKDeepLinkListener, ASDKCampaignPresenter, ASDKSystemEventsListener, ASDKLogListener>
58
+ // Gate-side protocol names confirmed against the published KMP 0.5.0-SNAPSHOT Obj-C header
59
+ // (AmplySDK.xcframework): ASDKCampaignPresenter (present(params:info:resolution:) + dismiss())
60
+ // and ASDKCampaignResolution (resolve(result:)).
61
+ @class AmplyGatePresenter;
62
+
63
+ // The module owns the GLOBAL routing table and the JS emitter; each registered gate gets its
64
+ // OWN AmplyGatePresenter instance (below) that owns the per-gate "current presentation" slot.
65
+ @interface Amply : NativeAmplyModuleSpecBase <NativeAmplyModuleSpec, ASDKDeepLinkListener, ASDKSystemEventsListener, ASDKLogListener>
59
66
  @property (nonatomic, strong) ASDKAmply *amplyInstance;
60
67
  @property (nonatomic, assign) BOOL deepLinkListenerRegistered;
61
- @property (nonatomic, assign) BOOL campaignPresenterRegistered;
68
+ @property (nonatomic, assign) BOOL gateRegistered;
62
69
  @property (nonatomic, assign) BOOL systemEventsListenerRegistered;
63
70
  @property (nonatomic, assign) BOOL lifecycleObserversRegistered;
64
71
  @property (nonatomic, assign) BOOL logListenerRegistered;
65
72
  @property (nonatomic, assign) AmplyLogLevel currentLogLevel;
66
73
  @property (nonatomic, strong) NSMutableArray<void (^)(ASDKAmply *)> *pendingPropertyOps;
67
- // Maps a bridge-minted mediationId -> the KMP CampaignResolution token awaiting an
68
- // outcome from JS. Guarded by @synchronized(self.pendingCompletions).
74
+ // Global routing table: mediationId -> the KMP CampaignResolution token awaiting a JS reply.
75
+ // Shared across every registered gate because resolveCampaign(mediationId) routes by exact id —
76
+ // that lookup is correct regardless of which gate minted the id. Guarded by @synchronized(self).
69
77
  @property (nonatomic, strong) NSMutableDictionary<NSString *, id<ASDKCampaignResolution>> *pendingCompletions;
78
+ // Retains the per-gate presenter instances; the KMP gate registry does not keep them alive.
79
+ @property (nonatomic, strong) NSMutableArray<AmplyGatePresenter *> *gatePresenters;
80
+
81
+ // Internal hooks used by AmplyGatePresenter (one instance per registerGate).
82
+ - (id<ASDKCampaignResolution>)takePendingCompletionForMediationId:(NSString *)mediationId;
83
+ - (void)parkPendingCompletion:(id<ASDKCampaignResolution>)resolution forMediationId:(NSString *)mediationId;
84
+ - (void)emitGatePresent:(NSDictionary *)payload;
85
+ @end
86
+
87
+ /**
88
+ * One ASDKCampaignPresenter per registerGate registration (per url-pattern).
89
+ *
90
+ * Concurrency model: KMP presents at most one gate per url-pattern at a time (the gate is modal —
91
+ * you cannot show two rewarded ads, or two presentations of the same pattern, at once). KMP's
92
+ * -dismiss is parameterless and is dispatched to the SPECIFIC presenter instance registered for
93
+ * that pattern. So the "which presentation does my dismiss() target" state must live PER-PRESENTER,
94
+ * here in `activeMediationId` — NOT in one global slot on the module. With one global slot, an
95
+ * overlap of two different-pattern gates (A present, B present, A dismiss) routes A's dismiss to
96
+ * B's token. Per-instance state makes A's dismiss abandon only A's own live presentation.
97
+ *
98
+ * Concurrent presentations of the SAME url-pattern are not supported: a second -present overwrites
99
+ * `activeMediationId`, so a later -dismiss targets the latest. That is acceptable and intended
100
+ * given the modal contract above. The module's pendingCompletions stays global (lookup by exact id
101
+ * is always correct), so a JS resolveCampaign for the overwritten id still settles its own token.
102
+ */
103
+ @interface AmplyGatePresenter : NSObject <ASDKCampaignPresenter>
104
+ @property (nonatomic, weak) Amply *module;
105
+ @property (nonatomic, copy) NSString *baseUrl;
106
+ // The mediationId this presenter most recently -present'ed and has not yet resolved/abandoned.
107
+ // -dismiss targets exactly this id, so it can only ever release THIS gate's live presentation.
108
+ // Guarded by @synchronized(self); only ever holds a live id (cleared when its token is taken).
109
+ @property (nonatomic, copy) NSString *activeMediationId;
70
110
  @end
71
111
 
72
112
  @implementation Amply
@@ -224,69 +264,142 @@ RCT_EXPORT_MODULE()
224
264
  }
225
265
  }
226
266
 
267
+ // Maps the KMP GateDecision enum/sealed value to the JS-facing decision map:
268
+ // { outcome: "proceed", reason: "completed" | "failOpen" } | { outcome: "cancelled" }
269
+ // The raw CampaignResult is never surfaced — the SDK collapses it into GateDecision.
270
+ static NSDictionary *AmplyGateDecisionToMap(ASDKGateDecision *decision) {
271
+ // Proceed carries a `reason` (Completed / FailOpen); Cancelled carries nothing.
272
+ if ([decision isKindOfClass:[ASDKGateDecisionProceed class]]) {
273
+ ASDKGateDecisionProceed *proceed = (ASDKGateDecisionProceed *)decision;
274
+ NSString *reason = @"failOpen";
275
+ if (proceed.reason == ASDKProceedReason.completed) {
276
+ reason = @"completed";
277
+ }
278
+ return @{@"outcome": @"proceed", @"reason": reason};
279
+ }
280
+ // Cancelled — or any unexpected shape — collapses to its terminal outcome. A
281
+ // Cancelled value is the only non-proceed terminal in the frozen API.
282
+ if ([decision isKindOfClass:[ASDKGateDecisionCancelled class]]) {
283
+ return @{@"outcome": @"cancelled"};
284
+ }
285
+ // Defensive: an unknown decision subtype fails open rather than stranding the caller.
286
+ RCTLogWarn(@"[AmplyReactNative] Unknown GateDecision subtype; failing open");
287
+ return @{@"outcome": @"proceed", @"reason": @"failOpen"};
288
+ }
289
+
227
290
  /**
228
- * Continuation form of track. The resolve block IS the continuation: it is resolved
229
- * with @"complete" or @"cancel" exactly once. The KMP trackEvent continuation overload
230
- * guarantees exactly-once main-thread delivery; __block + a resolved flag guards the
231
- * (defensive) double-settle. Fails open to @"complete" on any synchronous failure or
232
- * if the SDK is not initialized.
233
- *
234
- * The raw CampaignResult is never surfaced here — the SDK collapses it into the
235
- * onComplete/onCancel decision before this bridge ever sees it.
291
+ * Gated form of track. Backed by the K/N-generated async selector for the KMP
292
+ * `suspend fun trackGated(event:props:): GateDecision`. The resolve block is settled
293
+ * exactly once with the collapsed decision map; __block + a settled flag guards the
294
+ * (defensive) double-settle. This NEVER rejects: not-initialized, a thrown exception,
295
+ * or a K/N error all fail open to { outcome: "proceed", reason: "failOpen" }.
236
296
  */
237
- - (void)trackEventGated:(NSString *)event
238
- properties:(NSDictionary *)properties
239
- resolve:(RCTPromiseResolveBlock)resolve
240
- reject:(RCTPromiseRejectBlock)reject
297
+ - (void)trackGated:(NSString *)event
298
+ properties:(NSDictionary *)properties
299
+ resolve:(RCTPromiseResolveBlock)resolve
300
+ reject:(RCTPromiseRejectBlock)reject
241
301
  {
242
302
  if (!event || event.length == 0) {
243
- if (reject) {
244
- reject(@"AMP_INVALID_EVENT", @"Event 'event' is required", nil);
245
- }
303
+ // Even an invalid event fails open rather than rejecting — the JS contract is
304
+ // "trackGated never rejects".
305
+ RCTLogWarn(@"[AmplyReactNative] trackGated with empty event; failing open");
306
+ if (resolve) { resolve(@{@"outcome": @"proceed", @"reason": @"failOpen"}); }
246
307
  return;
247
308
  }
248
309
 
249
310
  NSDictionary *props = [properties isKindOfClass:[NSDictionary class]] ? properties : @{};
250
311
 
312
+ // Contract: resolve EXACTLY ONCE, ON MAIN. Every settle path — fail-open before init, the
313
+ // K/N success completion, the K/N error/cancellation completion, and the @catch — funnels
314
+ // through here. We always hop to the main queue (which serializes the once-guard, so the
315
+ // flag needs no further synchronization) and resolve at most once.
251
316
  __block BOOL settled = NO;
252
- void (^settle)(NSString *) = ^(NSString *decision) {
253
- if (settled) { return; }
254
- settled = YES;
255
- if (resolve) {
256
- resolve(decision);
257
- }
317
+ void (^settle)(NSDictionary *) = ^(NSDictionary *decision) {
318
+ dispatch_async(dispatch_get_main_queue(), ^{
319
+ if (settled) { return; }
320
+ settled = YES;
321
+ if (resolve) {
322
+ resolve(decision);
323
+ }
324
+ });
258
325
  };
326
+ NSDictionary *failOpen = @{@"outcome": @"proceed", @"reason": @"failOpen"};
259
327
 
260
328
  if (!self.amplyInstance) {
261
- // Fail open: not initialized -> proceed immediately (KMP state-machine row 1).
262
- RCTLogWarn(@"[AmplyReactNative] trackEventGated before init; proceeding (fail-open)");
263
- settle(@"complete");
329
+ // Fail open: not initialized -> proceed immediately (KMP gate state-machine row 1).
330
+ RCTLogWarn(@"[AmplyReactNative] trackGated before init; failing open");
331
+ settle(failOpen);
264
332
  return;
265
333
  }
266
334
 
267
335
  @try {
268
- [self.amplyInstance trackEventEventName:event
269
- properties:props
270
- onProceed:^{ settle(@"complete"); }
271
- onCancel:^{ settle(@"cancel"); }];
272
- RCTLogInfo(@"[AmplyReactNative] trackEventGated dispatched: %@", event);
336
+ // Confirmed against the published KMP 0.5.0-SNAPSHOT header: the ASDKAmply facade's
337
+ // `suspend fun trackGated(event:properties:)` projects to
338
+ // `-trackGatedEvent:properties:completionHandler:` with a (ASDKGateDecision*, NSError*) handler.
339
+ [self.amplyInstance trackGatedEvent:event
340
+ properties:props
341
+ completionHandler:^(ASDKGateDecision *decision, NSError *error) {
342
+ if (error) {
343
+ // A coroutine error/cancellation fails open — never strand the caller. settle() routes
344
+ // this onto main with the same once-guard as the success path (resolve once, on main).
345
+ RCTLogWarn(@"[AmplyReactNative] trackGated coroutine error; failing open: %@", error.localizedDescription);
346
+ settle(failOpen);
347
+ return;
348
+ }
349
+ // settle() already hops to main to match the deeplink/present delivery contract.
350
+ settle(AmplyGateDecisionToMap(decision));
351
+ }];
352
+ RCTLogInfo(@"[AmplyReactNative] trackGated dispatched: %@", event);
273
353
  } @catch (NSException *exception) {
274
- RCTLogError(@"[AmplyReactNative] trackEventGated failed; proceeding (fail-open): %@", exception.reason);
275
- settle(@"complete");
354
+ RCTLogError(@"[AmplyReactNative] trackGated threw; failing open: %@", exception.reason);
355
+ settle(failOpen);
276
356
  }
277
357
  }
278
358
 
279
- - (void)resolveCampaign:(NSString *)mediationId
280
- result:(NSString *)result
359
+ /**
360
+ * Atomically removes and returns the token parked under `mediationId` from the GLOBAL routing
361
+ * table. Returning the token to exactly one caller is what makes resolve and dismiss mutually-
362
+ * exclusive and at-most-once: whoever takes the token first owns the outcome; every later path
363
+ * gets nil and becomes a no-op. This is the single point that prevents the pending-resolution
364
+ * leak on timeout/cancel/dismiss. (The per-gate "current presentation" slot lives on each
365
+ * AmplyGatePresenter, not here — the presenter clears its own slot around -dismiss.)
366
+ */
367
+ - (id<ASDKCampaignResolution>)takePendingCompletionForMediationId:(NSString *)mediationId
281
368
  {
282
- id<ASDKCampaignResolution> completion = nil;
369
+ if (!mediationId) { return nil; }
283
370
  @synchronized (self) {
284
- if (!self.pendingCompletions) { return; }
285
- completion = self.pendingCompletions[mediationId];
286
- if (completion) {
371
+ if (!self.pendingCompletions) { return nil; }
372
+ id<ASDKCampaignResolution> token = self.pendingCompletions[mediationId];
373
+ if (token) {
287
374
  [self.pendingCompletions removeObjectForKey:mediationId];
288
375
  }
376
+ return token;
377
+ }
378
+ }
379
+
380
+ /** Parks a resolution token in the global routing table under `mediationId`. */
381
+ - (void)parkPendingCompletion:(id<ASDKCampaignResolution>)resolution forMediationId:(NSString *)mediationId
382
+ {
383
+ @synchronized (self) {
384
+ if (!self.pendingCompletions) {
385
+ self.pendingCompletions = [NSMutableDictionary new];
386
+ }
387
+ self.pendingCompletions[mediationId] = resolution;
289
388
  }
389
+ }
390
+
391
+ /** Surfaces a gate present dispatch to JS. Emitter delivery is async; hop to main to match deeplink. */
392
+ - (void)emitGatePresent:(NSDictionary *)payload
393
+ {
394
+ dispatch_async(dispatch_get_main_queue(), ^{
395
+ [self emitOnCampaignPresent:payload];
396
+ });
397
+ }
398
+
399
+ - (void)resolveCampaign:(NSString *)mediationId
400
+ result:(NSString *)result
401
+ {
402
+ id<ASDKCampaignResolution> completion = [self takePendingCompletionForMediationId:mediationId];
290
403
  if (!completion) {
291
404
  // Late/duplicate/unknown reply — the SDK token is the source of truth and is
292
405
  // already settled (or never existed). Ignore.
@@ -295,59 +408,58 @@ RCT_EXPORT_MODULE()
295
408
  }
296
409
  ASDKCampaignResult *mapped = AmplyCampaignResultFromString(result);
297
410
  RCTLogInfo(@"[AmplyReactNative] resolveCampaign mediationId=%@ result=%@", mediationId, result);
411
+ // Confirmed against the published header: CampaignResolution.resolve(result:) projects to
412
+ // -resolveResult:.
298
413
  [completion resolveResult:mapped];
299
414
  }
300
415
 
301
- - (void)registerCampaignPresenter
416
+ /**
417
+ * Register the bridge as the gate presenter for `baseUrl`. JS is the single capability
418
+ * registry and routes by baseUrl on its side; the native gate carries the abort policy
419
+ * and timeout.
420
+ *
421
+ * FLAG (native-compile gate): the frozen KMP 0.5.0 API is
422
+ * `registerGate(baseUrl, presenter, onAbort, timeoutMs)`.
423
+ * The selector below is written to the documented K/N convention
424
+ * (`-registerGateBaseUrl:presenter:onAbort:timeoutMs:`) and the ASDKAbortPolicy
425
+ * enum-case names (.cancel / .proceed) are assumed. BOTH the selector and the
426
+ * AbortPolicy projection MUST be confirmed against the published header before publish.
427
+ *
428
+ * Each registration gets its OWN AmplyGatePresenter instance owning its OWN "current
429
+ * presentation" slot, so a dismiss() delivered to this gate's presenter can only abandon THIS
430
+ * gate's live presentation — never one belonging to a different url-pattern's gate.
431
+ */
432
+ - (void)registerGate:(NSString *)baseUrl
433
+ onAbort:(NSString *)onAbort
434
+ timeoutMs:(double)timeoutMs
302
435
  {
303
436
  if (!self.amplyInstance) {
304
- RCTLogWarn(@"[AmplyReactNative] Cannot register campaign presenter - Amply not initialized");
305
- return;
306
- }
307
- if (self.campaignPresenterRegistered) {
308
- RCTLogInfo(@"[AmplyReactNative] Campaign presenter already registered");
437
+ RCTLogWarn(@"[AmplyReactNative] Cannot register gate - Amply not initialized");
309
438
  return;
310
439
  }
311
440
  if (!self.pendingCompletions) {
312
441
  self.pendingCompletions = [NSMutableDictionary new];
313
442
  }
314
- RCTLogInfo(@"[AmplyReactNative] Registering campaign presenter");
315
- // Catch-all registration: JS is the single capability registry and routes by URL.
316
- [self.amplyInstance registerCampaignPresenterPresenter:self];
317
- self.campaignPresenterRegistered = YES;
318
- }
319
-
320
- #pragma mark - ASDKCampaignPresenter
321
-
322
- /**
323
- * Called by the KMP SDK when a campaign dispatches a present (blocking) action.
324
- * We mint a mediationId, park the completion token, and surface the dispatch to JS.
325
- * JS later replies via resolveCampaign:result:.
326
- */
327
- - (void)onCampaignPresentUrl:(NSString *)url
328
- info:(NSDictionary<NSString *, id> *)info
329
- completion:(id<ASDKCampaignResolution>)completion
330
- {
331
- NSString *mediationId = [[NSUUID UUID] UUIDString];
332
- @synchronized (self) {
333
- if (!self.pendingCompletions) {
334
- self.pendingCompletions = [NSMutableDictionary new];
335
- }
336
- self.pendingCompletions[mediationId] = completion;
443
+ if (!self.gatePresenters) {
444
+ self.gatePresenters = [NSMutableArray new];
337
445
  }
338
446
 
339
- RCTLogInfo(@"[AmplyReactNative] Dispatching campaign present mediationId=%@ url=%@", mediationId, url);
340
-
341
- NSDictionary *payload = @{
342
- @"url": url ?: @"",
343
- @"info": info ?: @{},
344
- @"mediationId": mediationId
345
- };
346
-
347
- // Emitter delivery is async; emit on the main queue to match the deeplink path.
348
- dispatch_async(dispatch_get_main_queue(), ^{
349
- [self emitOnCampaignPresent:payload];
350
- });
447
+ ASDKAbortPolicy *policy = [onAbort isEqualToString:@"proceed"]
448
+ ? ASDKAbortPolicy.proceed
449
+ : ASDKAbortPolicy.cancel;
450
+
451
+ AmplyGatePresenter *presenter = [[AmplyGatePresenter alloc] init];
452
+ presenter.module = self;
453
+ presenter.baseUrl = baseUrl;
454
+ // Retain the presenter — the KMP gate registry does not keep it alive.
455
+ [self.gatePresenters addObject:presenter];
456
+
457
+ RCTLogInfo(@"[AmplyReactNative] Registering gate baseUrl=%@ onAbort=%@ timeoutMs=%f", baseUrl, onAbort, timeoutMs);
458
+ [self.amplyInstance registerGateBaseUrl:baseUrl
459
+ presenter:presenter
460
+ onAbort:policy
461
+ timeoutMs:(int64_t)timeoutMs];
462
+ self.gateRegistered = YES;
351
463
  }
352
464
 
353
465
  - (void)getRecentEvents:(double)limit
@@ -817,7 +929,9 @@ static ASDKDataSetType *AmplyDataSetTypeFromDictionary(NSDictionary *type, NSStr
817
929
  {
818
930
  [[NSNotificationCenter defaultCenter] removeObserver:self];
819
931
 
820
- // Fail open any in-flight mediated actions so no continuation is left hanging.
932
+ // Fail open any in-flight gate presentations so no caller is left hanging. Each per-gate
933
+ // presenter clears its own current-presentation slot when its token is taken below; on dealloc
934
+ // the presenters themselves are released with the module.
821
935
  NSArray<id<ASDKCampaignResolution>> *inFlight = nil;
822
936
  @synchronized (self) {
823
937
  inFlight = self.pendingCompletions.allValues;
@@ -887,3 +1001,83 @@ static ASDKDataSetType *AmplyDataSetTypeFromDictionary(NSDictionary *type, NSStr
887
1001
  }
888
1002
 
889
1003
  @end
1004
+
1005
+ #pragma mark - AmplyGatePresenter (one instance per registerGate / url-pattern)
1006
+
1007
+ @implementation AmplyGatePresenter
1008
+
1009
+ /**
1010
+ * Called by the KMP SDK when this gate dispatches its (blocking) presentation. We mint a
1011
+ * mediationId, park the resolution token in the module's GLOBAL routing table, record it as
1012
+ * THIS presenter's current presentation, and surface the dispatch to JS. JS later replies via
1013
+ * resolveCampaign:result: (which routes by exact id, so it is unaffected by overlap).
1014
+ *
1015
+ * Confirmed against the published header: the presenter signature is
1016
+ * `present(params:info:resolution:)` -> `-presentParams:info:resolution:`,
1017
+ * with params projected as NSDictionary<NSString *, NSString *>.
1018
+ */
1019
+ - (void)presentParams:(NSDictionary<NSString *, NSString *> *)params
1020
+ info:(NSDictionary<NSString *, id> *)info
1021
+ resolution:(id<ASDKCampaignResolution>)resolution
1022
+ {
1023
+ Amply *module = self.module;
1024
+ if (!module) {
1025
+ // Module gone (teardown race) — drop the token so the SDK's own abort machinery proceeds.
1026
+ RCTLogWarn(@"[AmplyReactNative] Gate[%@] present after module teardown; dropping", self.baseUrl);
1027
+ return;
1028
+ }
1029
+
1030
+ NSString *mediationId = [[NSUUID UUID] UUIDString];
1031
+ [module parkPendingCompletion:resolution forMediationId:mediationId];
1032
+ // Record THIS gate's live presentation so -dismiss releases exactly this token on abandon. A
1033
+ // second present() on this same presenter overwrites it (concurrent same-pattern not supported;
1034
+ // latest wins) — the prior id still lives in the global table for its own JS reply.
1035
+ @synchronized (self) {
1036
+ self.activeMediationId = mediationId;
1037
+ }
1038
+
1039
+ // `info` carries the action url under @"url"; surface it for JS-side baseUrl routing.
1040
+ NSString *url = [info[@"url"] isKindOfClass:[NSString class]] ? info[@"url"] : @"";
1041
+
1042
+ RCTLogInfo(@"[AmplyReactNative] Gate[%@] dispatching present mediationId=%@ url=%@", self.baseUrl, mediationId, url);
1043
+
1044
+ NSDictionary *payload = @{
1045
+ @"url": url,
1046
+ @"params": params ?: @{},
1047
+ @"info": info ?: @{},
1048
+ @"mediationId": mediationId
1049
+ };
1050
+
1051
+ [module emitGatePresent:payload];
1052
+ }
1053
+
1054
+ /**
1055
+ * Tear down any UI this presenter put on screen WITHOUT producing a result. Called by the SDK
1056
+ * when THIS gate's caller coroutine is cancelled/times out and the resolution token is abandoned.
1057
+ *
1058
+ * Presentation lives entirely in JS here (no native gate UI), so there is nothing to tear down —
1059
+ * BUT the parked token MUST be released or the global pendingCompletions leaks on every
1060
+ * timeout/cancel. We take THIS presenter's current presentation's token (at-most-once via
1061
+ * -takePendingCompletionForMediationId:) and simply drop it: the SDK's own abort machinery owns
1062
+ * the outcome here, so we must NOT also resolve it. A subsequent JS resolveCampaign: for the same
1063
+ * id finds nothing and is a harmless no-op. Crucially, because the active id is per-presenter, an
1064
+ * overlapping different-pattern gate's presentation can never be the one we abandon here.
1065
+ */
1066
+ - (void)dismiss
1067
+ {
1068
+ NSString *activeId = nil;
1069
+ @synchronized (self) {
1070
+ activeId = self.activeMediationId;
1071
+ self.activeMediationId = nil;
1072
+ }
1073
+ if (!activeId) {
1074
+ RCTLogInfo(@"[AmplyReactNative] Gate[%@] dismiss() with no active presentation", self.baseUrl);
1075
+ return;
1076
+ }
1077
+ Amply *module = self.module;
1078
+ id<ASDKCampaignResolution> abandoned = [module takePendingCompletionForMediationId:activeId];
1079
+ RCTLogInfo(@"[AmplyReactNative] Gate[%@] dismiss() releasing mediationId=%@ (token %@)",
1080
+ self.baseUrl, activeId, abandoned ? @"dropped" : @"already settled");
1081
+ }
1082
+
1083
+ @end
@@ -60,16 +60,16 @@ namespace facebook::react {
60
60
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "track", @selector(track:resolve:reject:), args, count);
61
61
  }
62
62
 
63
- static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_trackEventGated(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
64
- return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "trackEventGated", @selector(trackEventGated:properties:resolve:reject:), args, count);
63
+ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_trackGated(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
64
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "trackGated", @selector(trackGated:properties:resolve:reject:), args, count);
65
65
  }
66
66
 
67
67
  static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_resolveCampaign(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
68
68
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "resolveCampaign", @selector(resolveCampaign:result:), args, count);
69
69
  }
70
70
 
71
- static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_registerCampaignPresenter(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
72
- return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "registerCampaignPresenter", @selector(registerCampaignPresenter), args, count);
71
+ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_registerGate(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
72
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "registerGate", @selector(registerGate:onAbort:timeoutMs:), args, count);
73
73
  }
74
74
 
75
75
  static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_getRecentEvents(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
@@ -132,13 +132,13 @@ namespace facebook::react {
132
132
  methodMap_["track"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_track};
133
133
  setMethodArgConversionSelector(@"track", 0, @"JS_NativeAmplyModule_TrackEventPayload:");
134
134
 
135
- methodMap_["trackEventGated"] = MethodMetadata {2, __hostFunction_NativeAmplyModuleSpecJSI_trackEventGated};
135
+ methodMap_["trackGated"] = MethodMetadata {2, __hostFunction_NativeAmplyModuleSpecJSI_trackGated};
136
136
 
137
137
 
138
138
  methodMap_["resolveCampaign"] = MethodMetadata {2, __hostFunction_NativeAmplyModuleSpecJSI_resolveCampaign};
139
139
 
140
140
 
141
- methodMap_["registerCampaignPresenter"] = MethodMetadata {0, __hostFunction_NativeAmplyModuleSpecJSI_registerCampaignPresenter};
141
+ methodMap_["registerGate"] = MethodMetadata {3, __hostFunction_NativeAmplyModuleSpecJSI_registerGate};
142
142
 
143
143
 
144
144
  methodMap_["getRecentEvents"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_getRecentEvents};
@@ -78,13 +78,15 @@ namespace JS {
78
78
  - (void)track:(JS::NativeAmplyModule::TrackEventPayload &)payload
79
79
  resolve:(RCTPromiseResolveBlock)resolve
80
80
  reject:(RCTPromiseRejectBlock)reject;
81
- - (void)trackEventGated:(NSString *)event
82
- properties:(NSDictionary *)properties
83
- resolve:(RCTPromiseResolveBlock)resolve
84
- reject:(RCTPromiseRejectBlock)reject;
81
+ - (void)trackGated:(NSString *)event
82
+ properties:(NSDictionary *)properties
83
+ resolve:(RCTPromiseResolveBlock)resolve
84
+ reject:(RCTPromiseRejectBlock)reject;
85
85
  - (void)resolveCampaign:(NSString *)mediationId
86
86
  result:(NSString *)result;
87
- - (void)registerCampaignPresenter;
87
+ - (void)registerGate:(NSString *)baseUrl
88
+ onAbort:(NSString *)onAbort
89
+ timeoutMs:(double)timeoutMs;
88
90
  - (void)getRecentEvents:(double)limit
89
91
  resolve:(RCTPromiseResolveBlock)resolve
90
92
  reject:(RCTPromiseRejectBlock)reject;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amplytools/react-native-amply-sdk",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "React Native SDK for Amply: Mobile SDK to orchestrate in-app experiences and campaigns remotely, without app releases",
5
5
  "license": "MIT",
6
6
  "repository": {