Skip to content

Commit

Permalink
fix(crashlytics, ios): init w/componentsToRegister vs configureWithApp
Browse files Browse the repository at this point in the history
configureWithApp is scheduled for removal upstream

initialization logic we need to execute is moved to componentsToRegister,
but componentsToRegister runs earlier in app init lifecycle than configureWithApp

so we use a deferred execution block to make sure crashlytics component
exists before trying to configure it
  • Loading branch information
mikehardy committed Jun 18, 2024
1 parent a1f3329 commit ca07cad
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,4 @@
/// FIRApp and participate in dependency resolution and injection.
+ (NSArray<FIRComponent *> *)componentsToRegister;

/// Implement this method if the library needs notifications for lifecycle events. This method is
/// called when the developer calls `FirebaseApp.configure()`.
+ (void)configureWithApp:(FIRApp *)app;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
#import "RNFBCrashlyticsInitProvider.h"
#import <Firebase/Firebase.h>
#import <FirebaseCoreExtension/FIRAppInternal.h>
#import <FirebaseCoreExtension/FIRComponent.h>
#import <FirebaseCoreExtension/FIRComponentContainer.h>
#import <FirebaseCoreExtension/FIRComponentType.h>
#import <FirebaseCoreExtension/FIRLibrary.h>
#import <FirebaseCrashlytics/FIRCrashlytics.h>
#import "RNFBJSON.h"
#import "RNFBMeta.h"
#import "RNFBPreferences.h"
Expand All @@ -32,6 +37,19 @@
NSString *const KEY_CRASHLYTICS_JAVASCRIPT_EXCEPTION_HANDLER_CHAINING_ENABLED =
@"crashlytics_javascript_exception_handler_chaining_enabled";

/// Empty protocol to register this provider as a component with Core.
@protocol RNFBCrashlyticsInitProviderProtocol
@end

/// The name of the Crashlytics component in FirebaseCore's component system. Reference:
// https://github.com/firebase/firebase-ios-sdk/blob/main/Crashlytics/Crashlytics/FIRCrashlytics.m#L87-L89
@protocol FIRCrashlyticsInstanceProvider <NSObject>
@end

/// Privately conform to the protocol for component registration.
@interface RNFBCrashlyticsInitProvider () <RNFBCrashlyticsInitProviderProtocol, FIRLibrary>
@end

@implementation RNFBCrashlyticsInitProvider

+ (void)load {
Expand Down Expand Up @@ -79,26 +97,47 @@ + (BOOL)isCrashlyticsJavascriptExceptionHandlerChainingEnabled {
}

+ (NSArray<FIRComponent *> *)componentsToRegister {
return @[];
}
// Goal: enable or disable crashlytics logging based on firebase.json settings at app startup
//
// Problem: We need a correctly instantiated Crashlytics component in order to configure it
//
// Solution:
// 1) put the desired startup logic that requires Crashlytics dependency in deferrable block...
FIRComponentCreationBlock creationBlock =
^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
if (!container.app.isDefaultApp) {
return nil;
}

/*
* configureWithApp is automatically invoked by Firebase as this app is a registered FIRLibrary with
* the SDK.
*
* At this point "configure" has already been called on the FIRApp instance. This behavior is meant
* to mirror what is done in ReactNativeFirebaseCrashlyticsInitProvider.java
*
* This pattern may not be supported long term
* https://github.com/firebase/firebase-ios-sdk/issues/2933
*/
+ (void)configureWithApp:(FIRApp *)app {
// This setting is sticky. setCrashlyticsCollectionEnabled persists the setting to disk until it
// is explicitly set otherwise or the app is deleted. Jump to the setCrashlyticsCollectionEnabled
// definition to see implementation details.
[[FIRCrashlytics crashlytics]
setCrashlyticsCollectionEnabled:self.isCrashlyticsCollectionEnabled];
DLog(@"RNFBCrashlyticsInit initialization successful");
// Ensure it's cached so it returns the same instance every time messaging is called.
*isCacheable = YES;

// 2) ... ask the SDK component system to get a correctly running dependency...
// Note: This is a FIRCrashlytics *, directly cast it later for practical use, reference:
// https://github.com/firebase/firebase-ios-sdk/blob/main/Crashlytics/Crashlytics/FIRCrashlytics.m#L282-L284
id<FIRCrashlyticsInstanceProvider> crashlytics =
FIR_COMPONENT(FIRCrashlyticsInstanceProvider, container);

// This setting is sticky. setCrashlyticsCollectionEnabled persists the setting to disk until it
// is explicitly set otherwise or the app is deleted. Jump to the
// setCrashlyticsCollectionEnabled definition to see implementation details.
[(FIRCrashlytics *)crashlytics
setCrashlyticsCollectionEnabled:self.isCrashlyticsCollectionEnabled];

// For testing: filter for this in logs to make sure this block executes via
// xcrun simctl spawn booted log stream --level debug --style compact |grep RNFBCrashlyticsInit
DLog(@"RNFBCrashlyticsInit initialization successful");
return nil;
};

// 3) ...finally: register startup block to run w/Crashlytics dependency when ready
FIRComponent *crashlyticsInitProvider =
[FIRComponent componentWithProtocol:@protocol(RNFBCrashlyticsInitProviderProtocol)
instantiationTiming:FIRInstantiationTimingEagerInDefaultApp
dependencies:@[] // note this will go away in firebase-ios-sdk v11+
creationBlock:creationBlock];

return @[ crashlyticsInitProvider ];
}

@end

0 comments on commit ca07cad

Please sign in to comment.