@truewatchtech/react-native-mobile 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FTMobileReactNativeSDK.podspec +7 -5
- package/android/build.gradle +10 -3
- package/android/src/main/java/com/ft/sdk/reactnative/FTLogImpl.java +10 -2
- package/android/src/main/java/com/ft/sdk/reactnative/FTMobileImpl.java +45 -10
- package/android/src/main/java/com/ft/sdk/reactnative/FTRUMImpl.java +131 -6
- package/android/src/main/java/com/ft/sdk/reactnative/FTTraceImpl.java +10 -3
- package/android/src/main/java/com/ft/sdk/reactnative/JsLongTaskMonitor.java +281 -0
- package/android/src/main/java/com/ft/sdk/reactnative/ViewTiming.java +17 -0
- package/android/src/main/java/com/ft/sdk/reactnative/VisitorIdConfigMapper.java +50 -0
- package/android/src/main/java/com/ft/sdk/reactnative/utils/ReactNativeUtils.java +5 -12
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTMobileModule.java +14 -5
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTRUMModule.java +44 -1
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTTraceModule.java +14 -0
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTMobileModule.java +7 -2
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTRUMModule.java +39 -1
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTTraceModule.java +12 -0
- package/android/src/rnpost70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +21 -0
- package/android/src/rnpre70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +37 -0
- package/android/src/test/java/com/ft/sdk/reactnative/FTMobileImplTest.java +53 -0
- package/android/src/test/java/com/ft/sdk/reactnative/FTRUMModuleTest.java +117 -0
- package/android/src/test/java/com/ft/sdk/reactnative/JsLongTaskMonitorTest.java +446 -0
- package/android/src/test/java/com/ft/sdk/reactnative/utils/ReactNativeUtilsTest.java +33 -0
- package/ios/FTJSLongTaskMonitor.h +51 -0
- package/ios/FTJSLongTaskMonitor.m +250 -0
- package/ios/FTMobileReactNative.mm +54 -18
- package/ios/FTReactNativeLog.mm +9 -5
- package/ios/FTReactNativeRUM.mm +196 -31
- package/ios/FTReactNativeResource.h +4 -0
- package/ios/FTReactNativeResource.m +64 -0
- package/ios/FTReactNativeTrace.mm +59 -14
- package/ios/FTReactNativeUtils.m +10 -15
- package/ios/FTReactNativeViewTiming.h +18 -0
- package/ios/FTReactNativeWebSocket.h +10 -0
- package/ios/FTReactNativeWebSocket.mm +201 -0
- package/ios/FTWebSocketMetadataStore.h +25 -0
- package/ios/FTWebSocketMetadataStore.m +622 -0
- package/ios/FTWebSocketResourceData.h +20 -0
- package/ios/FTWebSocketResourceData.m +106 -0
- package/lib/commonjs/ft_logger.js +3 -1
- package/lib/commonjs/ft_logger.js.map +1 -1
- package/lib/commonjs/ft_mobile_agent.js +47 -4
- package/lib/commonjs/ft_mobile_agent.js.map +1 -1
- package/lib/commonjs/ft_rum.js +65 -5
- package/lib/commonjs/ft_rum.js.map +1 -1
- package/lib/commonjs/ft_tracing.js +14 -4
- package/lib/commonjs/ft_tracing.js.map +1 -1
- package/lib/commonjs/index.js +21 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/rum/FTBabelInteractionTracking.js +72 -0
- package/lib/commonjs/rum/FTBabelInteractionTracking.js.map +1 -0
- package/lib/commonjs/rum/FTBabelUtils.js +87 -0
- package/lib/commonjs/rum/FTBabelUtils.js.map +1 -0
- package/lib/commonjs/rum/FTRumActionTracking.js +10 -39
- package/lib/commonjs/rum/FTRumActionTracking.js.map +1 -1
- package/lib/commonjs/rum/FTRumActionTrackingContext.js +50 -0
- package/lib/commonjs/rum/FTRumActionTrackingContext.js.map +1 -0
- package/lib/commonjs/rum/FTRumViewTracking.js +147 -0
- package/lib/commonjs/rum/FTRumViewTracking.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketNative.js +73 -0
- package/lib/commonjs/rum/FTRumWebSocketNative.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketTiming.js +73 -0
- package/lib/commonjs/rum/FTRumWebSocketTiming.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketTracking.js +506 -0
- package/lib/commonjs/rum/FTRumWebSocketTracking.js.map +1 -0
- package/lib/commonjs/rum/longTasksUtils.js +25 -0
- package/lib/commonjs/rum/longTasksUtils.js.map +1 -0
- package/lib/commonjs/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeTrace.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js +11 -0
- package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/commonjs/visitorIdConfig.js +37 -0
- package/lib/commonjs/visitorIdConfig.js.map +1 -0
- package/lib/module/ft_logger.js +3 -1
- package/lib/module/ft_logger.js.map +1 -1
- package/lib/module/ft_mobile_agent.js +47 -4
- package/lib/module/ft_mobile_agent.js.map +1 -1
- package/lib/module/ft_rum.js +65 -5
- package/lib/module/ft_rum.js.map +1 -1
- package/lib/module/ft_tracing.js +14 -2
- package/lib/module/ft_tracing.js.map +1 -1
- package/lib/module/index.js +4 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/rum/FTBabelInteractionTracking.js +66 -0
- package/lib/module/rum/FTBabelInteractionTracking.js.map +1 -0
- package/lib/module/rum/FTBabelUtils.js +80 -0
- package/lib/module/rum/FTBabelUtils.js.map +1 -0
- package/lib/module/rum/FTRumActionTracking.js +10 -39
- package/lib/module/rum/FTRumActionTracking.js.map +1 -1
- package/lib/module/rum/FTRumActionTrackingContext.js +43 -0
- package/lib/module/rum/FTRumActionTrackingContext.js.map +1 -0
- package/lib/module/rum/FTRumViewTracking.js +140 -0
- package/lib/module/rum/FTRumViewTracking.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketNative.js +62 -0
- package/lib/module/rum/FTRumWebSocketNative.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketTiming.js +67 -0
- package/lib/module/rum/FTRumWebSocketTiming.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketTracking.js +499 -0
- package/lib/module/rum/FTRumWebSocketTracking.js.map +1 -0
- package/lib/module/rum/longTasksUtils.js +18 -0
- package/lib/module/rum/longTasksUtils.js.map +1 -0
- package/lib/module/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeTrace.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeWebSocket.js +8 -0
- package/lib/module/specs/NativeFTReactNativeWebSocket.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/module/visitorIdConfig.js +31 -0
- package/lib/module/visitorIdConfig.js.map +1 -0
- package/lib/typescript/ft_logger.d.ts +11 -1
- package/lib/typescript/ft_mobile_agent.d.ts +28 -0
- package/lib/typescript/ft_rum.d.ts +42 -5
- package/lib/typescript/ft_tracing.d.ts +9 -1
- package/lib/typescript/index.d.ts +5 -1
- package/lib/typescript/rum/FTBabelInteractionTracking.d.ts +30 -0
- package/lib/typescript/rum/FTBabelUtils.d.ts +1 -0
- package/lib/typescript/rum/FTRumActionTracking.d.ts +0 -2
- package/lib/typescript/rum/FTRumActionTrackingContext.d.ts +11 -0
- package/lib/typescript/rum/FTRumViewTracking.d.ts +32 -0
- package/lib/typescript/rum/FTRumWebSocketNative.d.ts +22 -0
- package/lib/typescript/rum/FTRumWebSocketTiming.d.ts +7 -0
- package/lib/typescript/rum/FTRumWebSocketTracking.d.ts +54 -0
- package/lib/typescript/rum/longTasksUtils.d.ts +8 -0
- package/lib/typescript/specs/NativeFTMobileReactNative.d.ts +15 -0
- package/lib/typescript/specs/NativeFTReactNativeRUM.d.ts +11 -1
- package/lib/typescript/specs/NativeFTReactNativeTrace.d.ts +12 -0
- package/lib/typescript/specs/NativeFTReactNativeWebSocket.d.ts +13 -0
- package/lib/typescript/version.d.ts +1 -1
- package/lib/typescript/visitorIdConfig.d.ts +11 -0
- package/package.json +6 -4
- package/src/ft_logger.tsx +11 -1
- package/src/ft_mobile_agent.tsx +79 -7
- package/src/ft_rum.tsx +111 -7
- package/src/ft_tracing.tsx +24 -2
- package/src/index.tsx +8 -0
- package/src/rum/FTBabelInteractionTracking.ts +124 -0
- package/src/rum/FTBabelUtils.tsx +99 -0
- package/src/rum/FTRumActionTracking.tsx +14 -43
- package/src/rum/FTRumActionTrackingContext.ts +68 -0
- package/src/rum/FTRumViewTracking.ts +179 -0
- package/src/rum/FTRumWebSocketNative.ts +107 -0
- package/src/rum/FTRumWebSocketTiming.ts +82 -0
- package/src/rum/FTRumWebSocketTracking.ts +722 -0
- package/src/rum/longTasksUtils.ts +17 -0
- package/src/specs/NativeFTMobileReactNative.ts +18 -0
- package/src/specs/NativeFTReactNativeRUM.ts +19 -1
- package/src/specs/NativeFTReactNativeTrace.ts +12 -0
- package/src/specs/NativeFTReactNativeWebSocket.ts +33 -0
- package/src/version.ts +1 -1
- package/src/visitorIdConfig.ts +123 -0
package/ios/FTReactNativeRUM.mm
CHANGED
|
@@ -7,21 +7,91 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
#import "FTReactNativeRUM.h"
|
|
10
|
-
#import <
|
|
11
|
-
#import <
|
|
12
|
-
#import <
|
|
13
|
-
#import
|
|
10
|
+
#import <TrueWatchSDK/FTMobileAgent.h>
|
|
11
|
+
#import <TrueWatchSDK/FTExternalDataManager.h>
|
|
12
|
+
#import <TrueWatchSDK/FTExternalDataManager+FrameworkIntegration.h>
|
|
13
|
+
#import "FTReactNativeViewTiming.h"
|
|
14
|
+
#import "FTReactNativeResource.h"
|
|
14
15
|
#import <React/RCTConvert.h>
|
|
16
|
+
#import <React/RCTBridge.h>
|
|
17
|
+
#import <UIKit/UIKit.h>
|
|
15
18
|
#import "FTReactNativeUtils.h"
|
|
19
|
+
#import "FTJSLongTaskMonitor.h"
|
|
20
|
+
#import <TrueWatchSDK/FTVisitorIDConfig+Private.h>
|
|
21
|
+
#include <math.h>
|
|
22
|
+
|
|
23
|
+
static FTVisitorIDConfig *FTVisitorIdConfigFromDictionary(id value, NSError **error) {
|
|
24
|
+
if (!value || value == NSNull.null) return nil;
|
|
25
|
+
NSString *reason = nil;
|
|
26
|
+
FTVisitorIDConfig *config = [FTVisitorIDConfig new];
|
|
27
|
+
if (![value isKindOfClass:NSDictionary.class]) {
|
|
28
|
+
reason = @"visitorIdConfig must be an object";
|
|
29
|
+
} else {
|
|
30
|
+
NSDictionary *map = value;
|
|
31
|
+
config.header = [map[@"header"] isKindOfClass:NSString.class] ? map[@"header"] : nil;
|
|
32
|
+
id hours = map[@"ttlHours"];
|
|
33
|
+
if (hours) {
|
|
34
|
+
double milliseconds = [hours isKindOfClass:NSNumber.class] ? [hours doubleValue] * 3600000 : NAN;
|
|
35
|
+
if (!isfinite(milliseconds) || milliseconds < 1 || milliseconds >= 9223372036854775807.0) reason = @"Visitor ID TTL is out of range";
|
|
36
|
+
else config.ttlHours = [hours doubleValue];
|
|
37
|
+
}
|
|
38
|
+
for (NSString *key in @[@"urlPrefixes", @"regularExpressions"]) {
|
|
39
|
+
id rules = map[key];
|
|
40
|
+
if (!rules) continue;
|
|
41
|
+
if (![rules isKindOfClass:NSArray.class]) { reason = @"Visitor ID rules must be arrays"; break; }
|
|
42
|
+
for (id rule in rules) {
|
|
43
|
+
if (![rule isKindOfClass:NSString.class] || [rule length] == 0) { reason = @"Invalid Visitor ID rule"; break; }
|
|
44
|
+
if ([key isEqualToString:@"urlPrefixes"]) {
|
|
45
|
+
[config addMatchWithURLPrefix:rule];
|
|
46
|
+
} else {
|
|
47
|
+
NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:rule options:0 error:error];
|
|
48
|
+
if (!expression) return nil;
|
|
49
|
+
[config addMatchWithRegularExpression:expression];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (reason) break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (reason) {
|
|
56
|
+
if (error) *error = [NSError errorWithDomain:@"com.truewatch.rn.visitor-id-config" code:1 userInfo:@{NSLocalizedDescriptionKey: reason}];
|
|
57
|
+
return nil;
|
|
58
|
+
}
|
|
59
|
+
return [config validatedCopyWithError:error];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@interface FTReactNativeRUM ()
|
|
63
|
+
@property (atomic, strong, nullable) FTJSLongTaskMonitor *jsLongTaskMonitor;
|
|
64
|
+
- (FTJSLongTaskMonitor *)getOrCreateLongTaskMonitor;
|
|
65
|
+
@end
|
|
66
|
+
|
|
16
67
|
|
|
17
68
|
@implementation FTReactNativeRUM
|
|
69
|
+
@synthesize bridge = _bridge;
|
|
18
70
|
RCT_EXPORT_MODULE()
|
|
71
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(setLongTaskContext:(NSDictionary *)context) {
|
|
72
|
+
[[self getOrCreateLongTaskMonitor] setBridgeContext:context ?: @{}];
|
|
73
|
+
return @YES;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
- (FTJSLongTaskMonitor *)getOrCreateLongTaskMonitor {
|
|
77
|
+
@synchronized (self) {
|
|
78
|
+
if (self.jsLongTaskMonitor == nil) {
|
|
79
|
+
self.jsLongTaskMonitor = [FTJSLongTaskMonitor monitorWithBridge:self.bridge];
|
|
80
|
+
}
|
|
81
|
+
return self.jsLongTaskMonitor;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
19
84
|
RCT_REMAP_METHOD(setConfig,
|
|
20
85
|
context:(NSDictionary *)context
|
|
21
86
|
findEventsWithResolver:(RCTPromiseResolveBlock)resolve
|
|
22
87
|
rejecter:(RCTPromiseRejectBlock)reject){
|
|
23
88
|
[self setConfig:context resolve:resolve reject:reject];
|
|
24
89
|
}
|
|
90
|
+
RCT_REMAP_METHOD(stopLongTaskTracking,
|
|
91
|
+
stopLongTaskTrackingWithResolver:(RCTPromiseResolveBlock)resolve
|
|
92
|
+
rejecter:(RCTPromiseRejectBlock)reject){
|
|
93
|
+
[self stopLongTaskTracking:resolve reject:reject];
|
|
94
|
+
}
|
|
25
95
|
RCT_REMAP_METHOD(startAction,
|
|
26
96
|
actionName:(NSString *)actionName actionType:(NSString *)actionType property:(NSDictionary *)property
|
|
27
97
|
findEventsWithResolver:(RCTPromiseResolveBlock)resolve
|
|
@@ -40,6 +110,21 @@ RCT_REMAP_METHOD(onCreateView,
|
|
|
40
110
|
rejecter:(RCTPromiseRejectBlock)reject){
|
|
41
111
|
[self onCreateView:viewName loadTime:loadTime resolve:resolve reject:reject];
|
|
42
112
|
}
|
|
113
|
+
RCT_REMAP_METHOD(onCreateViewWithLoadStart,
|
|
114
|
+
viewName:(NSString *)viewName loadTime:(double)loadTime loadStartEpochMs:(double)loadStartEpochMs
|
|
115
|
+
findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
116
|
+
[self onCreateViewWithLoadStart:viewName loadTime:loadTime loadStartEpochMs:loadStartEpochMs resolve:resolve reject:reject];
|
|
117
|
+
}
|
|
118
|
+
RCT_REMAP_METHOD(updateViewLoadingTime,
|
|
119
|
+
duration:(double)duration
|
|
120
|
+
findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
121
|
+
[self updateViewLoadingTime:duration resolve:resolve reject:reject];
|
|
122
|
+
}
|
|
123
|
+
RCT_REMAP_METHOD(startViewWithTrackingType,
|
|
124
|
+
viewName:(NSString *)viewName property:(NSDictionary *)property trackingType:(double)trackingType
|
|
125
|
+
findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
126
|
+
[self startViewWithTrackingType:viewName property:property trackingType:trackingType resolve:resolve reject:reject];
|
|
127
|
+
}
|
|
43
128
|
RCT_REMAP_METHOD(startView,
|
|
44
129
|
viewName:(NSString *)viewName property:(NSDictionary *)property
|
|
45
130
|
findEventsWithResolver:(RCTPromiseResolveBlock)resolve
|
|
@@ -88,6 +173,33 @@ RCT_REMAP_METHOD(addResource,
|
|
|
88
173
|
}
|
|
89
174
|
#endif
|
|
90
175
|
|
|
176
|
+
- (instancetype)init {
|
|
177
|
+
self = [super init];
|
|
178
|
+
if (self) {
|
|
179
|
+
[[NSNotificationCenter defaultCenter]
|
|
180
|
+
addObserver:self
|
|
181
|
+
selector:@selector(applicationDidBecomeActive:)
|
|
182
|
+
name:UIApplicationDidBecomeActiveNotification
|
|
183
|
+
object:nil];
|
|
184
|
+
[[NSNotificationCenter defaultCenter]
|
|
185
|
+
addObserver:self
|
|
186
|
+
selector:@selector(applicationWillResignActive:)
|
|
187
|
+
name:UIApplicationWillResignActiveNotification
|
|
188
|
+
object:nil];
|
|
189
|
+
}
|
|
190
|
+
return self;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
- (void)invalidate {
|
|
194
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
195
|
+
[self.jsLongTaskMonitor disableWithCompletion:nil];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
- (void)dealloc {
|
|
199
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
200
|
+
[_jsLongTaskMonitor disableWithCompletion:nil];
|
|
201
|
+
}
|
|
202
|
+
|
|
91
203
|
- (void)addAction:(NSString *)actionName actionType:(NSString *)actionType property:(NSDictionary *)property resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
92
204
|
[[FTExternalDataManager sharedManager] addAction:actionName actionType:actionType property:property];
|
|
93
205
|
resolve(nil);
|
|
@@ -104,29 +216,22 @@ RCT_REMAP_METHOD(addResource,
|
|
|
104
216
|
}
|
|
105
217
|
|
|
106
218
|
- (void)addResource:(NSString *)key resource:(NSDictionary *)content metrics:(NSDictionary *)metrics resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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];
|
|
219
|
+
FTAddReactNativeResource(key, content, metrics);
|
|
220
|
+
resolve(nil);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
- (void)onCreateViewWithLoadStart:(NSString *)viewName loadTime:(double)loadTime loadStartEpochMs:(double)loadStartEpochMs resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
224
|
+
NSNumber *duration = FTViewDurationNanoseconds(loadTime);
|
|
225
|
+
[[FTExternalDataManager sharedManager] onCreateView:viewName loadTime:duration loadStartEpochNs:duration.longLongValue > 0 ? FTViewEpochNanoseconds(loadStartEpochMs) : nil];
|
|
226
|
+
resolve(nil);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
- (void)startViewWithTrackingType:(NSString *)viewName property:(NSDictionary *)property trackingType:(double)trackingType resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
230
|
+
if (trackingType != 0 && trackingType != 1) {
|
|
231
|
+
reject(@"E_VIEW_TRACKING_TYPE", @"trackingType must be Manual (0) or Automatic (1)", nil);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
[[FTExternalDataManager sharedManager] startViewWithName:viewName property:property trackingType:(FTRUMViewTrackingType)(NSInteger)trackingType];
|
|
130
235
|
resolve(nil);
|
|
131
236
|
}
|
|
132
237
|
|
|
@@ -135,14 +240,31 @@ RCT_REMAP_METHOD(addResource,
|
|
|
135
240
|
resolve(nil);
|
|
136
241
|
}
|
|
137
242
|
|
|
243
|
+
- (void)updateViewLoadingTime:(double)duration resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
244
|
+
if (!isfinite(duration) || duration < 0 || duration > 9007199254740991.0 || duration != floor(duration)) {
|
|
245
|
+
reject(@"E_VIEW_LOADING_TIME", @"duration must be a non-negative safe integer in nanoseconds", nil);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
[[FTExternalDataManager sharedManager] updateViewLoadingTime:@((int64_t)duration)];
|
|
249
|
+
resolve(nil);
|
|
250
|
+
}
|
|
251
|
+
|
|
138
252
|
- (void)setConfig:(NSDictionary *)context resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
253
|
+
NSError *visitorError = nil;
|
|
254
|
+
FTVisitorIDConfig *visitorConfig = FTVisitorIdConfigFromDictionary(context[@"visitorIdConfig"], &visitorError);
|
|
255
|
+
if (visitorError) { reject(@"E_VISITOR_ID_CONFIG", visitorError.localizedDescription, visitorError); return; }
|
|
139
256
|
NSString *rumAppId = [RCTConvert NSString:context[@"iOSAppId"]];
|
|
140
257
|
FTRumConfig *rumConfig = [[FTRumConfig alloc]initWithAppid:rumAppId];
|
|
141
|
-
|
|
142
|
-
|
|
258
|
+
rumConfig.visitorIDConfig = visitorConfig;
|
|
259
|
+
if ([context.allKeys containsObject:@"sessionSamplingRate"]) {
|
|
260
|
+
rumConfig.sessionSamplingRate = [RCTConvert double:context[@"sessionSamplingRate"]];
|
|
261
|
+
} else if ([context.allKeys containsObject:@"sampleRate"]) {
|
|
262
|
+
rumConfig.sessionSamplingRate = [RCTConvert double:context[@"sampleRate"]] * 100;
|
|
143
263
|
}
|
|
144
|
-
if ([context.allKeys containsObject:@"
|
|
145
|
-
rumConfig.
|
|
264
|
+
if ([context.allKeys containsObject:@"sessionOnErrorSamplingRate"]) {
|
|
265
|
+
rumConfig.sessionOnErrorSamplingRate = [RCTConvert double:context[@"sessionOnErrorSamplingRate"]];
|
|
266
|
+
} else if ([context.allKeys containsObject:@"sessionOnErrorSampleRate"]) {
|
|
267
|
+
rumConfig.sessionOnErrorSamplingRate = [RCTConvert double:context[@"sessionOnErrorSampleRate"]] * 100;
|
|
146
268
|
}
|
|
147
269
|
if ([context.allKeys containsObject:@"enableNativeUserAction"]) {
|
|
148
270
|
rumConfig.enableTraceUserAction = [RCTConvert BOOL:context[@"enableNativeUserAction"]];
|
|
@@ -177,6 +299,11 @@ RCT_REMAP_METHOD(addResource,
|
|
|
177
299
|
if ([context.allKeys containsObject:@"nativeFreezeDurationMs"]){
|
|
178
300
|
rumConfig.freezeDurationMs = [RCTConvert double:context[@"nativeFreezeDurationMs"]];
|
|
179
301
|
}
|
|
302
|
+
BOOL enableLongTask = [RCTConvert BOOL:context[@"enableLongTask"]];
|
|
303
|
+
double configuredLongTaskThresholdMs = [context.allKeys containsObject:@"longTaskThresholdMs"]
|
|
304
|
+
? [RCTConvert double:context[@"longTaskThresholdMs"]]
|
|
305
|
+
: 100;
|
|
306
|
+
double longTaskThresholdMs = enableLongTask ? configuredLongTaskThresholdMs : 0;
|
|
180
307
|
if ([context.allKeys containsObject:@"globalContext"]) {
|
|
181
308
|
rumConfig.globalContext = [RCTConvert NSDictionary:context[@"globalContext"]];
|
|
182
309
|
}
|
|
@@ -201,9 +328,47 @@ RCT_REMAP_METHOD(addResource,
|
|
|
201
328
|
};
|
|
202
329
|
#endif
|
|
203
330
|
[[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
|
|
331
|
+
[self getOrCreateLongTaskMonitor];
|
|
332
|
+
[self.jsLongTaskMonitor setThresholdMilliseconds:longTaskThresholdMs];
|
|
333
|
+
if (longTaskThresholdMs > 0 && [self applicationIsActive]) {
|
|
334
|
+
[self.jsLongTaskMonitor start];
|
|
335
|
+
} else {
|
|
336
|
+
[self.jsLongTaskMonitor stop];
|
|
337
|
+
}
|
|
204
338
|
resolve(nil);
|
|
205
339
|
}
|
|
206
340
|
|
|
341
|
+
- (void)stopLongTaskTracking:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
342
|
+
if (self.jsLongTaskMonitor == nil) {
|
|
343
|
+
resolve(nil);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
[self.jsLongTaskMonitor disableWithCompletion:^{
|
|
347
|
+
resolve(nil);
|
|
348
|
+
}];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
|
352
|
+
[self.jsLongTaskMonitor start];
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
- (void)applicationWillResignActive:(NSNotification *)notification {
|
|
356
|
+
[self.jsLongTaskMonitor stop];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
- (BOOL)applicationIsActive {
|
|
360
|
+
__block BOOL active = NO;
|
|
361
|
+
dispatch_block_t readState = ^{
|
|
362
|
+
active = UIApplication.sharedApplication.applicationState == UIApplicationStateActive;
|
|
363
|
+
};
|
|
364
|
+
if (NSThread.isMainThread) {
|
|
365
|
+
readState();
|
|
366
|
+
} else {
|
|
367
|
+
dispatch_sync(dispatch_get_main_queue(), readState);
|
|
368
|
+
}
|
|
369
|
+
return active;
|
|
370
|
+
}
|
|
371
|
+
|
|
207
372
|
- (void)startAction:(NSString *)actionName actionType:(NSString *)actionType property:(NSDictionary *)property resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
208
373
|
[[FTExternalDataManager sharedManager] startAction:actionName actionType:actionType property:property];
|
|
209
374
|
resolve(nil);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#import "FTReactNativeResource.h"
|
|
2
|
+
#import "FTWebSocketResourceData.h"
|
|
3
|
+
#import <TrueWatchSDK/FTExternalDataManager.h>
|
|
4
|
+
#import <TrueWatchSDK/FTResourceMetricsModel.h>
|
|
5
|
+
#import <TrueWatchSDK/FTResourceContentModel.h>
|
|
6
|
+
#import <React/RCTConvert.h>
|
|
7
|
+
|
|
8
|
+
static NSString * const FTReactNativeWebSocketErrorDomain = @"com.truewatch.react-native.websocket";
|
|
9
|
+
|
|
10
|
+
void FTAddReactNativeResource(NSString *key, NSDictionary *content, NSDictionary *metrics) {
|
|
11
|
+
if (key.length==0 || content.allKeys.count == 0) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
FTResourceMetricsModel *metricsModel = nil;
|
|
15
|
+
if (metrics.allKeys.count>0) {
|
|
16
|
+
metricsModel = [[FTResourceMetricsModel alloc]init];
|
|
17
|
+
metricsModel.duration = [RCTConvert NSNumber:metrics[@"duration"]];
|
|
18
|
+
int64_t startNs, endNs;
|
|
19
|
+
if ([content[@"webSocketHandshake"] boolValue] && FTWebSocketResourceInterval(metrics, &startNs, &endNs)) {
|
|
20
|
+
metricsModel.fetchStartNsTimeInterval = startNs;
|
|
21
|
+
metricsModel.fetchEndNsTimeInterval = endNs;
|
|
22
|
+
}
|
|
23
|
+
metricsModel.resource_dns = [RCTConvert NSNumber:metrics[@"resource_dns"]];
|
|
24
|
+
metricsModel.resource_tcp = [RCTConvert NSNumber:metrics[@"resource_tcp"]];
|
|
25
|
+
metricsModel.resource_ssl = [RCTConvert NSNumber:metrics[@"resource_ssl"]];
|
|
26
|
+
metricsModel.resource_ttfb = [RCTConvert NSNumber:metrics[@"resource_ttfb"]];
|
|
27
|
+
metricsModel.resource_trans = [RCTConvert NSNumber:metrics[@"resource_trans"]];
|
|
28
|
+
metricsModel.resource_first_byte = [RCTConvert NSNumber:metrics[@"resource_first_byte"]];
|
|
29
|
+
if ([content[@"webSocketHandshake"] boolValue] && [metrics[@"resource_http_protocol"] isKindOfClass:NSString.class]) {
|
|
30
|
+
metricsModel.resourceHttpProtocol = metrics[@"resource_http_protocol"];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
FTResourceContentModel *contentModel = [[FTResourceContentModel alloc]init];
|
|
34
|
+
contentModel.url = [RCTConvert NSURL:content[@"url"]];
|
|
35
|
+
contentModel.httpMethod = [RCTConvert NSString:content[@"httpMethod"]];
|
|
36
|
+
contentModel.requestHeader = [RCTConvert NSDictionary:content[@"requestHeader"]];
|
|
37
|
+
contentModel.responseHeader = [RCTConvert NSDictionary:content[@"responseHeader"]];
|
|
38
|
+
contentModel.responseBody = [RCTConvert NSString:content[@"responseBody"]];
|
|
39
|
+
contentModel.httpStatusCode = [RCTConvert int:content[@"resourceStatus"]];
|
|
40
|
+
if ([content.allKeys containsObject:@"errorMessage"]) {
|
|
41
|
+
NSString *errorMessage = [RCTConvert NSString:content[@"errorMessage"]];
|
|
42
|
+
if (errorMessage.length > 0) {
|
|
43
|
+
NSInteger errorCode = -1;
|
|
44
|
+
if ([content.allKeys containsObject:@"errorCode"]) {
|
|
45
|
+
errorCode = [RCTConvert NSInteger:content[@"errorCode"]];
|
|
46
|
+
}
|
|
47
|
+
contentModel.errorMessage = errorMessage;
|
|
48
|
+
contentModel.error = [NSError errorWithDomain:FTReactNativeWebSocketErrorDomain
|
|
49
|
+
code:errorCode
|
|
50
|
+
userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if ([content.allKeys containsObject:@"resourceType"]) {
|
|
54
|
+
contentModel.resourceType = [RCTConvert NSString:content[@"resourceType"]];
|
|
55
|
+
}
|
|
56
|
+
if ([content.allKeys containsObject:@"webSocketHandshake"]) {
|
|
57
|
+
contentModel.webSocketHandshake = [RCTConvert BOOL:content[@"webSocketHandshake"]];
|
|
58
|
+
}
|
|
59
|
+
if ([content.allKeys containsObject:@"webSocketHandshakeState"]) {
|
|
60
|
+
contentModel.webSocketHandshakeState = [RCTConvert NSString:content[@"webSocketHandshakeState"]];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
[[FTExternalDataManager sharedManager] addResourceWithKey:key metrics:metricsModel content:contentModel];
|
|
64
|
+
}
|
|
@@ -8,12 +8,22 @@
|
|
|
8
8
|
|
|
9
9
|
#import "FTReactNativeTrace.h"
|
|
10
10
|
#import "FtMobileAgent.h"
|
|
11
|
-
#import <
|
|
12
|
-
#import <
|
|
13
|
-
#import <
|
|
14
|
-
#import
|
|
11
|
+
#import <TrueWatchSDK/FTMobileAgent.h>
|
|
12
|
+
#import <TrueWatchSDK/FTExternalDataManager.h>
|
|
13
|
+
#import <TrueWatchSDK/FTExternalDataManager+Private.h>
|
|
14
|
+
#import "FTWebSocketResourceData.h"
|
|
15
|
+
#import <TrueWatchSDK/FTResourceMetricsModel.h>
|
|
16
|
+
#import <TrueWatchSDK/FTResourceContentModel.h>
|
|
15
17
|
#import <React/RCTConvert.h>
|
|
16
|
-
#import <
|
|
18
|
+
#import <TrueWatchSDK/FTTraceManager.h>
|
|
19
|
+
|
|
20
|
+
@interface FTReactNativeTrace ()
|
|
21
|
+
// Keep no extra owner alive. Cancellation must not obtain a singleton after
|
|
22
|
+
// shutdown or depend on a WebSocket Resource having started.
|
|
23
|
+
@property (nonatomic, weak) id<FTExternalResourceProtocol> traceResourceDelegate;
|
|
24
|
+
- (nullable NSDictionary *)traceHeaderFieldsForURL:(NSString *)url key:(nullable NSString *)key;
|
|
25
|
+
@end
|
|
26
|
+
|
|
17
27
|
@implementation FTReactNativeTrace
|
|
18
28
|
RCT_EXPORT_MODULE()
|
|
19
29
|
|
|
@@ -32,6 +42,21 @@ RCT_REMAP_METHOD(getTraceHeaderFields,
|
|
|
32
42
|
[self getTraceHeaderFields:url key:key resolve:resolve reject:reject];
|
|
33
43
|
}
|
|
34
44
|
|
|
45
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getTraceHeaderFieldsSync:(NSString *)url
|
|
46
|
+
key:(NSString *)key) {
|
|
47
|
+
return [self traceHeaderFieldsForURL:url key:key];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
RCT_REMAP_METHOD(cancelWebSocketTrace,
|
|
51
|
+
cancelWebSocketTrace:(NSString *)key
|
|
52
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
53
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
54
|
+
@synchronized (self) {
|
|
55
|
+
FTWebSocketDiscardTraceResource(self.traceResourceDelegate, key);
|
|
56
|
+
}
|
|
57
|
+
resolve(nil);
|
|
58
|
+
}
|
|
59
|
+
|
|
35
60
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
36
61
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
37
62
|
return std::make_shared<facebook::react::NativeFTReactNativeTraceSpecJSI>(params);
|
|
@@ -42,12 +67,7 @@ RCT_REMAP_METHOD(getTraceHeaderFields,
|
|
|
42
67
|
}
|
|
43
68
|
|
|
44
69
|
- (void)getTraceHeaderFields:(NSString *)url key:(NSString *)key resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
45
|
-
NSDictionary *traceHeader =
|
|
46
|
-
if(key&&key.length>0){
|
|
47
|
-
traceHeader = [[FTExternalDataManager sharedManager] getTraceHeaderWithKey:key url:[NSURL URLWithString:url]];
|
|
48
|
-
}else{
|
|
49
|
-
traceHeader = [[FTExternalDataManager sharedManager] getTraceHeaderWithUrl:[NSURL URLWithString:url]];
|
|
50
|
-
}
|
|
70
|
+
NSDictionary *traceHeader = [self traceHeaderFieldsForURL:url key:key];
|
|
51
71
|
if (traceHeader) {
|
|
52
72
|
resolve(traceHeader);
|
|
53
73
|
}else{
|
|
@@ -55,10 +75,36 @@ RCT_REMAP_METHOD(getTraceHeaderFields,
|
|
|
55
75
|
}
|
|
56
76
|
}
|
|
57
77
|
|
|
78
|
+
- (nullable NSDictionary *)traceHeaderFieldsForURL:(NSString *)url key:(nullable NSString *)key {
|
|
79
|
+
NSURL *requestURL = [NSURL URLWithString:url];
|
|
80
|
+
if (!requestURL) {
|
|
81
|
+
return nil;
|
|
82
|
+
}
|
|
83
|
+
if (key.length > 0) {
|
|
84
|
+
@synchronized (self) {
|
|
85
|
+
// Use the exact delegate that creates the keyed correlation. Record it
|
|
86
|
+
// before generation, including a generation that subsequently throws.
|
|
87
|
+
id<FTExternalResourceProtocol> delegate = [FTExternalDataManager sharedManager].resourceDelegate;
|
|
88
|
+
self.traceResourceDelegate = delegate;
|
|
89
|
+
if ([delegate respondsToSelector:@selector(getTraceHeaderWithKey:url:)]) {
|
|
90
|
+
return [delegate getTraceHeaderWithKey:key url:requestURL];
|
|
91
|
+
}
|
|
92
|
+
return nil;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return [[FTExternalDataManager sharedManager] getTraceHeaderWithUrl:requestURL];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
- (void)invalidate {
|
|
99
|
+
@synchronized (self) { self.traceResourceDelegate = nil; }
|
|
100
|
+
}
|
|
101
|
+
|
|
58
102
|
- (void)setConfig:(NSDictionary *)context resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
59
103
|
FTTraceConfig *trace = [[FTTraceConfig alloc]init];
|
|
60
|
-
if ([context.allKeys containsObject:@"
|
|
61
|
-
|
|
104
|
+
if ([context.allKeys containsObject:@"traceSamplingRate"]) {
|
|
105
|
+
trace.traceSamplingRate = [RCTConvert double:context[@"traceSamplingRate"]];
|
|
106
|
+
} else if ([context.allKeys containsObject:@"sampleRate"]) {
|
|
107
|
+
trace.traceSamplingRate = [RCTConvert double:context[@"sampleRate"]] * 100;
|
|
62
108
|
}
|
|
63
109
|
if ([context.allKeys containsObject:@"traceType"]) {
|
|
64
110
|
int traceType = [RCTConvert int:context[@"traceType"]];
|
|
@@ -71,4 +117,3 @@ RCT_REMAP_METHOD(getTraceHeaderFields,
|
|
|
71
117
|
}
|
|
72
118
|
|
|
73
119
|
@end
|
|
74
|
-
|
package/ios/FTReactNativeUtils.m
CHANGED
|
@@ -9,24 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
@implementation FTReactNativeUtils
|
|
11
11
|
+ (BOOL)filterBlackResource:(NSURL *)url{
|
|
12
|
-
static
|
|
12
|
+
static NSRegularExpression *internalDevResourceRegex;
|
|
13
13
|
static dispatch_once_t onceToken;
|
|
14
14
|
dispatch_once(&onceToken, ^{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
[internalDevResourceBlacklist addObject:regularExpress];
|
|
20
|
-
NSString *rn = @"^http://localhost:808[0-9]/(hot|symbolicate|message|inspector|status|assets).*$";
|
|
21
|
-
NSRegularExpression * rnRegularExpress = [NSRegularExpression regularExpressionWithPattern:rn options:NSRegularExpressionCaseInsensitive error:&error];
|
|
22
|
-
[internalDevResourceBlacklist addObject:rnRegularExpress];
|
|
15
|
+
NSString *pattern = @"^(?:https?|wss?)://(?:(?:10|172|192)(?:\\.[0-9]+){3}|localhost|127\\.0\\.0\\.1|\\[::1\\]):808[0-9]/(?:hot|symbolicate|message|inspector|status|assets|logs|debugger-proxy)(?:[/?#].*)?$";
|
|
16
|
+
internalDevResourceRegex = [NSRegularExpression regularExpressionWithPattern:pattern
|
|
17
|
+
options:NSRegularExpressionCaseInsensitive
|
|
18
|
+
error:NULL];
|
|
23
19
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return YES;
|
|
28
|
-
}
|
|
20
|
+
NSString *absoluteString = url.absoluteString;
|
|
21
|
+
if (absoluteString.length == 0) {
|
|
22
|
+
return NO;
|
|
29
23
|
}
|
|
30
|
-
|
|
24
|
+
NSRange fullRange = NSMakeRange(0, absoluteString.length);
|
|
25
|
+
return [internalDevResourceRegex firstMatchInString:absoluteString options:0 range:fullRange] != nil;
|
|
31
26
|
}
|
|
32
27
|
@end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#include <math.h>
|
|
3
|
+
#include <stdint.h>
|
|
4
|
+
|
|
5
|
+
// Use an exclusive 2^63 bound: double cannot represent INT64_MAX exactly.
|
|
6
|
+
static inline NSNumber *FTViewDurationNanoseconds(double value) {
|
|
7
|
+
return isfinite(value) && value > 0 && value < 0x1p63 ? @((int64_t)value) : @0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static inline NSNumber *FTViewEpochNanoseconds(double milliseconds) {
|
|
11
|
+
if (!isfinite(milliseconds) || milliseconds <= 0 || milliseconds >= 0x1p63 / 1000000.0) return nil;
|
|
12
|
+
// Split before multiplication so integer millisecond epochs stay exact.
|
|
13
|
+
int64_t whole = (int64_t)milliseconds;
|
|
14
|
+
int64_t fraction = (int64_t)((milliseconds - whole) * 1000000.0);
|
|
15
|
+
if (whole > INT64_MAX / 1000000 || fraction > INT64_MAX - whole * 1000000) return nil;
|
|
16
|
+
int64_t nanoseconds = whole * 1000000 + fraction;
|
|
17
|
+
return nanoseconds > 0 ? @(nanoseconds) : nil;
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <React/RCTBridgeModule.h>
|
|
3
|
+
|
|
4
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
5
|
+
#import <FTSdkReactNative/FTSdkReactNative.h>
|
|
6
|
+
@interface FTReactNativeWebSocket : NSObject <NativeFTReactNativeWebSocketSpec>
|
|
7
|
+
#else
|
|
8
|
+
@interface FTReactNativeWebSocket : NSObject <RCTBridgeModule>
|
|
9
|
+
#endif
|
|
10
|
+
@end
|