firebase log level

This commit is contained in:
oscarz
2024-08-29 18:25:13 +08:00
parent 8500300d18
commit 27c160beaf
1165 changed files with 122916 additions and 1 deletions

View File

@ -0,0 +1,156 @@
/*
* 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 <FirebaseCore/FIRApp.h>
@class FIRComponentContainer;
@class FIRHeartbeatLogger;
@protocol FIRLibrary;
/**
* The internal interface to `FirebaseApp`. This is meant for first-party integrators, who need to
* receive `FirebaseApp` notifications, log info about the success or failure of their
* configuration, and access other internal functionality of `FirebaseApp`.
*/
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, FIRConfigType) {
FIRConfigTypeCore = 1,
FIRConfigTypeSDK = 2,
};
extern NSString *const kFIRDefaultAppName;
extern NSString *const kFIRAppReadyToConfigureSDKNotification;
extern NSString *const kFIRAppDeleteNotification;
extern NSString *const kFIRAppIsDefaultAppKey;
extern NSString *const kFIRAppNameKey;
extern NSString *const kFIRGoogleAppIDKey;
extern NSString *const kFirebaseCoreErrorDomain;
/**
* The format string for the `UserDefaults` key used for storing the data collection enabled flag.
* This includes formatting to append the `FirebaseApp`'s name.
*/
extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
/**
* The plist key used for storing the data collection enabled flag.
*/
extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey;
/** @var FirebaseAuthStateDidChangeInternalNotification
@brief The name of the @c NotificationCenter notification which is posted when the auth state
changes (e.g. a new token has been produced, a user logs in or out). The object parameter of
the notification is a dictionary possibly containing the key:
@c FirebaseAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not
contain this key it indicates a sign-out event took place.
*/
extern NSString *const FIRAuthStateDidChangeInternalNotification;
/** @var FirebaseAuthStateDidChangeInternalNotificationTokenKey
@brief A key present in the dictionary object parameter of the
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
key will contain the new access token.
*/
extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey;
/** @var FirebaseAuthStateDidChangeInternalNotificationAppKey
@brief A key present in the dictionary object parameter of the
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
key will contain the FirebaseApp associated with the auth instance.
*/
extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey;
/** @var FirebaseAuthStateDidChangeInternalNotificationUIDKey
@brief A key present in the dictionary object parameter of the
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
key will contain the new user's UID (or nil if there is no longer a user signed in).
*/
extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
@interface FIRApp ()
/**
* A flag indicating if this is the default app (has the default app name).
*/
@property(nonatomic, readonly) BOOL isDefaultApp;
/**
* The container of interop SDKs for this app.
*/
@property(nonatomic) FIRComponentContainer *container;
/**
* The heartbeat logger associated with this app.
*
* Firebase apps have a 1:1 relationship with heartbeat loggers.
*/
@property(readonly) FIRHeartbeatLogger *heartbeatLogger;
/**
* Checks if the default app is configured without trying to configure it.
*/
+ (BOOL)isDefaultAppConfigured;
/**
* Registers a given third-party library with the given version number to be reported for
* analytics.
*
* @param name Name of the library.
* @param version Version of the library.
*/
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version;
/**
* Registers a given internal library to be reported for analytics.
*
* @param library Optional parameter for component registration.
* @param name Name of the library.
*/
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name;
/**
* Registers a given internal library with the given version number to be reported for
* analytics. This should only be used for non-Firebase libraries that have their own versioning
* scheme.
*
* @param library Optional parameter for component registration.
* @param name Name of the library.
* @param version Version of the library.
*/
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name
withVersion:(nonnull NSString *)version;
/**
* A concatenated string representing all the third-party libraries and version numbers.
*/
+ (NSString *)firebaseUserAgent;
/**
* Can be used by the unit tests in each SDK to reset `FirebaseApp`. This method is thread unsafe.
*/
+ (void)resetApps;
/**
* Can be used by the unit tests in each SDK to set customized options.
*/
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,84 @@
/*
* Copyright 2018 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 <Foundation/Foundation.h>
@class FIRApp;
@class FIRComponentContainer;
NS_ASSUME_NONNULL_BEGIN
/// Provides a system to clean up cached instances returned from the component system.
NS_SWIFT_NAME(ComponentLifecycleMaintainer)
@protocol FIRComponentLifecycleMaintainer
/// The associated app will be deleted, clean up any resources as they are about to be deallocated.
- (void)appWillBeDeleted:(FIRApp *)app;
@end
typedef _Nullable id (^FIRComponentCreationBlock)(FIRComponentContainer *container,
BOOL *isCacheable)
NS_SWIFT_NAME(ComponentCreationBlock);
/// Describes the timing of instantiation. Note: new components should default to lazy unless there
/// is a strong reason to be eager.
typedef NS_ENUM(NSInteger, FIRInstantiationTiming) {
FIRInstantiationTimingLazy,
FIRInstantiationTimingAlwaysEager,
FIRInstantiationTimingEagerInDefaultApp
} NS_SWIFT_NAME(InstantiationTiming);
/// A component that can be used from other Firebase SDKs.
NS_SWIFT_NAME(Component)
@interface FIRComponent : NSObject
/// The protocol describing functionality provided from the `Component`.
@property(nonatomic, strong, readonly) Protocol *protocol;
/// The timing of instantiation.
@property(nonatomic, readonly) FIRInstantiationTiming instantiationTiming;
/// A block to instantiate an instance of the component with the appropriate dependencies.
@property(nonatomic, copy, readonly) FIRComponentCreationBlock creationBlock;
// There's an issue with long NS_SWIFT_NAMES that causes compilation to fail, disable clang-format
// for the next two methods.
// clang-format off
/// Creates a component with no dependencies that will be lazily initialized.
+ (instancetype)componentWithProtocol:(Protocol *)protocol
creationBlock:(FIRComponentCreationBlock)creationBlock
NS_SWIFT_NAME(init(_:creationBlock:));
/// Creates a component to be registered with the component container.
///
/// @param protocol - The protocol describing functionality provided by the component.
/// @param instantiationTiming - When the component should be initialized. Use .lazy unless there's
/// a good reason to be instantiated earlier.
/// @param creationBlock - A block to instantiate the component with a container, and if
/// @return A component that can be registered with the component container.
+ (instancetype)componentWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
creationBlock:(FIRComponentCreationBlock)creationBlock
NS_SWIFT_NAME(init(_:instantiationTiming:creationBlock:));
// clang-format on
/// Unavailable.
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// A type-safe macro to retrieve a component from a container. This should be used to retrieve
/// components instead of using the container directly.
#define FIR_COMPONENT(type, container) \
[FIRComponentType<id<type>> instanceForProtocol:@protocol(type) inContainer:container]
@class FIRApp;
/// A container that holds different components that are registered via the
/// `registerAsComponentRegistrant` call. These classes should conform to `ComponentRegistrant`
/// in order to properly register components for Core.
NS_SWIFT_NAME(FirebaseComponentContainer)
@interface FIRComponentContainer : NSObject
/// A weak reference to the app that an instance of the container belongs to.
@property(nonatomic, weak, readonly) FIRApp *app;
// TODO: See if we can get improved type safety here.
/// A Swift only API for fetching an instance since the top macro isn't available.
- (nullable id)__instanceForProtocol:(Protocol *)protocol NS_SWIFT_NAME(instance(for:));
/// Unavailable. Use the `container` property on `FirebaseApp`.
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,35 @@
/*
* Copyright 2018 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 <Foundation/Foundation.h>
@class FIRComponentContainer;
NS_ASSUME_NONNULL_BEGIN
/// Do not use directly. A placeholder type in order to provide a macro that will warn users of
/// mis-matched protocols.
NS_SWIFT_NAME(ComponentType)
@interface FIRComponentType<__covariant T> : NSObject
/// Do not use directly. A factory method to retrieve an instance that provides a specific
/// functionality.
+ (nullable T)instanceForProtocol:(Protocol *)protocol
inContainer:(FIRComponentContainer *)container;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,90 @@
// Copyright 2021 Google LLC
//
// 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#ifndef FIREBASE_BUILD_CMAKE
@class FIRHeartbeatsPayload;
#endif // FIREBASE_BUILD_CMAKE
/// Enum representing different daily heartbeat codes.
/// This enum is only used by clients using platform logging V1. This is because
/// the V1 payload only supports a single daily heartbeat.
typedef NS_ENUM(NSInteger, FIRDailyHeartbeatCode) {
/// Represents the absence of a daily heartbeat.
FIRDailyHeartbeatCodeNone = 0,
/// Represents the presence of a daily heartbeat.
FIRDailyHeartbeatCodeSome = 2,
};
@protocol FIRHeartbeatLoggerProtocol <NSObject>
/// Asynchronously logs a heartbeat.
- (void)log;
#ifndef FIREBASE_BUILD_CMAKE
/// Return the headerValue for the HeartbeatLogger.
- (NSString *_Nullable)headerValue;
#endif // FIREBASE_BUILD_CMAKE
/// Gets the heartbeat code for today.
- (FIRDailyHeartbeatCode)heartbeatCodeForToday;
@end
#ifndef FIREBASE_BUILD_CMAKE
/// Returns a nullable string header value from a given heartbeats payload.
///
/// This API returns `nil` when the given heartbeats payload is considered empty.
///
/// @param heartbeatsPayload The heartbeats payload.
NSString *_Nullable FIRHeaderValueFromHeartbeatsPayload(FIRHeartbeatsPayload *heartbeatsPayload);
#endif // FIREBASE_BUILD_CMAKE
/// A thread safe, synchronized object that logs and flushes platform logging info.
@interface FIRHeartbeatLogger : NSObject <FIRHeartbeatLoggerProtocol>
/// Designated initializer.
///
/// @param appID The app ID that this heartbeat logger corresponds to.
- (instancetype)initWithAppID:(NSString *)appID;
/// Asynchronously logs a new heartbeat corresponding to the Firebase User Agent, if needed.
///
/// @note This API is thread-safe.
- (void)log;
#ifndef FIREBASE_BUILD_CMAKE
/// Flushes heartbeats from storage into a structured payload of heartbeats.
///
/// This API is for clients using platform logging V2.
///
/// @note This API is thread-safe.
/// @return A payload of heartbeats.
- (FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload;
#endif // FIREBASE_BUILD_CMAKE
/// Gets today's corresponding heartbeat code.
///
/// This API is for clients using platform logging V1.
///
/// @note This API is thread-safe.
/// @return Heartbeat code indicating whether or not there is an unsent global heartbeat.
- (FIRDailyHeartbeatCode)heartbeatCodeForToday;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,39 @@
/*
* Copyright 2018 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.
*/
#ifndef FIRLibrary_h
#define FIRLibrary_h
#import <Foundation/Foundation.h>
@class FIRApp;
@class FIRComponent;
NS_ASSUME_NONNULL_BEGIN
/// Provide an interface to register a library for userAgent logging and availability to others.
NS_SWIFT_NAME(Library)
@protocol FIRLibrary
/// Returns one or more Components that will be registered in
/// FirebaseApp and participate in dependency resolution and injection.
+ (NSArray<FIRComponent *> *)componentsToRegister;
@end
NS_ASSUME_NONNULL_END
#endif /* FIRLibrary_h */

View File

