From 14aff8b9b9d340513261d9d8c73743355ed37dd9 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 24 Sep 2020 12:19:20 -0700 Subject: [PATCH 01/10] Stop requiring pods to be static frameworks --- AddNewPod.md | 2 - FirebaseABTesting.podspec | 1 - FirebaseAppDistribution.podspec | 1 - FirebaseAuth.podspec | 1 - FirebaseCore.podspec | 1 - FirebaseCore/CHANGELOG.md | 3 + FirebaseCoreDiagnostics.podspec | 1 - FirebaseCrashlytics.podspec | 1 - FirebaseDatabase.podspec | 1 - FirebaseDynamicLinks.podspec | 1 - FirebaseFirestore.podspec | 1 - FirebaseFunctions.podspec | 1 - FirebaseInAppMessaging.podspec | 1 - FirebaseInstallations.podspec | 1 - FirebaseInstanceID.podspec | 1 - FirebaseMessaging.podspec | 1 - FirebaseRemoteConfig.podspec | 1 - FirebaseStorage.podspec | 1 - FirebaseStorageSwift.podspec | 1 - GoogleDataTransport.podspec | 1 - GoogleUtilitiesComponents.podspec | 1 - docs/firebase_in_libraries.md | 76 ---- .../firebase_from_dynamic_framework.svg | 356 ------------------ .../firebase_from_static_framework.svg | 312 --------------- 24 files changed, 3 insertions(+), 765 deletions(-) delete mode 100644 docs/firebase_in_libraries.md delete mode 100644 docs/resources/firebase_from_dynamic_framework.svg delete mode 100644 docs/resources/firebase_from_static_framework.svg diff --git a/AddNewPod.md b/AddNewPod.md index 6f9d544b142..8c0d0ce576d 100644 --- a/AddNewPod.md +++ b/AddNewPod.md @@ -13,8 +13,6 @@ detailed instructions. Some Firebase specific guidance below: * `s.deployment_target` - Ideally should include ios, osx, and tvos. See [FirebaseCore.podspec](FirebaseCore.podspec) for the current Firebase minimum version settings. -* `s.static_framework` - By default, Firebase pods should be static frameworks. - * `s.dependency` - Dependencies on other Firebase pods and pods in this repo should specify a version and allow minor version updates - like `s.dependency 'FirebaseCore', '~> 6.6'`. When initially defined, choose the most recently released minor version of the dependency. diff --git a/FirebaseABTesting.podspec b/FirebaseABTesting.podspec index 18b327a8cfb..dd4b84adfa3 100644 --- a/FirebaseABTesting.podspec +++ b/FirebaseABTesting.podspec @@ -25,7 +25,6 @@ Firebase Cloud Messaging and Firebase Remote Config in your app. s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "FirebaseABTesting/Sources/" diff --git a/FirebaseAppDistribution.podspec b/FirebaseAppDistribution.podspec index 2d04b22ad85..16f56d867a5 100644 --- a/FirebaseAppDistribution.podspec +++ b/FirebaseAppDistribution.podspec @@ -18,7 +18,6 @@ iOS SDK for App Distribution for Firebase. s.ios.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "FirebaseAppDistribution/Sources/" diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index 35f07b97d39..adea669079d 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -23,7 +23,6 @@ supports email and password accounts, as well as several 3rd party authenticatio s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false source = 'FirebaseAuth/Sources/' diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index eb327f0a869..8ca8c7edaa8 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -22,7 +22,6 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = [ diff --git a/FirebaseCore/CHANGELOG.md b/FirebaseCore/CHANGELOG.md index 5180fb0d886..47b65a2272a 100644 --- a/FirebaseCore/CHANGELOG.md +++ b/FirebaseCore/CHANGELOG.md @@ -2,6 +2,9 @@ - [changed] Update minimum iOS version to iOS 10 except for Analytics which is now iOS 9. (#4847) - [changed] Update minimum macOS version to 10.12. - [added] Swift Package Manager support for Firebase Messaging. (#5641) +- [changed] The pods developed in this repo are no longer hard coded to be built as static + frameworks. Instead, their linkage will be controlled by the Podfile. Use the Podfile + option `use_frameworks! :linkage => :static` to get the Firebase 6.x linkage behavior. (#2022) # Firebase 6.33.0 - [fixed] Swift Package Manager - Define system framework and system library dependencies. This diff --git a/FirebaseCoreDiagnostics.podspec b/FirebaseCoreDiagnostics.podspec index 2a6699e0812..93ef6419486 100644 --- a/FirebaseCoreDiagnostics.podspec +++ b/FirebaseCoreDiagnostics.podspec @@ -24,7 +24,6 @@ non-Cocoapod integration. This library also respects the Firebase global data co s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false header_search_paths = { diff --git a/FirebaseCrashlytics.podspec b/FirebaseCrashlytics.podspec index f6baf1e9292..f04bd22ef47 100644 --- a/FirebaseCrashlytics.podspec +++ b/FirebaseCrashlytics.podspec @@ -17,7 +17,6 @@ Pod::Spec.new do |s| s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = [ diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index ad2dde955f1..a2b2203d9bc 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -21,7 +21,6 @@ Simplify your iOS development, grow your user base, and monetize more effectivel s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "FirebaseDatabase/Sources/" diff --git a/FirebaseDynamicLinks.podspec b/FirebaseDynamicLinks.podspec index 681fc2b2ac1..d9c8a74ea97 100644 --- a/FirebaseDynamicLinks.podspec +++ b/FirebaseDynamicLinks.podspec @@ -19,7 +19,6 @@ Firebase Dynamic Links are deep links that enhance user experience and increase s.ios.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = [ diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 75844917b18..589b14b1707 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -21,7 +21,6 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = [ diff --git a/FirebaseFunctions.podspec b/FirebaseFunctions.podspec index f1c1664da06..be2832f886c 100644 --- a/FirebaseFunctions.podspec +++ b/FirebaseFunctions.podspec @@ -20,7 +20,6 @@ Cloud Functions for Firebase. s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = [ diff --git a/FirebaseInAppMessaging.podspec b/FirebaseInAppMessaging.podspec index 694d64bccc5..4afd0ee5a04 100644 --- a/FirebaseInAppMessaging.podspec +++ b/FirebaseInAppMessaging.podspec @@ -20,7 +20,6 @@ See more product details at https://firebase.google.com/products/in-app-messagin s.ios.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "FirebaseInAppMessaging/" diff --git a/FirebaseInstallations.podspec b/FirebaseInstallations.podspec index 8c1e681b66a..8153e108246 100644 --- a/FirebaseInstallations.podspec +++ b/FirebaseInstallations.podspec @@ -22,7 +22,6 @@ Pod::Spec.new do |s| s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "FirebaseInstallations/Source/" diff --git a/FirebaseInstanceID.podspec b/FirebaseInstanceID.podspec index edc10219d77..6cc4eedb039 100644 --- a/FirebaseInstanceID.podspec +++ b/FirebaseInstanceID.podspec @@ -24,7 +24,6 @@ services. s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "Firebase/InstanceID/" diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index 7fa7926a650..44effb298ad 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -25,7 +25,6 @@ device, and it is completely free. s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "FirebaseMessaging/" diff --git a/FirebaseRemoteConfig.podspec b/FirebaseRemoteConfig.podspec index cefdcca934f..226d1f9f163 100644 --- a/FirebaseRemoteConfig.podspec +++ b/FirebaseRemoteConfig.podspec @@ -23,7 +23,6 @@ app update. s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false base_dir = "FirebaseRemoteConfig/Sources/" diff --git a/FirebaseStorage.podspec b/FirebaseStorage.podspec index 5428aae1cb3..3829e14a39d 100644 --- a/FirebaseStorage.podspec +++ b/FirebaseStorage.podspec @@ -22,7 +22,6 @@ Firebase Storage provides robust, secure file uploads and downloads from Firebas s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = [ diff --git a/FirebaseStorageSwift.podspec b/FirebaseStorageSwift.podspec index 77230de35ab..6a85d8f68b1 100644 --- a/FirebaseStorageSwift.podspec +++ b/FirebaseStorageSwift.podspec @@ -24,7 +24,6 @@ Firebase Storage provides robust, secure file uploads and downloads from Firebas s.watchos.deployment_target = '6.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = [ diff --git a/GoogleDataTransport.podspec b/GoogleDataTransport.podspec index a1d4d512ecf..bc54dcb2cb4 100644 --- a/GoogleDataTransport.podspec +++ b/GoogleDataTransport.podspec @@ -23,7 +23,6 @@ Shared library for iOS SDK data transport needs. # To develop or run the tests, >= 1.8.0 must be installed. s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.source_files = ['GoogleDataTransport/GDTCORLibrary/**/*', diff --git a/GoogleUtilitiesComponents.podspec b/GoogleUtilitiesComponents.podspec index 964a0b95471..6cfb6528f2d 100644 --- a/GoogleUtilitiesComponents.podspec +++ b/GoogleUtilitiesComponents.podspec @@ -24,7 +24,6 @@ Not intended for direct public usage. s.cocoapods_version = '>= 1.4.0' s.prefix_header_file = false - s.static_framework = true s.source_files = 'GoogleUtilitiesComponents/Sources/**/*.[mh]' s.public_header_files = 'GoogleUtilitiesComponents/Sources/Public/*.h', 'GoogleUtilitiesComponents/Sources/Private/*.h' diff --git a/docs/firebase_in_libraries.md b/docs/firebase_in_libraries.md deleted file mode 100644 index 2345c3511aa..00000000000 --- a/docs/firebase_in_libraries.md +++ /dev/null @@ -1,76 +0,0 @@ -# Using Firebase from a framework or a library - -Currently all source code and binary Firebase SDKs are compiled as static frameworks. Most -of the time you’ll link these frameworks directly to your app targets, but in some cases -it makes sense to link Firebase frameworks to your app indirectly, from another library or -another framework. This note talks about some of the pitfalls of this technique, almost -always hard-to-debug undefined behaviors, that all come down to code duplication and how -static and dynamic linking work. Your framework itself may be either static or dynamic. Let's -consider these two options in more detail. - -## Using Firebase SDKs from dynamic frameworks - -A dynamic framework is a bundle containing dynamic libraries and other resources. The dynamic -libraries bundled in the framework can themselves be compiled from static libraries, like -Firebase core and product libraries, meaning all of the symbols of the static libraries are -part of the dynamic framework bundle. What if your core app already directly links a static -Firebase library when you link to the same library indirectly from a dynamic framework? Well, -you end up with duplicate symbols in your app. This leads to undefined behavior (especially -when different versions of the static framework are linked to the app and the dynamic framework). -For example, a `dispatch_once` may or may not perform the correct initialization since there -are now two entities to initialize. Here are a couple more examples of issues related to this -undefined behavior: -[#4315](https://github.com/firebase/firebase-ios-sdk/issues/4315), -[#5152](https://github.com/firebase/firebase-ios-sdk/issues/5152). - -In this case you will most likely see warnings like the following in the console: - -```text -objc[40943]: Class FIRApp is implemented in both -~/Library/Developer/Xcode/DerivedData/FrameworkTest-apqjxlyrxvkbhhafhaypsbdquref/Build/Products/Debug-iphonesimulator/DynamicFramework.framework/DynamicFramework -(0x10b2a87f8) and -~/Library/Developer/CoreSimulator/Devices/4821F959-24A6-4D78-A102-4C5703103D99/data/Containers/Bundle/Application/F017D210-113A-4DAF-9E17-BDE455E71E06/FrameworkTest.app/FrameworkTest -(0x10ad2d348). One of the two will be used. Which one is undefined. -``` - -This commonly leads to a crash with the following error message: - -`The default FirebaseApp instance must be configured before the defaultFirebaseApp instance can be initialized.` - - - -**Figure 1: Using Firebase SDKs from dynamic framework** - -### Conclusions: - -- The Firebase SDKs may be used from an embedded dynamic framework in your project (e.g. for -code reuse purposes) only when Firebase is not used from the app directly. -- The Firebase SDKs should never be used from vendor dynamic frameworks because the version of -Firebase compiled into the dynamic framework will conflict with the versions compiled into the -app or included in any app bundles. - -## Using Firebase SDKs from static frameworks - -A static framework is a bundle containing static libraries and other resources. When a static -binary is built, it is not necessary to link any static or dynamic dependencies into the binary -because presence of the dependencies will be verified when the whole app is linked. This means -that both the static framework/library and your app will be able to "share" symbols (which is -basically what we need). - -The main downside of this approach arises when the static framework using Firebase is used -from, for example, an app and its extension. In this case, in contrast to a dynamic embedded framework, -a copy of the static framework will be added to both the app and each extension. It doesn't -lead to any symbol collisions, but it does increase the download size of your app. - - - -**Figure 2: Firebase SDKs from static framework** - -### Conclusions: - -- Using the Firebase SDKs in static frameworks and libraries is safe, meaning there will be -no symbol collisions, for both vendor and in-app internal libraries. -- If the static framework is used from an app and its extensions, then it will be copied to -each target, increasing the app download size. -- If the static framework is used from an app and its dynamic dependencies, then it will be -copied in both the app and its dependencies, which will result in undefined behavior. diff --git a/docs/resources/firebase_from_dynamic_framework.svg b/docs/resources/firebase_from_dynamic_framework.svg deleted file mode 100644 index 8a329a6e89e..00000000000 --- a/docs/resources/firebase_from_dynamic_framework.svg +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/resources/firebase_from_static_framework.svg b/docs/resources/firebase_from_static_framework.svg deleted file mode 100644 index 7d87469a3ac..00000000000 --- a/docs/resources/firebase_from_static_framework.svg +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a1d63e4a1572d8c16548ae1cf5abbb219d1b082b Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 24 Sep 2020 17:46:16 -0700 Subject: [PATCH 02/10] Fix tests --- FirebaseCore.podspec | 17 +++++-- .../Tests/SwiftUnit/FirebaseAppTests.swift | 1 + .../FirebaseCore-unit-Bridging-Header.h | 1 + FirebaseCore/Tests/Unit/FIRAppTest.m | 6 +-- FirebaseCore/Tests/Unit/FIRBundleUtilTest.m | 1 + .../Tests/Unit/FIRComponentContainerTest.m | 2 +- FirebaseCore/Tests/Unit/FIROptionsTest.m | 13 ++---- FirebaseCore/Tests/Unit/FIRTestCase.h | 21 --------- FirebaseCore/Tests/Unit/FIRTestCase.m | 46 ------------------- FirebaseDatabase.podspec | 24 ++++++---- FirebaseDatabase/Tests/Helpers/FTestBase.m | 5 +- .../Tests/Unit/RCNRemoteConfigTest.m | 40 ++++++++++------ 12 files changed, 66 insertions(+), 111 deletions(-) diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 8ca8c7edaa8..cc24878cbd7 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -53,7 +53,10 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration } s.test_spec 'unit' do |unit_tests| unit_tests.platforms = {:ios => '8.0', :osx => '10.11', :tvos => '10.0'} - unit_tests.source_files = 'FirebaseCore/Tests/Unit/**/*.[mh]' + unit_tests.source_files = [ + 'FirebaseCore/Tests/Unit/**/*.[mh]', + 'SharedTestUtilities/FIROptionsMock.[mh]', + ] unit_tests.requires_app_host = true unit_tests.dependency 'OCMock' unit_tests.resources = 'FirebaseCore/Tests/Unit/Resources/GoogleService-Info.plist' @@ -61,12 +64,16 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration s.test_spec 'swift-unit' do |swift_unit_tests| swift_unit_tests.platforms = {:ios => '8.0', :osx => '10.11', :tvos => '10.0'} - swift_unit_tests.source_files = 'FirebaseCore/Tests/SwiftUnit/**/*.swift', - 'FirebaseCore/Tests/SwiftUnit/**/*.h', - 'FirebaseCore/Tests/SwiftUnit/SwiftTestingUtilities/*' - swift_unit_tests.resources = 'FirebaseCore/Tests/Unit/Resources/GoogleService-Info.plist' + swift_unit_tests.source_files = [ + 'FirebaseCore/Tests/SwiftUnit/**/*.swift', + 'FirebaseCore/Tests/SwiftUnit/**/*.h', + 'FirebaseCore/Tests/SwiftUnit/SwiftTestingUtilities/*', + 'SharedTestUtilities/FIROptionsMock.[mh]', + ] swift_unit_tests.pod_target_xcconfig = { 'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/FirebaseCore/Tests/SwiftUnit/FirebaseCore-unit-Bridging-Header.h' } + swift_unit_tests.dependency 'OCMock' + swift_unit_tests.resources = 'FirebaseCore/Tests/Unit/Resources/GoogleService-Info.plist' end end diff --git a/FirebaseCore/Tests/SwiftUnit/FirebaseAppTests.swift b/FirebaseCore/Tests/SwiftUnit/FirebaseAppTests.swift index 5fecaa6cb2d..d90af3aba3d 100644 --- a/FirebaseCore/Tests/SwiftUnit/FirebaseAppTests.swift +++ b/FirebaseCore/Tests/SwiftUnit/FirebaseAppTests.swift @@ -23,6 +23,7 @@ private extension Constants { class FirebaseAppTests: XCTestCase { override func setUp() { super.setUp() + FIROptionsMock.mockFIROptions() } override func tearDown() { diff --git a/FirebaseCore/Tests/SwiftUnit/FirebaseCore-unit-Bridging-Header.h b/FirebaseCore/Tests/SwiftUnit/FirebaseCore-unit-Bridging-Header.h index d4acabe0954..4816efda40b 100644 --- a/FirebaseCore/Tests/SwiftUnit/FirebaseCore-unit-Bridging-Header.h +++ b/FirebaseCore/Tests/SwiftUnit/FirebaseCore-unit-Bridging-Header.h @@ -14,3 +14,4 @@ #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" #import "FirebaseCore/Tests/SwiftUnit/SwiftTestingUtilities/ExceptionCatcher.h" +#import "SharedTestUtilities/FIROptionsMock.h" diff --git a/FirebaseCore/Tests/Unit/FIRAppTest.m b/FirebaseCore/Tests/Unit/FIRAppTest.m index 17a0aef3d8d..7be2cf27735 100644 --- a/FirebaseCore/Tests/Unit/FIRAppTest.m +++ b/FirebaseCore/Tests/Unit/FIRAppTest.m @@ -19,8 +19,8 @@ #import "FirebaseCore/Sources/Private/FIRAppInternal.h" #import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h" #import "FirebaseCore/Sources/Private/FIROptionsInternal.h" - #import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h" +#import "SharedTestUtilities/FIROptionsMock.h" NSString *const kFIRTestAppName1 = @"test_app_name_1"; NSString *const kFIRTestAppName2 = @"test-app-name-2"; @@ -68,9 +68,7 @@ - (void)setUp { _observerMock = OCMObserverMock(); _mockCoreDiagnosticsConnector = OCMClassMock([FIRCoreDiagnosticsConnector class]); -#if SWIFT_PACKAGE - [self mockFIROptions]; -#endif + [FIROptionsMock mockFIROptions]; OCMStub(ClassMethod([self.mockCoreDiagnosticsConnector logCoreTelemetryWithOptions:[OCMArg any]])) .andDo(^(NSInvocation *invocation){ diff --git a/FirebaseCore/Tests/Unit/FIRBundleUtilTest.m b/FirebaseCore/Tests/Unit/FIRBundleUtilTest.m index 5b8c652ceb8..b0e414c88db 100644 --- a/FirebaseCore/Tests/Unit/FIRBundleUtilTest.m +++ b/FirebaseCore/Tests/Unit/FIRBundleUtilTest.m @@ -16,6 +16,7 @@ #import "FirebaseCore/Sources/FIRBundleUtil.h" #import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h" +#import "SharedTestUtilities/FIROptionsMock.h" static NSString *const kResultPath = @"resultPath"; static NSString *const kResourceName = @"resourceName"; diff --git a/FirebaseCore/Tests/Unit/FIRComponentContainerTest.m b/FirebaseCore/Tests/Unit/FIRComponentContainerTest.m index cedb2d6fdd3..86e492ac164 100644 --- a/FirebaseCore/Tests/Unit/FIRComponentContainerTest.m +++ b/FirebaseCore/Tests/Unit/FIRComponentContainerTest.m @@ -16,8 +16,8 @@ #import "FirebaseCore/Sources/FIRComponentContainerInternal.h" #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" - #import "FirebaseCore/Tests/Unit/FIRTestComponents.h" +#import "SharedTestUtilities/FIROptionsMock.h" /// Internally exposed methods and properties for testing. @interface FIRComponentContainer (TestInternal) diff --git a/FirebaseCore/Tests/Unit/FIROptionsTest.m b/FirebaseCore/Tests/Unit/FIROptionsTest.m index 0ad93652d83..23c924d73ab 100644 --- a/FirebaseCore/Tests/Unit/FIROptionsTest.m +++ b/FirebaseCore/Tests/Unit/FIROptionsTest.m @@ -18,6 +18,7 @@ #import "FirebaseCore/Sources/FIRVersion.h" #import "FirebaseCore/Sources/Private/FIRAppInternal.h" #import "FirebaseCore/Sources/Private/FIROptionsInternal.h" +#import "SharedTestUtilities/FIROptionsMock.h" extern NSString *const kFIRIsMeasurementEnabled; extern NSString *const kFIRIsAnalyticsCollectionEnabled; @@ -43,9 +44,7 @@ - (void)setUp { } - (void)testInit { -#if SWIFT_PACKAGE - [self mockFIROptions]; -#endif + [FIROptionsMock mockFIROptions]; NSDictionary *optionsDictionary = [FIROptions defaultOptionsDictionary]; FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; [self assertOptionsMatchDefaults:options andProjectID:YES]; @@ -75,9 +74,7 @@ - (void)testDefaultOptionsDictionaryWithInvalidSourceFile { } - (void)testDefaultOptions { -#if SWIFT_PACKAGE - [self mockFIROptions]; -#endif + [FIROptionsMock mockFIROptions]; FIROptions *options = [FIROptions defaultOptions]; [self assertOptionsMatchDefaults:options andProjectID:YES]; XCTAssertNil(options.deepLinkURLScheme); @@ -242,9 +239,7 @@ - (void)testCopyingProperties { } - (void)testCopyWithZone { -#if SWIFT_PACKAGE - [self mockFIROptions]; -#endif + [FIROptionsMock mockFIROptions]; // default options FIROptions *options = [FIROptions defaultOptions]; options.deepLinkURLScheme = kDeepLinkURLScheme; diff --git a/FirebaseCore/Tests/Unit/FIRTestCase.h b/FirebaseCore/Tests/Unit/FIRTestCase.h index 616f277144e..052f918c81a 100644 --- a/FirebaseCore/Tests/Unit/FIRTestCase.h +++ b/FirebaseCore/Tests/Unit/FIRTestCase.h @@ -17,29 +17,8 @@ #import #import "OCMock.h" -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const kAPIKey; -extern NSString *const kCustomizedAPIKey; -extern NSString *const kClientID; -extern NSString *const kTrackingID; -extern NSString *const kGCMSenderID; -extern NSString *const kAndroidClientID; -extern NSString *const kGoogleAppID; -extern NSString *const kDatabaseURL; -extern NSString *const kStorageBucket; - -extern NSString *const kDeepLinkURLScheme; -extern NSString *const kNewDeepLinkURLScheme; - -extern NSString *const kBundleID; -extern NSString *const kProjectID; - /** * Base test case for Firebase Core SDK tests. */ @interface FIRTestCase : XCTestCase -- (void)mockFIROptions; @end - -NS_ASSUME_NONNULL_END diff --git a/FirebaseCore/Tests/Unit/FIRTestCase.m b/FirebaseCore/Tests/Unit/FIRTestCase.m index 69b908949c2..3c17ef1e436 100644 --- a/FirebaseCore/Tests/Unit/FIRTestCase.m +++ b/FirebaseCore/Tests/Unit/FIRTestCase.m @@ -14,24 +14,6 @@ #import "FirebaseCore/Tests/Unit/FIRTestCase.h" -#import "FirebaseCore/Sources/Private/FIROptionsInternal.h" - -NSString *const kAndroidClientID = @"correct_android_client_id"; -NSString *const kAPIKey = @"correct_api_key"; -NSString *const kCustomizedAPIKey = @"customized_api_key"; -NSString *const kClientID = @"correct_client_id"; -NSString *const kTrackingID = @"correct_tracking_id"; -NSString *const kGCMSenderID = @"correct_gcm_sender_id"; -NSString *const kGoogleAppID = @"1:123:ios:123abc"; -NSString *const kDatabaseURL = @"https://abc-xyz-123.firebaseio.com"; -NSString *const kStorageBucket = @"project-id-123.storage.firebase.com"; - -NSString *const kDeepLinkURLScheme = @"comgoogledeeplinkurl"; -NSString *const kNewDeepLinkURLScheme = @"newdeeplinkurlfortest"; - -NSString *const kBundleID = @"com.google.FirebaseSDKTests"; -NSString *const kProjectID = @"abc-xyz-123"; - @interface FIRTestCase () @end @@ -42,34 +24,6 @@ - (void)setUp { [super setUp]; } -// Swift Package manager does not allow a test project to override a bundle in an app (or library). -- (void)mockFIROptions { - // Keys for the strings in the plist file. - NSString *const kFIRAPIKey = @"API_KEY"; - NSString *const kFIRTrackingID = @"TRACKING_ID"; - NSString *const kFIRGoogleAppID = @"GOOGLE_APP_ID"; - NSString *const kFIRClientID = @"CLIENT_ID"; - NSString *const kFIRGCMSenderID = @"GCM_SENDER_ID"; - NSString *const kFIRDatabaseURL = @"DATABASE_URL"; - NSString *const kFIRStorageBucket = @"STORAGE_BUCKET"; - NSString *const kFIRBundleID = @"BUNDLE_ID"; - NSString *const kFIRProjectID = @"PROJECT_ID"; - - NSDictionary *mockDictionary = @{ - kFIRAPIKey : kAPIKey, - kFIRBundleID : kBundleID, - kFIRClientID : kClientID, - kFIRDatabaseURL : kDatabaseURL, - kFIRGCMSenderID : kGCMSenderID, - kFIRGoogleAppID : kGoogleAppID, - kFIRProjectID : kProjectID, - kFIRStorageBucket : kStorageBucket, - kFIRTrackingID : kTrackingID, - }; - id optionsClassMock = OCMClassMock([FIROptions class]); - OCMStub([optionsClassMock defaultOptionsDictionary]).andReturn(mockDictionary); -} - - (void)tearDown { [super tearDown]; } diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index a2b2203d9bc..318b98ecba5 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -43,19 +43,27 @@ Simplify your iOS development, grow your user base, and monetize more effectivel } s.test_spec 'unit' do |unit_tests| - unit_tests.source_files = 'FirebaseDatabase/Tests/Unit/*.[mh]', - 'FirebaseDatabase/Tests/Helpers/*.[mh]', - 'FirebaseDatabase/Tests/third_party/*.[mh]', - 'SharedTestUtilities/FIRAuthInteropFake.[mh]', - 'SharedTestUtilities/FIRComponentTestUtilities.h' + unit_tests.source_files = [ + 'FirebaseDatabase/Tests/Unit/*.[mh]', + 'FirebaseDatabase/Tests/Helpers/*.[mh]', + 'FirebaseDatabase/Tests/third_party/*.[mh]', + 'SharedTestUtilities/FIRAuthInteropFake.[mh]', + 'SharedTestUtilities/FIRComponentTestUtilities.h', + 'SharedTestUtilities/FIROptionsMock.[mh]', + ] + unit_tests.dependency 'OCMock' unit_tests.resources = 'FirebaseDatabase/Tests/Resources/syncPointSpec.json', 'FirebaseDatabase/Tests/Resources/GoogleService-Info.plist' end s.test_spec 'integration' do |int_tests| - int_tests.source_files = 'FirebaseDatabase/Tests/Integration/*.[mh]', - 'FirebaseDatabase/Tests/Helpers/*.[mh]', - 'SharedTestUtilities/FIRAuthInteropFake.[mh]' + int_tests.source_files = [ + 'FirebaseDatabase/Tests/Integration/*.[mh]', + 'FirebaseDatabase/Tests/Helpers/*.[mh]', + 'SharedTestUtilities/FIRAuthInteropFake.[mh]', + 'SharedTestUtilities/FIROptionsMock.[mh]', + ] + int_tests.dependency 'OCMock' int_tests.resources = 'FirebaseDatabase/Tests/Resources/GoogleService-Info.plist' end end diff --git a/FirebaseDatabase/Tests/Helpers/FTestBase.m b/FirebaseDatabase/Tests/Helpers/FTestBase.m index 51e7b175928..26349d485a3 100644 --- a/FirebaseDatabase/Tests/Helpers/FTestBase.m +++ b/FirebaseDatabase/Tests/Helpers/FTestBase.m @@ -20,16 +20,15 @@ #import "FirebaseDatabase/Tests/Helpers/FIRTestAuthTokenProvider.h" #import "FirebaseDatabase/Tests/Helpers/FTestAuthTokenGenerator.h" #import "FirebaseDatabase/Tests/Helpers/FTestBase.h" +#import "SharedTestUtilities/FIROptionsMock.h" @implementation FTestBase + (void)setUp { static dispatch_once_t once; dispatch_once(&once, ^{ -#if !SWIFT_PACKAGE - // Disabled for now with SPM. configure is not needed for the unit tests. + [FIROptionsMock mockFIROptions]; [FIRApp configure]; -#endif }); } diff --git a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m index 4a7967230be..5be80a296b7 100644 --- a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m +++ b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m @@ -1143,22 +1143,39 @@ - (void)testInvalidKeyOrNamespace { return [dateFormatter stringFromDate:date]; } +// Manage different bundle locations for Swift Package Manager, CocoaPods static, CocoaPods dynamic. +- (void)setDefaultsFor:(FIRRemoteConfig *)config namespace:(NSString *)namespace { #if SWIFT_PACKAGE -- (NSDictionary *)getSPMDefaults { NSBundle *bundle = Firebase_RemoteConfigUnit_SWIFTPM_MODULE_BUNDLE(); NSString *plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"]; - return [[NSDictionary alloc] initWithContentsOfFile:plistFile]; -} +#else + NSBundle *bundle = [NSBundle mainBundle]; + NSString *plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"]; + if (plistFile != nil) { + if (namespace) { + [config setDefaultsFromPlistFileName:@"Defaults-testInfo" namespace:namespace]; + } else { + [config setDefaultsFromPlistFileName:@"Defaults-testInfo"]; + } + return; + } + // We've linked dynamically and the plist file is further down in the path. + plistFile = + [bundle pathForResource:@"PlugIns/FirebaseRemoteConfig-Unit-unit.xctest/Defaults-testInfo" + ofType:@"plist"]; #endif + NSDictionary *defaults = [[NSDictionary alloc] initWithContentsOfFile:plistFile]; + if (namespace) { + [config setDefaults:defaults namespace:namespace]; + } else { + [config setDefaults:defaults]; + } +} - (void)testSetDefaultsFromPlist { for (int i = 0; i < RCNTestRCNumTotalInstances; i++) { FIRRemoteConfig *config = _configInstances[i]; -#if SWIFT_PACKAGE - [config setDefaults:[self getSPMDefaults]]; -#else - [config setDefaultsFromPlistFileName:@"Defaults-testInfo"]; -#endif + [self setDefaultsFor:config namespace:nil]; XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue, UTCToLocal(@"2016-02-28 18:33:31")); XCTAssertEqual(_configInstances[i][@"isPaidUser"].boolValue, YES); @@ -1185,12 +1202,7 @@ - (void)testSetDefaultsFromPlist { - (void)testSetDefaultsAndNamespaceFromPlist { for (int i = 0; i < RCNTestRCNumTotalInstances; i++) { if (i == RCNTestRCInstanceDefault) { -#if SWIFT_PACKAGE - [_configInstances[i] setDefaults:[self getSPMDefaults] namespace:RCNTestsPerfNamespace]; -#else - [_configInstances[i] setDefaultsFromPlistFileName:@"Defaults-testInfo" - namespace:RCNTestsPerfNamespace]; -#endif + [self setDefaultsFor:_configInstances[i] namespace:RCNTestsPerfNamespace]; XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"lastCheckTime" namespace:RCNTestsPerfNamespace] .stringValue, From d4d33d9c6c0b616e83e3c98433daf364bc76e6e1 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 24 Sep 2020 18:12:14 -0700 Subject: [PATCH 03/10] missing add files --- Package.swift | 2 +- SharedTestUtilities/FIROptionsMock.h | 45 +++++++++++++++++++++ SharedTestUtilities/FIROptionsMock.m | 59 ++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 SharedTestUtilities/FIROptionsMock.h create mode 100644 SharedTestUtilities/FIROptionsMock.m diff --git a/Package.swift b/Package.swift index 299bb87c826..63168bf58f7 100644 --- a/Package.swift +++ b/Package.swift @@ -156,7 +156,7 @@ let package = Package( ), .testTarget( name: "CoreUnit", - dependencies: ["FirebaseCore", "OCMock"], + dependencies: ["FirebaseCore", "SharedTestUtilities", "OCMock"], path: "FirebaseCore/Tests/Unit", exclude: ["Resources/GoogleService-Info.plist"], cSettings: [ diff --git a/SharedTestUtilities/FIROptionsMock.h b/SharedTestUtilities/FIROptionsMock.h new file mode 100644 index 00000000000..e31b92659e7 --- /dev/null +++ b/SharedTestUtilities/FIROptionsMock.h @@ -0,0 +1,45 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import "OCMock.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const kAPIKey; +extern NSString *const kCustomizedAPIKey; +extern NSString *const kClientID; +extern NSString *const kTrackingID; +extern NSString *const kGCMSenderID; +extern NSString *const kAndroidClientID; +extern NSString *const kGoogleAppID; +extern NSString *const kDatabaseURL; +extern NSString *const kStorageBucket; + +extern NSString *const kDeepLinkURLScheme; +extern NSString *const kNewDeepLinkURLScheme; + +extern NSString *const kBundleID; +extern NSString *const kProjectID; + +/** + * Base test case for Firebase Core SDK tests. + */ +@interface FIROptionsMock : NSObject ++ (void)mockFIROptions; +@end + +NS_ASSUME_NONNULL_END diff --git a/SharedTestUtilities/FIROptionsMock.m b/SharedTestUtilities/FIROptionsMock.m new file mode 100644 index 00000000000..53642e15e24 --- /dev/null +++ b/SharedTestUtilities/FIROptionsMock.m @@ -0,0 +1,59 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "OCMock.h" + +#import "FirebaseCore/Sources/Private/FIROptionsInternal.h" +#import "SharedTestUtilities/FIROptionsMock.h" + +NSString *const kAndroidClientID = @"correct_android_client_id"; +NSString *const kAPIKey = @"correct_api_key"; +NSString *const kCustomizedAPIKey = @"customized_api_key"; +NSString *const kClientID = @"correct_client_id"; +NSString *const kTrackingID = @"correct_tracking_id"; +NSString *const kGCMSenderID = @"correct_gcm_sender_id"; +NSString *const kGoogleAppID = @"1:123:ios:123abc"; +NSString *const kDatabaseURL = @"https://abc-xyz-123.firebaseio.com"; +NSString *const kStorageBucket = @"project-id-123.storage.firebase.com"; + +NSString *const kDeepLinkURLScheme = @"comgoogledeeplinkurl"; +NSString *const kNewDeepLinkURLScheme = @"newdeeplinkurlfortest"; + +NSString *const kBundleID = @"com.google.FirebaseSDKTests"; +NSString *const kProjectID = @"abc-xyz-123"; + +@interface FIROptionsMock () + +@end + +@implementation FIROptionsMock + +// Swift Package manager does not allow a test project to override a bundle in an app (or library). ++ (void)mockFIROptions { + NSDictionary *mockDictionary = @{ + kFIRAPIKey : kAPIKey, + kFIRBundleID : kBundleID, + kFIRClientID : kClientID, + kFIRDatabaseURL : kDatabaseURL, + kFIRGCMSenderID : kGCMSenderID, + kFIRGoogleAppID : kGoogleAppID, + kFIRProjectID : kProjectID, + kFIRStorageBucket : kStorageBucket, + kFIRTrackingID : kTrackingID, + }; + id optionsClassMock = OCMClassMock([FIROptions class]); + OCMStub([optionsClassMock defaultOptionsDictionary]).andReturn(mockDictionary); +} + +@end From de608eb069846aa1297691896df54d53368be868 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 25 Sep 2020 08:01:01 -0700 Subject: [PATCH 04/10] test fixes --- FirebaseFirestoreSwift.podspec | 1 - FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m | 6 ++++++ Package.swift | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/FirebaseFirestoreSwift.podspec b/FirebaseFirestoreSwift.podspec index 276ae0a05a0..725ebd7f3ec 100644 --- a/FirebaseFirestoreSwift.podspec +++ b/FirebaseFirestoreSwift.podspec @@ -27,7 +27,6 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' - s.static_framework = true s.prefix_header_file = false s.requires_arc = true diff --git a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m index 5be80a296b7..4c3b40646cb 100644 --- a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m +++ b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m @@ -1160,9 +1160,15 @@ - (void)setDefaultsFor:(FIRRemoteConfig *)config namespace:(NSString *)namespace return; } // We've linked dynamically and the plist file is further down in the path. +#if TARGET_OS_OSX + plistFile = + [bundle pathForResource:@"Contents/Resources/Defaults-testInfo" + ofType:@"plist"]; +#else plistFile = [bundle pathForResource:@"PlugIns/FirebaseRemoteConfig-Unit-unit.xctest/Defaults-testInfo" ofType:@"plist"]; +#endif #endif NSDictionary *defaults = [[NSDictionary alloc] initWithContentsOfFile:plistFile]; if (namespace) { diff --git a/Package.swift b/Package.swift index 63168bf58f7..c68be59a685 100644 --- a/Package.swift +++ b/Package.swift @@ -555,7 +555,7 @@ let package = Package( .target( name: "SharedTestUtilities", - dependencies: ["FirebaseCore"], + dependencies: ["FirebaseCore", "OCMock"], path: "SharedTestUtilities", publicHeadersPath: "./", cSettings: [ From 4c095977e58debbc9a58c227e32b7f5afcdf995e Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 25 Sep 2020 09:18:41 -0700 Subject: [PATCH 05/10] test fixes --- .../Sample/ApiTests/EmailPasswordAuthTests.m | 2 +- .../Tests/Unit/RCNRemoteConfigTest.m | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m b/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m index 9b0f1719012..3c17c60b573 100644 --- a/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m +++ b/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m @@ -19,7 +19,7 @@ #import "FIRAuthApiTestsBase.h" /** The testing email address for testCreateAccountWithEmailAndPassword. */ -static NSString *const kNewEmailToCreateUser = @"user+email_new_user@example.com"; +static NSString *const kNewEmailToCreateUser = @"paul200925@example.com"; /** The testing email address for testSignInExistingUserWithEmailAndPassword. */ static NSString *const kExistingEmailToSignIn = @"user+email_existing_user@example.com"; diff --git a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m index 4c3b40646cb..6b7a09193c8 100644 --- a/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m +++ b/FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m @@ -1159,16 +1159,13 @@ - (void)setDefaultsFor:(FIRRemoteConfig *)config namespace:(NSString *)namespace } return; } - // We've linked dynamically and the plist file is further down in the path. -#if TARGET_OS_OSX - plistFile = - [bundle pathForResource:@"Contents/Resources/Defaults-testInfo" - ofType:@"plist"]; -#else - plistFile = - [bundle pathForResource:@"PlugIns/FirebaseRemoteConfig-Unit-unit.xctest/Defaults-testInfo" - ofType:@"plist"]; -#endif + // We've linked dynamically and the plist file is in the test's bundle. + for (bundle in [NSBundle allBundles]) { + plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"]; + if (plistFile != nil) { + break; + } + } #endif NSDictionary *defaults = [[NSDictionary alloc] initWithContentsOfFile:plistFile]; if (namespace) { From 4ecca84ffa401abf2f55905aef89cf254965233b Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 25 Sep 2020 09:40:51 -0700 Subject: [PATCH 06/10] auth integration test workaround --- .../Tests/Sample/SwiftApiTests/EmailPasswordTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift b/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift index 888240b738f..514fd4aa2bb 100644 --- a/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift +++ b/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift @@ -21,7 +21,7 @@ import XCTest class EmailPasswordTests: TestsBase { ///** The testing email address for testCreateAccountWithEmailAndPassword. */ - let kNewEmailToCreateUser = "user+email_new_user@example.com" + let kNewEmailToCreateUser = "paulswift200925@example.com@example.com" ///** The testing email address for testSignInExistingUserWithEmailAndPassword. */ let kExistingEmailToSignIn = "user+email_existing_user@example.com" From bf04b5c83c60c9634d334a29e0c322a3afdcc9c6 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 25 Sep 2020 11:22:57 -0700 Subject: [PATCH 07/10] FIAM dynamic linkage bundle --- .../Sample/SwiftApiTests/EmailPasswordTests.swift | 2 +- .../Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift b/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift index 514fd4aa2bb..a9739ad844f 100644 --- a/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift +++ b/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift @@ -21,7 +21,7 @@ import XCTest class EmailPasswordTests: TestsBase { ///** The testing email address for testCreateAccountWithEmailAndPassword. */ - let kNewEmailToCreateUser = "paulswift200925@example.com@example.com" + let kNewEmailToCreateUser = "paulswift200925@example.com" ///** The testing email address for testSignInExistingUserWithEmailAndPassword. */ let kExistingEmailToSignIn = "user+email_existing_user@example.com" diff --git a/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m b/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m index d58091bc7dc..99f8dfaf169 100644 --- a/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m +++ b/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m @@ -53,14 +53,17 @@ + (void)didReceiveConfigureSDKNotification:(NSNotification *)notification { + (NSBundle *)getViewResourceBundle { static NSBundle *resourceBundle; static dispatch_once_t onceToken; + Class myClass = [self class]; dispatch_once(&onceToken, ^{ - // TODO. This logic of finding the resource bundle may need to change once it's open - // sourced - NSBundle *containingBundle = [NSBundle mainBundle]; - // This is assuming the display resource bundle is contained in the main bundle - NSURL *bundleURL = [containingBundle URLForResource:@"InAppMessagingDisplayResources" - withExtension:@"bundle"]; + NSBundle *containingBundle; + NSURL *bundleURL; + // The containing bundle is different whether FIAM is statically or dynamically linked. + for (containingBundle in @[[NSBundle mainBundle], [NSBundle bundleForClass:myClass]]) { + bundleURL = [containingBundle URLForResource:@"InAppMessagingDisplayResources" + withExtension:@"bundle"]; + if (bundleURL != nil) break; + } if (bundleURL == nil) { FIRLogWarning(kFIRLoggerInAppMessagingDisplay, @"I-FID100007", @"FIAM Display Resource bundle " From fa41672d54c44a31aaaecd110cc50a0cce2710a6 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 25 Sep 2020 11:34:59 -0700 Subject: [PATCH 08/10] style --- .../Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m b/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m index 99f8dfaf169..656c493f2a7 100644 --- a/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m +++ b/FirebaseInAppMessaging/Sources/DefaultUI/FIRIAMDefaultDisplayImpl.m @@ -59,7 +59,7 @@ + (NSBundle *)getViewResourceBundle { NSBundle *containingBundle; NSURL *bundleURL; // The containing bundle is different whether FIAM is statically or dynamically linked. - for (containingBundle in @[[NSBundle mainBundle], [NSBundle bundleForClass:myClass]]) { + for (containingBundle in @[ [NSBundle mainBundle], [NSBundle bundleForClass:myClass] ]) { bundleURL = [containingBundle URLForResource:@"InAppMessagingDisplayResources" withExtension:@"bundle"]; if (bundleURL != nil) break; From a97f43d16f615ed52a7cf94db28b51f51bed8fdb Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 25 Sep 2020 12:19:55 -0700 Subject: [PATCH 09/10] Restore Auth tests --- FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m | 2 +- .../Tests/Sample/SwiftApiTests/EmailPasswordTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m b/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m index 3c17c60b573..9b0f1719012 100644 --- a/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m +++ b/FirebaseAuth/Tests/Sample/ApiTests/EmailPasswordAuthTests.m @@ -19,7 +19,7 @@ #import "FIRAuthApiTestsBase.h" /** The testing email address for testCreateAccountWithEmailAndPassword. */ -static NSString *const kNewEmailToCreateUser = @"paul200925@example.com"; +static NSString *const kNewEmailToCreateUser = @"user+email_new_user@example.com"; /** The testing email address for testSignInExistingUserWithEmailAndPassword. */ static NSString *const kExistingEmailToSignIn = @"user+email_existing_user@example.com"; diff --git a/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift b/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift index a9739ad844f..888240b738f 100644 --- a/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift +++ b/FirebaseAuth/Tests/Sample/SwiftApiTests/EmailPasswordTests.swift @@ -21,7 +21,7 @@ import XCTest class EmailPasswordTests: TestsBase { ///** The testing email address for testCreateAccountWithEmailAndPassword. */ - let kNewEmailToCreateUser = "paulswift200925@example.com" + let kNewEmailToCreateUser = "user+email_new_user@example.com" ///** The testing email address for testSignInExistingUserWithEmailAndPassword. */ let kExistingEmailToSignIn = "user+email_existing_user@example.com" From ba3f34a0da19507b095fffba3320162f1fb13aa8 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 28 Sep 2020 11:18:16 -0700 Subject: [PATCH 10/10] Restore docs --- docs/firebase_in_libraries.md | 87 +++++ .../firebase_from_dynamic_framework.svg | 356 ++++++++++++++++++ .../firebase_from_static_framework.svg | 312 +++++++++++++++ 3 files changed, 755 insertions(+) create mode 100644 docs/firebase_in_libraries.md create mode 100644 docs/resources/firebase_from_dynamic_framework.svg create mode 100644 docs/resources/firebase_from_static_framework.svg diff --git a/docs/firebase_in_libraries.md b/docs/firebase_in_libraries.md new file mode 100644 index 00000000000..d936df52223 --- /dev/null +++ b/docs/firebase_in_libraries.md @@ -0,0 +1,87 @@ +# Introduction + +Before the 7.0 release, all source code and binary Firebase SDKs are compiled as static frameworks. +With Firebase 7.0, CocoaPods developers can control whether Firebase is linked statically or +dynamically from the `Podfile`. Use the Podfile option `use_frameworks! :linkage => :static` to get +the Firebase 6.x linkage behavior. + +The zip and Carthage distributions continue to only be built for static linking. + +The Swift Package Manager distribution follows the Swift Package Manager defaults which is +currently static linking. + +# Using Firebase from a framework or a library + + Most of the time you’ll link Firebase frameworks directly to your app targets, but in some cases +it makes sense to link Firebase frameworks to your app indirectly, from another library or +another framework. This note talks about some of the pitfalls of this technique, almost +always hard-to-debug undefined behaviors, that all come down to code duplication and how +static and dynamic linking work. Your framework itself may be either static or dynamic. Let's +consider these two options in more detail. + +## Using Firebase SDKs from dynamic frameworks + +A dynamic framework is a bundle containing dynamic libraries and other resources. The dynamic +libraries bundled in the framework can themselves be compiled from static libraries, like +Firebase core and product libraries, meaning all of the symbols of the static libraries are +part of the dynamic framework bundle. What if your core app already directly links a static +Firebase library when you link to the same library indirectly from a dynamic framework? Well, +you end up with duplicate symbols in your app. This leads to undefined behavior (especially +when different versions of the static framework are linked to the app and the dynamic framework). +For example, a `dispatch_once` may or may not perform the correct initialization since there +are now two entities to initialize. Here are a couple more examples of issues related to this +undefined behavior: +[#4315](https://github.com/firebase/firebase-ios-sdk/issues/4315), +[#5152](https://github.com/firebase/firebase-ios-sdk/issues/5152). + +In this case you will most likely see warnings like the following in the console: + +```text +objc[40943]: Class FIRApp is implemented in both +~/Library/Developer/Xcode/DerivedData/FrameworkTest-apqjxlyrxvkbhhafhaypsbdquref/Build/Products/Debug-iphonesimulator/DynamicFramework.framework/DynamicFramework +(0x10b2a87f8) and +~/Library/Developer/CoreSimulator/Devices/4821F959-24A6-4D78-A102-4C5703103D99/data/Containers/Bundle/Application/F017D210-113A-4DAF-9E17-BDE455E71E06/FrameworkTest.app/FrameworkTest +(0x10ad2d348). One of the two will be used. Which one is undefined. +``` + +This commonly leads to a crash with the following error message: + +`The default FirebaseApp instance must be configured before the defaultFirebaseApp instance can be initialized.` + + + +**Figure 1: Using Firebase SDKs from dynamic framework** + +### Conclusions: + +- The Firebase SDKs may be used from an embedded dynamic framework in your project (e.g. for +code reuse purposes) only when Firebase is not used from the app directly. +- The Firebase SDKs should never be used from vendor dynamic frameworks because the version of +Firebase compiled into the dynamic framework will conflict with the versions compiled into the +app or included in any app bundles. + +## Using Firebase SDKs from static frameworks + +A static framework is a bundle containing static libraries and other resources. When a static +binary is built, it is not necessary to link any static or dynamic dependencies into the binary +because presence of the dependencies will be verified when the whole app is linked. This means +that both the static framework/library and your app will be able to "share" symbols (which is +basically what we need). + +The main downside of this approach arises when the static framework using Firebase is used +from, for example, an app and its extension. In this case, in contrast to a dynamic embedded framework, +a copy of the static framework will be added to both the app and each extension. It doesn't +lead to any symbol collisions, but it does increase the download size of your app. + + + +**Figure 2: Firebase SDKs from static framework** + +### Conclusions: + +- Using the Firebase SDKs in static frameworks and libraries is safe, meaning there will be +no symbol collisions, for both vendor and in-app internal libraries. +- If the static framework is used from an app and its extensions, then it will be copied to +each target, increasing the app download size. +- If the static framework is used from an app and its dynamic dependencies, then it will be +copied in both the app and its dependencies, which will result in undefined behavior. diff --git a/docs/resources/firebase_from_dynamic_framework.svg b/docs/resources/firebase_from_dynamic_framework.svg new file mode 100644 index 00000000000..8a329a6e89e --- /dev/null +++ b/docs/resources/firebase_from_dynamic_framework.svg @@ -0,0 +1,356 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/resources/firebase_from_static_framework.svg b/docs/resources/firebase_from_static_framework.svg new file mode 100644 index 00000000000..7d87469a3ac --- /dev/null +++ b/docs/resources/firebase_from_static_framework.svg @@ -0,0 +1,312 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +