Skip to content

Commit afc92b9

Browse files
Refactor FIRComponentContainer to decouple from ARCore logic
This change decouples `FIRComponentContainer` from the specific `ARCore` logic by moving the exclusion rule into `FIRApp` configuration. - Adds `excludedLibraryNames` to `FIROptions` (internal). - Updates `FIRApp` to detect ARCore and populate `excludedLibraryNames`. - Updates `FIRComponentContainer` to filter registrants based on `excludedLibraryNames` passed in options. - Adds `testExcludedComponents` unit test.
1 parent 03ce974 commit afc92b9

File tree

5 files changed

+78
-28
lines changed

5 files changed

+78
-28
lines changed

FirebaseCore/Sources/FIRApp.m

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,13 @@ - (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)opti
315315
if (self) {
316316
_name = [name copy];
317317
_options = [options copy];
318+
319+
if ([self isAppForARCore:name options:_options]) {
320+
NSMutableSet<NSString *> *excluded = [NSMutableSet setWithArray:_options.excludedLibraryNames ?: @[]];
321+
[excluded addObject:@"FIRAppCheckComponent"];
322+
_options.excludedLibraryNames = [excluded allObjects];
323+
}
324+
318325
_options.editingLocked = YES;
319326
_isDefaultApp = [name isEqualToString:kFIRDefaultAppName];
320327
_container = [[FIRComponentContainer alloc] initWithApp:self];
@@ -435,6 +442,24 @@ - (BOOL)isDataCollectionDefaultEnabled {
435442

436443
#pragma mark - private
437444

445+
- (BOOL)isAppForARCore:(NSString *)appName options:(FIROptions *)options {
446+
// First, check if the app name matches that of the one used by ARCore.
447+
if ([appName isEqualToString:@"ARCoreFIRApp"]) {
448+
// Second, check if the app's gcmSenderID matches that of ARCore. This
449+
// prevents false positives in the unlikely event a 3P Firebase app is
450+
// named `ARCoreFIRApp`.
451+
const char *p1 = "406756";
452+
const char *p2 = "893798";
453+
const char gcmSenderIDKey[27] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3],
454+
p2[3], p1[4], p2[4], p1[5], p2[5], p1[6], p2[6],
455+
p1[7], p2[7], p1[8], p2[8], p1[9], p2[9], p1[10],
456+
p2[10], p1[11], p2[11], p1[12], p2[12], '\0'};
457+
NSString *gcmSenderID = [NSString stringWithUTF8String:gcmSenderIDKey];
458+
return [options.GCMSenderID isEqualToString:gcmSenderID];
459+
}
460+
return NO;
461+
}
462+
438463
+ (void)sendNotificationsToSDKs:(FIRApp *)app {
439464
// TODO: Remove this notification once all SDKs are registered with `FIRCoreConfigurable`.
440465
NSNumber *isDefaultApp = [NSNumber numberWithBool:app.isDefaultApp];

FirebaseCore/Sources/FIRComponentContainer.m

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,6 @@ + (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass
6464

6565
- (instancetype)initWithApp:(FIRApp *)app {
6666
NSMutableSet<Class> *componentRegistrants = sFIRComponentRegistrants;
67-
// If the app being created is for the ARCore SDK, remove the App Check
68-
// component (if it exists) since it does not support App Check.
69-
if ([self isAppForARCore:app]) {
70-
Class klass = NSClassFromString(@"FIRAppCheckComponent");
71-
if (klass && [sFIRComponentRegistrants containsObject:klass]) {
72-
componentRegistrants = [componentRegistrants mutableCopy];
73-
[componentRegistrants removeObject:klass];
74-
}
75-
}
76-
7767
return [self initWithApp:app registrants:componentRegistrants];
7868
}
7969

@@ -84,6 +74,17 @@ - (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)all
8474
_cachedInstances = [NSMutableDictionary<NSString *, id> dictionary];
8575
_components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary];
8676

77+
NSArray<NSString *> *excludedLibraryNames = app.options.excludedLibraryNames;
78+
if (excludedLibraryNames.count > 0) {
79+
allRegistrants = [allRegistrants mutableCopy];
80+
for (NSString *libraryName in excludedLibraryNames) {
81+
Class klass = NSClassFromString(libraryName);
82+
if (klass) {
83+
[allRegistrants removeObject:klass];
84+
}
85+
}
86+
}
87+
8788
[self populateComponentsFromRegisteredClasses:allRegistrants forApp:app];
8889
}
8990
return self;
@@ -229,24 +230,6 @@ - (void)removeAllComponents {
229230

230231
#pragma mark - Helpers
231232

232-
- (BOOL)isAppForARCore:(FIRApp *)app {
233-
// First, check if the app name matches that of the one used by ARCore.
234-
if ([app.name isEqualToString:@"ARCoreFIRApp"]) {
235-
// Second, check if the app's gcmSenderID matches that of ARCore. This
236-
// prevents false positives in the unlikely event a 3P Firebase app is
237-
// named `ARCoreFIRApp`.
238-
const char *p1 = "406756";
239-
const char *p2 = "893798";
240-
const char gcmSenderIDKey[27] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3],
241-
p2[3], p1[4], p2[4], p1[5], p2[5], p1[6], p2[6],
242-
p1[7], p2[7], p1[8], p2[8], p1[9], p2[9], p1[10],
243-
p2[10], p1[11], p2[11], p1[12], p2[12], '\0'};
244-
NSString *gcmSenderID = [NSString stringWithUTF8String:gcmSenderIDKey];
245-
return [app.options.GCMSenderID isEqualToString:gcmSenderID];
246-
}
247-
return NO;
248-
}
249-
250233
@end
251234