@ -0,0 +1,148 @@
/*
* 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 <Foundation/Foundation.h>
#import <FirebaseCore/FIRLoggerLevel.h>
NS_ASSUME_NONNULL_BEGIN
/**
* The Firebase services used in Firebase logger.
*/
typedef NSString *const FIRLoggerService;
extern NSString *const kFIRLoggerAnalytics;
extern NSString *const kFIRLoggerCrash;
extern NSString *const kFIRLoggerCore;
extern NSString *const kFIRLoggerRemoteConfig;
/**
* The key used to store the logger's error count.
*/
extern NSString *const kFIRLoggerErrorCountKey;
/**
* The key used to store the logger's warning count.
*/
extern NSString *const kFIRLoggerWarningCountKey;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Enables or disables Analytics debug mode.
* If set to true, the logging level for Analytics will be set to FirebaseLoggerLevelDebug.
* Enabling the debug mode has no effect if the app is running from App Store.
* (required) analytics debug mode flag.
*/
void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode);
/**
* Gets the current FIRLoggerLevel.
*/
FIRLoggerLevel FIRGetLoggerLevel(void);
/**
* Changes the default logging level of FirebaseLoggerLevelNotice to a user-specified level.
* The default level cannot be set above FirebaseLoggerLevelNotice if the app is running from App
* Store. (required) log level (one of the FirebaseLoggerLevel enum values).
*/
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
/**
* Checks if the specified logger level is loggable given the current settings.
* (required) log level (one of the FirebaseLoggerLevel enum values).
* (required) whether or not this function is called from the Analytics component.
*/
BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent);
/**
* Logs a message to the Xcode console and the device log. If running from AppStore, will
* not log any messages with a level higher than FirebaseLoggerLevelNotice to avoid log spamming.
* (required) log level (one of the FirebaseLoggerLevel enum values).
* (required) service name of type FirebaseLoggerService.
* (required) message code starting with "I-" which means iOS, followed by a capitalized
* three-character service identifier and a six digit integer message ID that is unique
* within the service.
* An example of the message code is @"I-COR000001".
* (required) message string which can be a format string.
* (optional) variable arguments list obtained from calling va_start, used when message is a format
* string.
*/
extern void FIRLogBasic(FIRLoggerLevel level,
NSString *category,
NSString *messageCode,
NSString *message,
// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
// See: http://stackoverflow.com/q/29095469
#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
va_list args_ptr
#else
va_list _Nullable args_ptr
#endif
);
/**
* The following functions accept the following parameters in order:
* (required) service name of type FirebaseLoggerService.
* (required) message code starting from "I-" which means iOS, followed by a capitalized
* three-character service identifier and a six digit integer message ID that is unique
* within the service.
* An example of the message code is @"I-COR000001".
* See go/firebase-log-proposal for details.
* (required) message string which can be a format string.
* (optional) the list of arguments to substitute into the format string.
* Example usage:
* FirebaseLogError(kFirebaseLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
*/
extern void FIRLogError(NSString *category, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogWarning(NSString *category, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogNotice(NSString *category, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogInfo(NSString *category, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogDebug(NSString *category, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
NS_SWIFT_NAME(FirebaseLogger)
@interface FIRLoggerWrapper : NSObject
/// Logs a given message at a given log level.
///
/// - Parameters:
/// - level: The log level to use (defined by `FirebaseLoggerLevel` enum values).
/// - service: The service name of type `FirebaseLoggerService`.
/// - code: The message code. Starting with "I-" which means iOS, followed by a capitalized
/// three-character service identifier and a six digit integer message ID that is unique within
/// the service. An example of the message code is @"I-COR000001".
/// - message: Formatted string to be used as the log's message.
+ (void)logWithLevel:(FIRLoggerLevel)level
service:(NSString *)category
code:(NSString *)code
message:(NSString *)message
__attribute__((__swift_name__("log(level:service:code:message:)")));
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,106 @@
/*
* 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 <FirebaseCore/FIROptions.h>
/**
* Keys for the strings in the plist file.
*/
extern NSString *const kFIRAPIKey;
extern NSString *const kFIRTrackingID;
extern NSString *const kFIRGoogleAppID;
extern NSString *const kFIRClientID;
extern NSString *const kFIRGCMSenderID;
extern NSString *const kFIRAndroidClientID;
extern NSString *const kFIRDatabaseURL;
extern NSString *const kFIRStorageBucket;
extern NSString *const kFIRBundleID;
extern NSString *const kFIRProjectID;
/**
* Keys for the plist file name
*/
extern NSString *const kServiceInfoFileName;
extern NSString *const kServiceInfoFileType;
/**
* This header file exposes the initialization of FirebaseOptions to internal use.
*/
@interface FIROptions ()
/**
* `resetDefaultOptions` and `initInternalWithOptionsDictionary` are exposed only for unit tests.
*/
+ (void)resetDefaultOptions;
/**
* Initializes the options with dictionary. The above strings are the keys of the dictionary.
* This is the designated initializer.
*/
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary
NS_DESIGNATED_INITIALIZER;
/**
* `defaultOptions` and `defaultOptionsDictionary` are exposed in order to be used in FirebaseApp
* and other first party services.
*/
+ (FIROptions *)defaultOptions;
+ (NSDictionary *)defaultOptionsDictionary;
/**
* Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at
* runtime.
*/
@property(nonatomic, readonly) BOOL isAnalyticsCollectionExplicitlySet;
/**
* Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless
* explicitly disabled in GoogleService-Info.plist.
*/
@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled;
/**
* Whether or not Analytics Collection was completely disabled. If true, then
* isAnalyticsCollectionEnabled will be false.
*/
@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated;
/**
* The version ID of the client library, e.g. @"1100000".
*/
@property(nonatomic, readonly, copy) NSString *libraryVersionID;
/**
* The flag indicating whether this object was constructed with the values in the default plist
* file.
*/
@property(nonatomic) BOOL usingOptionsFromDefaultPlist;
/**
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
* GoogleService-Info.plist.
*/
@property(nonatomic, readonly) BOOL isMeasurementEnabled;
/**
* Whether or not editing is locked. This should occur after `FirebaseOptions` has been set on a
* `FirebaseApp`.
*/
@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
@end

View File

@ -0,0 +1,24 @@
// Copyright 2020 Google LLC
//
// 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 FirebaseCore;
#import "FIRAppInternal.h"
#import "FIRComponent.h"
#import "FIRComponentContainer.h"
#import "FIRComponentType.h"
#import "FIRHeartbeatLogger.h"
#import "FIRLibrary.h"
#import "FIRLogger.h"
#import "FIROptionsInternal.h"

View File

@ -0,0 +1,56 @@
/*
* 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 <Foundation/Foundation.h>
/// Values stored in analyticsEnabledState. Never alter these constants since they must match with
/// values persisted to disk.
typedef NS_ENUM(int64_t, FIRAnalyticsEnabledState) {
// 0 is the default value for keys not found stored in persisted config, so it cannot represent
// kFIRAnalyticsEnabledStateSetNo. It must represent kFIRAnalyticsEnabledStateNotSet.
kFIRAnalyticsEnabledStateNotSet = 0,
kFIRAnalyticsEnabledStateSetYes = 1,
kFIRAnalyticsEnabledStateSetNo = 2,
};
/// The user defaults key for the persisted measurementEnabledState value. FIRAPersistedConfig reads
/// measurementEnabledState using this same key.
static NSString *const kFIRAPersistedConfigMeasurementEnabledStateKey =
@"/google/measurement/measurement_enabled_state";
static NSString *const kFIRAnalyticsConfigurationSetEnabledNotification =
@"FIRAnalyticsConfigurationSetEnabledNotification";
static NSString *const kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification =
@"FIRAnalyticsConfigurationSetMinimumSessionIntervalNotification";
static NSString *const kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification =
@"FIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification";
@interface FIRAnalyticsConfiguration : NSObject
/// Returns the shared instance of FIRAnalyticsConfiguration.
+ (FIRAnalyticsConfiguration *)sharedInstance;
// Sets whether analytics collection is enabled for this app on this device. This setting is
// persisted across app sessions. By default it is enabled.
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled;
/// Sets whether analytics collection is enabled for this app on this device, and a flag to persist
/// the value or not. The setting should not be persisted if being set by the global data collection
/// flag.
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
persistSetting:(BOOL)shouldPersist;
@end

View File

@ -0,0 +1,62 @@
// 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 <Foundation/Foundation.h>
#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
@implementation FIRAnalyticsConfiguration
#pragma clang diagnostic pop
+ (FIRAnalyticsConfiguration *)sharedInstance {
static FIRAnalyticsConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FIRAnalyticsConfiguration alloc] init];
});
return sharedInstance;
}
- (void)postNotificationName:(NSString *)name value:(id)value {
if (!name.length || !value) {
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:name
object:self
userInfo:@{name : value}];
}
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled {
[self setAnalyticsCollectionEnabled:analyticsCollectionEnabled persistSetting:YES];
}
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
persistSetting:(BOOL)shouldPersist {
// Persist the measurementEnabledState. Use FIRAnalyticsEnabledState values instead of YES/NO.
FIRAnalyticsEnabledState analyticsEnabledState =
analyticsCollectionEnabled ? kFIRAnalyticsEnabledStateSetYes : kFIRAnalyticsEnabledStateSetNo;
if (shouldPersist) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@(analyticsEnabledState)
forKey:kFIRAPersistedConfigMeasurementEnabledStateKey];
[userDefaults synchronize];
}
[self postNotificationName:kFIRAnalyticsConfigurationSetEnabledNotification
value:@(analyticsCollectionEnabled)];
}
@end

View File

@ -0,0 +1,896 @@
// 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.
#include <sys/utsname.h>
#if __has_include(<UIKit/UIKit.h>)
#import <UIKit/UIKit.h>
#endif
#if __has_include(<AppKit/AppKit.h>)
#import <AppKit/AppKit.h>
#endif
#if __has_include(<WatchKit/WatchKit.h>)
#import <WatchKit/WatchKit.h>
#endif
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRApp.h"
#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h"
#import "FirebaseCore/Sources/FIRBundleUtil.h"
#import "FirebaseCore/Sources/FIRComponentContainerInternal.h"
#import "FirebaseCore/Sources/FIRConfigurationInternal.h"
#import "FirebaseCore/Sources/FIRFirebaseUserAgent.h"
#import "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseCore/Extension/FIRHeartbeatLogger.h"
#import "FirebaseCore/Extension/FIRLibrary.h"
#import "FirebaseCore/Extension/FIRLogger.h"
#import "FirebaseCore/Extension/FIROptionsInternal.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#import <objc/runtime.h>
NSString *const kFIRDefaultAppName = @"__FIRAPP_DEFAULT";
NSString *const kFIRAppReadyToConfigureSDKNotification = @"FIRAppReadyToConfigureSDKNotification";
NSString *const kFIRAppDeleteNotification = @"FIRAppDeleteNotification";
NSString *const kFIRAppIsDefaultAppKey = @"FIRAppIsDefaultAppKey";
NSString *const kFIRAppNameKey = @"FIRAppNameKey";
NSString *const kFIRGoogleAppIDKey = @"FIRGoogleAppIDKey";
NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat =
@"/google/firebase/global_data_collection_enabled:%@";
NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey =
@"FirebaseDataCollectionDefaultEnabled";
NSString *const kFIRAppDiagnosticsConfigurationTypeKey = @"ConfigType";
NSString *const kFIRAppDiagnosticsErrorKey = @"Error";
NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRApp";
NSString *const kFIRAppDiagnosticsSDKNameKey = @"SDKName";
NSString *const kFIRAppDiagnosticsSDKVersionKey = @"SDKVersion";
NSString *const kFIRAppDiagnosticsApplePlatformPrefix = @"apple-platform";
// Auth internal notification notification and key.
NSString *const FIRAuthStateDidChangeInternalNotification =
@"FIRAuthStateDidChangeInternalNotification";
NSString *const FIRAuthStateDidChangeInternalNotificationAppKey =
@"FIRAuthStateDidChangeInternalNotificationAppKey";
NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey =
@"FIRAuthStateDidChangeInternalNotificationTokenKey";
NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey =
@"FIRAuthStateDidChangeInternalNotificationUIDKey";
/**
* Error domain for exceptions and NSError construction.
*/
NSString *const kFirebaseCoreErrorDomain = @"com.firebase.core";
/**
* The URL to download plist files.
*/
static NSString *const kPlistURL = @"https://console.firebase.google.com/";
@interface FIRApp ()
#ifdef DEBUG
@property(nonatomic) BOOL alreadyOutputDataCollectionFlag;
#endif // DEBUG
@end
@implementation FIRApp
// This is necessary since our custom getter prevents `_options` from being created.
@synthesize options = _options;
static NSMutableDictionary *sAllApps;
static FIRApp *sDefaultApp;
+ (void)configure {
FIROptions *options = [FIROptions defaultOptions];
if (!options) {
#if DEBUG
[self findMisnamedGoogleServiceInfoPlist];
#endif // DEBUG
[NSException raise:kFirebaseCoreErrorDomain
format:@"`FirebaseApp.configure()` could not find "
@"a valid GoogleService-Info.plist in your project. Please download one "
@"from %@.",
kPlistURL];
}
[FIRApp configureWithOptions:options];
}
+ (void)configureWithOptions:(FIROptions *)options {
if (!options) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"Options is nil. Please pass a valid options."];
}
[FIRApp configureWithName:kFIRDefaultAppName options:options];
}
+ (NSCharacterSet *)applicationNameAllowedCharacters {
static NSCharacterSet *applicationNameAllowedCharacters;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableCharacterSet *allowedNameCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
[allowedNameCharacters addCharactersInString:@"-_"];
applicationNameAllowedCharacters = [allowedNameCharacters copy];
});
return applicationNameAllowedCharacters;
}
+ (void)configureWithName:(NSString *)name options:(FIROptions *)options {
if (!name || !options) {
[NSException raise:kFirebaseCoreErrorDomain format:@"Neither name nor options can be nil."];
}
if (name.length == 0) {
[NSException raise:kFirebaseCoreErrorDomain format:@"Name cannot be empty."];
}
if ([name isEqualToString:kFIRDefaultAppName]) {
if (sDefaultApp) {
// The default app already exists. Handle duplicate `configure` calls and return.
[self appWasConfiguredTwice:sDefaultApp usingOptions:options];
return;
}
FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configuring the default app.");
} else {
// Validate the app name and ensure it hasn't been configured already.
NSCharacterSet *nameCharacters = [NSCharacterSet characterSetWithCharactersInString:name];
if (![[self applicationNameAllowedCharacters] isSupersetOfSet:nameCharacters]) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"App name can only contain alphanumeric, "
@"hyphen (-), and underscore (_) characters"];
}
@synchronized(self) {
if (sAllApps && sAllApps[name]) {
// The app already exists. Handle a duplicate `configure` call and return.
[self appWasConfiguredTwice:sAllApps[name] usingOptions:options];
return;
}
}
FIRLogDebug(kFIRLoggerCore, @"I-COR000002", @"Configuring app named %@", name);
}
// Default instantiation, make sure we populate with Swift SDKs that can't register in time.
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self registerSwiftComponents];
});
@synchronized(self) {
FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
if (app.isDefaultApp) {
sDefaultApp = app;
}
[FIRApp addAppToAppDictionary:app];
// The FIRApp instance is ready to go, `sDefaultApp` is assigned, other SDKs are now ready to be
// instantiated.
[app.container instantiateEagerComponents];
[FIRApp sendNotificationsToSDKs:app];
}
}
/// Called when `configure` has been called multiple times for the same app. This can either throw
/// an exception (most cases) or ignore the duplicate configuration in situations where it's allowed
/// like an extension.
+ (void)appWasConfiguredTwice:(FIRApp *)app usingOptions:(FIROptions *)options {
// Only extensions should potentially be able to call `configure` more than once.
if (![GULAppEnvironmentUtil isAppExtension]) {
// Throw an exception since this is now an invalid state.
if (app.isDefaultApp) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"Default app has already been configured."];
} else {
[NSException raise:kFirebaseCoreErrorDomain
format:@"App named %@ has already been configured.", app.name];
}
}
// In an extension, the entry point could be called multiple times. As long as the options are
// identical we should allow multiple `configure` calls.
if ([options isEqual:app.options]) {
// Everything is identical but the extension's lifecycle triggered `configure` twice.
// Ignore duplicate calls and return since everything should still be in a valid state.
FIRLogDebug(kFIRLoggerCore, @"I-COR000035",
@"Ignoring second `configure` call in an extension.");
return;
} else {
[NSException raise:kFirebaseCoreErrorDomain
format:@"App named %@ has already been configured.", app.name];
}
}
+ (FIRApp *)defaultApp {
if (sDefaultApp) {
return sDefaultApp;
}
FIRLogError(kFIRLoggerCore, @"I-COR000003",
@"The default Firebase app has not yet been "
@"configured. Add `FirebaseApp.configure()` to your "
@"application initialization. This can be done in "
@"in the App Delegate's application(_:didFinishLaunchingWithOptions:)` "
@"(or the `@main` struct's initializer in SwiftUI). "
@"Read more: "
@"https://firebase.google.com/docs/ios/setup#initialize_firebase_in_your_app");
return nil;
}
+ (FIRApp *)appNamed:(NSString *)name {
@synchronized(self) {
if (sAllApps) {
FIRApp *app = sAllApps[name];
if (app) {
return app;
}
}
FIRLogError(kFIRLoggerCore, @"I-COR000004", @"App with name %@ does not exist.", name);
return nil;
}
}
+ (NSDictionary *)allApps {
@synchronized(self) {
if (!sAllApps) {
FIRLogError(kFIRLoggerCore, @"I-COR000005", @"No app has been configured yet.");
}
return [sAllApps copy];
}
}
// Public only for tests
+ (void)resetApps {
@synchronized(self) {
sDefaultApp = nil;
[sAllApps removeAllObjects];
sAllApps = nil;
[[self userAgent] reset];
}
}
- (void)deleteApp:(FIRAppVoidBoolCallback)completion {
@synchronized([self class]) {
if (sAllApps && sAllApps[self.name]) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000006", @"Deleting app named %@", self.name);
// Remove all registered libraries from the container to avoid creating new instances.
[self.container removeAllComponents];
// Remove all cached instances from the container before deleting the app.
[self.container removeAllCachedInstances];
[sAllApps removeObjectForKey:self.name];
[self clearDataCollectionSwitchFromUserDefaults];
if ([self.name isEqualToString:kFIRDefaultAppName]) {
sDefaultApp = nil;
}
NSDictionary *appInfoDict = @{kFIRAppNameKey : self.name};
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppDeleteNotification
object:[self class]
userInfo:appInfoDict];
completion(YES);
} else {
FIRLogError(kFIRLoggerCore, @"I-COR000007", @"App does not exist.");
completion(NO);
}
}
}
+ (void)addAppToAppDictionary:(FIRApp *)app {
if (!sAllApps) {
sAllApps = [NSMutableDictionary dictionary];
}
if ([app configureCore]) {
sAllApps[app.name] = app;
} else {
[NSException raise:kFirebaseCoreErrorDomain
format:@"Configuration fails. It may be caused by an invalid GOOGLE_APP_ID in "
@"GoogleService-Info.plist or set in the customized options."];
}
}
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options {
self = [super init];
if (self) {
_name = [name copy];
_options = [options copy];
_options.editingLocked = YES;
_isDefaultApp = [name isEqualToString:kFIRDefaultAppName];
_container = [[FIRComponentContainer alloc] initWithApp:self];
_heartbeatLogger = [[FIRHeartbeatLogger alloc] initWithAppID:self.options.googleAppID];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (BOOL)configureCore {
[self checkExpectedBundleID];
if (![self isAppIDValid]) {
return NO;
}
// Initialize the Analytics once there is a valid options under default app. Analytics should
// always initialize first by itself before the other SDKs.
if ([self.name isEqualToString:kFIRDefaultAppName]) {
Class firAnalyticsClass = NSClassFromString(@"FIRAnalytics");
if (firAnalyticsClass) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
SEL startWithConfigurationSelector = @selector(startWithConfiguration:options:);
#pragma clang diagnostic pop
if ([firAnalyticsClass respondsToSelector:startWithConfigurationSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[firAnalyticsClass performSelector:startWithConfigurationSelector
withObject:[FIRConfiguration sharedInstance].analyticsConfiguration
withObject:_options];
#pragma clang diagnostic pop
}
}
}
[self subscribeForAppDidBecomeActiveNotifications];
return YES;
}
- (FIROptions *)options {
return [_options copy];
}
- (void)setDataCollectionDefaultEnabled:(BOOL)dataCollectionDefaultEnabled {
#ifdef DEBUG
FIRLogDebug(kFIRLoggerCore, @"I-COR000034", @"Explicitly %@ data collection flag.",
dataCollectionDefaultEnabled ? @"enabled" : @"disabled");
self.alreadyOutputDataCollectionFlag = YES;
#endif // DEBUG
NSString *key =
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
[[NSUserDefaults standardUserDefaults] setBool:dataCollectionDefaultEnabled forKey:key];
// Core also controls the FirebaseAnalytics flag, so check if the Analytics flags are set
// within FIROptions and change the Analytics value if necessary. Analytics only works with the
// default app, so return if this isn't the default app.
if (!self.isDefaultApp) {
return;
}
// Check if the Analytics flag is explicitly set. If so, no further actions are necessary.
if ([self.options isAnalyticsCollectionExplicitlySet]) {
return;
}
// The Analytics flag has not been explicitly set, so update with the value being set.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[FIRAnalyticsConfiguration sharedInstance]
setAnalyticsCollectionEnabled:dataCollectionDefaultEnabled
persistSetting:NO];
#pragma clang diagnostic pop
}
- (BOOL)isDataCollectionDefaultEnabled {
// Check if it's been manually set before in code, and use that as the higher priority value.
NSNumber *defaultsObject = [[self class] readDataCollectionSwitchFromUserDefaultsForApp:self];
if (defaultsObject != nil) {
#ifdef DEBUG
if (!self.alreadyOutputDataCollectionFlag) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000031", @"Data Collection flag is %@ in user defaults.",
[defaultsObject boolValue] ? @"enabled" : @"disabled");
self.alreadyOutputDataCollectionFlag = YES;
}
#endif // DEBUG
return [defaultsObject boolValue];
}
// Read the Info.plist to see if the flag is set. If it's not set, it should default to `YES`.
// As per the implementation of `readDataCollectionSwitchFromPlist`, it's a cached value and has
// no performance impact calling multiple times.
NSNumber *collectionEnabledPlistValue = [[self class] readDataCollectionSwitchFromPlist];
if (collectionEnabledPlistValue != nil) {
#ifdef DEBUG
if (!self.alreadyOutputDataCollectionFlag) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000032", @"Data Collection flag is %@ in plist.",
[collectionEnabledPlistValue boolValue] ? @"enabled" : @"disabled");
self.alreadyOutputDataCollectionFlag = YES;
}
#endif // DEBUG
return [collectionEnabledPlistValue boolValue];
}
#ifdef DEBUG
if (!self.alreadyOutputDataCollectionFlag) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000033", @"Data Collection flag is not set.");
self.alreadyOutputDataCollectionFlag = YES;
}
#endif // DEBUG
return YES;
}
#pragma mark - private
+ (void)sendNotificationsToSDKs:(FIRApp *)app {
// TODO: Remove this notification once all SDKs are registered with `FIRCoreConfigurable`.
NSNumber *isDefaultApp = [NSNumber numberWithBool:app.isDefaultApp];
NSDictionary *appInfoDict = @{
kFIRAppNameKey : app.name,
kFIRAppIsDefaultAppKey : isDefaultApp,
kFIRGoogleAppIDKey : app.options.googleAppID
};
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppReadyToConfigureSDKNotification
object:self
userInfo:appInfoDict];
}
+ (NSError *)errorForMissingOptions {
NSDictionary *errorDict = @{
NSLocalizedDescriptionKey :
@"Unable to parse GoogleService-Info.plist in order to configure services.",
NSLocalizedRecoverySuggestionErrorKey :
@"Check formatting and location of GoogleService-Info.plist."
};
return [NSError errorWithDomain:kFirebaseCoreErrorDomain code:-100 userInfo:errorDict];
}
+ (NSError *)errorForInvalidAppID {
NSDictionary *errorDict = @{
NSLocalizedDescriptionKey : @"Unable to validate Google App ID",
NSLocalizedRecoverySuggestionErrorKey :
@"Check formatting and location of GoogleService-Info.plist or GoogleAppID set in the "
@"customized options."
};
return [NSError errorWithDomain:kFirebaseCoreErrorDomain code:-101 userInfo:errorDict];
}
+ (BOOL)isDefaultAppConfigured {
return (sDefaultApp != nil);
}
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version {
// Create the set of characters which aren't allowed, only if this feature is used.
NSMutableCharacterSet *allowedSet = [NSMutableCharacterSet alphanumericCharacterSet];
[allowedSet addCharactersInString:@"-_."];
NSCharacterSet *disallowedSet = [allowedSet invertedSet];
// Make sure the library name and version strings do not contain unexpected characters, and
// add the name/version pair to the dictionary.
if ([name rangeOfCharacterFromSet:disallowedSet].location == NSNotFound &&
[version rangeOfCharacterFromSet:disallowedSet].location == NSNotFound) {
[[self userAgent] setValue:version forComponent:name];
} else {
FIRLogError(kFIRLoggerCore, @"I-COR000027",
@"The library name (%@) or version number (%@) contain invalid characters. "
@"Only alphanumeric, dash, underscore and period characters are allowed.",
name, version);
}
}
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name {
[self registerInternalLibrary:library withName:name withVersion:FIRFirebaseVersion()];
}
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name
withVersion:(nonnull NSString *)version {
// This is called at +load time, keep the work to a minimum.
// Ensure the class given conforms to the proper protocol.
if (![(Class)library conformsToProtocol:@protocol(FIRLibrary)] ||
![(Class)library respondsToSelector:@selector(componentsToRegister)]) {
[NSException raise:NSInvalidArgumentException
format:@"Class %@ attempted to register components, but it does not conform to "
@"`FIRLibrary or provide a `componentsToRegister:` method.",
library];
}
[FIRComponentContainer registerAsComponentRegistrant:library];
[self registerLibrary:name withVersion:version];
}
+ (FIRFirebaseUserAgent *)userAgent {
static dispatch_once_t onceToken;
static FIRFirebaseUserAgent *_userAgent;
dispatch_once(&onceToken, ^{
_userAgent = [[FIRFirebaseUserAgent alloc] init];
[_userAgent setValue:FIRFirebaseVersion() forComponent:@"fire-ios"];
});
return _userAgent;
}
+ (NSString *)firebaseUserAgent {
return [[self userAgent] firebaseUserAgent];
}
- (void)checkExpectedBundleID {
NSArray *bundles = [FIRBundleUtil relevantBundles];
NSString *expectedBundleID = [self expectedBundleID];
// The checking is only done when the bundle ID is provided in the serviceInfo dictionary for
// backward compatibility.
if (expectedBundleID != nil && ![FIRBundleUtil hasBundleIdentifierPrefix:expectedBundleID
inBundles:bundles]) {
FIRLogError(kFIRLoggerCore, @"I-COR000008",
@"The project's Bundle ID is inconsistent with "
@"either the Bundle ID in '%@.%@', or the Bundle ID in the options if you are "
@"using a customized options. To ensure that everything can be configured "
@"correctly, you may need to make the Bundle IDs consistent. To continue with this "
@"plist file, you may change your app's bundle identifier to '%@'. Or you can "
@"download a new configuration file that matches your bundle identifier from %@ "
@"and replace the current one.",
kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
}
}
#pragma mark - private - App ID Validation
/**
* Validates the format of app ID and its included bundle ID hash contained in GOOGLE_APP_ID in the
* plist file. This is the main method for validating app ID.
*
* @return YES if the app ID fulfills the expected format and contains a hashed bundle ID, NO
* otherwise.
*/
- (BOOL)isAppIDValid {
NSString *appID = _options.googleAppID;
BOOL isValid = [FIRApp validateAppID:appID];
if (!isValid) {
NSString *expectedBundleID = [self expectedBundleID];
FIRLogError(kFIRLoggerCore, @"I-COR000009",
@"The GOOGLE_APP_ID either in the plist file "
@"'%@.%@' or the one set in the customized options is invalid. If you are using "
@"the plist file, use the iOS version of bundle identifier to download the file, "
@"and do not manually edit the GOOGLE_APP_ID. You may change your app's bundle "
@"identifier to '%@'. Or you can download a new configuration file that matches "
@"your bundle identifier from %@ and replace the current one.",
kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
};
return isValid;
}
+ (BOOL)validateAppID:(NSString *)appID {
// Failing validation only occurs when we are sure we are looking at a V2 app ID and it does not
// have a valid hashed bundle ID, otherwise we just warn about the potential issue.
if (!appID.length) {
return NO;
}
NSScanner *stringScanner = [NSScanner scannerWithString:appID];
stringScanner.charactersToBeSkipped = nil;
NSString *appIDVersion;
if (![stringScanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet]
intoString:&appIDVersion]) {
return NO;
}
if (![stringScanner scanString:@":" intoString:NULL]) {
// appIDVersion must be separated by ":"
return NO;
}
NSArray *knownVersions = @[ @"1" ];
if (![knownVersions containsObject:appIDVersion]) {
// Permit unknown yet properly formatted app ID versions.
FIRLogInfo(kFIRLoggerCore, @"I-COR000010", @"Unknown GOOGLE_APP_ID version: %@", appIDVersion);
return YES;
}
if (![self validateAppIDFormat:appID withVersion:appIDVersion]) {
return NO;
}
if (![self validateBundleIDHashWithinAppID:appID forVersion:appIDVersion]) {
return NO;
}
return YES;
}
+ (NSString *)actualBundleID {
return [[NSBundle mainBundle] bundleIdentifier];
}
/**
* Validates that the format of the app ID string is what is expected based on the supplied version.
* The version must end in ":".
*
* For v1 app ids the format is expected to be
* '<version #>:<project number>:ios:<hashed bundle id>'.
*
* This method does not verify that the contents of the app id are correct, just that they fulfill
* the expected format.
*
* @param appID Contents of GOOGLE_APP_ID from the plist file.
* @param version Indicates what version of the app id format this string should be.
* @return YES if provided string fulfills the expected format, NO otherwise.
*/
+ (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
if (!appID.length || !version.length) {
return NO;
}
NSScanner *stringScanner = [NSScanner scannerWithString:appID];
stringScanner.charactersToBeSkipped = nil;
// Skip version part
// '*<version #>*:<project number>:ios:<hashed bundle id>'
if (![stringScanner scanString:version intoString:NULL]) {
// The version part is missing or mismatched
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>*:*<project number>:ios:<hashed bundle id>'
if (![stringScanner scanString:@":" intoString:NULL]) {
// appIDVersion must be separated by ":"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:*<project number>*:ios:<hashed bundle id>'.
NSInteger projectNumber = NSNotFound;
if (![stringScanner scanInteger:&projectNumber]) {
// NO project number found.
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>*:*ios:<hashed bundle id>'.
if (![stringScanner scanString:@":" intoString:NULL]) {
// The project number must be separated by ":"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:*ios*:<hashed bundle id>'.
NSString *platform;
if (![stringScanner scanUpToString:@":" intoString:&platform]) {
return NO;
}
if (![platform isEqualToString:@"ios"]) {
// The platform must be @"ios"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:ios*:*<hashed bundle id>'.
if (![stringScanner scanString:@":" intoString:NULL]) {
// The platform must be separated by ":"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:ios:*<hashed bundle id>*'.
unsigned long long bundleIDHash = NSNotFound;
if (![stringScanner scanHexLongLong:&bundleIDHash]) {
// Hashed bundleID part is missing
return NO;
}
if (!stringScanner.isAtEnd) {
// There are not allowed characters in the hashed bundle ID part
return NO;
}
return YES;
}
/**
* Validates that the hashed bundle ID included in the app ID string is what is expected based on
* the supplied version.
*
* Note that the v1 hash algorithm is not permitted on the client and cannot be fully validated.
*
* @param appID Contents of GOOGLE_APP_ID from the plist file.
* @param version Indicates what version of the app id format this string should be.
* @return YES if provided string fulfills the expected hashed bundle ID and the version is known,
* NO otherwise.
*/
+ (BOOL)validateBundleIDHashWithinAppID:(NSString *)appID forVersion:(NSString *)version {
// Extract the hashed bundle ID from the given app ID.
// This assumes the app ID format is the same for all known versions below.
// If the app ID format changes in future versions, the tokenizing of the app
// ID format will need to take into account the version of the app ID.
NSArray *components = [appID componentsSeparatedByString:@":"];
if (components.count != 4) {
return NO;
}
NSString *suppliedBundleIDHashString = components[3];
if (!suppliedBundleIDHashString.length) {
return NO;
}
uint64_t suppliedBundleIDHash;
NSScanner *scanner = [NSScanner scannerWithString:suppliedBundleIDHashString];
if (![scanner scanHexLongLong:&suppliedBundleIDHash]) {
return NO;
}
if ([version isEqual:@"1"]) {
// The v1 hash algorithm is not permitted on the client so the actual hash cannot be validated.
return YES;
}
// Unknown version.
return NO;
}
- (NSString *)expectedBundleID {
return _options.bundleID;
}
// end App ID validation
#pragma mark - Reading From Plist & User Defaults
/**
* Clears the data collection switch from the standard NSUserDefaults for easier testing and
* readability.
*/
- (void)clearDataCollectionSwitchFromUserDefaults {
NSString *key =
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
/**
* Reads the data collection switch from the standard NSUserDefaults for easier testing and
* readability.
*/
+ (nullable NSNumber *)readDataCollectionSwitchFromUserDefaultsForApp:(FIRApp *)app {
// Read the object in user defaults, and only return if it's an NSNumber.
NSString *key =
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, app.name];
id collectionEnabledDefaultsObject = [[NSUserDefaults standardUserDefaults] objectForKey:key];
if ([collectionEnabledDefaultsObject isKindOfClass:[NSNumber class]]) {
return collectionEnabledDefaultsObject;
}
return nil;
}
/**
* Reads the data collection switch from the Info.plist for easier testing and readability. Will
* only read once from the plist and return the cached value.
*/
+ (nullable NSNumber *)readDataCollectionSwitchFromPlist {
static NSNumber *collectionEnabledPlistObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Read the data from the `Info.plist`, only assign it if it's there and an NSNumber.
id plistValue = [[NSBundle mainBundle]
objectForInfoDictionaryKey:kFIRGlobalAppDataCollectionEnabledPlistKey];
if (plistValue && [plistValue isKindOfClass:[NSNumber class]]) {
collectionEnabledPlistObject = (NSNumber *)plistValue;
}
});
return collectionEnabledPlistObject;
}
#pragma mark - Swift Components.
+ (void)registerSwiftComponents {
SEL componentsToRegisterSEL = @selector(componentsToRegister);
// Dictionary of class names that conform to `FIRLibrary` and their user agents. These should only
// be SDKs that are written in Swift but still visible to ObjC.
// This is only necessary for products that need to do work at launch during configuration.
NSDictionary<NSString *, NSString *> *swiftComponents = @{
@"FIRSessions" : @"fire-ses",
@"FIRAuthComponent" : @"fire-auth",
};
for (NSString *className in swiftComponents.allKeys) {
Class klass = NSClassFromString(className);
if (klass && [klass respondsToSelector:componentsToRegisterSEL]) {
[FIRApp registerInternalLibrary:klass withName:swiftComponents[className]];
}
}
// Swift libraries that don't need component behaviour
NSDictionary<NSString *, NSString *> *swiftLibraries = @{
@"FIRCombineAuthLibrary" : @"comb-auth",
@"FIRCombineFirestoreLibrary" : @"comb-firestore",
@"FIRCombineFunctionsLibrary" : @"comb-functions",
@"FIRCombineStorageLibrary" : @"comb-storage",
@"FIRFunctions" : @"fire-fun",
@"FIRStorage" : @"fire-str",
@"FIRVertexAIComponent" : @"fire-vertex",
};
for (NSString *className in swiftLibraries.allKeys) {
Class klass = NSClassFromString(className);
if (klass) {
[FIRApp registerLibrary:swiftLibraries[className] withVersion:FIRFirebaseVersion()];
}
}
}
#pragma mark - App Life Cycle
- (void)subscribeForAppDidBecomeActiveNotifications {
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
NSNotificationName notificationName = UIApplicationDidBecomeActiveNotification;
#elif TARGET_OS_OSX
NSNotificationName notificationName = NSApplicationDidBecomeActiveNotification;
#elif TARGET_OS_WATCH
// TODO(ncooke3): Remove when minimum supported watchOS version is watchOS 7.0.
// On watchOS 7.0+, heartbeats are logged when the watch app becomes active.
// On watchOS 6.0, heartbeats are logged when the Firebase app is configuring.
// While it does not cover all use cases, logging when the Firebase app is
// configuring is done because watchOS lifecycle notifications are a
// watchOS 7.0+ feature.
NSNotificationName notificationName = kFIRAppReadyToConfigureSDKNotification;
if (@available(watchOS 7.0, *)) {
notificationName = WKApplicationDidBecomeActiveNotification;
}
#endif
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidBecomeActive:)
name:notificationName
object:nil];
}
- (void)appDidBecomeActive:(NSNotification *)notification {
if ([self isDataCollectionDefaultEnabled]) {
// If changing the below line, consult with the Games team to ensure they
// are not negatively impacted. For more details, see
// go/firebase-game-sdk-user-agent-register-timing.
[self.heartbeatLogger log];
}
}
#if DEBUG
+ (void)findMisnamedGoogleServiceInfoPlist {
for (NSBundle *bundle in [NSBundle allBundles]) {
// Not recursive, but we're looking for misnames, not people accidentally
// hiding their config file in a subdirectory of their bundle.
NSArray *plistPaths = [bundle pathsForResourcesOfType:@"plist" inDirectory:nil];
for (NSString *path in plistPaths) {
@autoreleasepool {
NSDictionary<NSString *, id> *contents = [NSDictionary dictionaryWithContentsOfFile:path];
if (contents == nil) {
continue;
}
NSString *projectID = contents[@"PROJECT_ID"];
if (projectID != nil) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"`FirebaseApp.configure()` could not find the default "
@"configuration plist in your project, but did find one at "
@"%@. Please rename this file to GoogleService-Info.plist to "
@"use it as the default configuration.",
path];
}
}
}
}
}
#endif // DEBUG
@end

View File

@ -0,0 +1,53 @@
/*
* 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 <Foundation/Foundation.h>
/**
* This class provides utilities for accessing resources in bundles.
*/
@interface FIRBundleUtil : NSObject
/**
* Finds all relevant bundles, starting with [NSBundle mainBundle].
*/
+ (NSArray *)relevantBundles;
/**
* Reads the options dictionary from one of the provided bundles.
*
* @param resourceName The resource name, e.g. @"GoogleService-Info".
* @param fileType The file type (extension), e.g. @"plist".
* @param bundles The bundles to expect, in priority order. See also
* +[FIRBundleUtil relevantBundles].
*/
+ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
andFileType:(NSString *)fileType
inBundles:(NSArray *)bundles;
/**
* Finds URL schemes defined in all relevant bundles, starting with those from
* [NSBundle mainBundle].
*/
+ (NSArray *)relevantURLSchemes;
/**
* Checks if any of the given bundles have a matching bundle identifier prefix (removing extension
* suffixes).
*/
+ (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles;
@end

View File

@ -0,0 +1,79 @@
// 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 "FirebaseCore/Sources/FIRBundleUtil.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
@implementation FIRBundleUtil
+ (NSArray *)relevantBundles {
return @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ];
}
+ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
andFileType:(NSString *)fileType
inBundles:(NSArray *)bundles {
// Loop through all bundles to find the config dict.
for (NSBundle *bundle in bundles) {
NSString *path = [bundle pathForResource:resourceName ofType:fileType];
// Use the first one we find.
if (path) {
return path;
}
}
return nil;
}
+ (NSArray *)relevantURLSchemes {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (NSBundle *bundle in [[self class] relevantBundles]) {
NSArray *urlTypes = [bundle objectForInfoDictionaryKey:@"CFBundleURLTypes"];
for (NSDictionary *urlType in urlTypes) {
[result addObjectsFromArray:urlType[@"CFBundleURLSchemes"]];
}
}
return result;
}
+ (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles {
for (NSBundle *bundle in bundles) {
if ([bundle.bundleIdentifier isEqualToString:bundleIdentifier]) {
return YES;
}
if ([GULAppEnvironmentUtil isAppExtension]) {
// A developer could be using the same `FIROptions` for both their app and extension. Since
// extensions have a suffix added to the bundleID, we consider a matching prefix as valid.
NSString *appBundleIDFromExtension =
[self bundleIdentifierByRemovingLastPartFrom:bundle.bundleIdentifier];
if ([appBundleIDFromExtension isEqualToString:bundleIdentifier]) {
return YES;
}
}
}
return NO;
}
+ (NSString *)bundleIdentifierByRemovingLastPartFrom:(NSString *)bundleIdentifier {
NSString *bundleIDComponentsSeparator = @".";
NSMutableArray<NSString *> *bundleIDComponents =
[[bundleIdentifier componentsSeparatedByString:bundleIDComponentsSeparator] mutableCopy];
[bundleIDComponents removeLastObject];
return [bundleIDComponents componentsJoinedByString:bundleIDComponentsSeparator];
}
@end

View File

@ -0,0 +1,58 @@
/*
* Copyright 2018 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 "FirebaseCore/Extension/FIRComponent.h"
#import "FirebaseCore/Extension/FIRComponentContainer.h"
@interface FIRComponent ()
- (instancetype)initWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
creationBlock:(FIRComponentCreationBlock)creationBlock;
@end
@implementation FIRComponent
+ (instancetype)componentWithProtocol:(Protocol *)protocol
creationBlock:(FIRComponentCreationBlock)creationBlock {
return [[FIRComponent alloc] initWithProtocol:protocol
instantiationTiming:FIRInstantiationTimingLazy
creationBlock:creationBlock];
}
+ (instancetype)componentWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
creationBlock:(FIRComponentCreationBlock)creationBlock {
return [[FIRComponent alloc] initWithProtocol:protocol
instantiationTiming:instantiationTiming
creationBlock:creationBlock];
}
- (instancetype)initWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
creationBlock:(FIRComponentCreationBlock)creationBlock {
self = [super init];
if (self) {
_protocol = protocol;
_instantiationTiming = instantiationTiming;
_creationBlock = creationBlock;
}
return self;
}
@end

View File

@ -0,0 +1,251 @@
/*
* Copyright 2018 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 "FirebaseCore/Extension/FIRComponentContainer.h"
#import "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseCore/Extension/FIRComponent.h"
#import "FirebaseCore/Extension/FIRLibrary.h"
#import "FirebaseCore/Extension/FIRLogger.h"
#import "FirebaseCore/Extension/FIROptionsInternal.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRComponentContainer ()
/// The dictionary of components that are registered for a particular app. The key is an `NSString`
/// of the protocol.
@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRComponentCreationBlock> *components;
/// Cached instances of components that requested to be cached.
@property(nonatomic, strong) NSMutableDictionary<NSString *, id> *cachedInstances;
/// Protocols of components that have requested to be eagerly instantiated.
@property(nonatomic, strong, nullable) NSMutableArray<Protocol *> *eagerProtocolsToInstantiate;
@end
@implementation FIRComponentContainer
// Collection of all classes that register to provide components.
static NSMutableSet<Class> *sFIRComponentRegistrants;
#pragma mark - Public Registration
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sFIRComponentRegistrants = [[NSMutableSet<Class> alloc] init];
});
[self registerAsComponentRegistrant:klass inSet:sFIRComponentRegistrants];
}
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass
inSet:(NSMutableSet<Class> *)allRegistrants {
[allRegistrants addObject:klass];
}
#pragma mark - Internal Initialization
- (instancetype)initWithApp:(FIRApp *)app {
NSMutableSet<Class> *componentRegistrants = sFIRComponentRegistrants;
// If the app being created is for the ARCore SDK, remove the App Check
// component (if it exists) since it does not support App Check.
if ([self isAppForARCore:app]) {
Class klass = NSClassFromString(@"FIRAppCheckComponent");
if (klass && [sFIRComponentRegistrants containsObject:klass]) {
componentRegistrants = [componentRegistrants mutableCopy];
[componentRegistrants removeObject:klass];
}
}
return [self initWithApp:app registrants:componentRegistrants];
}
- (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)allRegistrants {
self = [super init];
if (self) {
_app = app;
_cachedInstances = [NSMutableDictionary<NSString *, id> dictionary];
_components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary];
[self populateComponentsFromRegisteredClasses:allRegistrants forApp:app];
}
return self;
}
- (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(FIRApp *)app {
// Keep track of any components that need to eagerly instantiate after all components are added.
self.eagerProtocolsToInstantiate = [[NSMutableArray alloc] init];
// Loop through the verified component registrants and populate the components array.
for (Class<FIRLibrary> klass in classes) {
// Loop through all the components being registered and store them as appropriate.
// Classes which do not provide functionality should use a dummy FIRComponentRegistrant
// protocol.
for (FIRComponent *component in [klass componentsToRegister]) {
// Check if the component has been registered before, and error out if so.
NSString *protocolName = NSStringFromProtocol(component.protocol);
if (self.components[protocolName]) {
FIRLogError(kFIRLoggerCore, @"I-COR000029",
@"Attempted to register protocol %@, but it already has an implementation.",
protocolName);
continue;
}
// Store the creation block for later usage.
self.components[protocolName] = component.creationBlock;
// Queue any protocols that should be eagerly instantiated. Don't instantiate them yet
// because they could depend on other components that haven't been added to the components
// array yet.
BOOL shouldInstantiateEager =
(component.instantiationTiming == FIRInstantiationTimingAlwaysEager);
BOOL shouldInstantiateDefaultEager =
(component.instantiationTiming == FIRInstantiationTimingEagerInDefaultApp &&
[app isDefaultApp]);
if (shouldInstantiateEager || shouldInstantiateDefaultEager) {
[self.eagerProtocolsToInstantiate addObject:component.protocol];
}
}
}
}
#pragma mark - Instance Creation
- (void)instantiateEagerComponents {
// After all components are registered, instantiate the ones that are requesting eager
// instantiation.
@synchronized(self) {
for (Protocol *protocol in self.eagerProtocolsToInstantiate) {
// Get an instance for the protocol, which will instantiate it since it couldn't have been
// cached yet. Ignore the instance coming back since we don't need it.
__unused id unusedInstance = [self instanceForProtocol:protocol];
}
// All eager instantiation is complete, clear the stored property now.
self.eagerProtocolsToInstantiate = nil;
}
}
/// Instantiate an instance of a class that conforms to the specified protocol.
/// This will:
/// - Call the block to create an instance if possible,
/// - Validate that the instance returned conforms to the protocol it claims to,
/// - Cache the instance if the block requests it
///
/// Note that this method assumes the caller already has @synchronized on self.
- (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
withBlock:(FIRComponentCreationBlock)creationBlock {
if (!creationBlock) {
return nil;
}
// Create an instance using the creation block.
BOOL shouldCache = NO;
id instance = creationBlock(self, &shouldCache);
if (!instance) {
return nil;
}
// An instance was created, validate that it conforms to the protocol it claims to.
NSString *protocolName = NSStringFromProtocol(protocol);
if (![instance conformsToProtocol:protocol]) {
FIRLogError(kFIRLoggerCore, @"I-COR000030",
@"An instance conforming to %@ was requested, but the instance provided does not "
@"conform to the protocol",
protocolName);
}
// The instance is ready to be returned, but check if it should be cached first before returning.
if (shouldCache) {
self.cachedInstances[protocolName] = instance;
}
return instance;
}
#pragma mark - Internal Retrieval
// Redirected for Swift users.
- (nullable id)__instanceForProtocol:(Protocol *)protocol {
return [self instanceForProtocol:protocol];
}
- (nullable id)instanceForProtocol:(Protocol *)protocol {
// Check if there is a cached instance, and return it if so.
NSString *protocolName = NSStringFromProtocol(protocol);
id cachedInstance;
@synchronized(self) {
cachedInstance = self.cachedInstances[protocolName];
if (!cachedInstance) {
// Use the creation block to instantiate an instance and return it.
FIRComponentCreationBlock creationBlock = self.components[protocolName];
cachedInstance = [self instantiateInstanceForProtocol:protocol withBlock:creationBlock];
}
}
return cachedInstance;
}
#pragma mark - Lifecycle
- (void)removeAllCachedInstances {
@synchronized(self) {
// Loop through the cache and notify each instance that is a maintainer to clean up after
// itself.
for (id instance in self.cachedInstances.allValues) {
if ([instance conformsToProtocol:@protocol(FIRComponentLifecycleMaintainer)] &&
[instance respondsToSelector:@selector(appWillBeDeleted:)]) {
[instance appWillBeDeleted:self.app];
}
}
// Empty the cache.
[self.cachedInstances removeAllObjects];
}
}
- (void)removeAllComponents {
@synchronized(self) {
[self.components removeAllObjects];
}
}
#pragma mark - Helpers
- (BOOL)isAppForARCore:(FIRApp *)app {
// First, check if the app name matches that of the one used by ARCore.
if ([app.name isEqualToString:@"ARCoreFIRApp"]) {
// Second, check if the app's gcmSenderID matches that of ARCore. This
// prevents false positives in the unlikely event a 3P Firebase app is
// named `ARCoreFIRApp`.
const char *p1 = "406756";
const char *p2 = "893798";
const char gcmSenderIDKey[27] = {p1[0], p2[0], p1[1], p2[1], p1[2], p2[2], p1[3],
p2[3], p1[4], p2[4], p1[5], p2[5], p1[6], p2[6],
p1[7], p2[7], p1[8], p2[8], p1[9], p2[9], p1[10],
p2[10], p1[11], p2[11], p1[12], p2[12], '\0'};
NSString *gcmSenderID = [NSString stringWithUTF8String:gcmSenderIDKey];
return [app.options.GCMSenderID isEqualToString:gcmSenderID];
}
return NO;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,50 @@
/*
* Copyright 2018 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 <Foundation/Foundation.h>
#import "FirebaseCore/Extension/FIRComponentContainer.h"
#import "FirebaseCore/Extension/FIRLibrary.h"
@class FIRApp;
NS_ASSUME_NONNULL_BEGIN
@interface FIRComponentContainer (Private)
/// Initializes a container for a given app. This should only be called by the app itself.
- (instancetype)initWithApp:(FIRApp *)app;
/// Retrieves an instance that conforms to the specified protocol. This will return `nil` if the
/// protocol wasn't registered, or if the instance couldn't be instantiated for the provided app.
- (nullable id)instanceForProtocol:(Protocol *)protocol
NS_SWIFT_UNAVAILABLE("Use `instance(for:)` from the FirebaseCoreExtension module instead.");
/// Instantiates all the components that have registered as "eager" after initialization.
- (void)instantiateEagerComponents;
/// Remove all of the cached instances stored and allow them to clean up after themselves.
- (void)removeAllCachedInstances;
/// Removes all the components. After calling this method no new instances will be created.
- (void)removeAllComponents;
/// Register a class to provide components for the interoperability system. The class should conform
/// to `FIRComponentRegistrant` and provide an array of `FIRComponent` objects.
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,29 @@
/*
* Copyright 2018 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 "FirebaseCore/Extension/FIRComponentType.h"
#import "FirebaseCore/Sources/FIRComponentContainerInternal.h"
@implementation FIRComponentType
+ (nullable id)instanceForProtocol:(Protocol *)protocol
inContainer:(FIRComponentContainer *)container {
// Forward the call to the container.
return [container instanceForProtocol:protocol];
}
@end

View File

@ -0,0 +1,46 @@
// 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 "FirebaseCore/Sources/FIRConfigurationInternal.h"
#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h"
extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
@implementation FIRConfiguration
+ (instancetype)sharedInstance {
static FIRConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FIRConfiguration alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
_analyticsConfiguration = [FIRAnalyticsConfiguration sharedInstance];
}
return self;
}
- (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel {
NSAssert(loggerLevel <= FIRLoggerLevelMax && loggerLevel >= FIRLoggerLevelMin,
@"Invalid logger level, %ld", (long)loggerLevel);
FIRSetLoggerLevel(loggerLevel);
}
@end

View File

@ -0,0 +1,29 @@
/*
* Copyright 2019 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 "FirebaseCore/Sources/Public/FirebaseCore/FIRConfiguration.h"
@class FIRAnalyticsConfiguration;
@interface FIRConfiguration ()
/**
* The configuration class for Firebase Analytics. This should be removed once the logic for
* enabling and disabling Analytics is moved to Analytics.
*/
@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration;
@end

View File

@ -0,0 +1,36 @@
/*
* Copyright 2020 Google LLC
*
* 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface FIRFirebaseUserAgent : NSObject
/** Returns the firebase user agent which consists of environment part and the components added via
* `setValue:forComponent` method. */
- (NSString *)firebaseUserAgent;
/** Sets value associated with the specified component. If value is `nil` then the component is
* removed. */
- (void)setValue:(nullable NSString *)value forComponent:(NSString *)componentName;
/** Resets manually added components. */
- (void)reset;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,107 @@
/*
* Copyright 2020 Google LLC
*
* 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 "FirebaseCore/Sources/FIRFirebaseUserAgent.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
@interface FIRFirebaseUserAgent ()
@property(nonatomic, readonly) NSMutableDictionary<NSString *, NSString *> *valuesByComponent;
@property(nonatomic, readonly) NSDictionary<NSString *, NSString *> *environmentComponents;
@property(nonatomic, readonly) NSString *firebaseUserAgent;
@end
@implementation FIRFirebaseUserAgent
@synthesize firebaseUserAgent = _firebaseUserAgent;
@synthesize environmentComponents = _environmentComponents;
- (instancetype)init {
self = [super init];
if (self) {
_valuesByComponent = [[NSMutableDictionary alloc] init];
}
return self;
}
- (NSString *)firebaseUserAgent {
@synchronized(self) {
if (_firebaseUserAgent == nil) {
NSMutableDictionary<NSString *, NSString *> *allComponents =
[self.valuesByComponent mutableCopy];
[allComponents setValuesForKeysWithDictionary:self.environmentComponents];
__block NSMutableArray<NSString *> *components =
[[NSMutableArray<NSString *> alloc] initWithCapacity:self.valuesByComponent.count];
[allComponents enumerateKeysAndObjectsUsingBlock:^(
NSString *_Nonnull name, NSString *_Nonnull value, BOOL *_Nonnull stop) {
[components addObject:[NSString stringWithFormat:@"%@/%@", name, value]];
}];
[components sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
_firebaseUserAgent = [components componentsJoinedByString:@" "];
}
return _firebaseUserAgent;
}
}
- (void)setValue:(nullable NSString *)value forComponent:(NSString *)componentName {
@synchronized(self) {
self.valuesByComponent[componentName] = value;
// Reset cached user agent string.
_firebaseUserAgent = nil;
}
}
- (void)reset {
@synchronized(self) {
// Reset components.
_valuesByComponent = [[[self class] environmentComponents] mutableCopy];
// Reset cached user agent string.
_firebaseUserAgent = nil;
}
}
#pragma mark - Environment components
- (NSDictionary<NSString *, NSString *> *)environmentComponents {
if (_environmentComponents == nil) {
_environmentComponents = [[self class] environmentComponents];
}
return _environmentComponents;
}
+ (NSDictionary<NSString *, NSString *> *)environmentComponents {
NSMutableDictionary<NSString *, NSString *> *components = [NSMutableDictionary dictionary];
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
NSString *xcodeVersion = info[@"DTXcodeBuild"];
NSString *appleSdkVersion = info[@"DTSDKBuild"];
NSString *isFromAppstoreFlagValue = [GULAppEnvironmentUtil isFromAppStore] ? @"true" : @"false";
components[@"apple-platform"] = [GULAppEnvironmentUtil applePlatform];
components[@"apple-sdk"] = appleSdkVersion;
components[@"appstore"] = isFromAppstoreFlagValue;
components[@"deploy"] = [GULAppEnvironmentUtil deploymentType];
components[@"device"] = [GULAppEnvironmentUtil deviceModel];
components[@"os-version"] = [GULAppEnvironmentUtil systemVersion];
components[@"xcode"] = xcodeVersion;
return [components copy];
}
@end

View File

@ -0,0 +1,97 @@
// Copyright 2021 Google LLC
//
// 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 <Foundation/Foundation.h>
#ifndef FIREBASE_BUILD_CMAKE
@import FirebaseCoreInternal;
#endif // FIREBASE_BUILD_CMAKE
#import "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseCore/Extension/FIRHeartbeatLogger.h"
#ifndef FIREBASE_BUILD_CMAKE
NSString *_Nullable FIRHeaderValueFromHeartbeatsPayload(FIRHeartbeatsPayload *heartbeatsPayload) {
if ([heartbeatsPayload isEmpty]) {
return nil;
}
return [heartbeatsPayload headerValue];
}
#endif // FIREBASE_BUILD_CMAKE
@interface FIRHeartbeatLogger ()
#ifndef FIREBASE_BUILD_CMAKE
@property(nonatomic, readonly) FIRHeartbeatController *heartbeatController;
#endif // FIREBASE_BUILD_CMAKE
@property(copy, readonly) NSString * (^userAgentProvider)(void);
@end
@implementation FIRHeartbeatLogger
- (instancetype)initWithAppID:(NSString *)appID {
return [self initWithAppID:appID userAgentProvider:[[self class] currentUserAgentProvider]];
}
- (instancetype)initWithAppID:(NSString *)appID
userAgentProvider:(NSString * (^)(void))userAgentProvider {
self = [super init];
if (self) {
#ifndef FIREBASE_BUILD_CMAKE
_heartbeatController = [[FIRHeartbeatController alloc] initWithId:[appID copy]];
#endif // FIREBASE_BUILD_CMAKE
_userAgentProvider = [userAgentProvider copy];
}
return self;
}
+ (NSString * (^)(void))currentUserAgentProvider {
return ^NSString * {
return [FIRApp firebaseUserAgent];
};
}
- (void)log {
NSString *userAgent = _userAgentProvider();
#ifndef FIREBASE_BUILD_CMAKE
[_heartbeatController log:userAgent];
#endif // FIREBASE_BUILD_CMAKE
}
#ifndef FIREBASE_BUILD_CMAKE
- (NSString *_Nullable)headerValue {
return FIRHeaderValueFromHeartbeatsPayload([self flushHeartbeatsIntoPayload]);
}
- (FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload {
FIRHeartbeatsPayload *payload = [_heartbeatController flush];
return payload;
}
#endif // FIREBASE_BUILD_CMAKE
- (FIRDailyHeartbeatCode)heartbeatCodeForToday {
#ifndef FIREBASE_BUILD_CMAKE
FIRHeartbeatsPayload *todaysHeartbeatPayload = [_heartbeatController flushHeartbeatFromToday];
if ([todaysHeartbeatPayload isEmpty]) {
return FIRDailyHeartbeatCodeNone;
} else {
return FIRDailyHeartbeatCodeSome;
}
#else
return FIRDailyHeartbeatCodeNone;
#endif // FIREBASE_BUILD_CMAKE
}
@end

View File

@ -0,0 +1,173 @@
// 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 "FirebaseCore/Extension/FIRLogger.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#import <GoogleUtilities/GULLogger.h>
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRLoggerLevel.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
NSString *const kFIRLoggerSubsystem = @"com.google.firebase";
NSString *const kFIRLoggerCore = @"[FirebaseCore]";
// All the FIRLoggerService definitions should be migrated to clients. Do not add new ones!
NSString *const kFIRLoggerAnalytics = @"[FirebaseAnalytics]";
NSString *const kFIRLoggerCrash = @"[FirebaseCrash]";
NSString *const kFIRLoggerRemoteConfig = @"[FirebaseRemoteConfig]";
/// Arguments passed on launch.
NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled";
NSString *const kFIREnableDebugModeApplicationArgument = @"-FIRDebugEnabled";
/// Key for the debug mode bit in NSUserDefaults.
NSString *const kFIRPersistedDebugModeKey = @"/google/firebase/debug_mode";
/// NSUserDefaults that should be used to store and read variables. If nil, `standardUserDefaults`
/// will be used.
static NSUserDefaults *sFIRLoggerUserDefaults;
static dispatch_once_t sFIRLoggerOnceToken;
// The sFIRAnalyticsDebugMode flag is here to support the -FIRDebugEnabled/-FIRDebugDisabled
// flags used by Analytics. Users who use those flags expect Analytics to log verbosely,
// while the rest of Firebase logs at the default level. This flag is introduced to support
// that behavior.
static BOOL sFIRAnalyticsDebugMode;
#ifdef DEBUG
/// The regex pattern for the message code.
static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$";
static NSRegularExpression *sMessageCodeRegex;
#endif
void FIRLoggerInitialize(void) {
dispatch_once(&sFIRLoggerOnceToken, ^{
// Register Firebase Version with GULLogger.
GULLoggerRegisterVersion(FIRFirebaseVersion());
NSArray *arguments = [NSProcessInfo processInfo].arguments;
// Use the standard NSUserDefaults if it hasn't been explicitly set.
if (sFIRLoggerUserDefaults == nil) {
sFIRLoggerUserDefaults = [NSUserDefaults standardUserDefaults];
}
BOOL forceDebugMode = NO;
BOOL debugMode = [sFIRLoggerUserDefaults boolForKey:kFIRPersistedDebugModeKey];
if ([arguments containsObject:kFIRDisableDebugModeApplicationArgument]) { // Default mode
[sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey];
} else if ([arguments containsObject:kFIREnableDebugModeApplicationArgument] ||
debugMode) { // Debug mode
[sFIRLoggerUserDefaults setBool:YES forKey:kFIRPersistedDebugModeKey];
forceDebugMode = YES;
}
GULLoggerInitialize();
if (forceDebugMode) {
GULLoggerForceDebug();
}
});
}
__attribute__((no_sanitize("thread"))) void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode) {
sFIRAnalyticsDebugMode = analyticsDebugMode;
}
FIRLoggerLevel FIRGetLoggerLevel(void) {
return (FIRLoggerLevel)GULGetLoggerLevel();
}
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel) {
FIRLoggerInitialize();
GULSetLoggerLevel((GULLoggerLevel)loggerLevel);
}
#ifdef DEBUG
void FIRResetLogger(void) {
extern void GULResetLogger(void);
sFIRLoggerOnceToken = 0;
[sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey];
sFIRLoggerUserDefaults = nil;
GULResetLogger();
}
void FIRSetLoggerUserDefaults(NSUserDefaults *defaults) {
sFIRLoggerUserDefaults = defaults;
}
#endif
/**
* Check if the level is high enough to be loggable.
*
* Analytics can override the log level with an intentional race condition.
* Add the attribute to get a clean thread sanitizer run.
*/
__attribute__((no_sanitize("thread"))) BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel,
BOOL analyticsComponent) {
FIRLoggerInitialize();
if (sFIRAnalyticsDebugMode && analyticsComponent) {
return YES;
}
return GULIsLoggableLevel((GULLoggerLevel)loggerLevel);
}
void FIRLogBasic(FIRLoggerLevel level,
NSString *category,
NSString *messageCode,
NSString *message,
va_list args_ptr) {
FIRLoggerInitialize();
GULOSLogBasic((GULLoggerLevel)level, kFIRLoggerSubsystem, category,
sFIRAnalyticsDebugMode && [kFIRLoggerAnalytics isEqualToString:category],
messageCode, message, args_ptr);
}
/**
* Generates the logging functions using macros.
*
* Calling FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configure %@ failed.", @"blah") shows:
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Error> [Firebase/Core][I-COR000001] Configure blah failed.
* Calling FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configure succeed.") shows:
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Debug> [Firebase/Core][I-COR000001] Configure succeed.
*/
#define FIR_LOGGING_FUNCTION(level) \
void FIRLog##level(NSString *category, NSString *messageCode, NSString *message, ...) { \
va_list args_ptr; \
va_start(args_ptr, message); \
FIRLogBasic(FIRLoggerLevel##level, category, messageCode, message, args_ptr); \
va_end(args_ptr); \
}
FIR_LOGGING_FUNCTION(Error)
FIR_LOGGING_FUNCTION(Warning)
FIR_LOGGING_FUNCTION(Notice)
FIR_LOGGING_FUNCTION(Info)
FIR_LOGGING_FUNCTION(Debug)
#undef FIR_LOGGING_FUNCTION
#pragma mark - FIRLoggerWrapper
@implementation FIRLoggerWrapper
+ (void)logWithLevel:(FIRLoggerLevel)level
service:(NSString *)service
code:(NSString *)code
message:(NSString *)message {
FIRLogBasic(level, service, code, message, NULL);
}
@end

View File

@ -0,0 +1,487 @@
// 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 "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseCore/Extension/FIRLogger.h"
#import "FirebaseCore/Extension/FIROptionsInternal.h"
#import "FirebaseCore/Sources/FIRBundleUtil.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
// 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 kFIRAndroidClientID = @"ANDROID_CLIENT_ID";
NSString *const kFIRDatabaseURL = @"DATABASE_URL";
NSString *const kFIRStorageBucket = @"STORAGE_BUCKET";
// The key to locate the expected bundle identifier in the plist file.
NSString *const kFIRBundleID = @"BUNDLE_ID";
// The key to locate the project identifier in the plist file.
NSString *const kFIRProjectID = @"PROJECT_ID";
NSString *const kFIRIsMeasurementEnabled = @"IS_MEASUREMENT_ENABLED";
NSString *const kFIRIsAnalyticsCollectionEnabled = @"FIREBASE_ANALYTICS_COLLECTION_ENABLED";
NSString *const kFIRIsAnalyticsCollectionDeactivated = @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED";
// Library version ID formatted like:
// @"5" // Major version (one or more digits)
// @"04" // Minor version (exactly 2 digits)
// @"01" // Build number (exactly 2 digits)
// @"000"; // Fixed "000"
NSString *kFIRLibraryVersionID;
// Plist file name.
NSString *const kServiceInfoFileName = @"GoogleService-Info";
// Plist file type.
NSString *const kServiceInfoFileType = @"plist";
// Exception raised from attempting to modify a FIROptions after it's been copied to a FIRApp.
NSString *const kFIRExceptionBadModification =
@"Attempted to modify options after it's set on FIRApp. Please modify all properties before "
@"initializing FIRApp.";
@interface FIROptions ()
/**
* This property maintains the actual configuration key-value pairs.
*/
@property(nonatomic, readwrite) NSMutableDictionary *optionsDictionary;
/**
* Calls `analyticsOptionsDictionaryWithInfoDictionary:` using [NSBundle mainBundle].infoDictionary.
* It combines analytics options from both the infoDictionary and the GoogleService-Info.plist.
* Values which are present in the main plist override values from the GoogleService-Info.plist.
*/
@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary;
/**
* Combination of analytics options from both the infoDictionary and the GoogleService-Info.plist.
* Values which are present in the infoDictionary override values from the GoogleService-Info.plist.
*/
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary;
/**
* Throw exception if editing is locked when attempting to modify an option.
*/
- (void)checkEditingLocked;
@end
@implementation FIROptions {
/// Backing variable for self.analyticsOptionsDictionary.
NSDictionary *_analyticsOptionsDictionary;
}
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 {
dispatch_once(&sDefaultOptionsOnceToken, ^{
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
if (defaultOptionsDictionary != nil) {
sDefaultOptions =
[[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
}
});
return sDefaultOptions;
}
#pragma mark - Private class methods
+ (NSDictionary *)defaultOptionsDictionary {
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;
}
// Returns the path of the plist file with a given file name.
+ (NSString *)plistFilePathWithName:(NSString *)fileName {
NSArray *bundles = [FIRBundleUtil relevantBundles];
NSString *plistFilePath =
[FIRBundleUtil optionsDictionaryPathWithResourceName:fileName
andFileType:kServiceInfoFileType
inBundles:bundles];
if (plistFilePath == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000012", @"Could not locate configuration file: '%@.%@'.",
fileName, kServiceInfoFileType);
}
return plistFilePath;
}
+ (void)resetDefaultOptions {
sDefaultOptions = nil;
sDefaultOptionsDictionary = nil;
sDefaultOptionsOnceToken = 0;
sDefaultOptionsDictionaryOnceToken = 0;
}
#pragma mark - Private instance methods
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)optionsDictionary {
self = [super init];
if (self) {
_optionsDictionary = [optionsDictionary mutableCopy];
_usingOptionsFromDefaultPlist = YES;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
FIROptions *newOptions = [(FIROptions *)[[self class] allocWithZone:zone]
initInternalWithOptionsDictionary:self.optionsDictionary];
if (newOptions) {
newOptions.deepLinkURLScheme = self.deepLinkURLScheme;
newOptions.appGroupID = self.appGroupID;
newOptions.editingLocked = self.isEditingLocked;
newOptions.usingOptionsFromDefaultPlist = self.usingOptionsFromDefaultPlist;
}
return newOptions;
}
#pragma mark - Public instance methods
- (instancetype)init {
// Unavailable.
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initWithContentsOfFile:(NSString *)plistPath {
self = [super init];
if (self) {
if (plistPath == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000013", @"The plist file path is nil.");
return nil;
}
_optionsDictionary = [[NSDictionary dictionaryWithContentsOfFile:plistPath] mutableCopy];
if (_optionsDictionary == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000014",
@"The configuration file at %@ does not exist or "
@"is not a well-formed plist file.",
plistPath);
return nil;
}
// TODO: Do we want to validate the dictionary here? It says we do that already in
// the public header.
}
return self;
}
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID GCMSenderID:(NSString *)GCMSenderID {
self = [super init];
if (self) {
NSMutableDictionary *mutableOptionsDict = [NSMutableDictionary dictionary];
[mutableOptionsDict setValue:googleAppID forKey:kFIRGoogleAppID];
[mutableOptionsDict setValue:GCMSenderID forKey:kFIRGCMSenderID];
[mutableOptionsDict setValue:[[NSBundle mainBundle] bundleIdentifier] forKey:kFIRBundleID];
self.optionsDictionary = mutableOptionsDict;
}
return self;
}
- (NSString *)APIKey {
return self.optionsDictionary[kFIRAPIKey];
}
- (void)checkEditingLocked {
if (self.isEditingLocked) {
[NSException raise:kFirebaseCoreErrorDomain format:kFIRExceptionBadModification];
}
}
- (void)setAPIKey:(NSString *)APIKey {
[self checkEditingLocked];
_optionsDictionary[kFIRAPIKey] = [APIKey copy];
}
- (NSString *)clientID {
return self.optionsDictionary[kFIRClientID];
}
- (void)setClientID:(NSString *)clientID {
[self checkEditingLocked];
_optionsDictionary[kFIRClientID] = [clientID copy];
}
- (NSString *)trackingID {
return self.optionsDictionary[kFIRTrackingID];
}
- (void)setTrackingID:(NSString *)trackingID {
[self checkEditingLocked];
_optionsDictionary[kFIRTrackingID] = [trackingID copy];
}
- (NSString *)GCMSenderID {
return self.optionsDictionary[kFIRGCMSenderID];
}
- (void)setGCMSenderID:(NSString *)GCMSenderID {
[self checkEditingLocked];
_optionsDictionary[kFIRGCMSenderID] = [GCMSenderID copy];
}
- (NSString *)projectID {
return self.optionsDictionary[kFIRProjectID];
}
- (void)setProjectID:(NSString *)projectID {
[self checkEditingLocked];
_optionsDictionary[kFIRProjectID] = [projectID copy];
}
- (NSString *)androidClientID {
return self.optionsDictionary[kFIRAndroidClientID];
}
- (void)setAndroidClientID:(NSString *)androidClientID {
[self checkEditingLocked];
_optionsDictionary[kFIRAndroidClientID] = [androidClientID copy];
}
- (NSString *)googleAppID {
return self.optionsDictionary[kFIRGoogleAppID];
}
- (void)setGoogleAppID:(NSString *)googleAppID {
[self checkEditingLocked];
_optionsDictionary[kFIRGoogleAppID] = [googleAppID copy];
}
- (NSString *)libraryVersionID {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// The unit tests are set up to catch anything that does not properly convert.
NSString *version = FIRFirebaseVersion();
NSArray *components = [version componentsSeparatedByString:@"."];
NSString *major = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:0] intValue]];
NSString *minor = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:1] intValue]];
NSString *patch = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:2] intValue]];
kFIRLibraryVersionID = [NSString stringWithFormat:@"%@%@%@000", major, minor, patch];
});
return kFIRLibraryVersionID;
}
- (void)setLibraryVersionID:(NSString *)libraryVersionID {
_optionsDictionary[kFIRLibraryVersionID] = [libraryVersionID copy];
}
- (NSString *)databaseURL {
return self.optionsDictionary[kFIRDatabaseURL];
}
- (void)setDatabaseURL:(NSString *)databaseURL {
[self checkEditingLocked];
_optionsDictionary[kFIRDatabaseURL] = [databaseURL copy];
}
- (NSString *)storageBucket {
return self.optionsDictionary[kFIRStorageBucket];
}
- (void)setStorageBucket:(NSString *)storageBucket {
[self checkEditingLocked];
_optionsDictionary[kFIRStorageBucket] = [storageBucket copy];
}
- (void)setDeepLinkURLScheme:(NSString *)deepLinkURLScheme {
[self checkEditingLocked];
_deepLinkURLScheme = [deepLinkURLScheme copy];
}
- (NSString *)bundleID {
return self.optionsDictionary[kFIRBundleID];
}
- (void)setBundleID:(NSString *)bundleID {
[self checkEditingLocked];
_optionsDictionary[kFIRBundleID] = [bundleID copy];
}
- (void)setAppGroupID:(NSString *)appGroupID {
[self checkEditingLocked];
_appGroupID = [appGroupID copy];
}
#pragma mark - Equality
- (BOOL)isEqual:(id)object {
if (!object || ![object isKindOfClass:[FIROptions class]]) {
return NO;
}
return [self isEqualToOptions:(FIROptions *)object];
}
- (BOOL)isEqualToOptions:(FIROptions *)options {
// Skip any non-FIROptions classes.
if (![options isKindOfClass:[FIROptions class]]) {
return NO;
}
// Check the internal dictionary and custom properties for differences.
if (![options.optionsDictionary isEqualToDictionary:self.optionsDictionary]) {
return NO;
}
// Validate extra properties not contained in the dictionary. Only validate it if one of the
// objects has the property set.
if ((options.deepLinkURLScheme != nil || self.deepLinkURLScheme != nil) &&
![options.deepLinkURLScheme isEqualToString:self.deepLinkURLScheme]) {
return NO;
}
if ((options.appGroupID != nil || self.appGroupID != nil) &&
![options.appGroupID isEqualToString:self.appGroupID]) {
return NO;
}
// Validate the Analytics options haven't changed with the Info.plist.
if (![options.analyticsOptionsDictionary isEqualToDictionary:self.analyticsOptionsDictionary]) {
return NO;
}
// We don't care about the `editingLocked` or `usingOptionsFromDefaultPlist` properties since
// those relate to lifecycle and construction, we only care if the contents of the options
// themselves are equal.
return YES;
}
- (NSUInteger)hash {
// This is strongly recommended for any object that implements a custom `isEqual:` method to
// ensure that dictionary and set behavior matches other `isEqual:` checks.
// Note: `self.analyticsOptionsDictionary` was left out here since it solely relies on the
// contents of the main bundle's `Info.plist`. We should avoid reading that file and the contents
// should be identical.
return self.optionsDictionary.hash ^ self.deepLinkURLScheme.hash ^ self.appGroupID.hash;
}
#pragma mark - Internal instance methods
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary {
if (_analyticsOptionsDictionary == nil) {
NSMutableDictionary *tempAnalyticsOptions = [[NSMutableDictionary alloc] init];
NSArray *measurementKeys = @[
kFIRIsMeasurementEnabled, kFIRIsAnalyticsCollectionEnabled,
kFIRIsAnalyticsCollectionDeactivated
];
for (NSString *key in measurementKeys) {
id value = infoDictionary[key] ?: self.optionsDictionary[key] ?: nil;
if (!value) {
continue;
}
tempAnalyticsOptions[key] = value;
}
_analyticsOptionsDictionary = tempAnalyticsOptions;
}
return _analyticsOptionsDictionary;
}
- (NSDictionary *)analyticsOptionsDictionary {
return [self analyticsOptionsDictionaryWithInfoDictionary:[NSBundle mainBundle].infoDictionary];
}
/**
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
* GoogleService-Info.plist. This uses the old plist flag IS_MEASUREMENT_ENABLED, which should still
* be supported.
*/
- (BOOL)isMeasurementEnabled {
if (self.isAnalyticsCollectionDeactivated) {
return NO;
}
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
if (value == nil) {
// TODO: This could probably be cleaned up since FIROptions shouldn't know about FIRApp or have
// to check if it's the default app. The FIROptions instance can't be modified after
// `+configure` is called, so it's not a good place to copy it either in case the flag is
// changed at runtime.
// If no values are set for Analytics, fall back to the global collection switch in FIRApp.
// Analytics only supports the default FIRApp, so check that first.
if (![FIRApp isDefaultAppConfigured]) {
return NO;
}
// Fall back to the default app's collection switch when the key is not in the dictionary.
return [FIRApp defaultApp].isDataCollectionDefaultEnabled;
}
return [value boolValue];
}
- (BOOL)isAnalyticsCollectionExplicitlySet {
// If it's de-activated, it classifies as explicitly set. If not, it's not a good enough
// indication that the developer wants FirebaseAnalytics enabled so continue checking.
if (self.isAnalyticsCollectionDeactivated) {
return YES;
}
// Check if the current Analytics flag is set.
id collectionEnabledObject = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
if (collectionEnabledObject && [collectionEnabledObject isKindOfClass:[NSNumber class]]) {
// It doesn't matter what the value is, it's explicitly set.
return YES;
}
// Check if the old measurement flag is set.
id measurementEnabledObject = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
if (measurementEnabledObject && [measurementEnabledObject isKindOfClass:[NSNumber class]]) {
// It doesn't matter what the value is, it's explicitly set.
return YES;
}
// No flags are set to explicitly enable or disable FirebaseAnalytics.
return NO;
}
- (BOOL)isAnalyticsCollectionEnabled {
if (self.isAnalyticsCollectionDeactivated) {
return NO;
}
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
if (value == nil) {
return self.isMeasurementEnabled; // Fall back to older plist flag.
}
return [value boolValue];
}
- (BOOL)isAnalyticsCollectionDeactivated {
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionDeactivated];
if (value == nil) {
return NO; // Analytics Collection is not deactivated when the key is not in the dictionary.
}
return [value boolValue];
}
@end

