Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(crashlytics)!: upgrade to new Firebase Crashlytics SDK #3580

Merged
merged 7 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions docs/crashlytics/android-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,23 @@ These steps are required, if you do not add these your app will most likely cras
"The Crashlytics build ID is missing. This occurs when Crashlytics tooling is absent from your app's build configuration.
Please review Crashlytics onboarding instructions and ensure you have a valid Crashlytics account."\_

## 1. Add the Fabric Maven repository
## 1. Add the Google repository (if it's not there already)

Add the following line to the `android/build.gradle` file:
Add the following line to the `android/build.gradle` file :

```groovy
// ..
buildscript {
// ..
repositories {
// ..
maven {
url 'https://maven.fabric.io/public'
}
google()
}
// ..
}
```

## 2. Add the Fabric Tools Plugin dependency
## 2. Add the Firebase Crashlytics Plugin dependency

Add the following dependency to the `android/build.gradle` file:

Expand All @@ -40,19 +38,20 @@ buildscript {
// ..
dependencies {
// ..
classpath 'io.fabric.tools:gradle:1.28.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
}
// ..
}
```

## 3. Apply the Fabric Tools Plugin to your app
## 3. Apply the Firebase Crashlytics Plugin to your app

Apply the `io.fabric` plugin by adding the following to the top of your `android/app/build.gradle` file:
Apply the `com.google.firebase.crashlytics` plugin by adding the following to the top of your `android/app/build.gradle` file:

```
apply plugin: 'com.android.application' // apply after this line
apply plugin: 'io.fabric'
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services' // apply after this line
apply plugin: 'com.google.firebase.crashlytics'
// ..
```

Expand All @@ -61,11 +60,24 @@ apply plugin: 'io.fabric'
Crashlytics NDK reporting allows you to capture Native Development Kit crashes, e.g. in React Native this will capture
crashes originating from the Yoga layout engine.

Add the `crashlytics` block line to the `android/app/build.gradle` file:
Add the `firebaseCrashlytics` block line to the `android/app/build.gradle` file:

```groovy
crashlytics {
enableNdk true
android {
// ...

buildTypes {
release {
/* Add the firebaseCrashlytics extension (by default,
* it's disabled to improve build speeds) and set
* nativeSymbolUploadEnabled to true. */

firebaseCrashlytics {
nativeSymbolUploadEnabled true
}
// ...
}
}
}
```

Expand Down
2 changes: 1 addition & 1 deletion docs/crashlytics/ios-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ add a 'New Run Script Phase':
In the new build phase, add a new script into the text box:

```
"${PODS_ROOT}/Fabric/run"
"${PODS_ROOT}/FirebaseCrashlytics/run"
```

![Script](https://prismic-io.s3.amazonaws.com/invertase%2Ff06cf5b3-884e-4cbc-8c3d-81072a254f1d_new+project+%281%29.png)
Expand Down
14 changes: 1 addition & 13 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,7 @@ project.ext {
// Overriding Library SDK Versions
firebase: [
// Override Firebase SDK Version
bom : "21.1.0",

// Override Crashlytics SDK Version
crashlytics : "2.10.0",

// Override Crashlytics SDK Version
crashlyticsNdk: "2.1.0"
bom : "21.1.0"
],
],
])
Expand All @@ -214,12 +208,6 @@ Open your projects `/ios/Podfile` and add any of the globals shown below to the
```ruby
# Override Firebase SDK Version
$FirebaseSDKVersion = '6.13.0'

# Override Fabric SDK Version
$FabricSDKVersion = '1.6.0'

# Override Crashlytics SDK Version
$CrashlyticsSDKVersion = '3.1.0'
```

Once changed, reinstall your projects pods via pod install and rebuild your project with `npx react-native run-ios`.
Expand Down
2 changes: 1 addition & 1 deletion packages/app/ios_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ if [[ ${_SEARCH_RESULT} ]]; then
if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == "true" ]]; then
echo "Disabled Crashlytics auto disabler." # do nothing
else
_PLIST_ENTRY_KEYS+=("firebase_crashlytics_collection_enabled")
_PLIST_ENTRY_KEYS+=("FirebaseCrashlyticsCollectionEnabled")
_PLIST_ENTRY_TYPES+=("bool")
_PLIST_ENTRY_VALUES+=("NO")
fi
Expand Down
20 changes: 2 additions & 18 deletions packages/crashlytics/RNFBCrashlytics.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ if coreVersionDetected != coreVersionRequired
Pod::UI.warn "NPM package '#{package['name']}' depends on '#{appPackage['name']}' v#{coreVersionRequired} but found v#{coreVersionDetected}, this might cause build issues or runtime crashes."
end

# Fabric SDK Override
fabric_sdk_version = '~> 1.10.2'

# Crashlytics SDK Override
crashlytics_sdk_version = '~> 3.14.0'

Pod::Spec.new do |s|
s.name = "RNFBCrashlytics"
s.version = package["version"]
Expand All @@ -44,19 +38,9 @@ Pod::Spec.new do |s|
firebase_sdk_version = $FirebaseSDKVersion
end

if defined?($CrashlyticsSDKVersion)
Pod::UI.puts "#{s.name}: Using user specified Crashlytics SDK version '#{$CrashlyticsSDKVersion}'"
crashlytics_sdk_version = $CrashlyticsSDKVersion
end

if defined?($FabricSDKVersion)
Pod::UI.puts "#{s.name}: Using user specified Fabric SDK version '#{$FabricSDKVersion}'"
fabric_sdk_version = $FabricSDKVersion
end

# Firebase dependencies
s.dependency 'Fabric', fabric_sdk_version
s.dependency 'Crashlytics', crashlytics_sdk_version
s.dependency 'Firebase/Core', firebase_sdk_version
s.dependency 'Firebase/Crashlytics', firebase_sdk_version

if defined?($RNFirebaseAsStaticFramework)
Pod::UI.puts "#{s.name}: Using overridden static_framework value of '#{$RNFirebaseAsStaticFramework}'"
Expand Down
10 changes: 4 additions & 6 deletions packages/crashlytics/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ project.ext {
],

firebase: [
crashlytics : "2.10.1",
crashlyticsNdk: "2.1.1" // not in BoM
bom: "25.3.0",
],
],
])
Expand All @@ -82,10 +81,9 @@ repositories {

dependencies {
api appProject
// don't use firebase bom here as crashlytics-ndk is not included in the bom, trying to partially use bom here fails
implementation "com.crashlytics.sdk.android:crashlytics:${ReactNative.ext.getVersion("firebase", "crashlytics")}"
// ndk not in Firebase BoM
implementation "com.crashlytics.sdk.android:crashlytics-ndk:${ReactNative.ext.getVersion("firebase", "crashlyticsNdk")}"
implementation platform("com.google.firebase:firebase-bom:${ReactNative.ext.getVersion("firebase", "bom")}")
implementation "com.google.firebase:firebase-crashlytics"
implementation "com.google.firebase:firebase-crashlytics-ndk"
}

ReactNative.shared.applyPackageVersion()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@
*/

import android.util.Log;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import com.crashlytics.android.ndk.CrashlyticsNdk;
import io.fabric.sdk.android.Fabric;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import io.invertase.firebase.common.ReactNativeFirebaseInitProvider;
import io.invertase.firebase.common.ReactNativeFirebaseJSON;
import io.invertase.firebase.common.ReactNativeFirebaseMeta;
Expand Down Expand Up @@ -53,35 +50,12 @@ static boolean isCrashlyticsCollectionEnabled() {
public boolean onCreate() {
super.onCreate();

if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled() && getContext() != null) {
ReactNativeFirebaseJSON json = ReactNativeFirebaseJSON.getSharedInstance();
boolean useNdk = json.getBooleanValue(KEY_CRASHLYTICS_NDK_ENABLED, true);
boolean debug = json.getBooleanValue(KEY_CRASHLYTICS_DEBUG_ENABLED, false);

try {
Fabric.Builder builder = new Fabric.Builder(getContext());

Crashlytics crashlyticsCore = new Crashlytics.Builder()
.core(new CrashlyticsCore.Builder().disabled(!debug && BuildConfig.DEBUG).build())
.build();

if (useNdk) {
builder.kits(crashlyticsCore, new CrashlyticsNdk());
} else {
builder.kits(crashlyticsCore);
}

builder.debuggable(debug);

Fabric.with(builder.build());

Log.i(TAG, "initialization successful");
} catch (IllegalStateException exception) {
Log.e(TAG, "initialization failed", exception);
return false;
}
} else {
Log.i(TAG, "auto collection disabled, skipping initialization");
try {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled());
mikehardy marked this conversation as resolved.
Show resolved Hide resolved
Log.i(TAG, "initialization successful");
} catch (Exception exception) {
Log.e(TAG, "initialization failed", exception);
return false;
}

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
* limitations under the License.
*
*/

import android.os.Handler;

import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashTest;
import com.crashlytics.android.core.CrashlyticsCore;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.facebook.react.bridge.*;
import io.invertase.firebase.common.ReactNativeFirebaseModule;
import io.invertase.firebase.common.ReactNativeFirebasePreferences;
Expand All @@ -38,32 +38,36 @@ public class ReactNativeFirebaseCrashlyticsModule extends ReactNativeFirebaseMod
@ReactMethod
public void crash() {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
Crashlytics.getInstance().core.log("Crash Test");
// async task so as not to get caught by the React Native redbox handler in debug
(new CrashTest()).crashAsyncTask(50);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
throw new RuntimeException("Crash Test");
}
}, 50);
}
}

@ReactMethod
public void log(String message) {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
Crashlytics.getInstance().core.log(message);
FirebaseCrashlytics.getInstance().log(message);
}
}

// For internal use only.
@ReactMethod
public void logPromise(String message, Promise promise) {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
Crashlytics.getInstance().core.log(message);
FirebaseCrashlytics.getInstance().log(message);
}
promise.resolve(null);
}

@ReactMethod
public void setAttribute(String key, String value, Promise promise) {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
Crashlytics.getInstance().core.setString(key, value);
FirebaseCrashlytics.getInstance().setCustomKey(key, value);
}
promise.resolve(null);
}
Expand All @@ -72,12 +76,12 @@ public void setAttribute(String key, String value, Promise promise) {
public void setAttributes(ReadableMap keyValuesMap, Promise promise) {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
ReadableMapKeySetIterator iterator = keyValuesMap.keySetIterator();
CrashlyticsCore crashlyticsCore = Crashlytics.getInstance().core;
FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();

while (iterator.hasNextKey()) {
String key = iterator.nextKey();
String value = keyValuesMap.getString(key);
crashlyticsCore.setString(key, value);
crashlytics.setCustomKey(key, value);
}
}

Expand All @@ -88,23 +92,7 @@ public void setAttributes(ReadableMap keyValuesMap, Promise promise) {
@ReactMethod
public void setUserId(String userId, Promise promise) {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
Crashlytics.getInstance().core.setUserIdentifier(userId);
}
promise.resolve(null);
}

@ReactMethod
public void setUserName(String userName, Promise promise) {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
Crashlytics.getInstance().core.setUserName(userName);
}
promise.resolve(null);
}

@ReactMethod
public void setUserEmail(String userEmail, Promise promise) {
if (ReactNativeFirebaseCrashlyticsInitProvider.isCrashlyticsCollectionEnabled()) {
Crashlytics.getInstance().core.setUserEmail(userEmail);
FirebaseCrashlytics.getInstance().setUserId(userId);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the init changes, are setUserName and setUserEmail going away?
If functionality is being reduced or changed there should be some migration documentation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://firebase.google.com/docs/crashlytics/upgrade-sdk?platform=ios#setuseridentifier_is_now_setuserid_setusername_and_setuseremail_are_removed

Reason for change
We adopted the method name setUserID to be consistent with other Firebase APIs and removed setUserName and setUserEmail to discourage logging PII through Crashlytics.

}
promise.resolve(null);
}
Expand Down Expand Up @@ -155,7 +143,7 @@ private void recordJavaScriptError(ReadableMap jsErrorMap) {

customException.setStackTrace(stackTraceElements);

Crashlytics.getInstance().core.logException(customException);
FirebaseCrashlytics.getInstance().recordException(customException);
}

@Override
Expand Down
30 changes: 0 additions & 30 deletions packages/crashlytics/e2e/crashlytics.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,36 +55,6 @@ describe('crashlytics()', () => {
});
});

describe('setUserName()', () => {
it('accepts string values', async () => {
await firebase.crashlytics().setUserName('invertase');
});

it('rejects none string values', async () => {
try {
await firebase.crashlytics().setUserName(666.1337);
return Promise.reject(new Error('Did not throw.'));
} catch (e) {
e.message.should.containEql('must be a string');
}
});
});

describe('setUserEmail()', () => {
it('accepts string values', async () => {
await firebase.crashlytics().setUserEmail('oss@invertase.io');
});

it('rejects none string values', async () => {
try {
await firebase.crashlytics().setUserEmail(666.1337);
return Promise.reject(new Error('Did not throw.'));
} catch (e) {
e.message.should.containEql('must be a string');
}
});
});

describe('setAttribute()', () => {
it('accepts string values', async () => {
await firebase.crashlytics().setAttribute('invertase', '1337');
Expand Down
Loading