252235
NS_ASSUME_NONNULL_END

FirebaseCore/Sources/FIROptions.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
// The key to locate the project identifier in the plist file.
3131
NSString *const kFIRProjectID = @"PROJECT_ID";
3232

33+
// The key to locate the excluded library names in the options dictionary.
34+
NSString *const kFIRExcludedLibraryNames = @"EXCLUDED_LIBRARY_NAMES";
35+
3336
NSString *const kFIRIsMeasurementEnabled = @"IS_MEASUREMENT_ENABLED";
3437
NSString *const kFIRIsAnalyticsCollectionEnabled = @"FIREBASE_ANALYTICS_COLLECTION_ENABLED";
3538
NSString *const kFIRIsAnalyticsCollectionDeactivated = @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED";
@@ -320,6 +323,15 @@ - (void)setAppGroupID:(NSString *)appGroupID {
320323
_appGroupID = [appGroupID copy];
321324
}
322325

326+
- (NSArray<NSString *> *)excludedLibraryNames {
327+
return self.optionsDictionary[kFIRExcludedLibraryNames];
328+
}
329+
330+
- (void)setExcludedLibraryNames:(NSArray<NSString *> *)excludedLibraryNames {
331+
[self checkEditingLocked];
332+
_optionsDictionary[kFIRExcludedLibraryNames] = [excludedLibraryNames copy];
333+
}
334+
323335
#pragma mark - Equality
324336

325337
- (BOOL)isEqual:(id)object {

FirebaseCore/Sources/FIROptionsInternal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,9 @@
7070
*/
7171
@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
7272

73+
/**
74+
* A list of class names to exclude from component registration.
75+
*/
76+
@property(nonatomic, copy, nullable) NSArray<NSString *> *excludedLibraryNames;
77+
7378
@end

FirebaseCore/Tests/Unit/FIRComponentContainerTest.m

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,4 +245,29 @@ - (FIRComponentContainer *)containerWithRegistrants:(NSArray<Class> *)registrant
245245
return container;
246246
}
247247

248+
- (void)testExcludedComponents {
249+
// Create options with excluded component.
250+
FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
251+
GCMSenderID:kGCMSenderID];
252+
options.excludedLibraryNames = @[ @"FIRTestClass" ];
253+
254+
_hostApp = [[FIRApp alloc] initInstanceWithName:@"fake_app_excluded" options:options];
255+
256+
NSMutableSet<Class> *allRegistrants = [NSMutableSet<Class> set];
257+
[FIRComponentContainer registerAsComponentRegistrant:[FIRTestClass class] inSet:allRegistrants];
258+
[FIRComponentContainer registerAsComponentRegistrant:[FIRTestClassCached class] inSet:allRegistrants];
259+
260+
// Initialize container with the app (which has the options).
261+
FIRComponentContainer *container = [[FIRComponentContainer alloc] initWithApp:_hostApp registrants:allRegistrants];
262+
_hostApp.container = container;
263+
264+
// FIRTestClass should be excluded, so FIRTestProtocol should not be registered.
265+
id<FIRTestProtocol> instance = FIR_COMPONENT(FIRTestProtocol, container);
266+
XCTAssertNil(instance, @"Instance should be nil because the component was excluded.");
267+
268+
// FIRTestClassCached should still be there.
269+
id<FIRTestProtocolCached> cachedInstance = FIR_COMPONENT(FIRTestProtocolCached, container);
270+
XCTAssertNotNil(cachedInstance, @"Cached instance should not be nil.");
271+
}
272+
248273
@end

0 commit comments

Comments
 (0)