@truewatchtech/react-native-mobile 0.4.1 → 0.4.3-alpha.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.
Files changed (130) hide show
  1. package/FTMobileReactNativeSDK.podspec +7 -5
  2. package/README.md +34 -0
  3. package/android/build.gradle +9 -2
  4. package/android/src/main/java/com/ft/sdk/reactnative/FTMobileImpl.java +45 -14
  5. package/android/src/main/java/com/ft/sdk/reactnative/FTRUMImpl.java +85 -2
  6. package/android/src/main/java/com/ft/sdk/reactnative/FTTraceImpl.java +5 -1
  7. package/android/src/main/java/com/ft/sdk/reactnative/JsLongTaskMonitor.java +281 -0
  8. package/android/src/main/java/com/ft/sdk/reactnative/utils/ReactNativeUtils.java +5 -12
  9. package/android/src/newarch/java/com/ft/sdk/reactnative/FTMobileModule.java +9 -5
  10. package/android/src/newarch/java/com/ft/sdk/reactnative/FTRUMModule.java +26 -1
  11. package/android/src/newarch/java/com/ft/sdk/reactnative/FTTraceModule.java +14 -0
  12. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTMobileModule.java +2 -2
  13. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTRUMModule.java +29 -1
  14. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTTraceModule.java +12 -0
  15. package/android/src/rnpost70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +21 -0
  16. package/android/src/rnpre70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +37 -0
  17. package/android/src/test/java/com/ft/sdk/reactnative/FTRUMModuleTest.java +117 -0
  18. package/android/src/test/java/com/ft/sdk/reactnative/JsLongTaskMonitorTest.java +446 -0
  19. package/android/src/test/java/com/ft/sdk/reactnative/utils/ReactNativeUtilsTest.java +33 -0
  20. package/ios/FTJSLongTaskMonitor.h +51 -0
  21. package/ios/FTJSLongTaskMonitor.m +250 -0
  22. package/ios/FTMobileReactNative.mm +23 -16
  23. package/ios/FTReactNativeLog.mm +3 -4
  24. package/ios/FTReactNativeRUM.mm +103 -28
  25. package/ios/FTReactNativeResource.h +4 -0
  26. package/ios/FTReactNativeResource.m +58 -0
  27. package/ios/FTReactNativeTrace.mm +56 -13
  28. package/ios/FTReactNativeUtils.m +10 -15
  29. package/ios/FTReactNativeWebSocket.h +10 -0
  30. package/ios/FTReactNativeWebSocket.mm +201 -0
  31. package/ios/FTWebSocketMetadataStore.h +25 -0
  32. package/ios/FTWebSocketMetadataStore.m +622 -0
  33. package/ios/FTWebSocketResourceData.h +16 -0
  34. package/ios/FTWebSocketResourceData.m +75 -0
  35. package/lib/commonjs/ft_mobile_agent.js +36 -3
  36. package/lib/commonjs/ft_mobile_agent.js.map +1 -1
  37. package/lib/commonjs/ft_rum.js +46 -2
  38. package/lib/commonjs/ft_rum.js.map +1 -1
  39. package/lib/commonjs/ft_tracing.js +12 -3
  40. package/lib/commonjs/ft_tracing.js.map +1 -1
  41. package/lib/commonjs/index.js +14 -0
  42. package/lib/commonjs/index.js.map +1 -1
  43. package/lib/commonjs/rum/FTBabelInteractionTracking.js +72 -0
  44. package/lib/commonjs/rum/FTBabelInteractionTracking.js.map +1 -0
  45. package/lib/commonjs/rum/FTBabelUtils.js +87 -0
  46. package/lib/commonjs/rum/FTBabelUtils.js.map +1 -0
  47. package/lib/commonjs/rum/FTRumActionTracking.js +10 -39
  48. package/lib/commonjs/rum/FTRumActionTracking.js.map +1 -1
  49. package/lib/commonjs/rum/FTRumActionTrackingContext.js +50 -0
  50. package/lib/commonjs/rum/FTRumActionTrackingContext.js.map +1 -0
  51. package/lib/commonjs/rum/FTRumWebSocketNative.js +73 -0
  52. package/lib/commonjs/rum/FTRumWebSocketNative.js.map +1 -0
  53. package/lib/commonjs/rum/FTRumWebSocketTiming.js +55 -0
  54. package/lib/commonjs/rum/FTRumWebSocketTiming.js.map +1 -0
  55. package/lib/commonjs/rum/FTRumWebSocketTracking.js +510 -0
  56. package/lib/commonjs/rum/FTRumWebSocketTracking.js.map +1 -0
  57. package/lib/commonjs/rum/longTasksUtils.js +25 -0
  58. package/lib/commonjs/rum/longTasksUtils.js.map +1 -0
  59. package/lib/commonjs/specs/NativeFTMobileReactNative.js.map +1 -1
  60. package/lib/commonjs/specs/NativeFTReactNativeRUM.js.map +1 -1
  61. package/lib/commonjs/specs/NativeFTReactNativeTrace.js.map +1 -1
  62. package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js +11 -0
  63. package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js.map +1 -0
  64. package/lib/commonjs/version.js +1 -1
  65. package/lib/commonjs/version.js.map +1 -1
  66. package/lib/module/ft_mobile_agent.js +36 -3
  67. package/lib/module/ft_mobile_agent.js.map +1 -1
  68. package/lib/module/ft_rum.js +46 -2
  69. package/lib/module/ft_rum.js.map +1 -1
  70. package/lib/module/ft_tracing.js +12 -1
  71. package/lib/module/ft_tracing.js.map +1 -1
  72. package/lib/module/index.js +3 -1
  73. package/lib/module/index.js.map +1 -1
  74. package/lib/module/rum/FTBabelInteractionTracking.js +66 -0
  75. package/lib/module/rum/FTBabelInteractionTracking.js.map +1 -0
  76. package/lib/module/rum/FTBabelUtils.js +80 -0
  77. package/lib/module/rum/FTBabelUtils.js.map +1 -0
  78. package/lib/module/rum/FTRumActionTracking.js +10 -39
  79. package/lib/module/rum/FTRumActionTracking.js.map +1 -1
  80. package/lib/module/rum/FTRumActionTrackingContext.js +43 -0
  81. package/lib/module/rum/FTRumActionTrackingContext.js.map +1 -0
  82. package/lib/module/rum/FTRumWebSocketNative.js +62 -0
  83. package/lib/module/rum/FTRumWebSocketNative.js.map +1 -0
  84. package/lib/module/rum/FTRumWebSocketTiming.js +49 -0
  85. package/lib/module/rum/FTRumWebSocketTiming.js.map +1 -0
  86. package/lib/module/rum/FTRumWebSocketTracking.js +503 -0
  87. package/lib/module/rum/FTRumWebSocketTracking.js.map +1 -0
  88. package/lib/module/rum/longTasksUtils.js +18 -0
  89. package/lib/module/rum/longTasksUtils.js.map +1 -0
  90. package/lib/module/specs/NativeFTMobileReactNative.js.map +1 -1
  91. package/lib/module/specs/NativeFTReactNativeRUM.js.map +1 -1
  92. package/lib/module/specs/NativeFTReactNativeTrace.js.map +1 -1
  93. package/lib/module/specs/NativeFTReactNativeWebSocket.js +8 -0
  94. package/lib/module/specs/NativeFTReactNativeWebSocket.js.map +1 -0
  95. package/lib/module/version.js +1 -1
  96. package/lib/module/version.js.map +1 -1
  97. package/lib/typescript/ft_mobile_agent.d.ts +7 -0
  98. package/lib/typescript/ft_rum.d.ts +9 -0
  99. package/lib/typescript/index.d.ts +3 -1
  100. package/lib/typescript/rum/FTBabelInteractionTracking.d.ts +30 -0
  101. package/lib/typescript/rum/FTBabelUtils.d.ts +1 -0
  102. package/lib/typescript/rum/FTRumActionTracking.d.ts +0 -2
  103. package/lib/typescript/rum/FTRumActionTrackingContext.d.ts +11 -0
  104. package/lib/typescript/rum/FTRumWebSocketNative.d.ts +23 -0
  105. package/lib/typescript/rum/FTRumWebSocketTiming.d.ts +2 -0
  106. package/lib/typescript/rum/FTRumWebSocketTracking.d.ts +56 -0
  107. package/lib/typescript/rum/longTasksUtils.d.ts +8 -0
  108. package/lib/typescript/specs/NativeFTMobileReactNative.d.ts +14 -0
  109. package/lib/typescript/specs/NativeFTReactNativeRUM.d.ts +4 -0
  110. package/lib/typescript/specs/NativeFTReactNativeTrace.d.ts +12 -0
  111. package/lib/typescript/specs/NativeFTReactNativeWebSocket.d.ts +13 -0
  112. package/lib/typescript/version.d.ts +1 -1
  113. package/package.json +7 -6
  114. package/src/ft_mobile_agent.tsx +49 -6
  115. package/src/ft_rum.tsx +61 -2
  116. package/src/ft_tracing.tsx +15 -1
  117. package/src/index.tsx +4 -0
  118. package/src/rum/FTBabelInteractionTracking.ts +124 -0
  119. package/src/rum/FTBabelUtils.tsx +99 -0
  120. package/src/rum/FTRumActionTracking.tsx +14 -43
  121. package/src/rum/FTRumActionTrackingContext.ts +68 -0
  122. package/src/rum/FTRumWebSocketNative.ts +106 -0
  123. package/src/rum/FTRumWebSocketTiming.ts +50 -0
  124. package/src/rum/FTRumWebSocketTracking.ts +727 -0
  125. package/src/rum/longTasksUtils.ts +17 -0
  126. package/src/specs/NativeFTMobileReactNative.ts +17 -0
  127. package/src/specs/NativeFTReactNativeRUM.ts +4 -0
  128. package/src/specs/NativeFTReactNativeTrace.ts +12 -0
  129. package/src/specs/NativeFTReactNativeWebSocket.ts +33 -0
  130. package/src/version.ts +1 -1
