@react-native-firebase/crashlytics 26.3.3 → 27.0.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 +19 -0
- package/RNFBCrashlytics.podspec +7 -1
- package/android/build.gradle +7 -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/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/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 +58 -0
- package/ios/RNFBCrashlytics/RNFBCrashlyticsHelper.m +109 -0
- package/ios/RNFBCrashlytics/RNFBCrashlyticsModule.mm +39 -71
- package/ios/RNFBCrashlytics.xcodeproj/project.pbxproj +14 -0
- package/ios/RNFBCrashlyticsUnitTests/HostStubs/Firebase/FIRCrashlyticsHostStub.m +207 -0
- package/ios/RNFBCrashlyticsUnitTests/HostStubs/Firebase/Firebase.h +47 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsDebuggerProbeTests.swift +112 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsHelperHostStubTests.m +119 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsUnitTests.xcodeproj/project.pbxproj +261 -0
- package/ios/RNFBCrashlyticsUnitTests/RNFBCrashlyticsUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBCrashlyticsUnitTests.xcscheme +69 -0
- package/ios_config.sh +28 -5
- package/lib/version.ts +1 -1
- package/package.json +5 -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
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [27.0.0](https://github.com/invertase/react-native-firebase/compare/v26.4.0...v27.0.0) (2026-10-19)
|
|
7
|
+
|
|
8
|
+
### ⚠ BREAKING CHANGES
|
|
9
|
+
|
|
10
|
+
- **app, sdk:** bump Firebase Android 35, Apple 13, and JS 13
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- **app, sdk:** bump Firebase Android 35, Apple 13, and JS 13 ([0ca03d1](https://github.com/invertase/react-native-firebase/commit/0ca03d11b0c271fb37cfbee8a0c3074a21969f0b))
|
|
15
|
+
- **test-expo:** add Expo Router example with a route per package ([#9313](https://github.com/invertase/react-native-firebase/issues/9313)) ([50a946a](https://github.com/invertase/react-native-firebase/commit/50a946ab4f6c11670890940b55e8bb71f6332dcf))
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
- **crashlytics, ios:** resolve target-folder GoogleService-Info.plist for SPM dSYM upload ([a2b7e57](https://github.com/invertase/react-native-firebase/commit/a2b7e5789b6df07d3aa2729f734582de6a5feb92)), closes [#9304](https://github.com/invertase/react-native-firebase/issues/9304)
|
|
20
|
+
|
|
21
|
+
## [26.4.0](https://github.com/invertase/react-native-firebase/compare/v26.3.3...v26.4.0) (2026-09-05)
|
|
22
|
+
|
|
23
|
+
**Note:** Version bump only for package @react-native-firebase/crashlytics
|
|
24
|
+
|
|
6
25
|
## [26.3.3](https://github.com/invertase/react-native-firebase/compare/v26.3.2...v26.3.3) (2026-09-01)
|
|
7
26
|
|
|
8
27
|
### Bug Fixes
|
package/RNFBCrashlytics.podspec
CHANGED
|
@@ -32,8 +32,14 @@ Pod::Spec.new do |s|
|
|
|
32
32
|
s.tvos.deployment_target = firebase_tvos_target
|
|
33
33
|
s.source_files = 'ios/**/*.{h,m,mm,cpp,swift}'
|
|
34
34
|
s.private_header_files = "ios/**/*.h"
|
|
35
|
-
|
|
35
|
+
# CocoaPods FileList uses FNM_PATHNAME: `ios/*UnitTests/**` does not match nested
|
|
36
|
+
# files under HostStubs; use `ios/*UnitTests/**/*` (same as sibling packages).
|
|
37
|
+
s.exclude_files = 'ios/generated/RCTThirdPartyComponentsProvider.*', 'ios/generated/RCTAppDependencyProvider.*', 'ios/generated/RCTModuleProviders.*', 'ios/generated/RCTModulesConformingToProtocolsProvider.*', 'ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.*', 'ios/*UnitTests/**/*'
|
|
38
|
+
s.swift_version = '5.10'
|
|
39
|
+
|
|
40
|
+
# DEFINES_MODULE is required for the generated RNFBCrashlytics-Swift.h interface.
|
|
36
41
|
s.pod_target_xcconfig = {
|
|
42
|
+
"DEFINES_MODULE" => "YES",
|
|
37
43
|
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ios/generated/RNFBCrashlyticsTurboModules\" \"$(PODS_TARGET_SRCROOT)/ios/generated\"",
|
|
38
44
|
"CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES" => "YES",
|
|
39
45
|
}
|
package/android/build.gradle
CHANGED
|
@@ -13,6 +13,7 @@ buildscript {
|
|
|
13
13
|
|
|
14
14
|
dependencies {
|
|
15
15
|
classpath("com.android.tools.build:gradle:8.4.0")
|
|
16
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.findProperty('kotlinVersion') ?: '2.4.0'}")
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -21,6 +22,8 @@ plugins {
|
|
|
21
22
|
id "io.invertase.gradle.build" version "1.5"
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
apply plugin: "org.jetbrains.kotlin.android"
|
|
26
|
+
|
|
24
27
|
def appProject
|
|
25
28
|
if (findProject(':@react-native-firebase_app')) {
|
|
26
29
|
appProject = project(':@react-native-firebase_app')
|
|
@@ -99,6 +102,10 @@ dependencies {
|
|
|
99
102
|
implementation platform("com.google.firebase:firebase-bom:${ReactNative.ext.getVersion("firebase", "bom")}")
|
|
100
103
|
implementation "com.google.firebase:firebase-crashlytics"
|
|
101
104
|
implementation "com.google.firebase:firebase-crashlytics-ndk"
|
|
105
|
+
|
|
106
|
+
testImplementation "junit:junit:4.13.2"
|
|
107
|
+
testImplementation "org.robolectric:robolectric:4.14.1"
|
|
108
|
+
testImplementation "org.mockito:mockito-core:5.14.2"
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
ReactNative.shared.applyPackageVersion()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package io.invertase.firebase.crashlytics
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Preference / meta / JSON keys for Crashlytics init. Historical Java shape was a
|
|
22
|
+
* package-private class of `static final String` fields; Kotlin [object] + `@JvmField`
|
|
23
|
+
* keeps `import static …Constants.*` and `Constants.KEY_…` working for same-module Java.
|
|
24
|
+
*/
|
|
25
|
+
internal object Constants {
|
|
26
|
+
@JvmField
|
|
27
|
+
val KEY_CRASHLYTICS_NDK_ENABLED: String = "crashlytics_ndk_enabled"
|
|
28
|
+
|
|
29
|
+
@JvmField
|
|
30
|
+
val KEY_CRASHLYTICS_DEBUG_ENABLED: String = "crashlytics_debug_enabled"
|
|
31
|
+
|
|
32
|
+
@JvmField
|
|
33
|
+
val KEY_CRASHLYTICS_AUTO_COLLECTION_ENABLED: String = "crashlytics_auto_collection_enabled"
|
|
34
|
+
|
|
35
|
+
@JvmField
|
|
36
|
+
val KEY_CRASHLYTICS_IS_ERROR_GENERATION_ON_JS_CRASH_ENABLED: String =
|
|
37
|
+
"crashlytics_is_error_generation_on_js_crash_enabled"
|
|
38
|
+
|
|
39
|
+
@JvmField
|
|
40
|
+
val KEY_CRASHLYTICS_JAVASCRIPT_EXCEPTION_HANDLER_CHAINING_ENABLED: String =
|
|
41
|
+
"crashlytics_javascript_exception_handler_chaining_enabled"
|
|
42
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package io.invertase.firebase.crashlytics
|
|
1
|
+
package io.invertase.firebase.crashlytics
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
@@ -21,8 +21,6 @@ package io.invertase.firebase.crashlytics;
|
|
|
21
21
|
* This class is purely cosmetic - to indicate on the Crashlytics console that it's a JavaScript
|
|
22
22
|
* error rather than the generic `java.lang.Exception`.
|
|
23
23
|
*/
|
|
24
|
-
class JavaScriptError
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
24
|
+
internal class JavaScriptError(
|
|
25
|
+
message: String,
|
|
26
|
+
) : Exception(message)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package io.invertase.firebase.crashlytics
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Public static helpers for native/Android callers that need Crashlytics without going through the
|
|
24
|
+
* TurboModule. Historical Java shape was a class of static methods; Kotlin [object] + `@JvmStatic`
|
|
25
|
+
* keeps `ReactNativeFirebaseCrashlyticsNativeHelper.recordNativeException(...)` working for Java.
|
|
26
|
+
*/
|
|
27
|
+
object ReactNativeFirebaseCrashlyticsNativeHelper {
|
|
28
|
+
@JvmStatic
|
|
29
|
+
fun recordNativeException(throwable: Throwable) {
|
|
30
|
+
FirebaseCrashlytics.getInstance().recordException(throwable)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@JvmStatic
|
|
34
|
+
fun log(message: String) {
|
|
35
|
+
FirebaseCrashlytics.getInstance().log(message)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@JvmStatic
|
|
39
|
+
fun setCustomKey(
|
|
40
|
+
key: String,
|
|
41
|
+
value: String,
|
|
42
|
+
) {
|
|
43
|
+
FirebaseCrashlytics.getInstance().setCustomKey(key, value)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package io.invertase.firebase.crashlytics
|
|
1
|
+
package io.invertase.firebase.crashlytics
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
@@ -21,8 +21,6 @@ package io.invertase.firebase.crashlytics;
|
|
|
21
21
|
* This class is purely cosmetic - to indicate on the Crashlytics console that it's an
|
|
22
22
|
* UnhandledPromiseRejection JS error rather than the generic `java.lang.Exception`.
|
|
23
23
|
*/
|
|
24
|
-
class UnhandledPromiseRejection
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
24
|
+
internal class UnhandledPromiseRejection(
|
|
25
|
+
message: String,
|
|
26
|
+
) : Exception(message)
|
package/android/src/test/java/io/invertase/firebase/crashlytics/ConstantsAndJsErrorTypesTest.java
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package io.invertase.firebase.crashlytics;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import static io.invertase.firebase.crashlytics.Constants.KEY_CRASHLYTICS_AUTO_COLLECTION_ENABLED;
|
|
21
|
+
import static io.invertase.firebase.crashlytics.Constants.KEY_CRASHLYTICS_DEBUG_ENABLED;
|
|
22
|
+
import static io.invertase.firebase.crashlytics.Constants.KEY_CRASHLYTICS_IS_ERROR_GENERATION_ON_JS_CRASH_ENABLED;
|
|
23
|
+
import static io.invertase.firebase.crashlytics.Constants.KEY_CRASHLYTICS_JAVASCRIPT_EXCEPTION_HANDLER_CHAINING_ENABLED;
|
|
24
|
+
import static io.invertase.firebase.crashlytics.Constants.KEY_CRASHLYTICS_NDK_ENABLED;
|
|
25
|
+
import static org.junit.Assert.assertEquals;
|
|
26
|
+
import static org.junit.Assert.assertTrue;
|
|
27
|
+
|
|
28
|
+
import java.lang.reflect.Field;
|
|
29
|
+
import java.lang.reflect.Modifier;
|
|
30
|
+
import org.junit.Test;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* JVM coverage for Kotlin-ported {@link Constants}, {@link JavaScriptError}, and {@link
|
|
34
|
+
* UnhandledPromiseRejection}. Asserts preference-key string values, Java static-field shape for
|
|
35
|
+
* InitProvider static imports, and cosmetic Crashlytics console type names.
|
|
36
|
+
*/
|
|
37
|
+
public class ConstantsAndJsErrorTypesTest {
|
|
38
|
+
|
|
39
|
+
@Test
|
|
40
|
+
public void constants_stringValuesMatchExpectedKeys() {
|
|
41
|
+
assertEquals("crashlytics_ndk_enabled", KEY_CRASHLYTICS_NDK_ENABLED);
|
|
42
|
+
assertEquals("crashlytics_debug_enabled", KEY_CRASHLYTICS_DEBUG_ENABLED);
|
|
43
|
+
assertEquals("crashlytics_auto_collection_enabled", KEY_CRASHLYTICS_AUTO_COLLECTION_ENABLED);
|
|
44
|
+
assertEquals(
|
|
45
|
+
"crashlytics_is_error_generation_on_js_crash_enabled",
|
|
46
|
+
KEY_CRASHLYTICS_IS_ERROR_GENERATION_ON_JS_CRASH_ENABLED);
|
|
47
|
+
assertEquals(
|
|
48
|
+
"crashlytics_javascript_exception_handler_chaining_enabled",
|
|
49
|
+
KEY_CRASHLYTICS_JAVASCRIPT_EXCEPTION_HANDLER_CHAINING_ENABLED);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Test
|
|
53
|
+
public void constants_fieldsArePublicStaticForJavaStaticImport() throws Exception {
|
|
54
|
+
String[] names = {
|
|
55
|
+
"KEY_CRASHLYTICS_NDK_ENABLED",
|
|
56
|
+
"KEY_CRASHLYTICS_DEBUG_ENABLED",
|
|
57
|
+
"KEY_CRASHLYTICS_AUTO_COLLECTION_ENABLED",
|
|
58
|
+
"KEY_CRASHLYTICS_IS_ERROR_GENERATION_ON_JS_CRASH_ENABLED",
|
|
59
|
+
"KEY_CRASHLYTICS_JAVASCRIPT_EXCEPTION_HANDLER_CHAINING_ENABLED",
|
|
60
|
+
};
|
|
61
|
+
for (String name : names) {
|
|
62
|
+
Field field = Constants.class.getField(name);
|
|
63
|
+
int modifiers = field.getModifiers();
|
|
64
|
+
assertTrue(name, Modifier.isPublic(modifiers));
|
|
65
|
+
assertTrue(name, Modifier.isStatic(modifiers));
|
|
66
|
+
assertTrue(name, Modifier.isFinal(modifiers));
|
|
67
|
+
assertEquals(name, String.class, field.getType());
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test
|
|
72
|
+
public void javaScriptError_constructsWithMessageAndCosmeticSimpleName() {
|
|
73
|
+
JavaScriptError error = new JavaScriptError("js-boom");
|
|
74
|
+
assertEquals("js-boom", error.getMessage());
|
|
75
|
+
assertEquals("JavaScriptError", error.getClass().getSimpleName());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Test
|
|
79
|
+
public void unhandledPromiseRejection_constructsWithMessageAndCosmeticSimpleName() {
|
|
80
|
+
UnhandledPromiseRejection error = new UnhandledPromiseRejection("promise-boom");
|
|
81
|
+
assertEquals("promise-boom", error.getMessage());
|
|
82
|
+
assertEquals("UnhandledPromiseRejection", error.getClass().getSimpleName());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
package io.invertase.firebase.crashlytics;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import static org.junit.Assert.assertSame;
|
|
21
|
+
import static org.junit.Assert.assertTrue;
|
|
22
|
+
import static org.mockito.Mockito.mock;
|
|
23
|
+
import static org.mockito.Mockito.mockStatic;
|
|
24
|
+
import static org.mockito.Mockito.verify;
|
|
25
|
+
|
|
26
|
+
import com.google.firebase.crashlytics.FirebaseCrashlytics;
|
|
27
|
+
import java.lang.reflect.Method;
|
|
28
|
+
import java.lang.reflect.Modifier;
|
|
29
|
+
import java.util.Arrays;
|
|
30
|
+
import org.junit.Test;
|
|
31
|
+
import org.junit.runner.RunWith;
|
|
32
|
+
import org.mockito.MockedStatic;
|
|
33
|
+
import org.robolectric.RobolectricTestRunner;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* JVM coverage for {@link ReactNativeFirebaseCrashlyticsNativeHelper}. Stubs {@link
|
|
37
|
+
* FirebaseCrashlytics#getInstance()} via mockito-mockstatic so the helper can be exercised without
|
|
38
|
+
* initializing the Firebase SDK. Robolectric supplies an Android runtime for Firebase class
|
|
39
|
+
* loading; AndroidTest-AD-1.
|
|
40
|
+
*/
|
|
41
|
+
@RunWith(RobolectricTestRunner.class)
|
|
42
|
+
public class ReactNativeFirebaseCrashlyticsNativeHelperTest {
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
public void javaStaticShapePreservedForNativeCallers() throws Exception {
|
|
46
|
+
assertTrue(Modifier.isPublic(ReactNativeFirebaseCrashlyticsNativeHelper.class.getModifiers()));
|
|
47
|
+
assertTrue(Modifier.isFinal(ReactNativeFirebaseCrashlyticsNativeHelper.class.getModifiers()));
|
|
48
|
+
assertSame(
|
|
49
|
+
ReactNativeFirebaseCrashlyticsNativeHelper.INSTANCE,
|
|
50
|
+
ReactNativeFirebaseCrashlyticsNativeHelper.INSTANCE);
|
|
51
|
+
|
|
52
|
+
Method record =
|
|
53
|
+
ReactNativeFirebaseCrashlyticsNativeHelper.class.getMethod(
|
|
54
|
+
"recordNativeException", Throwable.class);
|
|
55
|
+
Method log = ReactNativeFirebaseCrashlyticsNativeHelper.class.getMethod("log", String.class);
|
|
56
|
+
Method setCustomKey =
|
|
57
|
+
ReactNativeFirebaseCrashlyticsNativeHelper.class.getMethod(
|
|
58
|
+
"setCustomKey", String.class, String.class);
|
|
59
|
+
|
|
60
|
+
for (Method method : Arrays.asList(record, log, setCustomKey)) {
|
|
61
|
+
assertTrue(method.getName(), Modifier.isStatic(method.getModifiers()));
|
|
62
|
+
assertTrue(method.getName(), Modifier.isPublic(method.getModifiers()));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
public void recordNativeException_forwardsToFirebaseCrashlytics() {
|
|
68
|
+
FirebaseCrashlytics crashlytics = mock(FirebaseCrashlytics.class);
|
|
69
|
+
Throwable throwable = new RuntimeException("native-boom");
|
|
70
|
+
|
|
71
|
+
try (MockedStatic<FirebaseCrashlytics> firebaseCrashlytics =
|
|
72
|
+
mockStatic(FirebaseCrashlytics.class)) {
|
|
73
|
+
firebaseCrashlytics.when(FirebaseCrashlytics::getInstance).thenReturn(crashlytics);
|
|
74
|
+
|
|
75
|
+
ReactNativeFirebaseCrashlyticsNativeHelper.recordNativeException(throwable);
|
|
76
|
+
|
|
77
|
+
verify(crashlytics).recordException(throwable);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Test
|
|
82
|
+
public void log_forwardsToFirebaseCrashlytics() {
|
|
83
|
+
FirebaseCrashlytics crashlytics = mock(FirebaseCrashlytics.class);
|
|
84
|
+
|
|
85
|
+
try (MockedStatic<FirebaseCrashlytics> firebaseCrashlytics =
|
|
86
|
+
mockStatic(FirebaseCrashlytics.class)) {
|
|
87
|
+
firebaseCrashlytics.when(FirebaseCrashlytics::getInstance).thenReturn(crashlytics);
|
|
88
|
+
|
|
89
|
+
ReactNativeFirebaseCrashlyticsNativeHelper.log("native-log");
|
|
90
|
+
|
|
91
|
+
verify(crashlytics).log("native-log");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Test
|
|
96
|
+
public void setCustomKey_forwardsToFirebaseCrashlytics() {
|
|
97
|
+
FirebaseCrashlytics crashlytics = mock(FirebaseCrashlytics.class);
|
|
98
|
+
|
|
99
|
+
try (MockedStatic<FirebaseCrashlytics> firebaseCrashlytics =
|
|
100
|
+
mockStatic(FirebaseCrashlytics.class)) {
|
|
101
|
+
firebaseCrashlytics.when(FirebaseCrashlytics::getInstance).thenReturn(crashlytics);
|
|
102
|
+
|
|
103
|
+
ReactNativeFirebaseCrashlyticsNativeHelper.setCustomKey("k", "v");
|
|
104
|
+
|
|
105
|
+
verify(crashlytics).setCustomKey("k", "v");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
package/dist/module/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { FirebaseApp } from '@react-native-firebase/app';
|
|
2
2
|
import './types/internal';
|
|
3
3
|
import type { Crashlytics } from './types/crashlytics';
|
|
4
|
-
export declare const SDK_VERSION = "
|
|
4
|
+
export declare const SDK_VERSION = "27.0.0";
|
|
5
5
|
export declare function getCrashlytics(app?: FirebaseApp): Crashlytics;
|
|
6
6
|
export declare function checkForUnsentReports(crashlytics: Crashlytics): Promise<boolean>;
|
|
7
7
|
export declare function deleteUnsentReports(crashlytics: Crashlytics): Promise<void>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "
|
|
1
|
+
export declare const version = "27.0.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
import Darwin
|
|
18
|
+
import Foundation
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Debugger-attach detection previously inline in `RNFBCrashlyticsModule.mm`
|
|
22
|
+
* (`-isDebuggerAttached`).
|
|
23
|
+
*
|
|
24
|
+
* Pre-port contracts:
|
|
25
|
+
* - Once-token / dispatch_once-style caching of the result
|
|
26
|
+
* - `sysctl` `CTL_KERN` / `KERN_PROC` / `KERN_PROC_PID` / `getpid()` → `kinfo_proc`
|
|
27
|
+
* - `sysctl` failure → ELog-equivalent + treat as not attached (then still evaluate
|
|
28
|
+
* `P_TRACED` on the returned buffer, matching the pre-port control flow)
|
|
29
|
+
* - `P_TRACED` bit set → true
|
|
30
|
+
*
|
|
31
|
+
* Foundation + Darwin only so unit tests do not link Firebase / React.
|
|
32
|
+
*/
|
|
33
|
+
@objc(RNFBCrashlyticsDebuggerProbe)
|
|
34
|
+
public final class RNFBCrashlyticsDebuggerProbe: NSObject {
|
|
35
|
+
|
|
36
|
+
/// Injectable `sysctl` seam for unit tests. Production uses `makeDefaultSysctl()`.
|
|
37
|
+
typealias SysctlProbe = (
|
|
38
|
+
_ name: UnsafeMutablePointer<Int32>,
|
|
39
|
+
_ namelen: u_int,
|
|
40
|
+
_ oldp: UnsafeMutableRawPointer?,
|
|
41
|
+
_ oldlenp: UnsafeMutablePointer<Int>,
|
|
42
|
+
_ newp: UnsafeMutableRawPointer?,
|
|
43
|
+
_ newlen: Int
|
|
44
|
+
) -> Int32
|
|
45
|
+
|
|
46
|
+
private static let lock = NSLock()
|
|
47
|
+
private static var cachedResult: Bool?
|
|
48
|
+
/// Mirrors `static dispatch_once_t` — evaluate at most once in production path.
|
|
49
|
+
private static var onceTokenConsumed = false
|
|
50
|
+
|
|
51
|
+
/// Test hook: replace `sysctl`. Restored by `resetForTesting()`.
|
|
52
|
+
static var sysctlImpl: SysctlProbe = makeDefaultSysctl()
|
|
53
|
+
|
|
54
|
+
private static func makeDefaultSysctl() -> SysctlProbe {
|
|
55
|
+
{ name, namelen, oldp, oldlenp, newp, newlen in
|
|
56
|
+
Darwin.sysctl(name, namelen, oldp, oldlenp, newp, newlen)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Pre-port `-isDebuggerAttached` (once-cached).
|
|
61
|
+
@objc
|
|
62
|
+
public static func isDebuggerAttached() -> Bool {
|
|
63
|
+
lock.lock()
|
|
64
|
+
defer { lock.unlock() }
|
|
65
|
+
if onceTokenConsumed, let cached = cachedResult {
|
|
66
|
+
return cached
|
|
67
|
+
}
|
|
68
|
+
let value = evaluateIsDebuggerAttached(using: sysctlImpl)
|
|
69
|
+
cachedResult = value
|
|
70
|
+
onceTokenConsumed = true
|
|
71
|
+
return value
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Clears once-cache and restores the default `sysctl` implementation (tests only).
|
|
75
|
+
static func resetForTesting() {
|
|
76
|
+
lock.lock()
|
|
77
|
+
defer { lock.unlock() }
|
|
78
|
+
cachedResult = nil
|
|
79
|
+
onceTokenConsumed = false
|
|
80
|
+
sysctlImpl = makeDefaultSysctl()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Evaluates without consuming the production once-cache (tests only).
|
|
84
|
+
static func evaluateUncachedForTesting(using probe: SysctlProbe) -> Bool {
|
|
85
|
+
evaluateIsDebuggerAttached(using: probe)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Core logic matching the pre-port `dispatch_once` body.
|
|
89
|
+
private static func evaluateIsDebuggerAttached(using probe: SysctlProbe) -> Bool {
|
|
90
|
+
var debuggerIsAttached = false
|
|
91
|
+
|
|
92
|
+
var info = kinfo_proc()
|
|
93
|
+
var infoSize = MemoryLayout<kinfo_proc>.size
|
|
94
|
+
var name: [Int32] = [
|
|
95
|
+
CTL_KERN,
|
|
96
|
+
KERN_PROC,
|
|
97
|
+
KERN_PROC_PID,
|
|
98
|
+
getpid(),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
// `name` is always 4 elements — `baseAddress` is non-nil.
|
|
102
|
+
let sysctlResult = name.withUnsafeMutableBufferPointer { nameBuf -> Int32 in
|
|
103
|
+
probe(nameBuf.baseAddress!, 4, &info, &infoSize, nil, 0)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if sysctlResult == -1 {
|
|
107
|
+
// ELog is NSLog with function/line prefix (RNFBSharedUtils.h).
|
|
108
|
+
let err = String(cString: strerror(errno))
|
|
109
|
+
NSLog(
|
|
110
|
+
"%@ [Line %d] Crashlytics ERROR: Checking for a running debugger via sysctl() failed: %@",
|
|
111
|
+
#function, #line, err)
|
|
112
|
+
debuggerIsAttached = false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if !debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0 {
|
|
116
|
+
debuggerIsAttached = true
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return debuggerIsAttached
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|