@react-native-firebase/crashlytics 26.4.0 → 27.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/RNFBCrashlytics.podspec +7 -1
- package/android/build.gradle +8 -0
- package/android/proguard-rules.pro +20 -0
- package/android/src/main/java/io/invertase/firebase/crashlytics/Constants.kt +42 -0
- package/android/src/main/java/io/invertase/firebase/crashlytics/{JavaScriptError.java → JavaScriptError.kt} +4 -6
- package/android/src/main/java/io/invertase/firebase/crashlytics/NativeRNFBTurboCrashlytics.java +11 -4
- package/android/src/main/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsFatalExit.java +117 -0
- package/android/src/main/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsInternal.java +80 -0
- package/android/src/main/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsNativeHelper.kt +45 -0
- package/android/src/main/java/io/invertase/firebase/crashlytics/{UnhandledPromiseRejection.java → UnhandledPromiseRejection.kt} +4 -6
- package/android/src/test/java/io/invertase/firebase/crashlytics/ConstantsAndJsErrorTypesTest.java +84 -0
- package/android/src/test/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsFatalExitBarrierTest.java +122 -0
- package/android/src/test/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsFatalExitTest.java +189 -0
- package/android/src/test/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsInternalTest.java +199 -0
- package/android/src/test/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsNativeHelperTest.java +108 -0
- package/dist/module/version.js +1 -1
- package/dist/typescript/lib/index.d.ts +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/ios/RNFBCrashlytics/RNFBCrashlyticsDebuggerProbe.swift +121 -0
- package/ios/RNFBCrashlytics/RNFBCrashlyticsHelper.h +98 -0
- package/ios/RNFBCrashlytics/RNFBCrashlyticsHelper.m +182 -0
- package/ios/RNFBCrashlytics/RNFBCrashlyticsModule.mm +57 -75
- package/ios/RNFBCrashlytics.xcodeproj/project.pbxproj +14 -0
- package/ios/RNFBCrashlyticsUnitTests/HostStubs/Firebase/FIRCrashlyticsHostStub.m +273 -0
- package/ios/RNFBCrashlyticsUnitTests/HostStubs/Firebase/Firebase.h +56 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsDebuggerProbeTests.swift +112 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsFatalExitTests.m +268 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsHelperHostStubTests.m +119 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsUnitTests.xcodeproj/project.pbxproj +265 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBCrashlyticsUnitTests.xcscheme +69 -0
- package/ios_config.sh +38 -5
- package/lib/version.ts +1 -1
- package/package.json +4 -4
- package/android/src/main/java/io/invertase/firebase/crashlytics/Constants.java +0 -29
- package/android/src/main/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsNativeHelper.java +0 -18
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#import <Foundation/Foundation.h>
|
|
19
|
+
|
|
20
|
+
// Plain Objective-C helper (see docs/ios-spm.mdx and
|
|
21
|
+
// okf-bundle/ios-spm-native-imports.md) that owns every call touching
|
|
22
|
+
// `FIRCrashlytics` / exception models for RNFBCrashlyticsModule, plus the
|
|
23
|
+
// Swift debugger probe. This keeps RNFBCrashlyticsModule.mm free of Firebase
|
|
24
|
+
// Crashlytics imports and `*-Swift.h`: under SPM an Objective-C++ (.mm)
|
|
25
|
+
// TurboModule cannot `@import` Firebase (or parse a Swift-generated header
|
|
26
|
+
// that uses module imports) when C++ modules are disabled (required by React
|
|
27
|
+
// Native's JSI headers).
|
|
28
|
+
@interface RNFBCrashlyticsHelper : NSObject
|
|
29
|
+
|
|
30
|
+
/** Cached `sysctl` debugger-attach probe (Swift `RNFBCrashlyticsDebuggerProbe`). */
|
|
31
|
+
+ (BOOL)isDebuggerAttached;
|
|
32
|
+
|
|
33
|
+
+ (void)checkForUnsentReportsWithCompletion:(void (^)(BOOL unsentReports))completion;
|
|
34
|
+
|
|
35
|
+
+ (void)deleteUnsentReports;
|
|
36
|
+
|
|
37
|
+
+ (BOOL)didCrashDuringPreviousExecution;
|
|
38
|
+
|
|
39
|
+
+ (void)log:(NSString *)message;
|
|
40
|
+
|
|
41
|
+
+ (void)sendUnsentReports;
|
|
42
|
+
|
|
43
|
+
+ (void)setCustomValue:(NSString *)value forKey:(NSString *)key;
|
|
44
|
+
|
|
45
|
+
/** Applies each entry via `setCustomValue:forKey:` (Module setAttributes path). */
|
|
46
|
+
+ (void)setCustomKeysAndValues:(NSDictionary *)attributes;
|
|
47
|
+
|
|
48
|
+
+ (void)setUserID:(NSString *)userId;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Records a JS exception model.
|
|
52
|
+
* Each frame dictionary uses keys: `fn` (symbol), `file`, `line` (number).
|
|
53
|
+
*/
|
|
54
|
+
+ (void)recordJavaScriptErrorWithMessage:(NSString *)message
|
|
55
|
+
frames:(NSArray<NSDictionary *> *)frames
|
|
56
|
+
isUnhandledPromiseRejection:(BOOL)isUnhandledPromiseRejection;
|
|
57
|
+
|
|
58
|
+
@end
|
|
59
|
+
|
|
60
|
+
/** Upper bound (seconds) a JS fatal waits for its record to save before the process exits. */
|
|
61
|
+
FOUNDATION_EXPORT const NSTimeInterval RNFBCrashlyticsFatalExitWatchdogTimeout;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Records a JS fatal, then terminates the process once the record has had a chance to save.
|
|
65
|
+
*
|
|
66
|
+
* `-[FIRCrashlytics recordExceptionModel:]` saves asynchronously: once Crashlytics' context-init
|
|
67
|
+
* promise is fulfilled, the save block is dispatched to the main queue. Terminating on the
|
|
68
|
+
* calling queue right after recording usually loses that save. Instead the terminator is
|
|
69
|
+
* enqueued on `terminateQueue` (the main queue in production), so it runs FIFO after the save
|
|
70
|
+
* block. A watchdog on `watchdogQueue` terminates after `watchdogTimeout` if `terminateQueue`
|
|
71
|
+
* is hung. The terminator runs at most once.
|
|
72
|
+
*
|
|
73
|
+
* Known limit: a JS fatal recorded before Crashlytics finished initialising queues its save as a
|
|
74
|
+
* promise observer instead of a main-queue block, so it can still be lost.
|
|
75
|
+
* The FIFO ordering also relies on FBLPromise's default dispatch queue being the main queue; if an
|
|
76
|
+
* app changes it, the save runs off the main queue and the terminator can run first, losing the
|
|
77
|
+
* report. The watchdog only covers a hung `terminateQueue`.
|
|
78
|
+
*/
|
|
79
|
+
@interface RNFBCrashlyticsFatalExit : NSObject
|
|
80
|
+
|
|
81
|
+
/** Production instance: terminates with `exit(0)` on the main queue, 2 s watchdog. */
|
|
82
|
+
+ (instancetype)shared;
|
|
83
|
+
|
|
84
|
+
- (instancetype)initWithTerminator:(dispatch_block_t)terminator
|
|
85
|
+
terminateQueue:(dispatch_queue_t)terminateQueue
|
|
86
|
+
watchdogQueue:(dispatch_queue_t)watchdogQueue
|
|
87
|
+
watchdogTimeout:(NSTimeInterval)watchdogTimeout NS_DESIGNATED_INITIALIZER;
|
|
88
|
+
|
|
89
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Runs `record` synchronously, then schedules termination, even when `record` raises. Returns NO
|
|
93
|
+
* without running `record` when an earlier call already started terminating (a second fatal
|
|
94
|
+
* while waiting).
|
|
95
|
+
*/
|
|
96
|
+
- (BOOL)recordThenTerminate:(dispatch_block_t)record;
|
|
97
|
+
|
|
98
|
+
@end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#if __has_include(<Firebase/Firebase.h>)
|
|
19
|
+
#import <Firebase/Firebase.h>
|
|
20
|
+
#elif __has_include(<FirebaseCrashlytics/FirebaseCrashlytics.h>)
|
|
21
|
+
#import <FirebaseCore/FirebaseCore.h>
|
|
22
|
+
#import <FirebaseCrashlytics/FirebaseCrashlytics.h>
|
|
23
|
+
#else
|
|
24
|
+
@import FirebaseCore;
|
|
25
|
+
@import FirebaseCrashlytics;
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
#if __has_include(<RNFBCrashlytics/RNFBCrashlytics-Swift.h>)
|
|
29
|
+
#import <RNFBCrashlytics/RNFBCrashlytics-Swift.h>
|
|
30
|
+
#elif __has_include("RNFBCrashlytics-Swift.h")
|
|
31
|
+
#import "RNFBCrashlytics-Swift.h"
|
|
32
|
+
#elif __has_include("RNFBCrashlyticsDebuggerProbe-Swift.inc")
|
|
33
|
+
#import "RNFBCrashlyticsDebuggerProbe-Swift.inc"
|
|
34
|
+
#else
|
|
35
|
+
#error "RNFBCrashlyticsDebuggerProbe Swift interface not found"
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
#import <stdatomic.h>
|
|
39
|
+
|
|
40
|
+
#import "RNFBCrashlyticsHelper.h"
|
|
41
|
+
|
|
42
|
+
@implementation RNFBCrashlyticsHelper
|
|
43
|
+
|
|
44
|
+
+ (BOOL)isDebuggerAttached {
|
|
45
|
+
return [RNFBCrashlyticsDebuggerProbe isDebuggerAttached];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
+ (void)checkForUnsentReportsWithCompletion:(void (^)(BOOL unsentReports))completion {
|
|
49
|
+
[[FIRCrashlytics crashlytics] checkForUnsentReportsWithCompletion:^(BOOL unsentReports) {
|
|
50
|
+
if (completion != nil) {
|
|
51
|
+
completion(unsentReports);
|
|
52
|
+
}
|
|
53
|
+
}];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
+ (void)deleteUnsentReports {
|
|
57
|
+
[[FIRCrashlytics crashlytics] deleteUnsentReports];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
+ (BOOL)didCrashDuringPreviousExecution {
|
|
61
|
+
return [[FIRCrashlytics crashlytics] didCrashDuringPreviousExecution];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
+ (void)log:(NSString *)message {
|
|
65
|
+
[[FIRCrashlytics crashlytics] log:message];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
+ (void)sendUnsentReports {
|
|
69
|
+
[[FIRCrashlytics crashlytics] sendUnsentReports];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
+ (void)setCustomValue:(NSString *)value forKey:(NSString *)key {
|
|
73
|
+
[[FIRCrashlytics crashlytics] setCustomValue:value forKey:key];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
+ (void)setCustomKeysAndValues:(NSDictionary *)attributes {
|
|
77
|
+
NSArray *keys = [attributes allKeys];
|
|
78
|
+
for (NSString *key in keys) {
|
|
79
|
+
[[FIRCrashlytics crashlytics] setCustomValue:attributes[key] forKey:key];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
+ (void)setUserID:(NSString *)userId {
|
|
84
|
+
[[FIRCrashlytics crashlytics] setUserID:userId];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
+ (void)recordJavaScriptErrorWithMessage:(NSString *)message
|
|
88
|
+
frames:(NSArray<NSDictionary *> *)frames
|
|
89
|
+
isUnhandledPromiseRejection:(BOOL)isUnhandledPromiseRejection {
|
|
90
|
+
NSMutableArray *stackTrace = [[NSMutableArray alloc] init];
|
|
91
|
+
for (NSDictionary *stackFrame in frames) {
|
|
92
|
+
NSString *symbol = stackFrame[@"fn"];
|
|
93
|
+
NSString *file = stackFrame[@"file"];
|
|
94
|
+
uint32_t line = (uint32_t)[stackFrame[@"line"] unsignedIntValue];
|
|
95
|
+
FIRStackFrame *customFrame = [FIRStackFrame stackFrameWithSymbol:symbol file:file line:line];
|
|
96
|
+
[stackTrace addObject:customFrame];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
NSString *name = @"JavaScriptError";
|
|
100
|
+
if (isUnhandledPromiseRejection) {
|
|
101
|
+
name = @"UnhandledPromiseRejection";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
FIRExceptionModel *exceptionModel = [FIRExceptionModel exceptionModelWithName:name
|
|
105
|
+
reason:message];
|
|
106
|
+
exceptionModel.stackTrace = stackTrace;
|
|
107
|
+
|
|
108
|
+
[[FIRCrashlytics crashlytics] recordExceptionModel:exceptionModel];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@end
|
|
112
|
+
|
|
113
|
+
const NSTimeInterval RNFBCrashlyticsFatalExitWatchdogTimeout = 2.0;
|
|
114
|
+
|
|
115
|
+
@implementation RNFBCrashlyticsFatalExit {
|
|
116
|
+
dispatch_block_t _terminator;
|
|
117
|
+
dispatch_queue_t _terminateQueue;
|
|
118
|
+
dispatch_queue_t _watchdogQueue;
|
|
119
|
+
NSTimeInterval _watchdogTimeout;
|
|
120
|
+
atomic_bool _exiting;
|
|
121
|
+
atomic_bool _terminated;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
+ (instancetype)shared {
|
|
125
|
+
static RNFBCrashlyticsFatalExit *shared;
|
|
126
|
+
static dispatch_once_t once;
|
|
127
|
+
dispatch_once(&once, ^{
|
|
128
|
+
shared = [[RNFBCrashlyticsFatalExit alloc]
|
|
129
|
+
initWithTerminator:^{
|
|
130
|
+
// Coverage exception: ends the process, so no test can run it.
|
|
131
|
+
exit(0);
|
|
132
|
+
}
|
|
133
|
+
terminateQueue:dispatch_get_main_queue()
|
|
134
|
+
watchdogQueue:dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
|
|
135
|
+
watchdogTimeout:RNFBCrashlyticsFatalExitWatchdogTimeout];
|
|
136
|
+
});
|
|
137
|
+
return shared;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- (instancetype)initWithTerminator:(dispatch_block_t)terminator
|
|
141
|
+
terminateQueue:(dispatch_queue_t)terminateQueue
|
|
142
|
+
watchdogQueue:(dispatch_queue_t)watchdogQueue
|
|
143
|
+
watchdogTimeout:(NSTimeInterval)watchdogTimeout {
|
|
144
|
+
if (self = [super init]) {
|
|
145
|
+
_terminator = [terminator copy];
|
|
146
|
+
_terminateQueue = terminateQueue;
|
|
147
|
+
_watchdogQueue = watchdogQueue;
|
|
148
|
+
_watchdogTimeout = watchdogTimeout;
|
|
149
|
+
atomic_init(&_exiting, false);
|
|
150
|
+
atomic_init(&_terminated, false);
|
|
151
|
+
}
|
|
152
|
+
return self;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
- (BOOL)recordThenTerminate:(dispatch_block_t)record {
|
|
156
|
+
if (atomic_exchange(&_exiting, true)) {
|
|
157
|
+
return NO;
|
|
158
|
+
}
|
|
159
|
+
@try {
|
|
160
|
+
record();
|
|
161
|
+
} @catch (NSException *exception) {
|
|
162
|
+
// The guard is already set, so terminate anyway rather than leave the app running with every
|
|
163
|
+
// later fatal ignored.
|
|
164
|
+
NSLog(@"RNFBCrashlytics: recording JS fatal failed: %@", exception);
|
|
165
|
+
}
|
|
166
|
+
dispatch_block_t terminateOnce = ^{
|
|
167
|
+
[self terminateOnce];
|
|
168
|
+
};
|
|
169
|
+
// FIFO behind the Crashlytics save block that recordExceptionModel: enqueued on main.
|
|
170
|
+
dispatch_async(_terminateQueue, terminateOnce);
|
|
171
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_watchdogTimeout * NSEC_PER_SEC)),
|
|
172
|
+
_watchdogQueue, terminateOnce);
|
|
173
|
+
return YES;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
- (void)terminateOnce {
|
|
177
|
+
if (!atomic_exchange(&_terminated, true)) {
|
|
178
|
+
_terminator();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@end
|
|
@@ -15,23 +15,17 @@
|
|
|
15
15
|
*
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// This module intentionally has no Firebase imports and no `*-Swift.h` —
|
|
19
|
+
// see RNFBCrashlyticsHelper.h. Every Firebase Crashlytics SDK call (and the
|
|
20
|
+
// Swift debugger probe) is routed through the plain Objective-C
|
|
21
|
+
// RNFBCrashlyticsHelper class instead, which can safely `@import`
|
|
22
|
+
// FirebaseCrashlytics / import the generated Swift interface because it
|
|
23
|
+
// compiles as ObjC, not ObjC++.
|
|
21
24
|
#import <React/RCTConvert.h>
|
|
22
25
|
#import <React/RCTLog.h>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
#if __has_include(<Firebase/Firebase.h>)
|
|
26
|
-
#import <Firebase/Firebase.h>
|
|
27
|
-
#elif __has_include(<FirebaseCrashlytics/FirebaseCrashlytics.h>)
|
|
28
|
-
#import <FirebaseCore/FirebaseCore.h>
|
|
29
|
-
#import <FirebaseCrashlytics/FirebaseCrashlytics.h>
|
|
30
|
-
#else
|
|
31
|
-
@import FirebaseCore;
|
|
32
|
-
@import FirebaseCrashlytics;
|
|
33
|
-
#endif
|
|
26
|
+
|
|
34
27
|
#import "RNFBApp/RNFBSharedUtils.h"
|
|
28
|
+
#import "RNFBCrashlyticsHelper.h"
|
|
35
29
|
#import "RNFBCrashlyticsInitProvider.h"
|
|
36
30
|
#import "RNFBCrashlyticsModule.h"
|
|
37
31
|
#import "RNFBPreferences.h"
|
|
@@ -58,7 +52,7 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
58
52
|
constants[@"isCrashlyticsJavascriptExceptionHandlerChainingEnabled"] =
|
|
59
53
|
@([RCTConvert BOOL:@([RNFBCrashlyticsInitProvider
|
|
60
54
|
isCrashlyticsJavascriptExceptionHandlerChainingEnabled])]);
|
|
61
|
-
if ([
|
|
55
|
+
if ([RNFBCrashlyticsHelper isDebuggerAttached]) {
|
|
62
56
|
RCTLog(
|
|
63
57
|
@"Crashlytics - WARNING: Debugger detected. Crashlytics will not receive crash reports.");
|
|
64
58
|
}
|
|
@@ -74,14 +68,15 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
74
68
|
}
|
|
75
69
|
|
|
76
70
|
- (void)checkForUnsentReports:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
77
|
-
|
|
71
|
+
(void)reject;
|
|
72
|
+
[RNFBCrashlyticsHelper checkForUnsentReportsWithCompletion:^(BOOL unsentReports) {
|
|
78
73
|
resolve([NSNumber numberWithBool:unsentReports]);
|
|
79
74
|
}];
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
- (void)crash {
|
|
83
78
|
if ([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled]) {
|
|
84
|
-
if ([
|
|
79
|
+
if ([RNFBCrashlyticsHelper isDebuggerAttached]) {
|
|
85
80
|
RCTLog(
|
|
86
81
|
@"Crashlytics - WARNING: Debugger detected. Crashlytics will not receive crash reports.");
|
|
87
82
|
}
|
|
@@ -96,15 +91,21 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
96
91
|
- (void)crashWithStackPromise:(JS::NativeRNFBTurboCrashlytics::JavaScriptErrorObject &)jsErrorDict
|
|
97
92
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
98
93
|
reject:(RCTPromiseRejectBlock)reject {
|
|
94
|
+
(void)reject;
|
|
99
95
|
if ([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled]) {
|
|
100
|
-
if ([
|
|
96
|
+
if ([RNFBCrashlyticsHelper isDebuggerAttached]) {
|
|
101
97
|
RCTLog(
|
|
102
98
|
@"Crashlytics - WARNING: Debugger detected. Crashlytics will not receive crash reports.");
|
|
103
99
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
// Coverage exception: this branch ends the process, so neither XCTest nor e2e can observe it.
|
|
101
|
+
// Keep it a thin delegate; ordering, watchdog and the second-fatal guard live in
|
|
102
|
+
// RNFBCrashlyticsFatalExit (unit-tested). The promise is intentionally never resolved: that
|
|
103
|
+
// keeps the JS global handler suspended (no chaining to the previous handler, no
|
|
104
|
+
// fall-through) until termination.
|
|
105
|
+
ELog(@"Crashlytics - Logging crash and terminating app.");
|
|
106
|
+
[[RNFBCrashlyticsFatalExit shared]
|
|
107
|
+
recordThenTerminate:[self javaScriptErrorRecorder:jsErrorDict]];
|
|
108
|
+
return;
|
|
108
109
|
} else {
|
|
109
110
|
RCTLog(@"Crashlytics - INFO: crashlytics collection is not enabled, not crashing.");
|
|
110
111
|
}
|
|
@@ -112,37 +113,41 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
- (void)deleteUnsentReports:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
115
|
-
|
|
116
|
+
(void)reject;
|
|
117
|
+
[RNFBCrashlyticsHelper deleteUnsentReports];
|
|
116
118
|
resolve([NSNull null]);
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
- (void)didCrashOnPreviousExecution:(RCTPromiseResolveBlock)resolve
|
|
120
122
|
reject:(RCTPromiseRejectBlock)reject {
|
|
121
|
-
|
|
123
|
+
(void)reject;
|
|
124
|
+
BOOL didCrash = [RNFBCrashlyticsHelper didCrashDuringPreviousExecution];
|
|
122
125
|
resolve([NSNumber numberWithBool:didCrash]);
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
- (void)log:(NSString *)message {
|
|
126
|
-
[
|
|
129
|
+
[RNFBCrashlyticsHelper log:message];
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
- (void)logPromise:(NSString *)message
|
|
130
133
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
131
134
|
reject:(RCTPromiseRejectBlock)reject {
|
|
132
|
-
|
|
135
|
+
(void)reject;
|
|
136
|
+
[RNFBCrashlyticsHelper log:message];
|
|
133
137
|
resolve([NSNull null]);
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
- (void)sendUnsentReports {
|
|
137
|
-
[
|
|
141
|
+
[RNFBCrashlyticsHelper sendUnsentReports];
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
- (void)setAttribute:(NSString *)key
|
|
141
145
|
value:(NSString *)value
|
|
142
146
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
143
147
|
reject:(RCTPromiseRejectBlock)reject {
|
|
148
|
+
(void)reject;
|
|
144
149
|
if ([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled]) {
|
|
145
|
-
[
|
|
150
|
+
[RNFBCrashlyticsHelper setCustomValue:value forKey:key];
|
|
146
151
|
}
|
|
147
152
|
resolve([NSNull null]);
|
|
148
153
|
}
|
|
@@ -150,12 +155,9 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
150
155
|
- (void)setAttributes:(NSDictionary *)attributes
|
|
151
156
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
152
157
|
reject:(RCTPromiseRejectBlock)reject {
|
|
158
|
+
(void)reject;
|
|
153
159
|
if ([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled]) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
for (NSString *key in keys) {
|
|
157
|
-
[[FIRCrashlytics crashlytics] setCustomValue:attributes[key] forKey:key];
|
|
158
|
-
}
|
|
160
|
+
[RNFBCrashlyticsHelper setCustomKeysAndValues:attributes];
|
|
159
161
|
}
|
|
160
162
|
resolve([NSNull null]);
|
|
161
163
|
}
|
|
@@ -163,8 +165,9 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
163
165
|
- (void)setUserId:(NSString *)userId
|
|
164
166
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
165
167
|
reject:(RCTPromiseRejectBlock)reject {
|
|
168
|
+
(void)reject;
|
|
166
169
|
if ([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled]) {
|
|
167
|
-
[
|
|
170
|
+
[RNFBCrashlyticsHelper setUserID:userId];
|
|
168
171
|
}
|
|
169
172
|
resolve([NSNull null]);
|
|
170
173
|
}
|
|
@@ -178,6 +181,7 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
178
181
|
- (void)recordErrorPromise:(JS::NativeRNFBTurboCrashlytics::JavaScriptErrorObject &)jsErrorDict
|
|
179
182
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
180
183
|
reject:(RCTPromiseRejectBlock)reject {
|
|
184
|
+
(void)reject;
|
|
181
185
|
if ([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled]) {
|
|
182
186
|
[self recordJavaScriptError:jsErrorDict];
|
|
183
187
|
}
|
|
@@ -187,60 +191,38 @@ RCT_EXPORT_MODULE(NativeRNFBTurboCrashlytics)
|
|
|
187
191
|
- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled
|
|
188
192
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
189
193
|
reject:(RCTPromiseRejectBlock)reject {
|
|
194
|
+
(void)reject;
|
|
190
195
|
[[RNFBPreferences shared] setBooleanValue:@"crashlytics_auto_collection_enabled"
|
|
191
196
|
boolValue:enabled];
|
|
192
197
|
resolve([NSNull null]);
|
|
193
198
|
}
|
|
194
199
|
|
|
195
200
|
- (void)recordJavaScriptError:(JS::NativeRNFBTurboCrashlytics::JavaScriptErrorObject &)jsErrorDict {
|
|
201
|
+
[self javaScriptErrorRecorder:jsErrorDict]();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Converts the codegen struct to Foundation values up front, so the returned block captures no
|
|
205
|
+
// C++ references and can be handed to RNFBCrashlyticsFatalExit.
|
|
206
|
+
- (dispatch_block_t)javaScriptErrorRecorder:
|
|
207
|
+
(JS::NativeRNFBTurboCrashlytics::JavaScriptErrorObject &)jsErrorDict {
|
|
196
208
|
NSString *message = jsErrorDict.message();
|
|
197
209
|
auto stackFrames = jsErrorDict.frames();
|
|
198
|
-
NSMutableArray *
|
|
210
|
+
NSMutableArray *frames = [[NSMutableArray alloc] init];
|
|
199
211
|
BOOL isUnhandledPromiseRejection = jsErrorDict.isUnhandledRejection();
|
|
200
212
|
|
|
201
213
|
for (const auto &stackFrame : stackFrames) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
NSString *name = @"JavaScriptError";
|
|
209
|
-
if (isUnhandledPromiseRejection) {
|
|
210
|
-
name = @"UnhandledPromiseRejection";
|
|
214
|
+
[frames addObject:@{
|
|
215
|
+
@"fn" : stackFrame.fn() ?: @"",
|
|
216
|
+
@"file" : stackFrame.file() ?: @"",
|
|
217
|
+
@"line" : @((NSUInteger)stackFrame.line()),
|
|
218
|
+
}];
|
|
211
219
|
}
|
|
212
220
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
- (BOOL)isDebuggerAttached {
|
|
221
|
-
static BOOL debuggerIsAttached = NO;
|
|
222
|
-
|
|
223
|
-
static dispatch_once_t debuggerPredicate;
|
|
224
|
-
dispatch_once(&debuggerPredicate, ^{
|
|
225
|
-
struct kinfo_proc info;
|
|
226
|
-
size_t info_size = sizeof(info);
|
|
227
|
-
int name[4];
|
|
228
|
-
|
|
229
|
-
name[0] = CTL_KERN;
|
|
230
|
-
name[1] = KERN_PROC;
|
|
231
|
-
name[2] = KERN_PROC_PID;
|
|
232
|
-
name[3] = getpid();
|
|
233
|
-
|
|
234
|
-
if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) {
|
|
235
|
-
ELog(@"Crashlytics ERROR: Checking for a running debugger via sysctl() failed: %s",
|
|
236
|
-
strerror(errno));
|
|
237
|
-
debuggerIsAttached = false;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (!debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0) debuggerIsAttached = true;
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
return debuggerIsAttached;
|
|
221
|
+
return ^{
|
|
222
|
+
[RNFBCrashlyticsHelper recordJavaScriptErrorWithMessage:message
|
|
223
|
+
frames:frames
|
|
224
|
+
isUnhandledPromiseRejection:isUnhandledPromiseRejection];
|
|
225
|
+
};
|
|
244
226
|
}
|
|
245
227
|
|
|
246
228
|
@end
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
2744B98621F45429004F8E3F /* RNFBCrashlyticsModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2744B98521F45429004F8E3F /* RNFBCrashlyticsModule.mm */; };
|
|
11
11
|
2748D82422371C1C00FC8DC8 /* RNFBCrashlyticsInitProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 2748D82322371C1C00FC8DC8 /* RNFBCrashlyticsInitProvider.m */; };
|
|
12
12
|
C66637BB25FA560700DCAA51 /* RNFBCrashlyticsNativeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = C66637BA25FA560700DCAA51 /* RNFBCrashlyticsNativeHelper.m */; };
|
|
13
|
+
RNFBCL000000000000000001 /* RNFBCrashlyticsDebuggerProbe.swift in Sources */ = {isa = PBXBuildFile; fileRef = RNFBCL000000000000000002 /* RNFBCrashlyticsDebuggerProbe.swift */; };
|
|
14
|
+
RNFBCL000000000000000003 /* RNFBCrashlyticsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = RNFBCL000000000000000005 /* RNFBCrashlyticsHelper.m */; };
|
|
13
15
|
/* End PBXBuildFile section */
|
|
14
16
|
|
|
15
17
|
/* Begin PBXCopyFilesBuildPhase section */
|
|
@@ -32,6 +34,9 @@
|
|
|
32
34
|
2748D82322371C1C00FC8DC8 /* RNFBCrashlyticsInitProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBCrashlyticsInitProvider.m; sourceTree = "<group>"; };
|
|
33
35
|
C66637BA25FA560700DCAA51 /* RNFBCrashlyticsNativeHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBCrashlyticsNativeHelper.m; sourceTree = "<group>"; };
|
|
34
36
|
C66637BD25FA561D00DCAA51 /* RNFBCrashlyticsNativeHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFBCrashlyticsNativeHelper.h; sourceTree = "<group>"; };
|
|
37
|
+
RNFBCL000000000000000002 /* RNFBCrashlyticsDebuggerProbe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBCrashlyticsDebuggerProbe.swift; path = RNFBCrashlytics/RNFBCrashlyticsDebuggerProbe.swift; sourceTree = SOURCE_ROOT; };
|
|
38
|
+
RNFBCL000000000000000004 /* RNFBCrashlyticsHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFBCrashlyticsHelper.h; path = RNFBCrashlytics/RNFBCrashlyticsHelper.h; sourceTree = SOURCE_ROOT; };
|
|
39
|
+
RNFBCL000000000000000005 /* RNFBCrashlyticsHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFBCrashlyticsHelper.m; path = RNFBCrashlytics/RNFBCrashlyticsHelper.m; sourceTree = SOURCE_ROOT; };
|
|
35
40
|
/* End PBXFileReference section */
|
|
36
41
|
|
|
37
42
|
/* Begin PBXFrameworksBuildPhase section */
|
|
@@ -58,6 +63,9 @@
|
|
|
58
63
|
children = (
|
|
59
64
|
2744B98421F45429004F8E3F /* RNFBCrashlyticsModule.h */,
|
|
60
65
|
2744B98521F45429004F8E3F /* RNFBCrashlyticsModule.mm */,
|
|
66
|
+
RNFBCL000000000000000004 /* RNFBCrashlyticsHelper.h */,
|
|
67
|
+
RNFBCL000000000000000005 /* RNFBCrashlyticsHelper.m */,
|
|
68
|
+
RNFBCL000000000000000002 /* RNFBCrashlyticsDebuggerProbe.swift */,
|
|
61
69
|
2748D82222371C0900FC8DC8 /* RNFBCrashlyticsInitProvider.h */,
|
|
62
70
|
2748D82322371C1C00FC8DC8 /* RNFBCrashlyticsInitProvider.m */,
|
|
63
71
|
C66637BD25FA561D00DCAA51 /* RNFBCrashlyticsNativeHelper.h */,
|
|
@@ -133,8 +141,10 @@
|
|
|
133
141
|
buildActionMask = 2147483647;
|
|
134
142
|
files = (
|
|
135
143
|
C66637BB25FA560700DCAA51 /* RNFBCrashlyticsNativeHelper.m in Sources */,
|
|
144
|
+
RNFBCL000000000000000003 /* RNFBCrashlyticsHelper.m in Sources */,
|
|
136
145
|
2748D82422371C1C00FC8DC8 /* RNFBCrashlyticsInitProvider.m in Sources */,
|
|
137
146
|
2744B98621F45429004F8E3F /* RNFBCrashlyticsModule.mm in Sources */,
|
|
147
|
+
RNFBCL000000000000000001 /* RNFBCrashlyticsDebuggerProbe.swift in Sources */,
|
|
138
148
|
);
|
|
139
149
|
runOnlyForDeploymentPostprocessing = 0;
|
|
140
150
|
};
|
|
@@ -160,6 +170,7 @@
|
|
|
160
170
|
CODE_SIGN_STYLE = Automatic;
|
|
161
171
|
COPY_PHASE_STRIP = NO;
|
|
162
172
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
173
|
+
DEFINES_MODULE = YES;
|
|
163
174
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
164
175
|
GCC_DYNAMIC_NO_PIC = NO;
|
|
165
176
|
GCC_OPTIMIZATION_LEVEL = 0;
|
|
@@ -176,6 +187,7 @@
|
|
|
176
187
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
177
188
|
SDKROOT = iphoneos;
|
|
178
189
|
SKIP_INSTALL = YES;
|
|
190
|
+
SWIFT_VERSION = 5.10;
|
|
179
191
|
TARGETED_DEVICE_FAMILY = "1,2";
|
|
180
192
|
};
|
|
181
193
|
name = Debug;
|
|
@@ -199,6 +211,7 @@
|
|
|
199
211
|
CODE_SIGN_STYLE = Automatic;
|
|
200
212
|
COPY_PHASE_STRIP = NO;
|
|
201
213
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
214
|
+
DEFINES_MODULE = YES;
|
|
202
215
|
ENABLE_NS_ASSERTIONS = NO;
|
|
203
216
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
204
217
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
@@ -210,6 +223,7 @@
|
|
|
210
223
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
211
224
|
SDKROOT = iphoneos;
|
|
212
225
|
SKIP_INSTALL = YES;
|
|
226
|
+
SWIFT_VERSION = 5.10;
|
|
213
227
|
TARGETED_DEVICE_FAMILY = "1,2";
|
|
214
228
|
VALIDATE_PRODUCT = YES;
|
|
215
229
|
};
|