@@ -0,0 +1,250 @@
1
+ #import "FTJSLongTaskMonitor.h"
2
+
3
+ #import <QuartzCore/CADisplayLink.h>
4
+ #import <TrueWatchSDK/FTExternalDataManager.h>
5
+ #import <React/RCTBridge.h>
6
+ #import <React/RCTBridge+Private.h>
7
+ #import <React/RCTJSThread.h>
8
+
9
+ static const double FTNanosecondsPerSecond = 1000000000.0;
10
+
11
+ @interface FTReactBridgeJSQueue : NSObject <FTJSLongTaskQueue>
12
+ @property (nonatomic, weak) RCTBridge *bridge;
13
+ - (instancetype)initWithBridge:(RCTBridge *)bridge;
14
+ @end
15
+
16
+ @implementation FTReactBridgeJSQueue
17
+ - (instancetype)initWithBridge:(RCTBridge *)bridge {
18
+ self = [super init];
19
+ if (self) {
20
+ _bridge = bridge;
21
+ }
22
+ return self;
23
+ }
24
+
25
+ - (BOOL)dispatchBlock:(dispatch_block_t)block {
26
+ RCTBridge *bridge = self.bridge;
27
+ if (bridge == nil) {
28
+ return NO;
29
+ }
30
+ @try {
31
+ // RCTBridgeProxy.valid is always NO, including while bridgeless RN is active.
32
+ [bridge dispatchBlock:block queue:RCTJSThread];
33
+ return YES;
34
+ } @catch (NSException *exception) {
35
+ // Bridge teardown may race a start request. Monitoring must not take down the host.
36
+ return NO;
37
+ }
38
+ }
39
+ @end
40
+
41
+ // The display link retains this callback target, not the scheduler that owns it.
42
+ @interface FTJSLongTaskDisplayLinkTarget : NSObject
43
+ @property (nonatomic, copy) FTJSLongTaskFrameCallback callback;
44
+ @end
45
+
46
+ @implementation FTJSLongTaskDisplayLinkTarget
47
+ - (void)displayLinkDidFire:(CADisplayLink *)displayLink {
48
+ self.callback(displayLink.timestamp);
49
+ }
50
+ @end
51
+
52
+ @interface FTCADisplayLinkScheduler : NSObject <FTJSLongTaskFrameScheduler>
53
+ @property (nonatomic, copy) FTJSLongTaskFrameCallback callback;
54
+ @property (nonatomic, strong, nullable) CADisplayLink *displayLink;
55
+ - (instancetype)initWithCallback:(FTJSLongTaskFrameCallback)callback;
56
+ @end
57
+
58
+ @implementation FTCADisplayLinkScheduler
59
+ - (instancetype)initWithCallback:(FTJSLongTaskFrameCallback)callback {
60
+ self = [super init];
61
+ if (self) {
62
+ _callback = [callback copy];
63
+ }
64
+ return self;
65
+ }
66
+
67
+ - (void)start {
68
+ [self stop];
69
+ FTJSLongTaskDisplayLinkTarget *target = [FTJSLongTaskDisplayLinkTarget new];
70
+ target.callback = self.callback;
71
+ self.displayLink = [CADisplayLink displayLinkWithTarget:target selector:@selector(displayLinkDidFire:)];
72
+ [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
73
+ }
74
+
75
+ - (void)stop {
76
+ // CADisplayLink.invalidate is thread safe and releases its run loop and target
77
+ // references without requiring a live JS queue.
78
+ [self.displayLink invalidate];
79
+ self.displayLink = nil;
80
+ }
81
+
82
+ - (void)dealloc {
83
+ [_displayLink invalidate];
84
+ }
85
+ @end
86
+
87
+ @interface FTCADisplayLinkSchedulerFactory : NSObject <FTJSLongTaskFrameSchedulerFactory>
88
+ @end
89
+
90
+ @implementation FTCADisplayLinkSchedulerFactory
91
+ - (id<FTJSLongTaskFrameScheduler>)createSchedulerWithCallback:(FTJSLongTaskFrameCallback)callback {
92
+ return [[FTCADisplayLinkScheduler alloc] initWithCallback:callback];
93
+ }
94
+ @end
95
+
96
+ @interface FTNativeLongTaskReporter : NSObject <FTJSLongTaskReporter>
97
+ @end
98
+
99
+ @implementation FTNativeLongTaskReporter
100
+ - (void)reportLongTaskWithDurationNanoseconds:(int64_t)durationNanoseconds property:(NSDictionary *)property {
101
+ [[FTExternalDataManager sharedManager]
102
+ addLongTaskWithStack:@""
103
+ duration:@(durationNanoseconds)
104
+ property:property];
105
+ }
106
+ @end
107
+
108
+ @interface FTJSLongTaskMonitor ()
109
+ @property (nonatomic, strong) id<FTJSLongTaskQueue> queue;
110
+ @property (nonatomic, strong) id<FTJSLongTaskFrameSchedulerFactory> schedulerFactory;
111
+ @property (nonatomic, strong) id<FTJSLongTaskReporter> reporter;
112
+ @property (nonatomic, strong) NSObject *stateLock;
113
+ @property (nonatomic, assign) NSTimeInterval thresholdSeconds;
114
+ @property (nonatomic, assign) NSUInteger generation;
115
+ @property (nonatomic, assign) BOOL requestedRunning;
116
+ @property (nonatomic, strong, nullable) id<FTJSLongTaskFrameScheduler> scheduler;
117
+ @property (nonatomic, assign) NSUInteger activeGeneration;
118
+ @property (nonatomic, assign) CFTimeInterval lastFrameTimestamp;
119
+ @property (atomic, copy) NSDictionary *bridgeContext;
120
+ @end
121
+
122
+ @implementation FTJSLongTaskMonitor
123
+
124
+ + (instancetype)monitorWithBridge:(RCTBridge *)bridge {
125
+ return [[self alloc]
126
+ initWithQueue:[[FTReactBridgeJSQueue alloc] initWithBridge:bridge]
127
+ schedulerFactory:[FTCADisplayLinkSchedulerFactory new]
128
+ reporter:[FTNativeLongTaskReporter new]];
129
+ }
130
+
131
+ - (instancetype)initWithQueue:(id<FTJSLongTaskQueue>)queue
132
+ schedulerFactory:(id<FTJSLongTaskFrameSchedulerFactory>)schedulerFactory
133
+ reporter:(id<FTJSLongTaskReporter>)reporter {
134
+ self = [super init];
135
+ if (self) {
136
+ _queue = queue;
137
+ _schedulerFactory = schedulerFactory;
138
+ _reporter = reporter;
139
+ _stateLock = [NSObject new];
140
+ _bridgeContext = @{};
141
+ }
142
+ return self;
143
+ }
144
+
145
+ - (void)setThresholdMilliseconds:(double)thresholdMilliseconds {
146
+ @synchronized (self.stateLock) {
147
+ self.thresholdSeconds = thresholdMilliseconds <= 0 ? 0 : thresholdMilliseconds / 1000.0;
148
+ if (thresholdMilliseconds <= 0) {
149
+ [self stopLocked];
150
+ }
151
+ }
152
+ }
153
+
154
+ - (void)start {
155
+ NSUInteger startGeneration;
156
+ @synchronized (self.stateLock) {
157
+ if (self.thresholdSeconds == 0) {
158
+ return;
159
+ }
160
+ self.requestedRunning = YES;
161
+ startGeneration = ++self.generation;
162
+ }
163
+
164
+ __weak typeof(self) weakSelf = self;
165
+ BOOL dispatched = [self.queue dispatchBlock:^{
166
+ // A pending block must not keep a destroyed monitor (and its queue) alive.
167
+ typeof(self) strongSelf = weakSelf;
168
+ if (strongSelf == nil) {
169
+ return;
170
+ }
171
+ @synchronized (strongSelf.stateLock) {
172
+ if (strongSelf.generation != startGeneration || !strongSelf.requestedRunning) {
173
+ return;
174
+ }
175
+ [strongSelf.scheduler stop];
176
+ strongSelf.scheduler = [strongSelf.schedulerFactory createSchedulerWithCallback:^(CFTimeInterval timestamp) {
177
+ [weakSelf handleFrameTimestamp:timestamp generation:startGeneration];
178
+ }];
179
+ strongSelf.activeGeneration = startGeneration;
180
+ strongSelf.lastFrameTimestamp = 0;
181
+ [strongSelf.scheduler start];
182
+ }
183
+ }];
184
+ if (!dispatched) {
185
+ @synchronized (self.stateLock) {
186
+ if (self.generation == startGeneration) {
187
+ [self stopLocked];
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ - (void)stop {
194
+ [self stopWithCompletion:nil];
195
+ }
196
+
197
+ - (void)disableWithCompletion:(dispatch_block_t)completion {
198
+ @synchronized (self.stateLock) {
199
+ self.thresholdSeconds = 0;
200
+ [self stopLocked];
201
+ }
202
+ if (completion != nil) {
203
+ completion();
204
+ }
205
+ }
206
+
207
+ - (void)stopWithCompletion:(dispatch_block_t)completion {
208
+ @synchronized (self.stateLock) {
209
+ [self stopLocked];
210
+ }
211
+ if (completion != nil) {
212
+ completion();
213
+ }
214
+ }
215
+
216
+ // Caller holds stateLock, also used by scheduler installation and frame callbacks.
217
+ // Cleanup deliberately never dispatches to RN: an invalid proxy may silently drop it.
218
+ - (void)stopLocked {
219
+ self.requestedRunning = NO;
220
+ self.generation++;
221
+ [self.scheduler stop];
222
+ self.scheduler = nil;
223
+ self.activeGeneration = 0;
224
+ self.lastFrameTimestamp = 0;
225
+ }
226
+
227
+ - (void)handleFrameTimestamp:(CFTimeInterval)timestamp generation:(NSUInteger)generation {
228
+ @synchronized (self.stateLock) {
229
+ if (!self.requestedRunning || self.generation != generation ||
230
+ self.activeGeneration != generation || self.scheduler == nil) {
231
+ return;
232
+ }
233
+ if (self.lastFrameTimestamp != 0) {
234
+ NSTimeInterval duration = timestamp - self.lastFrameTimestamp;
235
+ if (duration > self.thresholdSeconds) {
236
+ int64_t durationNanoseconds = (int64_t)(duration * FTNanosecondsPerSecond);
237
+ // Capture at detection; later updates must not change an already reported event.
238
+ NSDictionary *property = self.bridgeContext;
239
+ [self.reporter reportLongTaskWithDurationNanoseconds:durationNanoseconds property:property];
240
+ }
241
+ }
242
+ self.lastFrameTimestamp = timestamp;
243
+ }
244
+ }
245
+
246
+ - (void)dealloc {
247
+ [_scheduler stop];
248
+ }
249
+
250
+ @end
@@ -8,12 +8,13 @@
8
8
 
9
9
  #import "FTMobileReactNative.h"
10
10
  #import <React/RCTConvert.h>
11
- #import <FTMobileSDK/FTMobileAgent.h>
12
- #import <FTMobileSDK/FTMobileConfig+Private.h>
13
- #import <FTMobileSDK/FTThreadDispatchManager.h>
14
- #import <FTMobileSDK/FTConstants.h>
15
- #import <FTMobileSDK/FTJSONUtil.h>
16
- #import <FTMobileSDK/FTRemoteConfigModel+Private.h>
11
+ #import <TrueWatchSDK/FTMobileAgent.h>
12
+ #import <TrueWatchSDK/FTSDKConfig+Private.h>
13
+ #import <TrueWatchSDK/FTThreadDispatchManager.h>
14
+ #import <TrueWatchSDK/FTConstants.h>
15
+ #import <TrueWatchSDK/FTJSONUtil.h>
16
+ #import <TrueWatchSDK/FTRemoteConfigModel+Private.h>
17
+ #import "FTWebSocketMetadataStore.h"
17
18
 
18
19
  static NSString *const FTRemoteConfigCallbackEvent = @"ft_remote_config_callback";
19
20
 
@@ -373,14 +374,14 @@ RCT_REMAP_METHOD(clearAllData,
373
374
  #endif
374
375
  - (void)sdkConfig:(NSDictionary *)context resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
375
376
  [FTThreadDispatchManager performBlockDispatchMainSyncSafe:^{
376
- FTMobileConfig *config;
377
+ FTSDKConfig *config;
377
378
  NSString *datakitUrl = [RCTConvert NSString:context[@"datakitUrl"]];
378
379
  NSString *dataWayUrl = [RCTConvert NSString:context[@"datawayUrl"]];
379
380
  NSString *clientToken = [RCTConvert NSString:context[@"clientToken"]];
380
381
  if(dataWayUrl && dataWayUrl.length>0 && clientToken && clientToken.length>0){
381
- config = [[FTMobileConfig alloc]initWithDatawayUrl:dataWayUrl clientToken:clientToken];
382
+ config = [[FTSDKConfig alloc]initWithDatawayUrl:dataWayUrl clientToken:clientToken];
382
383
  }else if(datakitUrl && datakitUrl.length>0){
383
- config = [[FTMobileConfig alloc]initWithDatakitUrl:datakitUrl];
384
+ config = [[FTSDKConfig alloc]initWithDatakitUrl:datakitUrl];
384
385
  }else{
385
386
  resolve(nil);
386
387
  return;
@@ -421,6 +422,12 @@ RCT_REMAP_METHOD(clearAllData,
421
422
  if ([context.allKeys containsObject:@"compressIntakeRequests"]) {
422
423
  config.compressIntakeRequests = [RCTConvert BOOL:context[@"compressIntakeRequests"]];
423
424
  }
425
+ if ([context.allKeys containsObject:@"enableDataFilter"]) {
426
+ config.enableDataFilter = [RCTConvert BOOL:context[@"enableDataFilter"]];
427
+ }
428
+ if ([context.allKeys containsObject:@"dataFilters"]) {
429
+ config.dataFilters = [RCTConvert NSDictionary:context[@"dataFilters"]];
430
+ }
424
431
  if ([context.allKeys containsObject:@"globalContext"]) {
425
432
  config.globalContext = [RCTConvert NSDictionary:context[@"globalContext"]];
426
433
  }
@@ -484,6 +491,7 @@ RCT_REMAP_METHOD(clearAllData,
484
491
  };
485
492
  }
486
493
  [FTMobileAgent startWithConfigOptions:config];
494
+ [FTWebSocketMetadataStore sdkDidStart];
487
495
  resolve(nil);
488
496
  }];
489
497
  }
@@ -531,6 +539,7 @@ RCT_REMAP_METHOD(clearAllData,
531
539
  }
532
540
 
533
541
  - (void)shutDown:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
542
+ [FTWebSocketMetadataStore shutDown];
534
543
  [FTMobileAgent shutDown];
535
544
  resolve(nil);
536
545
  }
@@ -547,9 +556,8 @@ RCT_REMAP_METHOD(clearAllData,
547
556
 
548
557
  }];
549
558
  }
550
- RCT_REMAP_METHOD(updateRemoteConfig,
551
- updateRemoteConfig_findEventsWithResolver:(RCTPromiseResolveBlock)resolve
552
- rejecter:(RCTPromiseRejectBlock)reject){
559
+ RCT_EXPORT_METHOD(updateRemoteConfig:(RCTPromiseResolveBlock)resolve
560
+ reject:(RCTPromiseRejectBlock)reject){
553
561
  if (!_remoteConfigurationEnabled) {
554
562
  reject(@"E_REMOTE_CONFIG_DISABLED", @"Remote configuration is not enabled.", nil);
555
563
  return;
@@ -574,11 +582,10 @@ RCT_REMAP_METHOD(updateRemoteConfig,
574
582
  return nil;
575
583
  }];
576
584
  }
577
- RCT_REMAP_METHOD(updateRemoteConfigWithMiniUpdateInterval,
578
- interval:(int)interval
585
+ RCT_EXPORT_METHOD(updateRemoteConfigWithMiniUpdateInterval:(double)interval
579
586
  rules:(NSArray *)rules
580
- findEventsWithResolver:(RCTPromiseResolveBlock)resolve
581
- rejecter:(RCTPromiseRejectBlock)reject){
587
+ resolve:(RCTPromiseResolveBlock)resolve
588
+ reject:(RCTPromiseRejectBlock)reject){
582
589
  if (!_remoteConfigurationEnabled) {
583
590
  reject(@"E_REMOTE_CONFIG_DISABLED", @"Remote configuration is not enabled.", nil);
584
591
  return;
@@ -7,9 +7,9 @@
7
7
  //
8
8
 
9
9
  #import "FTReactNativeLog.h"
10
- #import <FTMobileSDK/FTMobileAgent.h>
10
+ #import <TrueWatchSDK/FTMobileAgent.h>
11
11
  #import <React/RCTConvert.h>
12
- #import <FTMobileSDK/FTLogger+Private.h>
12
+ #import <TrueWatchSDK/FTLogger+Private.h>
13
13
  @implementation FTReactNativeLog
14
14
  RCT_EXPORT_MODULE()
15
15
 
@@ -43,7 +43,7 @@ RCT_REMAP_METHOD(logWithStatusString,
43
43
  - (void)logConfig:(NSDictionary *)context resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
44
44
  FTLoggerConfig *logger = [[FTLoggerConfig alloc]init];
45
45
  if ([context.allKeys containsObject:@"sampleRate"]) {
46
- logger.samplerate = [RCTConvert double:context[@"sampleRate"]]*100;
46
+ logger.sampleRate = [RCTConvert double:context[@"sampleRate"]] * 100;
47
47
  }
48
48
  NSArray<NSNumber *>*filters = [RCTConvert NSNumberArray:context[@"logLevelFilters"]];
49
49
  if (filters) {
@@ -76,4 +76,3 @@ RCT_REMAP_METHOD(logWithStatusString,
76
76
  }
77
77
 
78
78
  @end
79
-
@@ -7,21 +7,48 @@
7
7
  //
8
8
 
9
9
  #import "FTReactNativeRUM.h"
10
- #import <FTMobileSDK/FTMobileAgent.h>
11
- #import <FTMobileSDK/FTExternalDataManager.h>
12
- #import <FTMobileSDK/FTResourceMetricsModel.h>
13
- #import <FTMobileSDK/FTResourceContentModel.h>
10
+ #import <TrueWatchSDK/FTMobileAgent.h>
11
+ #import <TrueWatchSDK/FTExternalDataManager.h>
12
+ #import "FTReactNativeResource.h"
14
13
  #import <React/RCTConvert.h>
14
+ #import <React/RCTBridge.h>
15
+ #import <UIKit/UIKit.h>
15
16
  #import "FTReactNativeUtils.h"
17
+ #import "FTJSLongTaskMonitor.h"
18
+
19
+ @interface FTReactNativeRUM ()
20
+ @property (atomic, strong, nullable) FTJSLongTaskMonitor *jsLongTaskMonitor;
21
+ - (FTJSLongTaskMonitor *)getOrCreateLongTaskMonitor;
22
+ @end
23
+
16
24
 
17
25
  @implementation FTReactNativeRUM
26
+ @synthesize bridge = _bridge;
18
27
  RCT_EXPORT_MODULE()
28
+ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(setLongTaskContext:(NSDictionary *)context) {
29
+ [[self getOrCreateLongTaskMonitor] setBridgeContext:context ?: @{}];
30
+ return @YES;
31
+ }
32
+
33
+ - (FTJSLongTaskMonitor *)getOrCreateLongTaskMonitor {
34
+ @synchronized (self) {
35
+ if (self.jsLongTaskMonitor == nil) {
36
+ self.jsLongTaskMonitor = [FTJSLongTaskMonitor monitorWithBridge:self.bridge];
37
+ }
38
+ return self.jsLongTaskMonitor;
39
+ }
40
+ }
19
41
  RCT_REMAP_METHOD(setConfig,
20
42
  context:(NSDictionary *)context
21
43
  findEventsWithResolver:(RCTPromiseResolveBlock)resolve
22
44
  rejecter:(RCTPromiseRejectBlock)reject){
23
45
  [self setConfig:context resolve:resolve reject:reject];
24
46
  }
47
+ RCT_REMAP_METHOD(stopLongTaskTracking,
48
+ stopLongTaskTrackingWithResolver:(RCTPromiseResolveBlock)resolve
49
+ rejecter:(RCTPromiseRejectBlock)reject){
50
+ [self stopLongTaskTracking:resolve reject:reject];
51
+ }
25
52
  RCT_REMAP_METHOD(startAction,
26
53
  actionName:(NSString *)actionName actionType:(NSString *)actionType property:(NSDictionary *)property
27
54
  findEventsWithResolver:(RCTPromiseResolveBlock)resolve
@@ -88,6 +115,33 @@ RCT_REMAP_METHOD(addResource,
88
115
  }
89
116
  #endif
90
117
 
118
+ - (instancetype)init {
119
+ self = [super init];
120
+ if (self) {
121
+ [[NSNotificationCenter defaultCenter]
122
+ addObserver:self
123
+ selector:@selector(applicationDidBecomeActive:)
124
+ name:UIApplicationDidBecomeActiveNotification
125
+ object:nil];
126
+ [[NSNotificationCenter defaultCenter]
127
+ addObserver:self
128
+ selector:@selector(applicationWillResignActive:)
129
+ name:UIApplicationWillResignActiveNotification
130
+ object:nil];
131
+ }
132
+ return self;
133
+ }
134
+
135
+ - (void)invalidate {
136
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
137
+ [self.jsLongTaskMonitor disableWithCompletion:nil];
138
+ }
139
+
140
+ - (void)dealloc {
141
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
142
+ [_jsLongTaskMonitor disableWithCompletion:nil];
143
+ }
144
+
91
145
  - (void)addAction:(NSString *)actionName actionType:(NSString *)actionType property:(NSDictionary *)property resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
92
146
  [[FTExternalDataManager sharedManager] addAction:actionName actionType:actionType property:property];
93
147
  resolve(nil);
@@ -104,29 +158,7 @@ RCT_REMAP_METHOD(addResource,
104
158
  }
105
159
 
106
160
  - (void)addResource:(NSString *)key resource:(NSDictionary *)content metrics:(NSDictionary *)metrics resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
107
- if (key.length==0 || content.allKeys.count == 0) {
108
- return;
109
- }
110
- FTResourceMetricsModel *metricsModel = nil;
111
- if (metrics.allKeys.count>0) {
112
- metricsModel = [[FTResourceMetricsModel alloc]init];
113
- metricsModel.duration = [RCTConvert NSNumber:metrics[@"duration"]];
114
- metricsModel.resource_dns = [RCTConvert NSNumber:metrics[@"resource_dns"]];
115
- metricsModel.resource_tcp = [RCTConvert NSNumber:metrics[@"resource_tcp"]];
116
- metricsModel.resource_ssl = [RCTConvert NSNumber:metrics[@"resource_ssl"]];
117
- metricsModel.resource_ttfb = [RCTConvert NSNumber:metrics[@"resource_ttfb"]];
118
- metricsModel.resource_trans = [RCTConvert NSNumber:metrics[@"resource_trans"]];
119
- metricsModel.resource_first_byte = [RCTConvert NSNumber:metrics[@"resource_first_byte"]];
120
- }
121
- FTResourceContentModel *contentModel = [[FTResourceContentModel alloc]init];
122
- contentModel.url = [RCTConvert NSURL:content[@"url"]];
123
- contentModel.httpMethod = [RCTConvert NSString:content[@"httpMethod"]];
124
- contentModel.requestHeader = [RCTConvert NSDictionary:content[@"requestHeader"]];
125
- contentModel.responseHeader = [RCTConvert NSDictionary:content[@"responseHeader"]];
126
- contentModel.responseBody = [RCTConvert NSString:content[@"responseBody"]];
127
- contentModel.httpStatusCode = [RCTConvert int:content[@"resourceStatus"]];
128
-
129
- [[FTExternalDataManager sharedManager] addResourceWithKey:key metrics:metricsModel content:contentModel];
161
+ FTAddReactNativeResource(key, content, metrics);
130
162
  resolve(nil);
131
163
  }
132
164
 
@@ -139,7 +171,7 @@ RCT_REMAP_METHOD(addResource,
139
171
  NSString *rumAppId = [RCTConvert NSString:context[@"iOSAppId"]];
140
172
  FTRumConfig *rumConfig = [[FTRumConfig alloc]initWithAppid:rumAppId];
141
173
  if ([context.allKeys containsObject:@"sampleRate"]) {
142
- rumConfig.samplerate = [RCTConvert double:context[@"sampleRate"]]*100;
174
+ rumConfig.sampleRate = [RCTConvert double:context[@"sampleRate"]] * 100;
143
175
  }
144
176
  if ([context.allKeys containsObject:@"sessionOnErrorSampleRate"]) {
145
177
  rumConfig.sessionOnErrorSampleRate = [RCTConvert double:context[@"sessionOnErrorSampleRate"]]*100;
@@ -177,6 +209,11 @@ RCT_REMAP_METHOD(addResource,
177
209
  if ([context.allKeys containsObject:@"nativeFreezeDurationMs"]){
178
210
  rumConfig.freezeDurationMs = [RCTConvert double:context[@"nativeFreezeDurationMs"]];
179
211
  }
212
+ BOOL enableLongTask = [RCTConvert BOOL:context[@"enableLongTask"]];
213
+ double configuredLongTaskThresholdMs = [context.allKeys containsObject:@"longTaskThresholdMs"]
214
+ ? [RCTConvert double:context[@"longTaskThresholdMs"]]
215
+ : 100;
216
+ double longTaskThresholdMs = enableLongTask ? configuredLongTaskThresholdMs : 0;
180
217
  if ([context.allKeys containsObject:@"globalContext"]) {
181
218
  rumConfig.globalContext = [RCTConvert NSDictionary:context[@"globalContext"]];
182
219
  }
@@ -201,9 +238,47 @@ RCT_REMAP_METHOD(addResource,
201
238
  };
202
239
  #endif
203
240
  [[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
241
+ [self getOrCreateLongTaskMonitor];
242
+ [self.jsLongTaskMonitor setThresholdMilliseconds:longTaskThresholdMs];
243
+ if (longTaskThresholdMs > 0 && [self applicationIsActive]) {
244
+ [self.jsLongTaskMonitor start];
245
+ } else {
246
+ [self.jsLongTaskMonitor stop];
247
+ }
204
248
  resolve(nil);
205
249
  }
206
250
 
251
+ - (void)stopLongTaskTracking:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
252
+ if (self.jsLongTaskMonitor == nil) {
253
+ resolve(nil);
254
+ return;
255
+ }
256
+ [self.jsLongTaskMonitor disableWithCompletion:^{
257
+ resolve(nil);
258
+ }];
259
+ }
260
+
261
+ - (void)applicationDidBecomeActive:(NSNotification *)notification {
262
+ [self.jsLongTaskMonitor start];
263
+ }
264
+
265
+ - (void)applicationWillResignActive:(NSNotification *)notification {
266
+ [self.jsLongTaskMonitor stop];
267
+ }
268
+
269
+ - (BOOL)applicationIsActive {
270
+ __block BOOL active = NO;
271
+ dispatch_block_t readState = ^{
272
+ active = UIApplication.sharedApplication.applicationState == UIApplicationStateActive;
273
+ };
274
+ if (NSThread.isMainThread) {
275
+ readState();
276
+ } else {
277
+ dispatch_sync(dispatch_get_main_queue(), readState);
278
+ }
279
+ return active;
280
+ }
281
+
207
282
  - (void)startAction:(NSString *)actionName actionType:(NSString *)actionType property:(NSDictionary *)property resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
208
283
  [[FTExternalDataManager sharedManager] startAction:actionName actionType:actionType property:property];
209
284
  resolve(nil);
@@ -0,0 +1,4 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ // Shared model conversion for the public RUM bridge and internal WebSocket path.
4
+ FOUNDATION_EXPORT void FTAddReactNativeResource(NSString *key, NSDictionary *content, NSDictionary *metrics);
@@ -0,0 +1,58 @@
1
+ #import "FTReactNativeResource.h"
2
+ #import <TrueWatchSDK/FTExternalDataManager.h>
3
+ #import <TrueWatchSDK/FTResourceMetricsModel.h>
4
+ #import <TrueWatchSDK/FTResourceContentModel.h>
5
+ #import <React/RCTConvert.h>
6
+
7
+ static NSString * const FTReactNativeWebSocketErrorDomain = @"com.truewatch.react-native.websocket";
8
+
9
+ void FTAddReactNativeResource(NSString *key, NSDictionary *content, NSDictionary *metrics) {
10
+ if (key.length==0 || content.allKeys.count == 0) {
11
+ return;
12
+ }
13
+ FTResourceMetricsModel *metricsModel = nil;
14
+ if (metrics.allKeys.count>0) {
15
+ metricsModel = [[FTResourceMetricsModel alloc]init];
16
+ metricsModel.duration = [RCTConvert NSNumber:metrics[@"duration"]];
17
+ metricsModel.resource_dns = [RCTConvert NSNumber:metrics[@"resource_dns"]];
18
+ metricsModel.resource_tcp = [RCTConvert NSNumber:metrics[@"resource_tcp"]];
19
+ metricsModel.resource_ssl = [RCTConvert NSNumber:metrics[@"resource_ssl"]];
20
+ metricsModel.resource_ttfb = [RCTConvert NSNumber:metrics[@"resource_ttfb"]];
21
+ metricsModel.resource_trans = [RCTConvert NSNumber:metrics[@"resource_trans"]];
22
+ metricsModel.resource_first_byte = [RCTConvert NSNumber:metrics[@"resource_first_byte"]];
23
+ if ([content[@"webSocketHandshake"] boolValue] && [metrics[@"resource_http_protocol"] isKindOfClass:NSString.class]) {
24
+ metricsModel.resourceHttpProtocol = metrics[@"resource_http_protocol"];
25
+ }
26
+ }
27
+ FTResourceContentModel *contentModel = [[FTResourceContentModel alloc]init];
28
+ contentModel.url = [RCTConvert NSURL:content[@"url"]];
29
+ contentModel.httpMethod = [RCTConvert NSString:content[@"httpMethod"]];
30
+ contentModel.requestHeader = [RCTConvert NSDictionary:content[@"requestHeader"]];
31
+ contentModel.responseHeader = [RCTConvert NSDictionary:content[@"responseHeader"]];
32
+ contentModel.responseBody = [RCTConvert NSString:content[@"responseBody"]];
33
+ contentModel.httpStatusCode = [RCTConvert int:content[@"resourceStatus"]];
34
+ if ([content.allKeys containsObject:@"errorMessage"]) {
35
+ NSString *errorMessage = [RCTConvert NSString:content[@"errorMessage"]];
36
+ if (errorMessage.length > 0) {
37
+ NSInteger errorCode = -1;
38
+ if ([content.allKeys containsObject:@"errorCode"]) {
39
+ errorCode = [RCTConvert NSInteger:content[@"errorCode"]];
40
+ }
41
+ contentModel.errorMessage = errorMessage;
42
+ contentModel.error = [NSError errorWithDomain:FTReactNativeWebSocketErrorDomain
43
+ code:errorCode
44
+ userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
45
+ }
46
+ }
47
+ if ([content.allKeys containsObject:@"resourceType"]) {
48
+ contentModel.resourceType = [RCTConvert NSString:content[@"resourceType"]];
49
+ }
50
+ if ([content.allKeys containsObject:@"webSocketHandshake"]) {
51
+ contentModel.webSocketHandshake = [RCTConvert BOOL:content[@"webSocketHandshake"]];
52
+ }
53
+ if ([content.allKeys containsObject:@"webSocketHandshakeState"]) {
54
+ contentModel.webSocketHandshakeState = [RCTConvert NSString:content[@"webSocketHandshakeState"]];
55
+ }
56
+
57
+ [[FTExternalDataManager sharedManager] addResourceWithKey:key metrics:metricsModel content:contentModel];
58
+ }