View File

@ -0,0 +1,152 @@
/*
* 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 "FirebaseCore/Sources/FIRTimestampInternal.h"
NS_ASSUME_NONNULL_BEGIN
static const int kNanosPerSecond = 1000000000;
@implementation FIRTimestamp (Internal)
#pragma mark - Internal public methods
- (NSString *)ISO8601String {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSDate *secondsDate = [NSDate dateWithTimeIntervalSince1970:self.seconds];
NSString *secondsString = [formatter stringFromDate:secondsDate];
if (secondsString.length != 19) {
[NSException raise:@"Invalid ISO string" format:@"Invalid ISO string: %@", secondsString];
}
NSString *nanosString = [NSString stringWithFormat:@"%09d", self.nanoseconds];
return [NSString stringWithFormat:@"%@.%@Z", secondsString, nanosString];
}
@end
@implementation FIRTimestamp
#pragma mark - Constructors
+ (instancetype)timestampWithDate:(NSDate *)date {
double secondsDouble;
double fraction = modf(date.timeIntervalSince1970, &secondsDouble);
// GCP Timestamps always have non-negative nanos.
if (fraction < 0) {
fraction += 1.0;
secondsDouble -= 1.0;
}
int64_t seconds = (int64_t)secondsDouble;
int32_t nanos = (int32_t)(fraction * kNanosPerSecond);
return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanos];
}
+ (instancetype)timestampWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds {
return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanoseconds];
}
+ (instancetype)timestamp {
return [FIRTimestamp timestampWithDate:[NSDate date]];
}
- (instancetype)initWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds {
self = [super init];
if (self) {
if (nanoseconds < 0) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp nanoseconds out of range: %d", nanoseconds];
}
if (nanoseconds >= 1e9) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp nanoseconds out of range: %d", nanoseconds];
}
// Midnight at the beginning of 1/1/1 is the earliest timestamp supported.
if (seconds < -62135596800L) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp seconds out of range: %lld", seconds];
}
// This will break in the year 10,000.
if (seconds >= 253402300800L) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp seconds out of range: %lld", seconds];
}
_seconds = seconds;
_nanoseconds = nanoseconds;
}
return self;
}
#pragma mark - NSObject methods
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[FIRTimestamp class]]) {
return NO;
}
return [self isEqualToTimestamp:(FIRTimestamp *)object];
}
- (NSUInteger)hash {
return (NSUInteger)((self.seconds >> 32) ^ self.seconds ^ self.nanoseconds);
}
- (NSString *)description {
return [NSString stringWithFormat:@"<FIRTimestamp: seconds=%lld nanoseconds=%d>", self.seconds,
self.nanoseconds];
}
/** Implements NSCopying without actually copying because timestamps are immutable. */
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
return self;
}
#pragma mark - Public methods
- (NSDate *)dateValue {
NSTimeInterval interval = (NSTimeInterval)self.seconds + ((NSTimeInterval)self.nanoseconds) / 1e9;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
- (NSComparisonResult)compare:(FIRTimestamp *)other {
if (self.seconds < other.seconds) {
return NSOrderedAscending;
} else if (self.seconds > other.seconds) {
return NSOrderedDescending;
}
if (self.nanoseconds < other.nanoseconds) {
return NSOrderedAscending;
} else if (self.nanoseconds > other.nanoseconds) {
return NSOrderedDescending;
}
return NSOrderedSame;
}
#pragma mark - Private methods
- (BOOL)isEqualToTimestamp:(FIRTimestamp *)other {
return [self compare:other] == NSOrderedSame;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,35 @@
/*
* Copyright 2018 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 "FirebaseCore/Sources/Public/FirebaseCore/FIRTimestamp.h"
NS_ASSUME_NONNULL_BEGIN
/** Internal FIRTimestamp API we don't want exposed in our public header files. */
@interface FIRTimestamp (Internal)
/**
* Converts the given date to an ISO 8601 timestamp string, useful for rendering in JSON.
*
* ISO 8601 dates times in UTC look like this: "1912-04-14T23:40:00.000000000Z".
*
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format
*/
- (NSString *)ISO8601String;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,32 @@
/*
* 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 "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
#ifndef Firebase_VERSION
#error "Firebase_VERSION is not defined: add -DFirebase_VERSION=... to the build invocation"
#endif
// The following two macros supply the incantation so that the C
// preprocessor does not try to parse the version as a floating
// point number. See
// https://www.guyrutenberg.com/2008/12/20/expanding-macros-into-string-constants-in-c/
#define STR(x) STR_EXPAND(x)
#define STR_EXPAND(x) #x
NSString* FIRFirebaseVersion(void) {
return @STR(Firebase_VERSION);
}

View File

@ -0,0 +1,129 @@
/*
* 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 <Foundation/Foundation.h>
@class FIROptions;
NS_ASSUME_NONNULL_BEGIN
/** A block that takes a BOOL and has no return value. */
typedef void (^FIRAppVoidBoolCallback)(BOOL success)
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
/**
* The entry point of Firebase SDKs.
*
* Initialize and configure `FirebaseApp` using `FirebaseApp.configure()`
* or other customized ways as shown below.
*
* The logging system has two modes: default mode and debug mode. In default mode, only logs with
* log level Notice, Warning and Error will be sent to device. In debug mode, all logs will be sent
* to device. The log levels that Firebase uses are consistent with the ASL log levels.
*
* Enable debug mode by passing the `-FIRDebugEnabled` argument to the application. You can add this
* argument in the application's Xcode scheme. When debug mode is enabled via `-FIRDebugEnabled`,
* further executions of the application will also be in debug mode. In order to return to default
* mode, you must explicitly disable the debug mode with the application argument
* `-FIRDebugDisabled`.
*
* It is also possible to change the default logging level in code by calling
* `FirebaseConfiguration.shared.setLoggerLevel(_:)` with the desired level.
*/
NS_SWIFT_NAME(FirebaseApp)
@interface FIRApp : NSObject
/**
* Configures a default Firebase app. Raises an exception if any configuration step fails. The
* default app is named "__FIRAPP_DEFAULT". This method should be called after the app is launched
* and before using Firebase services. This method should be called from the main thread and
* contains synchronous file I/O (reading GoogleService-Info.plist from disk).
*/
+ (void)configure;
/**
* Configures the default Firebase app with the provided options. The default app is named
* "__FIRAPP_DEFAULT". Raises an exception if any configuration step fails. This method should be
* called from the main thread.
*
* @param options The Firebase application options used to configure the service.
*/
+ (void)configureWithOptions:(FIROptions *)options NS_SWIFT_NAME(configure(options:));
/**
* Configures a Firebase app with the given name and options. Raises an exception if any
* configuration step fails. This method should be called from the main thread.
*
* @param name The application's name given by the developer. The name should should only contain
Letters, Numbers and Underscore.
* @param options The Firebase application options used to configure the services.
*/
// clang-format off
+ (void)configureWithName:(NSString *)name
options:(FIROptions *)options NS_SWIFT_NAME(configure(name:options:));
// clang-format on
/**
* Returns the default app, or `nil` if the default app does not exist.
*/
+ (nullable FIRApp *)defaultApp NS_SWIFT_NAME(app());
/**
* Returns a previously created `FirebaseApp` instance with the given name, or `nil` if no such app
* exists. This method is thread safe.
*/
+ (nullable FIRApp *)appNamed:(NSString *)name NS_SWIFT_NAME(app(name:));
/**
* Returns the set of all extant `FirebaseApp` instances, or `nil` if there are no `FirebaseApp`
* instances. This method is thread safe.
*/
@property(class, readonly, nullable) NSDictionary<NSString *, FIRApp *> *allApps;
/**
* Cleans up the current `FirebaseApp`, freeing associated data and returning its name to the pool
* for future use. This method is thread safe.
*/
- (void)deleteApp:(void (^)(BOOL success))completion;
/**
* `FirebaseApp` instances should not be initialized directly. Call `FirebaseApp.configure()`,
* `FirebaseApp.configure(options:)`, or `FirebaseApp.configure(name:options:)` directly.
*/
- (instancetype)init NS_UNAVAILABLE;
/**
* Gets the name of this app.
*/
@property(nonatomic, copy, readonly) NSString *name;
/**
* Gets a copy of the options for this app. These are non-modifiable.
*/
@property(nonatomic, copy, readonly) FIROptions *options;
/**
* Gets or sets whether automatic data collection is enabled for all products. Defaults to `true`
* unless `FirebaseDataCollectionDefaultEnabled` is set to `NO` in your app's Info.plist. This value
* is persisted across runs of the app so that it can be set once when users have consented to
* collection.
*/
@property(nonatomic, readwrite, getter=isDataCollectionDefaultEnabled)
BOOL dataCollectionDefaultEnabled;
@end
NS_ASSUME_NONNULL_END

View File

@ -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 <Foundation/Foundation.h>
#import "FIRLoggerLevel.h"
NS_ASSUME_NONNULL_BEGIN
/**
* This interface provides global level properties that the developer can tweak.
*/
NS_SWIFT_NAME(FirebaseConfiguration)
@interface FIRConfiguration : NSObject
/** Returns the shared configuration object. */
@property(class, nonatomic, readonly) FIRConfiguration *sharedInstance NS_SWIFT_NAME(shared);
/**
* Sets the logging level for internal Firebase logging. Firebase will only log messages
* that are logged at or below `loggerLevel`. The messages are logged both to the Xcode
* console and to the device's log. Note that if an app is running from AppStore, it will
* never log above `.notice` even if `loggerLevel` is set to a higher (more verbose)
* setting.
*
* @param loggerLevel The maximum logging level. The default level is set to FIRLoggerLevelNotice.
*/
- (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,38 @@
/*
* 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.
*/
// Note that importing GULLoggerLevel.h will lead to a non-modular header
// import error.
/**
* The log levels used by internal logging.
*/
typedef NS_ENUM(NSInteger, FIRLoggerLevel) {
/** Error level, matches ASL_LEVEL_ERR. */
FIRLoggerLevelError = 3,
/** Warning level, matches ASL_LEVEL_WARNING. */
FIRLoggerLevelWarning = 4,
/** Notice level, matches ASL_LEVEL_NOTICE. */
FIRLoggerLevelNotice = 5,
/** Info level, matches ASL_LEVEL_INFO. */
FIRLoggerLevelInfo = 6,
/** Debug level, matches ASL_LEVEL_DEBUG. */
FIRLoggerLevelDebug = 7,
/** Minimum log level. */
FIRLoggerLevelMin = FIRLoggerLevelError,
/** Maximum log level. */
FIRLoggerLevelMax = FIRLoggerLevelDebug
} NS_SWIFT_NAME(FirebaseLoggerLevel);

View File

@ -0,0 +1,131 @@
/*
* 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* This class provides constant fields of Google APIs.
*/
NS_SWIFT_NAME(FirebaseOptions)
@interface FIROptions : NSObject <NSCopying>
/**
* Returns the default options. The first time this is called it synchronously reads
* GoogleService-Info.plist from disk.
*/
+ (nullable FIROptions *)defaultOptions NS_SWIFT_NAME(defaultOptions());
/**
* An API key used for authenticating requests from your Apple app, e.g.
* The key must begin with "A" and contain exactly 39 alphanumeric characters, used to identify your
* app to Google servers.
*/
@property(nonatomic, copy, nullable) NSString *APIKey NS_SWIFT_NAME(apiKey);
/**
* The bundle ID for the application. Defaults to `Bundle.main.bundleIdentifier` when not set
* manually or in a plist.
*/
@property(nonatomic, copy) NSString *bundleID;
/**
* The OAuth2 client ID for Apple applications used to authenticate Google users, for example
* @"12345.apps.googleusercontent.com", used for signing in with Google.
*/
@property(nonatomic, copy, nullable) NSString *clientID;
/**
* Unused.
*/
@property(nonatomic, copy, nullable) NSString *trackingID DEPRECATED_ATTRIBUTE;
/**
* The Project Number from the Google Developer's console, for example @"012345678901", used to
* configure Firebase Cloud Messaging.
*/
@property(nonatomic, copy) NSString *GCMSenderID NS_SWIFT_NAME(gcmSenderID);
/**
* The Project ID from the Firebase console, for example @"abc-xyz-123".
*/
@property(nonatomic, copy, nullable) NSString *projectID;
/**
* Unused.
*/
@property(nonatomic, copy, nullable) NSString *androidClientID DEPRECATED_ATTRIBUTE;
/**
* The Google App ID that is used to uniquely identify an instance of an app.
*/
@property(nonatomic, copy) NSString *googleAppID;
/**
* The database root URL, e.g. @"http://abc-xyz-123.firebaseio.com".
*/
@property(nonatomic, copy, nullable) NSString *databaseURL;
/**
* The URL scheme used to set up Durable Deep Link service.
*/
@property(nonatomic, copy, nullable) NSString *deepLinkURLScheme;
/**
* The Google Cloud Storage bucket name, e.g. @"abc-xyz-123.storage.firebase.com".
*/
@property(nonatomic, copy, nullable) NSString *storageBucket;
/**
* The App Group identifier to share data between the application and the application extensions.
* The App Group must be configured in the application and on the Apple Developer Portal. Default
* value `nil`.
*/
@property(nonatomic, copy, nullable) NSString *appGroupID;
/**
* Initializes a customized instance of FirebaseOptions from the file at the given plist file path.
* This will read the file synchronously from disk.
* For example:
* ```swift
* if let path = Bundle.main.path(forResource:"GoogleServices-Info", ofType:"plist") {
* let options = FirebaseOptions(contentsOfFile: path)
* }
* ```
* Note that it is not possible to customize `FirebaseOptions` for Firebase Analytics which expects
* a static file named `GoogleServices-Info.plist` -
* https://github.com/firebase/firebase-ios-sdk/issues/230.
* Returns `nil` if the plist file does not exist or is invalid.
*/
- (nullable instancetype)initWithContentsOfFile:(NSString *)plistPath NS_DESIGNATED_INITIALIZER;
/**
* Initializes a customized instance of `FirebaseOptions` with required fields. Use the mutable
* properties to modify fields for configuring specific services. Note that it is not possible to
* customize `FirebaseOptions` for Firebase Analytics which expects a static file named
* `GoogleServices-Info.plist` - https://github.com/firebase/firebase-ios-sdk/issues/230.
*/
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID
GCMSenderID:(NSString *)GCMSenderID
NS_SWIFT_NAME(init(googleAppID:gcmSenderID:))NS_DESIGNATED_INITIALIZER;
/** Unavailable. Please use `init(contentsOfFile:)` or `init(googleAppID:gcmSenderID:)` instead. */
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,89 @@
/*
* Copyright 2018 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* A Timestamp represents a point in time independent of any time zone or calendar, represented as
* seconds and fractions of seconds at nanosecond resolution in UTC Epoch time. It is encoded using
* the Proleptic Gregorian Calendar which extends the Gregorian calendar backwards to year one. It
* is encoded assuming all minutes are 60 seconds long, i.e. leap seconds are "smeared" so that no
* leap second table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59.999999999Z. By restricting to that range, we ensure that we can convert to
* and from RFC 3339 date strings.
*
* @see https://github.com/google/protobuf/blob/main/src/google/protobuf/timestamp.proto for the
* reference timestamp definition.
*/
NS_SWIFT_NAME(Timestamp)
@interface FIRTimestamp : NSObject <NSCopying>
/** :nodoc: */
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a new timestamp.
*
* @param seconds the number of seconds since epoch.
* @param nanoseconds the number of nanoseconds after the seconds.
*/
- (instancetype)initWithSeconds:(int64_t)seconds
nanoseconds:(int32_t)nanoseconds NS_DESIGNATED_INITIALIZER;
/**
* Creates a new timestamp.
*
* @param seconds the number of seconds since epoch.
* @param nanoseconds the number of nanoseconds after the seconds.
*/
+ (instancetype)timestampWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds;
/** Creates a new timestamp from the given date. */
+ (instancetype)timestampWithDate:(NSDate *)date;
/** Creates a new timestamp with the current date / time. */
+ (instancetype)timestamp;
/** Returns a new `Date` corresponding to this timestamp. This may lose precision. */
- (NSDate *)dateValue;
/**
* Returns the result of comparing the receiver with another timestamp.
* @param other the other timestamp to compare.
* @return `orderedAscending` if `other` is chronologically following self,
* `orderedDescending` if `other` is chronologically preceding self,
* `orderedSame` otherwise.
*/
- (NSComparisonResult)compare:(FIRTimestamp *)other;
/**
* Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
* Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.
*/
@property(nonatomic, assign, readonly) int64_t seconds;
/**
* Non-negative fractions of a second at nanosecond resolution. Negative second values with
* fractions must still have non-negative nanos values that count forward in time.
* Must be from 0 to 999,999,999 inclusive.
*/
@property(nonatomic, assign, readonly) int32_t nanoseconds;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,25 @@
/*
* Copyright 2020 Google LLC
*
* 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** Returns the current version of Firebase. */
NS_SWIFT_NAME(FirebaseVersion())
NSString* FIRFirebaseVersion(void);
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,22 @@
/*
* 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 "FIRApp.h"
#import "FIRConfiguration.h"
#import "FIRLoggerLevel.h"
#import "FIROptions.h"
#import "FIRTimestamp.h"
#import "FIRVersion.h"

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
</plist>

202
Pods/FirebaseCore/LICENSE generated Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

302
Pods/FirebaseCore/README.md generated Normal file
View File

@ -0,0 +1,302 @@
<p align="center">
<a href="https://cocoapods.org/pods/Firebase">
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=CocoaPods"/>
</a>
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=Swift%20Package%20Index&color=red"/>
</a>
<a href="https://cocoapods.org/pods/Firebase">
<img src="https://img.shields.io/github/license/Firebase/firebase-ios-sdk?style=flat"/>
</a><br/>
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dplatforms"/>
</a>
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dswift-versions"/>
</a>
</p>
# Firebase Apple Open Source Development
This repository contains the source code for all Apple platform Firebase SDKs except FirebaseAnalytics.
Firebase is an app development platform with tools to help you build, grow, and
monetize your app. More information about Firebase can be found on the
[official Firebase website](https://firebase.google.com).
## Installation
See the subsections below for details about the different installation methods. Where
available, it's recommended to install any libraries with a `Swift` suffix to get the
best experience when writing your app in Swift.
1. [Standard pod install](#standard-pod-install)
2. [Swift Package Manager](#swift-package-manager)
3. [Installing from the GitHub repo](#installing-from-github)
4. [Experimental Carthage](#carthage-ios-only)
### Standard pod install
For instructions on the standard pod install, visit:
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Swift Package Manager
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
found in the [SwiftPackageManager.md](SwiftPackageManager.md) Markdown file.
### Installing from GitHub
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See [the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```ruby
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
```
To access FirebaseMessaging via a checked-out version of the firebase-ios-sdk repo:
```ruby
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution can be found at
[Carthage.md](Carthage.md).
### Using Firebase from a Framework or a library
For details on using Firebase from a Framework or a library, refer to [firebase_in_libraries.md](docs/firebase_in_libraries.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 15.2 (or later)
CocoaPods is still the canonical way to develop, but much of the repo now supports
development with Swift Package Manager.
### CocoaPods
Install the following:
* CocoaPods 1.12.0 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
For the pod that you want to develop:
```ruby
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
```
Note: If the CocoaPods cache is out of date, you may need to run
`pod repo update` before the `pod gen` command.
Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
those platforms. Since 10.2, Xcode does not properly handle multi-platform
CocoaPods workspaces.
Firestore has a self-contained Xcode project. See
[Firestore/README](Firestore/README.md) Markdown file.
#### Development for Catalyst
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
* Check the Mac box in the App-iOS Build Settings
* Sign the App in the Settings Signing & Capabilities tab
* Click Pods in the Project Manager
* Add Signing to the iOS host app and unit test targets
* Select the Unit-unit scheme
* Run it to build and test
Alternatively, disable signing in each target:
* Go to Build Settings tab
* Click `+`
* Select `Add User-Defined Setting`
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
### Swift Package Manager
* To enable test schemes: `./scripts/setup_spm_tests.sh`
* `open Package.swift` or double click `Package.swift` in Finder.
* Xcode will open the project
* Choose a scheme for a library to build or test suite to run
* Choose a target platform by selecting the run destination along with the scheme
### Adding a New Firebase Pod
Refer to [AddNewPod](AddNewPod.md) Markdown file for details.
### Managing Headers and Imports
For information about managing headers and imports, see [HeadersImports](HeadersImports.md) Markdown file.
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/main/scripts/check.sh)
before creating a pull request (PR).
GitHub Actions will verify that any code changes are done in a style-compliant
way. Install `clang-format` and `mint`:
```console
brew install clang-format@18
brew install mint
```
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
### Running Sample Apps
To run the sample apps and integration tests, you'll need a valid
`GoogleService-Info.plist
` file. The Firebase Xcode project contains dummy plist
files without real values, but they can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g., `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
### Coverage Report Generation
For coverage report generation instructions, see [scripts/code_coverage_report/README](scripts/code_coverage_report/README.md) Markdown file.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
For specific Firebase Auth development, refer to the [Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
The Firebase Database Integration tests can be run against a locally running Database Emulator
or against a production instance.
To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
running the integration test.
To run against a production instance, provide a valid `GoogleServices-Info.plist` and copy it to
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
running.
### Firebase Dynamic Links
Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
### Firebase Performance Monitoring
For specific Firebase Performance Monitoring development, see
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
integrating Performance with the dev test App.
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[StorageIntegration.swift](FirebaseStorage/Tests/Integration/StorageIntegration.swift).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the
Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications and will not receive push notifications.
To receive push notifications, follow the steps above and run the app on a physical device.
### Vertex AI for Firebase
See the [Vertex AI for Firebase README](FirebaseVertexAI#development) for
instructions about building and testing the SDK.
## Building with Firebase on Apple platforms
Firebase provides official beta support for macOS, Catalyst, and tvOS. visionOS and watchOS
are community supported. Thanks to community contributions for many of the multi-platform PRs.
At this time, most of Firebase's products are available across Apple platforms. There are still
a few gaps, especially on visionOS and watchOS. For details about the current support matrix, see
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
in Firebase's documentation.
### visionOS
Where supported, visionOS works as expected with the exception of Firestore via Swift Package
Manager where it is required to use the source distribution.
To enable the Firestore source distribution, quit Xcode and open the desired
project from the command line with the `FIREBASE_SOURCE_FIRESTORE` environment
variable: `open --env FIREBASE_SOURCE_FIRESTORE /path/to/project.xcodeproj`.
To go back to using the binary distribution of Firestore, quit Xcode and open
Xcode like normal, without the environment variable.
### watchOS
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
work on watchOS. See the [Independent Watch App Sample](Example/watchOSSample).
Keep in mind that watchOS is not officially supported by Firebase. While we can catch basic unit
test issues with GitHub Actions, there may be some changes where the SDK no longer works as expected
on watchOS. If you encounter this, please
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
During app setup in the console, you may get to a step that mentions something like "Checking if the
app has communicated with our servers". This relies on Analytics and will not work on watchOS.
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
#### Additional Crashlytics Notes
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
## Combine
Thanks to contributions from the community, _FirebaseCombineSwift_ contains support for Apple's Combine
framework. This module is currently under development and not yet supported for use in production
environments. For more details, please refer to the [docs](FirebaseCombineSwift/README.md).
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase Apple SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
Apple SDK.
## License
The contents of this repository are licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).