Skip to content
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
3 changes: 3 additions & 0 deletions FirebaseCore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Unreleased
- [changed] Added thread safety to `[FIROptions defaultOptions]` method. (#5915)

# v6.8.0 -- M73
- [changed] Functionally neutral refactor to simplify FirebaseCore's header usage and replace
Interop pods with headers only. This change is the reason most of the Firebase pods have a minor
Expand Down
48 changes: 25 additions & 23 deletions FirebaseCore/Sources/FIROptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -90,40 +90,40 @@ @implementation FIROptions {

static FIROptions *sDefaultOptions = nil;
static NSDictionary *sDefaultOptionsDictionary = nil;
static dispatch_once_t sDefaultOptionsOnceToken;
static dispatch_once_t sDefaultOptionsDictionaryOnceToken;

#pragma mark - Public only for internal class methods

+ (FIROptions *)defaultOptions {
if (sDefaultOptions != nil) {
return sDefaultOptions;
}

NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
if (defaultOptionsDictionary == nil) {
return nil;
}
dispatch_once(&sDefaultOptionsOnceToken, ^{
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
if (defaultOptionsDictionary != nil) {
sDefaultOptions =
[[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
}
});

sDefaultOptions = [[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
return sDefaultOptions;
}

#pragma mark - Private class methods

+ (NSDictionary *)defaultOptionsDictionary {
if (sDefaultOptionsDictionary != nil) {
return sDefaultOptionsDictionary;
}
NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
if (plistFilePath == nil) {
return nil;
}
sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
if (sDefaultOptionsDictionary == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000011",
@"The configuration file is not a dictionary: "
@"'%@.%@'.",
kServiceInfoFileName, kServiceInfoFileType);
}
dispatch_once(&sDefaultOptionsDictionaryOnceToken, ^{
NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
if (plistFilePath == nil) {
return;
}
sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
if (sDefaultOptionsDictionary == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000011",
@"The configuration file is not a dictionary: "
@"'%@.%@'.",
kServiceInfoFileName, kServiceInfoFileType);
}
});

return sDefaultOptionsDictionary;
}

Expand All @@ -144,6 +144,8 @@ + (NSString *)plistFilePathWithName:(NSString *)fileName {
+ (void)resetDefaultOptions {
sDefaultOptions = nil;
sDefaultOptionsDictionary = nil;
sDefaultOptionsOnceToken = 0;
sDefaultOptionsDictionaryOnceToken = 0;
}

#pragma mark - Private instance methods
Expand Down
55 changes: 48 additions & 7 deletions FirebaseCore/Tests/Unit/FIROptionsTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,38 @@ - (void)testDefaultOptions {
XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme);
}

- (void)testDefaultOptionsAreInitializedOnce {
id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
OCMExpect([mockBundleUtil optionsDictionaryPathWithResourceName:kServiceInfoFileName
andFileType:kServiceInfoFileType
inBundles:[FIRBundleUtil relevantBundles]])
.andReturn([self validGoogleServicesInfoPlistPath]);
XCTAssertNotNil([FIROptions defaultOptions]);
OCMVerifyAll(mockBundleUtil);

OCMReject([mockBundleUtil optionsDictionaryPathWithResourceName:OCMOCK_ANY
andFileType:OCMOCK_ANY
inBundles:OCMOCK_ANY]);
XCTAssertNotNil([FIROptions defaultOptions]);
OCMVerifyAll(mockBundleUtil);
}

- (void)testDefaultOptionsDictionaryIsInitializedOnce {
id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
OCMExpect([mockBundleUtil optionsDictionaryPathWithResourceName:kServiceInfoFileName
andFileType:kServiceInfoFileType
inBundles:[FIRBundleUtil relevantBundles]])
.andReturn([self validGoogleServicesInfoPlistPath]);
XCTAssertNotNil([FIROptions defaultOptionsDictionary]);
OCMVerifyAll(mockBundleUtil);

OCMReject([mockBundleUtil optionsDictionaryPathWithResourceName:OCMOCK_ANY
andFileType:OCMOCK_ANY
inBundles:OCMOCK_ANY]);
XCTAssertNotNil([FIROptions defaultOptionsDictionary]);
OCMVerifyAll(mockBundleUtil);
}

- (void)testInitCustomizedOptions {
FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
GCMSenderID:kGCMSenderID];
Expand All @@ -98,13 +130,7 @@ - (void)testInitCustomizedOptions {
}

- (void)testInitWithContentsOfFile {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info"
ofType:@"plist"];
if (filePath == nil) {
// Use bundleForClass to allow GoogleService-Info.plist to be in the test target's bundle.
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
filePath = [bundle pathForResource:@"GoogleService-Info" ofType:@"plist"];
}
NSString *filePath = [self validGoogleServicesInfoPlistPath];
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
[self assertOptionsMatchDefaults:options andProjectID:YES];
XCTAssertNil(options.deepLinkURLScheme);
Expand Down Expand Up @@ -627,4 +653,19 @@ - (void)testVersionConsistency2 {
XCTAssertEqualObjects(str, [NSString stringWithUTF8String:(const char *)FIRCoreVersionString]);
}

#pragma mark - Helpers

- (NSString *)validGoogleServicesInfoPlistPath {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info"
ofType:@"plist"];
if (filePath == nil) {
// Use bundleForClass to allow GoogleService-Info.plist to be in the test target's bundle.
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
filePath = [bundle pathForResource:@"GoogleService-Info" ofType:@"plist"];
}

XCTAssertNotNil(filePath);
return filePath;
}

@end