diff options
Diffstat (limited to 'StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig')
51 files changed, 6981 insertions, 0 deletions
diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRAppInternal.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRAppInternal.h new file mode 100644 index 00000000..9a0c943d --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRAppInternal.h @@ -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. + */ + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRApp.h" +#else +#import <FirebaseCore/FIRApp.h> +#endif + +// The has_include is a workaround so the old IID needed for the FIS tests can find FIRErrors.h +#if __has_include("FirebaseCore/Sources/Private/FIRErrors.h") +#import "FirebaseCore/Sources/Private/FIRErrors.h" +#else +#import <FirebaseCore/FIRErrors.h> +#endif + +@class FIRComponentContainer; +@protocol FIRLibrary; + +/** + * The internal interface to FIRApp. This is meant for first-party integrators, who need to receive + * FIRApp notifications, log info about the success or failure of their configuration, and access + * other internal functionality of FIRApp. + * + * TODO(b/28296561): Restructure this header. + */ +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; + +/** + * The format string for the User Defaults key used for storing the data collection enabled flag. + * This includes formatting to append the Firebase App's name. + */ +extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat; + +/** + * The plist key used for storing the data collection enabled flag. + */ +extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey; + +/** + * A notification fired containing diagnostic information when SDK errors occur. + */ +extern NSString *const kFIRAppDiagnosticsNotification; + +/** @var FIRAuthStateDidChangeInternalNotification + @brief The name of the @c NSNotificationCenter 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 FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not + contain this key it indicates a sign-out event took place. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotification; + +/** @var FIRAuthStateDidChangeInternalNotificationTokenKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the new access token. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey; + +/** @var FIRAuthStateDidChangeInternalNotificationAppKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the FIRApp associated with the auth instance. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey; + +/** @var FIRAuthStateDidChangeInternalNotificationUIDKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification 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; + +/** + * Creates an error for failing to configure a subspec service. This method is called by each + * FIRApp notification listener. + */ ++ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain + errorCode:(FIRErrorCode)code + service:(NSString *)service + reason:(NSString *)reason; +/** + * 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 with the given version number to be reported for + * analytics. + * + * @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; + +/** + * Used by each SDK to send logs about SDK configuration status to Clearcut. + * + * @note This API is a no-op, please remove calls to it. + */ +- (void)sendLogsWithServiceName:(NSString *)serviceName + version:(NSString *)version + error:(NSError *)error; + +/** + * Can be used by the unit tests in eack SDK to reset FIRApp. 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 diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponent.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponent.h new file mode 100644 index 00000000..cb51ee70 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponent.h @@ -0,0 +1,91 @@ +/* + * 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); + +@class FIRDependency; + +/// 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; + +/// An array of dependencies for the component. +@property(nonatomic, copy, readonly) NSArray<FIRDependency *> *dependencies; + +/// 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 dependencies - Any dependencies the `implementingClass` has, optional or required. +/// @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 + dependencies:(NSArray<FIRDependency *> *)dependencies + creationBlock:(FIRComponentCreationBlock)creationBlock +NS_SWIFT_NAME(init(_:instantiationTiming:dependencies:creationBlock:)); + +// clang-format on + +/// Unavailable. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponentContainer.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponentContainer.h new file mode 100644 index 00000000..db2bafef --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponentContainer.h @@ -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> + +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponentType.h") +#import "FirebaseCore/Sources/Private/FIRComponentType.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#else +#import <FirebaseCore/FIRComponentType.h> +#import <FirebaseCore/FIRLibrary.h> +#endif + +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 `FIRComponentRegistrant` +/// 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; + +/// Unavailable. Use the `container` property on `FIRApp`. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponentType.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponentType.h new file mode 100644 index 00000000..6f2aca7b --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRComponentType.h @@ -0,0 +1,34 @@ +/* + * 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. ++ (T)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h new file mode 100644 index 00000000..76c0c05f --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h @@ -0,0 +1,35 @@ +/* + * 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 <Foundation/Foundation.h> + +@class FIRDiagnosticsData; +@class FIROptions; + +NS_ASSUME_NONNULL_BEGIN + +/** Connects FIRCore with the CoreDiagnostics library. */ +@interface FIRCoreDiagnosticsConnector : NSObject + +/** Logs FirebaseCore related data. + * + * @param options The options object containing data to log. + */ ++ (void)logCoreTelemetryWithOptions:(FIROptions *)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRDependency.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRDependency.h new file mode 100644 index 00000000..46e9b7ea --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRDependency.h @@ -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 dependency on a specific protocol's functionality. +NS_SWIFT_NAME(Dependency) +@interface FIRDependency : NSObject + +/// The protocol describing functionality being depended on. +@property(nonatomic, strong, readonly) Protocol *protocol; + +/// A flag to specify if the dependency is required or not. +@property(nonatomic, readonly) BOOL isRequired; + +/// Initializes a dependency that is required. Calls `initWithProtocol:isRequired` with `YES` for +/// the required parameter. +/// Creates a required dependency on the specified protocol's functionality. ++ (instancetype)dependencyWithProtocol:(Protocol *)protocol; + +/// Creates a dependency on the specified protocol's functionality and specify if it's required for +/// the class's functionality. ++ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required; + +/// Use `dependencyWithProtocol:isRequired:` instead. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRErrorCode.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRErrorCode.h new file mode 100644 index 00000000..c90d9eec --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRErrorCode.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/** Error codes in Firebase error domain. */ +typedef NS_ENUM(NSInteger, FIRErrorCode) { + /** + * Unknown error. + */ + FIRErrorCodeUnknown = 0, + /** + * Loading data from the GoogleService-Info.plist file failed. This is a fatal error and should + * not be ignored. Further calls to the API will fail and/or possibly cause crashes. + */ + FIRErrorCodeInvalidPlistFile = -100, + + /** + * Validating the Google App ID format failed. + */ + FIRErrorCodeInvalidAppID = -101, + + /** + * Error code for failing to configure a specific service. It's deprecated, but + * still used after copybara. + */ + FIRErrorCodeConfigFailed = -114, +}; diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRErrors.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRErrors.h new file mode 100644 index 00000000..19e47328 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRErrors.h @@ -0,0 +1,24 @@ +/* + * 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> + +#include "FIRErrorCode.h" + +extern NSString *const kFirebaseErrorDomain; +extern NSString *const kFirebaseConfigErrorDomain; +extern NSString *const kFirebaseCoreErrorDomain; +extern NSString *const kFirebasePerfErrorDomain; diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h new file mode 100644 index 00000000..bfff73e5 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h @@ -0,0 +1,39 @@ +// 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRHeartbeatInfo : NSObject + +// Enum representing the different heartbeat codes. +typedef NS_ENUM(NSInteger, FIRHeartbeatInfoCode) { + FIRHeartbeatInfoCodeNone = 0, + FIRHeartbeatInfoCodeSDK = 1, + FIRHeartbeatInfoCodeGlobal = 2, + FIRHeartbeatInfoCodeCombined = 3, +}; + +/** + * Get heartbeat code requred for the sdk. + * @param heartbeatTag String representing the sdk heartbeat tag. + * @return Heartbeat code indicating whether or not an sdk/global heartbeat + * needs to be sent + */ ++ (FIRHeartbeatInfoCode)heartbeatCodeForTag:(NSString *)heartbeatTag; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRLibrary.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRLibrary.h new file mode 100644 index 00000000..e7a9e077 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRLibrary.h @@ -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. + */ + +#ifndef FIRLibrary_h +#define FIRLibrary_h + +#import <Foundation/Foundation.h> + +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponent.h") +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#else +#import <FirebaseCore/FIRComponent.h> +#endif + +@class FIRApp; + +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 FIRComponents that will be registered in +/// FIRApp and participate in dependency resolution and injection. ++ (NSArray<FIRComponent *> *)componentsToRegister; + +@optional +/// Implement this method if the library needs notifications for lifecycle events. This method is +/// called when the developer calls `FirebaseApp.configure()`. ++ (void)configureWithApp:(FIRApp *)app; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* FIRLibrary_h */ diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRLogger.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRLogger.h new file mode 100644 index 00000000..6fd77844 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIRLogger.h @@ -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 <Foundation/Foundation.h> + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRLoggerLevel.h" +#else +#import <FirebaseCore/FIRLoggerLevel.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + * The Firebase services used in Firebase logger. + */ +typedef NSString *const FIRLoggerService; + +extern FIRLoggerService kFIRLoggerABTesting; +extern FIRLoggerService kFIRLoggerAdMob; +extern FIRLoggerService kFIRLoggerAnalytics; +extern FIRLoggerService kFIRLoggerAuth; +extern FIRLoggerService kFIRLoggerCrash; +extern FIRLoggerService kFIRLoggerCore; +extern FIRLoggerService kFIRLoggerMLKit; +extern FIRLoggerService kFIRLoggerPerf; +extern FIRLoggerService 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 YES, the logging level for Analytics will be set to FIRLoggerLevelDebug. + * Enabling the debug mode has no effect if the app is running from App Store. + * (required) analytics debug mode flag. + */ +void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode); + +/** + * Changes the default logging level of FIRLoggerLevelNotice to a user-specified level. + * The default level cannot be set above FIRLoggerLevelNotice if the app is running from App Store. + * (required) log level (one of the FIRLoggerLevel enum values). + */ +void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel); + +/** + * Checks if the specified logger level is loggable given the current settings. + * (required) log level (one of the FIRLoggerLevel 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 FIRLoggerLevelNotice to avoid log spamming. + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) service name of type FIRLoggerService. + * (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, + FIRLoggerService service, + 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 FIRLoggerService. + * (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: + * FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name); + */ +extern void FIRLogError(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogWarning(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogNotice(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogInfo(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogDebug(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +@interface FIRLoggerWrapper : NSObject + +/** + * Objective-C wrapper for FIRLogBasic to allow weak linking to FIRLogger + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) service name of type FIRLoggerService. + * (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. + */ + ++ (void)logWithLevel:(FIRLoggerLevel)level + withService:(FIRLoggerService)service + withCode:(NSString *)messageCode + withMessage:(NSString *)message + withArgs:(va_list)args; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIROptionsInternal.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIROptionsInternal.h new file mode 100644 index 00000000..acaf4586 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FIROptionsInternal.h @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIROptions.h" +#else +#import <FirebaseCore/FIROptions.h> +#endif + +/** + * 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 FIROptions 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; + +/** + * defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp 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 YES, then + * isAnalyticsCollectionEnabled will be NO. + */ +@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 Analytics was enabled in the developer console. + */ +@property(nonatomic, readonly) BOOL isAnalyticsEnabled; + +/** + * Whether or not SignIn was enabled in the developer console. + */ +@property(nonatomic, readonly) BOOL isSignInEnabled; + +/** + * Whether or not editing is locked. This should occur after FIROptions has been set on a FIRApp. + */ +@property(nonatomic, getter=isEditingLocked) BOOL editingLocked; + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FirebaseCoreInternal.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FirebaseCoreInternal.h new file mode 100644 index 00000000..93af6cb8 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseCore/Sources/Private/FirebaseCoreInternal.h @@ -0,0 +1,31 @@ +// 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. + +// An umbrella header, for any other libraries in this repo to access Firebase Public and Private +// headers. Any package manager complexity should be handled here. + +#if SWIFT_PACKAGE +@import FirebaseCore; +#else +#import <FirebaseCore/FirebaseCore.h> +#endif + +#import "FirebaseCore/Sources/Private/FIRAppInternal.h" +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#import "FirebaseCore/Sources/Private/FIRComponentContainer.h" +#import "FirebaseCore/Sources/Private/FIRDependency.h" +#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#import "FirebaseCore/Sources/Private/FIRLogger.h" +#import "FirebaseCore/Sources/Private/FIROptionsInternal.h" diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h new file mode 100644 index 00000000..cd40f172 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h @@ -0,0 +1,23 @@ +// 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. + +// An umbrella header, for any other libraries in this repo to access Firebase +// Installations Public headers. Any package manager complexity should be +// handled here. + +#if SWIFT_PACKAGE +@import FirebaseInstallations; +#else +#import <FirebaseInstallations/FirebaseInstallations.h> +#endif diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRConfigValue.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRConfigValue.m new file mode 100644 index 00000000..09d9f880 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRConfigValue.m @@ -0,0 +1,91 @@ +/* + * 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 <FirebaseRemoteConfig/FIRRemoteConfig.h> + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h" + +@implementation FIRRemoteConfigValue { + /// Data backing the config value. + NSData *_data; + FIRRemoteConfigSource _source; +} + +/// Designated initializer +- (instancetype)initWithData:(NSData *)data source:(FIRRemoteConfigSource)source { + self = [super init]; + if (self) { + _data = [data copy]; + _source = source; + } + return self; +} + +/// Superclass's designated initializer +- (instancetype)init { + return [self initWithData:nil source:FIRRemoteConfigSourceStatic]; +} + +/// The string is a UTF-8 representation of NSData. +- (NSString *)stringValue { + return [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding]; +} + +/// Number representation of a UTF-8 string. +- (NSNumber *)numberValue { + return [NSNumber numberWithDouble:self.stringValue.doubleValue]; +} + +/// Internal representation of the FIRRemoteConfigValue as a NSData object. +- (NSData *)dataValue { + return _data; +} + +/// Boolean representation of a UTF-8 string. +- (BOOL)boolValue { + return self.stringValue.boolValue; +} + +/// Returns a foundation object (NSDictionary / NSArray) representation for JSON data. +- (id)JSONValue { + NSError *error; + if (!_data) { + return nil; + } + id JSONObject = [NSJSONSerialization JSONObjectWithData:_data options:kNilOptions error:&error]; + if (error) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000065", @"Error parsing data as JSON."); + return nil; + } + return JSONObject; +} + +/// Debug description showing the representations of all types. +- (NSString *)debugDescription { + NSString *content = [NSString + stringWithFormat:@"Boolean: %d, String: %@, Number: %@, JSON:%@, Data: %@, Source: %zd", + self.boolValue, self.stringValue, self.numberValue, self.JSONValue, _data, + (long)self.source]; + return [NSString stringWithFormat:@"<%@: %p, %@>", [self class], self, content]; +} + +/// Copy method. +- (id)copyWithZone:(NSZone *)zone { + FIRRemoteConfigValue *value = [[[self class] allocWithZone:zone] initWithData:_data]; + return value; +} +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m new file mode 100644 index 00000000..143b6e6d --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m @@ -0,0 +1,656 @@ +/* + * 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 <FirebaseRemoteConfig/FIRRemoteConfig.h> + +#import <FirebaseABTesting/FIRExperimentController.h> +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h" +#import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h" +#import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h" +#import "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigContent.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigExperiment.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h" +#import "FirebaseRemoteConfig/Sources/RCNDevice.h" + +/// Remote Config Error Domain. +/// TODO: Rename according to obj-c style for constants. +NSString *const FIRRemoteConfigErrorDomain = @"com.google.remoteconfig.ErrorDomain"; +/// Remote Config Error Info End Time Seconds; +NSString *const FIRRemoteConfigThrottledEndTimeInSecondsKey = @"error_throttled_end_time_seconds"; +/// Remote Config Developer Mode Key +static NSString *const kRemoteConfigDeveloperKey = @"_rcn_developer"; +/// Minimum required time interval between fetch requests made to the backend. +static NSString *const kRemoteConfigMinimumFetchIntervalKey = @"_rcn_minimum_fetch_interval"; +/// Timeout value for waiting on a fetch response. +static NSString *const kRemoteConfigFetchTimeoutKey = @"_rcn_fetch_timeout"; + +@interface FIRRemoteConfigSettings () { + BOOL _developerModeEnabled; +} +@end + +// Implementations depend upon multiple deprecated APIs +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +@implementation FIRRemoteConfigSettings +- (instancetype)initWithDeveloperModeEnabled:(BOOL)developerModeEnabled { + self = [self init]; + if (self) { + _developerModeEnabled = developerModeEnabled; + } + return self; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _developerModeEnabled = NO; + _minimumFetchInterval = RCNDefaultMinimumFetchInterval; + _fetchTimeout = RCNHTTPDefaultConnectionTimeout; + } + return self; +} + +- (BOOL)isDeveloperModeEnabled { + return _developerModeEnabled; +} + +@end + +@implementation FIRRemoteConfig { + /// All the config content. + RCNConfigContent *_configContent; + RCNConfigDBManager *_DBManager; + RCNConfigSettings *_settings; + RCNConfigFetch *_configFetch; + RCNConfigExperiment *_configExperiment; + dispatch_queue_t _queue; + NSString *_appName; +} + +static NSMutableDictionary<NSString *, NSMutableDictionary<NSString *, FIRRemoteConfig *> *> + *RCInstances; + ++ (nonnull FIRRemoteConfig *)remoteConfigWithApp:(FIRApp *_Nonnull)firebaseApp { + return [FIRRemoteConfig remoteConfigWithFIRNamespace:FIRNamespaceGoogleMobilePlatform + app:firebaseApp]; +} + ++ (nonnull FIRRemoteConfig *)remoteConfigWithFIRNamespace:(NSString *_Nonnull)firebaseNamespace { + if (![FIRApp isDefaultAppConfigured]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000047", + @"FIRApp not configured. Please make sure you have called [FIRApp configure]"); + // TODO: Maybe throw an exception here? That'd be a breaking change though, but at this point + // RC can't work as expected. + } + + return [FIRRemoteConfig remoteConfigWithFIRNamespace:firebaseNamespace app:[FIRApp defaultApp]]; +} + ++ (nonnull FIRRemoteConfig *)remoteConfigWithFIRNamespace:(NSString *_Nonnull)firebaseNamespace + app:(FIRApp *_Nonnull)firebaseApp { + // Use the provider to generate and return instances of FIRRemoteConfig for this specific app and + // namespace. This will ensure the app is configured before Remote Config can return an instance. + id<FIRRemoteConfigProvider> provider = + FIR_COMPONENT(FIRRemoteConfigProvider, firebaseApp.container); + return [provider remoteConfigForNamespace:firebaseNamespace]; +} + ++ (FIRRemoteConfig *)remoteConfig { + // If the default app is not configured at this point, warn the developer. + if (![FIRApp isDefaultAppConfigured]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000047", + @"FIRApp not configured. Please make sure you have called [FIRApp configure]"); + // TODO: Maybe throw an exception here? That'd be a breaking change though, but at this point + // RC can't work as expected. + } + + return [FIRRemoteConfig remoteConfigWithFIRNamespace:FIRNamespaceGoogleMobilePlatform + app:[FIRApp defaultApp]]; +} + +/// Singleton instance of serial queue for queuing all incoming RC calls. ++ (dispatch_queue_t)sharedRemoteConfigSerialQueue { + static dispatch_once_t onceToken; + static dispatch_queue_t sharedRemoteConfigQueue; + dispatch_once(&onceToken, ^{ + sharedRemoteConfigQueue = + dispatch_queue_create(RCNRemoteConfigQueueLabel, DISPATCH_QUEUE_SERIAL); + }); + return sharedRemoteConfigQueue; +} + +/// Designated initializer +- (instancetype)initWithAppName:(NSString *)appName + FIROptions:(FIROptions *)options + namespace:(NSString *)FIRNamespace + DBManager:(RCNConfigDBManager *)DBManager + configContent:(RCNConfigContent *)configContent + analytics:(nullable id<FIRAnalyticsInterop>)analytics { + self = [super init]; + if (self) { + _appName = appName; + _DBManager = DBManager; + // The fully qualified Firebase namespace is namespace:firappname. + _FIRNamespace = [NSString stringWithFormat:@"%@:%@", FIRNamespace, appName]; + + // Initialize RCConfigContent if not already. + _configContent = configContent; + _settings = [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager + namespace:_FIRNamespace + firebaseAppName:appName + googleAppID:options.googleAppID]; + + FIRExperimentController *experimentController = [FIRExperimentController sharedInstance]; + _configExperiment = [[RCNConfigExperiment alloc] initWithDBManager:_DBManager + experimentController:experimentController]; + /// Serial queue for read and write lock. + _queue = [FIRRemoteConfig sharedRemoteConfigSerialQueue]; + + // Initialize with default config settings. + [self setDefaultConfigSettings]; + _configFetch = [[RCNConfigFetch alloc] initWithContent:_configContent + DBManager:_DBManager + settings:_settings + analytics:analytics + experiment:_configExperiment + queue:_queue + namespace:_FIRNamespace + options:options]; + + [_settings loadConfigFromMetadataTable]; + } + return self; +} + +// Initialize with default config settings. +- (void)setDefaultConfigSettings { + // Set the default config settings. + self->_settings.fetchTimeout = RCNHTTPDefaultConnectionTimeout; + self->_settings.minimumFetchInterval = RCNDefaultMinimumFetchInterval; +} + +- (void)ensureInitializedWithCompletionHandler: + (nonnull FIRRemoteConfigInitializationCompletion)completionHandler { + __weak FIRRemoteConfig *weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ + FIRRemoteConfig *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + BOOL initializationSuccess = [self->_configContent initializationSuccessful]; + NSError *error = nil; + if (!initializationSuccess) { + error = [[NSError alloc] + initWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{NSLocalizedDescriptionKey : @"Timed out waiting for database load."}]; + } + completionHandler(error); + }); +} + +#pragma mark - fetch + +- (void)fetchWithCompletionHandler:(FIRRemoteConfigFetchCompletion)completionHandler { + dispatch_async(_queue, ^{ + [self fetchWithExpirationDuration:self->_settings.minimumFetchInterval + completionHandler:completionHandler]; + }); +} + +- (void)fetchWithExpirationDuration:(NSTimeInterval)expirationDuration + completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler { + FIRRemoteConfigFetchCompletion completionHandlerCopy = nil; + if (completionHandler) { + completionHandlerCopy = [completionHandler copy]; + } + [_configFetch fetchConfigWithExpirationDuration:expirationDuration + completionHandler:completionHandlerCopy]; +} + +#pragma mark - fetchAndActivate + +- (void)fetchAndActivateWithCompletionHandler: + (FIRRemoteConfigFetchAndActivateCompletion)completionHandler { + __weak FIRRemoteConfig *weakSelf = self; + FIRRemoteConfigFetchCompletion fetchCompletion = + ^(FIRRemoteConfigFetchStatus fetchStatus, NSError *fetchError) { + FIRRemoteConfig *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + // Fetch completed. We are being called on the main queue. + // If fetch is successful, try to activate the fetched config + if (fetchStatus == FIRRemoteConfigFetchStatusSuccess && !fetchError) { + [strongSelf activateWithCompletionHandler:^(NSError *_Nullable activateError) { + if (completionHandler) { + FIRRemoteConfigFetchAndActivateStatus status = + activateError ? FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData + : FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote; + completionHandler(status, nil); + } + }]; + } else if (completionHandler) { + FIRRemoteConfigFetchAndActivateStatus status = + fetchStatus == FIRRemoteConfigFetchStatusSuccess + ? FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData + : FIRRemoteConfigFetchAndActivateStatusError; + completionHandler(status, fetchError); + } + }; + [self fetchWithCompletionHandler:fetchCompletion]; +} + +#pragma mark - apply + +- (BOOL)activateFetched { + // TODO: We block on the async activate to complete. This method is deprecated and needs + // to be removed at the next possible breaking change. + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block BOOL didActivate = NO; + [self activateWithCompletionHandler:^(NSError *_Nullable error) { + didActivate = error ? false : true; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + return didActivate; +} + +typedef void (^FIRRemoteConfigActivateChangeCompletion)(BOOL changed, NSError *_Nullable error); + +- (void)activateWithCompletion:(FIRRemoteConfigActivateChangeCompletion)completion { + [self activateWithEitherHandler:completion deprecatedHandler:nil]; +} + +- (void)activateWithCompletionHandler:(FIRRemoteConfigActivateCompletion)completionHandler { + [self activateWithEitherHandler:nil deprecatedHandler:completionHandler]; +} + +- (void)activateWithEitherHandler:(FIRRemoteConfigActivateChangeCompletion)completion + deprecatedHandler:(FIRRemoteConfigActivateCompletion)deprecatedHandler { + __weak FIRRemoteConfig *weakSelf = self; + void (^applyBlock)(void) = ^(void) { + FIRRemoteConfig *strongSelf = weakSelf; + if (!strongSelf) { + NSError *error = [NSError errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{@"ActivationFailureReason" : @"Internal Error."}]; + if (completion) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + completion(NO, error); + }); + } else if (deprecatedHandler) { + deprecatedHandler(error); + } + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000068", @"Internal error activating config."); + return; + } + // Check if the last fetched config has already been activated. Fetches with no data change are + // ignored. + if (strongSelf->_settings.lastETagUpdateTime == 0 || + strongSelf->_settings.lastETagUpdateTime <= strongSelf->_settings.lastApplyTimeInterval) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000069", + @"Most recently fetched config is already activated."); + if (completion) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + completion(NO, nil); + }); + } else if (deprecatedHandler) { + NSError *error = [NSError + errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{ + @"ActivationFailureReason" : @"Most recently fetched config already activated" + }]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + deprecatedHandler(error); + }); + } + return; + } + [strongSelf->_configContent copyFromDictionary:self->_configContent.fetchedConfig + toSource:RCNDBSourceActive + forNamespace:self->_FIRNamespace]; + [strongSelf updateExperiments]; + strongSelf->_settings.lastApplyTimeInterval = [[NSDate date] timeIntervalSince1970]; + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000069", @"Config activated."); + if (completion) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + completion(YES, nil); + }); + } else if (deprecatedHandler) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + deprecatedHandler(nil); + }); + } + }; + dispatch_async(_queue, applyBlock); +} + +- (void)updateExperiments { + [self->_configExperiment updateExperiments]; +} + +#pragma mark - helpers +- (NSString *)fullyQualifiedNamespace:(NSString *)namespace { + // If this is already a fully qualified namespace, return. + if ([namespace rangeOfString:@":"].location != NSNotFound) { + return namespace; + } + NSString *fullyQualifiedNamespace = [NSString stringWithFormat:@"%@:%@", namespace, _appName]; + return fullyQualifiedNamespace; +} + +#pragma mark - Get Config Result + +- (FIRRemoteConfigValue *)objectForKeyedSubscript:(NSString *)key { + return [self configValueForKey:key]; +} + +- (FIRRemoteConfigValue *)configValueForKey:(NSString *)key { + return [self configValueForKey:key namespace:_FIRNamespace]; +} + +- (FIRRemoteConfigValue *)configValueForKey:(NSString *)key namespace:(NSString *)aNamespace { + if (!key || !aNamespace) { + return [[FIRRemoteConfigValue alloc] initWithData:[NSData data] + source:FIRRemoteConfigSourceStatic]; + } + NSString *FQNamespace = [self fullyQualifiedNamespace:aNamespace]; + + __block FIRRemoteConfigValue *value; + dispatch_sync(_queue, ^{ + value = self->_configContent.activeConfig[FQNamespace][key]; + if (value) { + if (value.source != FIRRemoteConfigSourceRemote) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000001", + @"Key %@ should come from source:%zd instead coming from source: %zd.", key, + (long)FIRRemoteConfigSourceRemote, (long)value.source); + } + return; + } + value = self->_configContent.defaultConfig[FQNamespace][key]; + if (value) { + return; + } + + value = [[FIRRemoteConfigValue alloc] initWithData:[NSData data] + source:FIRRemoteConfigSourceStatic]; + }); + return value; +} + +- (FIRRemoteConfigValue *)configValueForKey:(NSString *)key source:(FIRRemoteConfigSource)source { + return [self configValueForKey:key namespace:_FIRNamespace source:source]; +} + +- (FIRRemoteConfigValue *)configValueForKey:(NSString *)key + namespace:(NSString *)aNamespace + source:(FIRRemoteConfigSource)source { + if (!key || !aNamespace) { + return [[FIRRemoteConfigValue alloc] initWithData:[NSData data] + source:FIRRemoteConfigSourceStatic]; + } + NSString *FQNamespace = [self fullyQualifiedNamespace:aNamespace]; + + __block FIRRemoteConfigValue *value; + dispatch_sync(_queue, ^{ + if (source == FIRRemoteConfigSourceRemote) { + value = self->_configContent.activeConfig[FQNamespace][key]; + } else if (source == FIRRemoteConfigSourceDefault) { + value = self->_configContent.defaultConfig[FQNamespace][key]; + } else { + value = [[FIRRemoteConfigValue alloc] initWithData:[NSData data] + source:FIRRemoteConfigSourceStatic]; + } + }); + return value; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained[])stackbuf + count:(NSUInteger)len { + __block NSUInteger localValue; + dispatch_sync(_queue, ^{ + localValue = + [self->_configContent.activeConfig[self->_FIRNamespace] countByEnumeratingWithState:state + objects:stackbuf + count:len]; + }); + return localValue; +} + +#pragma mark - Properties + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-property-ivar" +/// Last fetch completion time. +- (NSDate *)lastFetchTime { + __block NSDate *fetchTime; + dispatch_sync(_queue, ^{ + NSTimeInterval lastFetchTime = self->_settings.lastFetchTimeInterval; + fetchTime = [NSDate dateWithTimeIntervalSince1970:lastFetchTime]; + }); + return fetchTime; +} +#pragma clang diagnostic pop + +- (FIRRemoteConfigFetchStatus)lastFetchStatus { + __block FIRRemoteConfigFetchStatus currentStatus; + dispatch_sync(_queue, ^{ + currentStatus = self->_settings.lastFetchStatus; + }); + return currentStatus; +} + +- (NSArray *)allKeysFromSource:(FIRRemoteConfigSource)source { + return [self allKeysFromSource:source namespace:_FIRNamespace]; +} + +- (NSArray *)allKeysFromSource:(FIRRemoteConfigSource)source namespace:(NSString *)aNamespace { + __block NSArray *keys = [[NSArray alloc] init]; + dispatch_sync(_queue, ^{ + if (!aNamespace) { + return; + } + NSString *FQNamespace = [self fullyQualifiedNamespace:aNamespace]; + switch (source) { + case FIRRemoteConfigSourceDefault: + if (self->_configContent.defaultConfig[FQNamespace]) { + keys = [[self->_configContent.defaultConfig[FQNamespace] allKeys] copy]; + } + break; + case FIRRemoteConfigSourceRemote: + if (self->_configContent.activeConfig[FQNamespace]) { + keys = [[self->_configContent.activeConfig[FQNamespace] allKeys] copy]; + } + break; + default: + break; + } + }); + return keys; +} + +- (nonnull NSSet *)keysWithPrefix:(nullable NSString *)prefix { + return [self keysWithPrefix:prefix namespace:_FIRNamespace]; +} + +- (nonnull NSSet *)keysWithPrefix:(nullable NSString *)prefix + namespace:(nullable NSString *)aNamespace { + __block NSMutableSet *keys = [[NSMutableSet alloc] init]; + __block NSString *namespaceToCheck = aNamespace; + dispatch_sync(_queue, ^{ + if (!namespaceToCheck.length) { + return; + } + NSString *FQNamespace = [self fullyQualifiedNamespace:namespaceToCheck]; + if (self->_configContent.activeConfig[FQNamespace]) { + NSArray *allKeys = [self->_configContent.activeConfig[FQNamespace] allKeys]; + if (!prefix.length) { + keys = [NSMutableSet setWithArray:allKeys]; + } else { + for (NSString *key in allKeys) { + if ([key hasPrefix:prefix]) { + [keys addObject:key]; + } + } + } + } + }); + return [keys copy]; +} + +#pragma mark - Defaults + +- (void)setDefaults:(NSDictionary<NSString *, NSObject *> *)defaults { + [self setDefaults:defaults namespace:_FIRNamespace]; +} + +- (void)setDefaults:(NSDictionary<NSString *, NSObject *> *)defaultConfig + namespace:(NSString *)aNamespace { + if (!aNamespace) { + FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000036", @"The namespace cannot be empty or nil."); + return; + } + NSString *FQNamespace = [self fullyQualifiedNamespace:aNamespace]; + NSDictionary *defaultConfigCopy = [[NSDictionary alloc] init]; + if (defaultConfig) { + defaultConfigCopy = [defaultConfig copy]; + } + void (^setDefaultsBlock)(void) = ^(void) { + NSDictionary *namespaceToDefaults = @{FQNamespace : defaultConfigCopy}; + [self->_configContent copyFromDictionary:namespaceToDefaults + toSource:RCNDBSourceDefault + forNamespace:FQNamespace]; + self->_settings.lastSetDefaultsTimeInterval = [[NSDate date] timeIntervalSince1970]; + }; + dispatch_async(_queue, setDefaultsBlock); +} + +- (FIRRemoteConfigValue *)defaultValueForKey:(NSString *)key { + return [self defaultValueForKey:key namespace:_FIRNamespace]; +} + +- (FIRRemoteConfigValue *)defaultValueForKey:(NSString *)key namespace:(NSString *)aNamespace { + if (!key || !aNamespace) { + return nil; + } + NSString *FQNamespace = [self fullyQualifiedNamespace:aNamespace]; + __block FIRRemoteConfigValue *value; + dispatch_sync(_queue, ^{ + NSDictionary *defaultConfig = self->_configContent.defaultConfig; + value = defaultConfig[FQNamespace][key]; + if (value) { + if (value.source != FIRRemoteConfigSourceDefault) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000002", + @"Key %@ should come from source:%zd instead coming from source: %zd", key, + (long)FIRRemoteConfigSourceDefault, (long)value.source); + } + } + }); + return value; +} + +- (void)setDefaultsFromPlistFileName:(nullable NSString *)fileName { + return [self setDefaultsFromPlistFileName:fileName namespace:_FIRNamespace]; +} + +- (void)setDefaultsFromPlistFileName:(nullable NSString *)fileName + namespace:(nullable NSString *)namespace { + if (!namespace || namespace.length == 0) { + FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000036", @"The namespace cannot be empty or nil."); + return; + } + NSString *FQNamespace = [self fullyQualifiedNamespace:namespace]; + if (!fileName || fileName.length == 0) { + FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000037", + @"The plist file '%@' could not be found by Remote Config.", fileName); + return; + } + NSArray *bundles = @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ]; + + for (NSBundle *bundle in bundles) { + NSString *plistFile = [bundle pathForResource:fileName ofType:@"plist"]; + // Use the first one we find. + if (plistFile) { + NSDictionary *defaultConfig = [[NSDictionary alloc] initWithContentsOfFile:plistFile]; + if (defaultConfig) { + [self setDefaults:defaultConfig namespace:FQNamespace]; + } + return; + } + } + FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000037", + @"The plist file '%@' could not be found by Remote Config.", fileName); +} + +#pragma mark - custom variables + +- (FIRRemoteConfigSettings *)configSettings { + __block BOOL developerModeEnabled = NO; + __block NSTimeInterval minimumFetchInterval = RCNDefaultMinimumFetchInterval; + __block NSTimeInterval fetchTimeout = RCNHTTPDefaultConnectionTimeout; + dispatch_sync(_queue, ^{ + developerModeEnabled = [self->_settings.customVariables[kRemoteConfigDeveloperKey] boolValue]; + minimumFetchInterval = self->_settings.minimumFetchInterval; + fetchTimeout = self->_settings.fetchTimeout; + }); + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000066", + @"Successfully read configSettings. Developer Mode: %@, Minimum Fetch Interval:%f, " + @"Fetch timeout: %f", + developerModeEnabled ? @"true" : @"false", minimumFetchInterval, fetchTimeout); + FIRRemoteConfigSettings *settings = + [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:developerModeEnabled]; + settings.minimumFetchInterval = minimumFetchInterval; + settings.fetchTimeout = fetchTimeout; + /// The NSURLSession needs to be recreated whenever the fetch timeout may be updated. + [_configFetch recreateNetworkSession]; + return settings; +} + +- (void)setConfigSettings:(FIRRemoteConfigSettings *)configSettings { + void (^setConfigSettingsBlock)(void) = ^(void) { + if (!configSettings) { + return; + } + + NSDictionary *settingsToSave = @{ + kRemoteConfigDeveloperKey : @(configSettings.isDeveloperModeEnabled), + }; + self->_settings.customVariables = settingsToSave; + self->_settings.minimumFetchInterval = configSettings.minimumFetchInterval; + self->_settings.fetchTimeout = configSettings.fetchTimeout; + /// The NSURLSession needs to be recreated whenever the fetch timeout may be updated. + [self->_configFetch recreateNetworkSession]; + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000067", + @"Successfully set configSettings. Developer Mode: %@, Minimum Fetch Interval:%f, " + @"Fetch timeout:%f", + configSettings.isDeveloperModeEnabled ? @"true" : @"false", + configSettings.minimumFetchInterval, configSettings.fetchTimeout); + }; + dispatch_async(_queue, setConfigSettingsBlock); +} + +#pragma clang diagnostic push // "-Wdeprecated-declarations" + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h new file mode 100644 index 00000000..f31d5c4e --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h @@ -0,0 +1,58 @@ +/* + * 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 <Foundation/Foundation.h> + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" + +@class FIRApp; +@class FIRRemoteConfig; + +NS_ASSUME_NONNULL_BEGIN + +/// Provides and creates instances of Remote Config based on the namespace provided. Used in the +/// interop registration process to keep track of RC instances for each `FIRApp` instance. +@protocol FIRRemoteConfigProvider + +/// Cached instances of Remote Config objects. +@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRRemoteConfig *> *instances; + +/// Default method for retrieving a Remote Config instance, or creating one if it doesn't exist. +- (FIRRemoteConfig *)remoteConfigForNamespace:(NSString *)remoteConfigNamespace; + +@end + +/// A concrete implementation for FIRRemoteConfigInterop to create Remote Config instances and +/// register with Core's component system. +@interface FIRRemoteConfigComponent : NSObject <FIRRemoteConfigProvider, FIRLibrary> + +/// The FIRApp that instances will be set up with. +@property(nonatomic, weak, readonly) FIRApp *app; + +/// Cached instances of Remote Config objects. +@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRRemoteConfig *> *instances; + +/// Default method for retrieving a Remote Config instance, or creating one if it doesn't exist. +- (FIRRemoteConfig *)remoteConfigForNamespace:(NSString *)remoteConfigNamespace; + +/// Default initializer. +- (instancetype)initWithApp:(FIRApp *)app NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __attribute__((unavailable("Use `initWithApp:`."))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m new file mode 100644 index 00000000..b82c13dc --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m @@ -0,0 +1,117 @@ +/* + * 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 "FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h" + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigContent.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h" +#import "Interop/Analytics/Public/FIRAnalyticsInterop.h" + +#ifndef FIRRemoteConfig_VERSION +#error "FIRRemoteConfig_VERSION is not defined: \ +add -DFIRRemoteConfig_VERSION=... to the build invocation" +#endif + +#define STR(x) STR_EXPAND(x) +#define STR_EXPAND(x) #x + +@implementation FIRRemoteConfigComponent + +/// Default method for retrieving a Remote Config instance, or creating one if it doesn't exist. +- (FIRRemoteConfig *)remoteConfigForNamespace:(NSString *)remoteConfigNamespace { + if (!remoteConfigNamespace) { + // TODO: Throw an error? Return nil? What do we want to do? + return nil; + } + + // Validate the required information is available. + FIROptions *options = self.app.options; + NSString *errorPropertyName; + if (options.googleAppID.length == 0) { + errorPropertyName = @"googleAppID"; + } else if (options.GCMSenderID.length == 0) { + errorPropertyName = @"GCMSenderID"; + } + + if (errorPropertyName) { + [NSException + raise:kFirebaseConfigErrorDomain + format:@"%@", + [NSString + stringWithFormat: + @"Firebase Remote Config is missing the required %@ property from the " + @"configured FirebaseApp and will not be able to function properly. Please " + @"fix this issue to ensure that Firebase is correctly configured.", + errorPropertyName]]; + } + + FIRRemoteConfig *instance = self.instances[remoteConfigNamespace]; + if (!instance) { + FIRApp *app = self.app; + id<FIRAnalyticsInterop> analytics = + app.isDefaultApp ? FIR_COMPONENT(FIRAnalyticsInterop, app.container) : nil; + instance = [[FIRRemoteConfig alloc] initWithAppName:app.name + FIROptions:app.options + namespace:remoteConfigNamespace + DBManager:[RCNConfigDBManager sharedInstance] + configContent:[RCNConfigContent sharedInstance] + analytics:analytics]; + self.instances[remoteConfigNamespace] = instance; + } + + return instance; +} + +/// Default initializer. +- (instancetype)initWithApp:(FIRApp *)app { + self = [super init]; + if (self) { + _app = app; + _instances = [[NSMutableDictionary alloc] initWithCapacity:1]; + } + return self; +} + +#pragma mark - Lifecycle + ++ (void)load { + // Register as an internal library to be part of the initialization process. The name comes from + // go/firebase-sdk-platform-info. + [FIRApp registerInternalLibrary:self + withName:@"fire-rc" + withVersion:[NSString stringWithUTF8String:STR(FIRRemoteConfig_VERSION)]]; +} + +#pragma mark - Interoperability + ++ (NSArray<FIRComponent *> *)componentsToRegister { + FIRDependency *analyticsDep = [FIRDependency dependencyWithProtocol:@protocol(FIRAnalyticsInterop) + isRequired:NO]; + FIRComponent *rcProvider = [FIRComponent + componentWithProtocol:@protocol(FIRRemoteConfigProvider) + instantiationTiming:FIRInstantiationTimingAlwaysEager + dependencies:@[ analyticsDep ] + creationBlock:^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) { + // Cache the component so instances of Remote Config are cached. + *isCacheable = YES; + return [[FIRRemoteConfigComponent alloc] initWithApp:container.app]; + }]; + return @[ rcProvider ]; +} + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h new file mode 100644 index 00000000..8c58d852 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h @@ -0,0 +1,75 @@ +/* + * 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 <FirebaseRemoteConfig/FIRRemoteConfig.h> + +#import <FirebaseRemoteConfig/RCNConfigFetch.h> +#import <FirebaseRemoteConfig/RCNConfigSettings.h> +#import "Interop/Analytics/Public/FIRAnalyticsInterop.h" + +@class FIROptions; +@class RCNConfigContent; +@class RCNConfigDBManager; + +NS_ASSUME_NONNULL_BEGIN + +@class RCNConfigSettings; + +@interface FIRRemoteConfig () { + NSString *_FIRNamespace; +} + +/// Internal settings +@property(nonatomic, readonly, strong) RCNConfigSettings *settings; + +/// Config settings are custom settings. +@property(nonatomic, readwrite, strong, nonnull) RCNConfigFetch *configFetch; + +/// Returns the FIRRemoteConfig instance for your namespace and for the default Firebase App. +/// This singleton object contains the complete set of Remote Config parameter values available to +/// the app, including the Active Config and Default Config.. This object also caches values fetched +/// from the Remote Config Server until they are copied to the Active Config by calling +/// activateFetched. When you fetch values from the Remote Config Server using the default Firebase +/// namespace service, you should use this class method to create a shared instance of the +/// FIRRemoteConfig object to ensure that your app will function properly with the Remote Config +/// Server and the Firebase service. This API is used internally by 2P teams. ++ (FIRRemoteConfig *)remoteConfigWithFIRNamespace:(NSString *)remoteConfigNamespace + NS_SWIFT_NAME(remoteConfig(FIRNamespace:)); + +/// Returns the FIRRemoteConfig instance for your namespace and for the default 3P developer's app. +/// This singleton object contains the complete set of Remote Config parameter values available to +/// the app, including the Active Config and Default Config. This object also caches values fetched +/// from the Remote Config Server until they are copied to the Active Config by calling +/// activateFetched. When you fetch values from the Remote Config Server using the default Firebase +/// namespace service, you should use this class method to create a shared instance of the +/// FIRRemoteConfig object to ensure that your app will function properly with the Remote Config +/// Server and the Firebase service. ++ (FIRRemoteConfig *)remoteConfigWithFIRNamespace:(NSString *)remoteConfigNamespace + app:(FIRApp *)app + NS_SWIFT_NAME(remoteConfig(FIRNamespace:app:)); + +/// Initialize a FIRRemoteConfig instance with all the required parameters directly. This exists so +/// tests can create FIRRemoteConfig objects without needing FIRApp. +- (instancetype)initWithAppName:(NSString *)appName + FIROptions:(FIROptions *)options + namespace:(NSString *)FIRNamespace + DBManager:(RCNConfigDBManager *)DBManager + configContent:(RCNConfigContent *)configContent + analytics:(nullable id<FIRAnalyticsInterop>)analytics; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h new file mode 100644 index 00000000..00c18a11 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h @@ -0,0 +1,61 @@ +/* + * 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 <Foundation/Foundation.h> + +#import <FirebaseRemoteConfig/FIRRemoteConfig.h> +#import "Interop/Analytics/Public/FIRAnalyticsInterop.h" + +@class FIROptions; +@class RCNConfigContent; +@class RCNConfigSettings; +@class RCNConfigExperiment; +@class RCNConfigDBManager; + +NS_ASSUME_NONNULL_BEGIN + +/// Completion handler invoked by NSSessionFetcher. +typedef void (^RCNConfigFetcherCompletion)(NSData *data, NSURLResponse *response, NSError *error); + +@interface RCNConfigFetch : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer +- (instancetype)initWithContent:(RCNConfigContent *)content + DBManager:(RCNConfigDBManager *)DBManager + settings:(RCNConfigSettings *)settings + analytics:(nullable id<FIRAnalyticsInterop>)analytics + experiment:(nullable RCNConfigExperiment *)experiment + queue:(dispatch_queue_t)queue + namespace:(NSString *)firebaseNamespace + options:(FIROptions *)firebaseOptions NS_DESIGNATED_INITIALIZER; + +/// Fetches config data keyed by namespace. Completion block will be called on the main queue. +/// @param expirationDuration Expiration duration, in seconds. +/// @param completionHandler Callback handler. +- (void)fetchConfigWithExpirationDuration:(NSTimeInterval)expirationDuration + completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler; + +/// Add the ability to update NSURLSession's timeout after a session has already been created. +- (void)recreateNetworkSession; + +/// Provide fetchSession for tests to override. +@property(nonatomic, readwrite, strong, nonnull) NSURLSession *fetchSession; + +NS_ASSUME_NONNULL_END + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h new file mode 100644 index 00000000..0d077c55 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h @@ -0,0 +1,123 @@ +/* + * 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 <Foundation/Foundation.h> + +#import <FirebaseRemoteConfig/FIRRemoteConfig.h> + +@class RCNConfigDBManager; + +/// This internal class contains a set of variables that are unique among all the config instances. +/// It also handles all metadata and internal metadata. This class is not thread safe and does not +/// inherently allow for synchronized accesss. Callers are responsible for synchronization +/// (currently using serial dispatch queues). +@interface RCNConfigSettings : NSObject + +/// The time interval that config data stays fresh. +@property(nonatomic, readwrite, assign) NSTimeInterval minimumFetchInterval; + +/// The timeout to set for outgoing fetch requests. +@property(nonatomic, readwrite, assign) NSTimeInterval fetchTimeout; + +#pragma mark - Data required by config request. +/// Device authentication ID required by config request. +@property(nonatomic, copy) NSString *deviceAuthID; +/// Secret Token required by config request. +@property(nonatomic, copy) NSString *secretToken; +/// Device data version of checkin information. +@property(nonatomic, copy) NSString *deviceDataVersion; +/// InstallationsID. +@property(nonatomic, copy) NSString *configInstallationsIdentifier; +/// Installations token. +@property(nonatomic, copy) NSString *configInstallationsToken; + +/// A list of successful fetch timestamps in milliseconds. +/// TODO Not used anymore. Safe to remove. +@property(nonatomic, readonly, copy) NSArray *successFetchTimes; +/// A list of failed fetch timestamps in milliseconds. +@property(nonatomic, readonly, copy) NSArray *failureFetchTimes; +/// Custom variable (aka App context digest). This is the pending custom variables request before +/// fetching. +@property(nonatomic, copy) NSDictionary *customVariables; +/// Cached internal metadata from internal metadata table. It contains customized information such +/// as HTTP connection timeout, HTTP read timeout, success/failure throttling rate and time +/// interval. Client has the default value of each parameters, they are only saved in +/// internalMetadata if they have been customize by developers. +@property(nonatomic, readonly, copy) NSDictionary *internalMetadata; +/// Device conditions since last successful fetch from the backend. Device conditions including +/// app +/// version, iOS version, device localte, language, GMP project ID and Game project ID. Used for +/// determing whether to throttle. +@property(nonatomic, readonly, copy) NSDictionary *deviceContext; +/// Bundle Identifier +@property(nonatomic, readonly, copy) NSString *bundleIdentifier; +/// The time of last successful config fetch. +@property(nonatomic, readonly, assign) NSTimeInterval lastFetchTimeInterval; +/// Last fetch status. +@property(nonatomic, readwrite, assign) FIRRemoteConfigFetchStatus lastFetchStatus; +/// The reason that last fetch failed. +@property(nonatomic, readwrite, assign) FIRRemoteConfigError lastFetchError; +/// The time of last apply timestamp. +@property(nonatomic, readwrite, assign) NSTimeInterval lastApplyTimeInterval; +/// The time of last setDefaults timestamp. +@property(nonatomic, readwrite, assign) NSTimeInterval lastSetDefaultsTimeInterval; +/// The latest eTag value stored from the last successful response. +@property(nonatomic, readwrite, assign) NSString *lastETag; +/// The timestamp of the last eTag update. +@property(nonatomic, readwrite, assign) NSTimeInterval lastETagUpdateTime; + +#pragma mark Throttling properties + +/// Throttling intervals are based on https://cloud.google.com/storage/docs/exponential-backoff +/// Returns true if client has fetched config and has not got back from server. This is used to +/// determine whether there is another config task infight when fetching. +@property(atomic, readwrite, assign) BOOL isFetchInProgress; +/// Returns the current retry interval in seconds set for exponential backoff. +@property(nonatomic, readwrite, assign) double exponentialBackoffRetryInterval; +/// Returns the time in seconds until the next request is allowed while in exponential backoff mode. +@property(nonatomic, readonly, assign) NSTimeInterval exponentialBackoffThrottleEndTime; + +#pragma mark Throttling Methods + +/// Designated initializer. +- (instancetype)initWithDatabaseManager:(RCNConfigDBManager *)manager + namespace:(NSString *)FIRNamespace + firebaseAppName:(NSString *)appName + googleAppID:(NSString *)googleAppID; + +/// Returns a fetch request with the latest device and config change. +/// Whenever user issues a fetch api call, collect the latest request. +/// @param userProperties User properties to set to config request. +/// @return Config fetch request string +- (NSString *)nextRequestWithUserProperties:(NSDictionary *)userProperties; + +/// Returns metadata from metadata table. +- (NSDictionary *)loadConfigFromMetadataTable; + +/// Updates internal content with the latest successful config response. +- (void)updateInternalContentWithResponse:(NSDictionary *)response; + +/// Updates the metadata table with the current fetch status. +/// @param fetchSuccess True if fetch was successful. +- (void)updateMetadataWithFetchSuccessStatus:(BOOL)fetchSuccess; + +/// Returns true if we are in exponential backoff mode and it is not yet the next request time. +- (BOOL)shouldThrottle; + +/// Returns true if the last fetch is outside the minimum fetch interval supplied. +- (BOOL)hasMinimumFetchIntervalElapsed:(NSTimeInterval)minimumFetchInterval; + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Public/FIRRemoteConfig.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Public/FIRRemoteConfig.h new file mode 100644 index 00000000..b51847b2 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Public/FIRRemoteConfig.h @@ -0,0 +1,376 @@ +/* + * 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 <Foundation/Foundation.h> + +@class FIRApp; + +/// The Firebase Remote Config service default namespace, to be used if the API method does not +/// specify a different namespace. Use the default namespace if configuring from the Google Firebase +/// service. +extern NSString *const _Nonnull FIRNamespaceGoogleMobilePlatform NS_SWIFT_NAME( + NamespaceGoogleMobilePlatform); + +/// Key used to manage throttling in NSError user info when the refreshing of Remote Config +/// parameter values (data) is throttled. The value of this key is the elapsed time since 1970, +/// measured in seconds. +extern NSString *const _Nonnull FIRRemoteConfigThrottledEndTimeInSecondsKey NS_SWIFT_NAME( + RemoteConfigThrottledEndTimeInSecondsKey); + +/// Indicates whether updated data was successfully fetched. +typedef NS_ENUM(NSInteger, FIRRemoteConfigFetchStatus) { + /// Config has never been fetched. + FIRRemoteConfigFetchStatusNoFetchYet, + /// Config fetch succeeded. + FIRRemoteConfigFetchStatusSuccess, + /// Config fetch failed. + FIRRemoteConfigFetchStatusFailure, + /// Config fetch was throttled. + FIRRemoteConfigFetchStatusThrottled, +} NS_SWIFT_NAME(RemoteConfigFetchStatus); + +/// Indicates whether updated data was successfully fetched and activated. +typedef NS_ENUM(NSInteger, FIRRemoteConfigFetchAndActivateStatus) { + /// The remote fetch succeeded and fetched data was activated. + FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote, + /// The fetch and activate succeeded from already fetched but yet unexpired config data. You can + /// control this using minimumFetchInterval property in FIRRemoteConfigSettings. + FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData, + /// The fetch and activate failed. + FIRRemoteConfigFetchAndActivateStatusError +} NS_SWIFT_NAME(RemoteConfigFetchAndActivateStatus); + +/// Remote Config error domain that handles errors when fetching data from the service. +extern NSString *const _Nonnull FIRRemoteConfigErrorDomain NS_SWIFT_NAME(RemoteConfigErrorDomain); +/// Firebase Remote Config service fetch error. +typedef NS_ENUM(NSInteger, FIRRemoteConfigError) { + /// Unknown or no error. + FIRRemoteConfigErrorUnknown = 8001, + /// Frequency of fetch requests exceeds throttled limit. + FIRRemoteConfigErrorThrottled = 8002, + /// Internal error that covers all internal HTTP errors. + FIRRemoteConfigErrorInternalError = 8003, +} NS_SWIFT_NAME(RemoteConfigError); + +/// Enumerated value that indicates the source of Remote Config data. Data can come from +/// the Remote Config service, the DefaultConfig that is available when the app is first installed, +/// or a static initialized value if data is not available from the service or DefaultConfig. +typedef NS_ENUM(NSInteger, FIRRemoteConfigSource) { + FIRRemoteConfigSourceRemote, ///< The data source is the Remote Config service. + FIRRemoteConfigSourceDefault, ///< The data source is the DefaultConfig defined for this app. + FIRRemoteConfigSourceStatic, ///< The data doesn't exist, return a static initialized value. +} NS_SWIFT_NAME(RemoteConfigSource); + +/// Completion handler invoked by fetch methods when they get a response from the server. +/// +/// @param status Config fetching status. +/// @param error Error message on failure. +typedef void (^FIRRemoteConfigFetchCompletion)(FIRRemoteConfigFetchStatus status, + NSError *_Nullable error) + NS_SWIFT_NAME(RemoteConfigFetchCompletion); + +/// Completion handler invoked by activate method upon completion. +/// @param error Error message on failure. Nil if activation was successful. +typedef void (^FIRRemoteConfigActivateCompletion)(NSError *_Nullable error) + NS_SWIFT_NAME(RemoteConfigActivateCompletion); + +/// Completion handler invoked upon completion of Remote Config initialization. +/// +/// @param initializationError nil if initialization succeeded. +typedef void (^FIRRemoteConfigInitializationCompletion)(NSError *_Nullable initializationError) + NS_SWIFT_NAME(RemoteConfigInitializationCompletion); + +/// Completion handler invoked by the fetchAndActivate method. Used to convey status of fetch and, +/// if successful, resultant activate call +/// @param status Config fetching status. +/// @param error Error message on failure of config fetch +typedef void (^FIRRemoteConfigFetchAndActivateCompletion)( + FIRRemoteConfigFetchAndActivateStatus status, NSError *_Nullable error) + NS_SWIFT_NAME(RemoteConfigFetchAndActivateCompletion); + +#pragma mark - FIRRemoteConfigValue +/// This class provides a wrapper for Remote Config parameter values, with methods to get parameter +/// values as different data types. +NS_SWIFT_NAME(RemoteConfigValue) +@interface FIRRemoteConfigValue : NSObject <NSCopying> +/// Gets the value as a string. +@property(nonatomic, readonly, nullable) NSString *stringValue; +/// Gets the value as a number value. +@property(nonatomic, readonly, nullable) NSNumber *numberValue; +/// Gets the value as a NSData object. +@property(nonatomic, readonly, nonnull) NSData *dataValue; +/// Gets the value as a boolean. +@property(nonatomic, readonly) BOOL boolValue; +/// Gets a foundation object (NSDictionary / NSArray) by parsing the value as JSON. This method uses +/// NSJSONSerialization's JSONObjectWithData method with an options value of 0. +@property(nonatomic, readonly, nullable) id JSONValue NS_SWIFT_NAME(jsonValue); +/// Identifies the source of the fetched value. +@property(nonatomic, readonly) FIRRemoteConfigSource source; +@end + +#pragma mark - FIRRemoteConfigSettings +/// Firebase Remote Config settings. +NS_SWIFT_NAME(RemoteConfigSettings) +@interface FIRRemoteConfigSettings : NSObject +/// Indicates the default value in seconds to set for the minimum interval that needs to elapse +/// before a fetch request can again be made to the Remote Config backend. After a fetch request to +/// the backend has succeeded, no additional fetch requests to the backend will be allowed until the +/// minimum fetch interval expires. Note that you can override this default on a per-fetch request +/// basis using -[FIRRemoteConfig fetchWithExpirationDuration:completionHandler]. For E.g. setting +/// the expiration duration to 0 in the fetch request will override the minimumFetchInterval and +/// allow the request to the backend. +@property(nonatomic, assign) NSTimeInterval minimumFetchInterval; +/// Indicates the default value in seconds to abandon a pending fetch request made to the backend. +/// This value is set for outgoing requests as the timeoutIntervalForRequest as well as the +/// timeoutIntervalForResource on the NSURLSession's configuration. +@property(nonatomic, assign) NSTimeInterval fetchTimeout; +/// Indicates whether Developer Mode is enabled. +@property(nonatomic, readonly) BOOL isDeveloperModeEnabled DEPRECATED_MSG_ATTRIBUTE( + "This no longer needs to be set during development. Refer to documentation for additional " + "details."); +/// Initializes FIRRemoteConfigSettings, which is used to set properties for custom settings. To +/// make custom settings take effect, pass the FIRRemoteConfigSettings instance to the +/// configSettings property of FIRRemoteConfig. +- (nonnull FIRRemoteConfigSettings *)initWithDeveloperModeEnabled:(BOOL)developerModeEnabled + DEPRECATED_MSG_ATTRIBUTE("This no longer needs to be set during development. Refer to " + "documentation for additional details."); +@end + +#pragma mark - FIRRemoteConfig +/// Firebase Remote Config class. The shared instance method +remoteConfig can be created and used +/// to fetch, activate and read config results and set default config results. +NS_SWIFT_NAME(RemoteConfig) +@interface FIRRemoteConfig : NSObject <NSFastEnumeration> +/// Last successful fetch completion time. +@property(nonatomic, readwrite, strong, nullable) NSDate *lastFetchTime; +/// Last fetch status. The status can be any enumerated value from FIRRemoteConfigFetchStatus. +@property(nonatomic, readonly, assign) FIRRemoteConfigFetchStatus lastFetchStatus; +/// Config settings are custom settings. +@property(nonatomic, readwrite, strong, nonnull) FIRRemoteConfigSettings *configSettings; + +/// Returns the FIRRemoteConfig instance configured for the default Firebase app. This singleton +/// object contains the complete set of Remote Config parameter values available to the app, +/// including the Active Config and Default Config. This object also caches values fetched from the +/// Remote Config Server until they are copied to the Active Config by calling activateFetched. When +/// you fetch values from the Remote Config Server using the default Firebase namespace service, you +/// should use this class method to create a shared instance of the FIRRemoteConfig object to ensure +/// that your app will function properly with the Remote Config Server and the Firebase service. ++ (nonnull FIRRemoteConfig *)remoteConfig NS_SWIFT_NAME(remoteConfig()); + +/// Returns the FIRRemoteConfig instance for your (non-default) Firebase appID. Note that Firebase +/// analytics does not work for non-default app instances. This singleton object contains the +/// complete set of Remote Config parameter values available to the app, including the Active Config +/// and Default Config. This object also caches values fetched from the Remote Config Server until +/// they are copied to the Active Config by calling activateFetched. When you fetch values from the +/// Remote Config Server using the default Firebase namespace service, you should use this class +/// method to create a shared instance of the FIRRemoteConfig object to ensure that your app will +/// function properly with the Remote Config Server and the Firebase service. ++ (nonnull FIRRemoteConfig *)remoteConfigWithApp:(nonnull FIRApp *)app + NS_SWIFT_NAME(remoteConfig(app:)); + +/// Unavailable. Use +remoteConfig instead. +- (nonnull instancetype)init __attribute__((unavailable("Use +remoteConfig instead."))); + +/// Ensures initialization is complete and clients can begin querying for Remote Config values. +/// @param completionHandler Initialization complete callback with error parameter. +- (void)ensureInitializedWithCompletionHandler: + (void (^_Nonnull)(NSError *_Nullable initializationError))completionHandler; +#pragma mark - Fetch +/// Fetches Remote Config data with a callback. Call activateFetched to make fetched data available +/// to your app. +/// +/// Note: This method uses a Firebase Installations token to identify the app instance, and once +/// it's called, it periodically sends data to the Firebase backend. (see +/// `[FIRInstallations authTokenWithCompletion:]`). +/// To stop the periodic sync, developers need to call `[FIRInstallations deleteWithCompletion:]` +/// and avoid calling this method again. +/// +/// @param completionHandler Fetch operation callback with status and error parameters. +- (void)fetchWithCompletionHandler:(void (^_Nullable)(FIRRemoteConfigFetchStatus status, + NSError *_Nullable error))completionHandler; + +/// Fetches Remote Config data and sets a duration that specifies how long config data lasts. +/// Call activateFetched to make fetched data available to your app. +/// +/// Note: This method uses a Firebase Installations token to identify the app instance, and once +/// it's called, it periodically sends data to the Firebase backend. (see +/// `[FIRInstallations authTokenWithCompletion:]`). +/// To stop the periodic sync, developers need to call `[FIRInstallations deleteWithCompletion:]` +/// and avoid calling this method again. +/// +/// @param expirationDuration Override the (default or optionally set minimumFetchInterval property +/// in FIRRemoteConfigSettings) minimumFetchInterval for only the current request, in seconds. +/// Setting a value of 0 seconds will force a fetch to the backend. +/// @param completionHandler Fetch operation callback with status and error parameters. +- (void)fetchWithExpirationDuration:(NSTimeInterval)expirationDuration + completionHandler:(void (^_Nullable)(FIRRemoteConfigFetchStatus status, + NSError *_Nullable error))completionHandler; + +/// Fetches Remote Config data and if successful, activates fetched data. Optional completion +/// handler callback is invoked after the attempted activation of data, if the fetch call succeeded. +/// +/// Note: This method uses a Firebase Installations token to identify the app instance, and once +/// it's called, it periodically sends data to the Firebase backend. (see +/// `[FIRInstallations authTokenWithCompletion:]`). +/// To stop the periodic sync, developers need to call `[FIRInstallations deleteWithCompletion:]` +/// and avoid calling this method again. +/// +/// @param completionHandler Fetch operation callback with status and error parameters. +- (void)fetchAndActivateWithCompletionHandler: + (void (^_Nullable)(FIRRemoteConfigFetchAndActivateStatus status, + NSError *_Nullable error))completionHandler; + +#pragma mark - Apply + +/// Applies Fetched Config data to the Active Config, causing updates to the behavior and appearance +/// of the app to take effect (depending on how config data is used in the app). +/// @param completion Activate operation callback with changed and error parameters. +- (void)activateWithCompletion:(void (^_Nullable)(BOOL changed, + NSError *_Nullable error))completion; + +/// Applies Fetched Config data to the Active Config, causing updates to the behavior and appearance +/// of the app to take effect (depending on how config data is used in the app). +/// @param completionHandler Activate operation callback. +- (void)activateWithCompletionHandler:(nullable FIRRemoteConfigActivateCompletion)completionHandler + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig activateWithCompletion:] instead."); + +/// This method is deprecated. Please use -[FIRRemoteConfig activateWithCompletionHandler:] instead. +/// Applies Fetched Config data to the Active Config, causing updates to the behavior and appearance +/// of the app to take effect (depending on how config data is used in the app). +/// Returns true if there was a Fetched Config, and it was activated. +/// Returns false if no Fetched Config was found, or the Fetched Config was already activated. +- (BOOL)activateFetched DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig activate] instead."); + +#pragma mark - Get Config +/// Enables access to configuration values by using object subscripting syntax. +/// <pre> +/// // Example: +/// FIRRemoteConfig *config = [FIRRemoteConfig remoteConfig]; +/// FIRRemoteConfigValue *value = config[@"yourKey"]; +/// BOOL b = value.boolValue; +/// NSNumber *number = config[@"yourKey"].numberValue; +/// </pre> +- (nonnull FIRRemoteConfigValue *)objectForKeyedSubscript:(nonnull NSString *)key; + +/// Gets the config value. +/// @param key Config key. +- (nonnull FIRRemoteConfigValue *)configValueForKey:(nullable NSString *)key; + +/// Gets the config value of a given namespace. +/// @param key Config key. +/// @param aNamespace Config results under a given namespace. +- (nonnull FIRRemoteConfigValue *)configValueForKey:(nullable NSString *)key + namespace:(nullable NSString *)aNamespace + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig configValueForKey:] instead."); + +/// Gets the config value of a given namespace and a given source. +/// @param key Config key. +/// @param source Config value source. +- (nonnull FIRRemoteConfigValue *)configValueForKey:(nullable NSString *)key + source:(FIRRemoteConfigSource)source; + +/// Gets the config value of a given namespace and a given source. +/// @param key Config key. +/// @param aNamespace Config results under a given namespace. +/// @param source Config value source. +- (nonnull FIRRemoteConfigValue *)configValueForKey:(nullable NSString *)key + namespace:(nullable NSString *)aNamespace + source:(FIRRemoteConfigSource)source + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig configValueForKey:source:] instead."); + +/// Gets all the parameter keys from a given source and a given namespace. +/// +/// @param source The config data source. +/// @return An array of keys under the given source and namespace. +- (nonnull NSArray<NSString *> *)allKeysFromSource:(FIRRemoteConfigSource)source; + +/// Gets all the parameter keys from a given source and a given namespace. +/// +/// @param source The config data source. +/// @param aNamespace The config data namespace. +/// @return An array of keys under the given source and namespace. +- (nonnull NSArray<NSString *> *)allKeysFromSource:(FIRRemoteConfigSource)source + namespace:(nullable NSString *)aNamespace + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig allKeysFromSource:] instead."); + +/// Returns the set of parameter keys that start with the given prefix, from the default namespace +/// in the active config. +/// +/// @param prefix The key prefix to look for. If prefix is nil or empty, returns all the +/// keys. +/// @return The set of parameter keys that start with the specified prefix. +- (nonnull NSSet<NSString *> *)keysWithPrefix:(nullable NSString *)prefix; + +/// Returns the set of parameter keys that start with the given prefix, from the given namespace in +/// the active config. +/// +/// @param prefix The key prefix to look for. If prefix is nil or empty, returns all the +/// keys in the given namespace. +/// @param aNamespace The namespace in which to look up the keys. If the namespace is invalid, +/// returns an empty set. +/// @return The set of parameter keys that start with the specified prefix. +- (nonnull NSSet<NSString *> *)keysWithPrefix:(nullable NSString *)prefix + namespace:(nullable NSString *)aNamespace + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig keysWithPrefix:] instead."); + +#pragma mark - Defaults +/// Sets config defaults for parameter keys and values in the default namespace config. +/// @param defaults A dictionary mapping a NSString * key to a NSObject * value. +- (void)setDefaults:(nullable NSDictionary<NSString *, NSObject *> *)defaults; + +/// Sets config defaults for parameter keys and values in the default namespace config. +/// +/// @param defaults A dictionary mapping a NSString * key to a NSObject * value. +/// @param aNamespace Config under a given namespace. +- (void)setDefaults:(nullable NSDictionary<NSString *, NSObject *> *)defaults + namespace:(nullable NSString *)aNamespace + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig setDefaults:] instead."); + +/// Sets default configs from plist for default namespace; +/// @param fileName The plist file name, with no file name extension. For example, if the plist file +/// is defaultSamples.plist, call: +/// [[FIRRemoteConfig remoteConfig] setDefaultsFromPlistFileName:@"defaultSamples"]; +- (void)setDefaultsFromPlistFileName:(nullable NSString *)fileName + NS_SWIFT_NAME(setDefaults(fromPlist:)); + +/// Sets default configs from plist for a given namespace; +/// @param fileName The plist file name, with no file name extension. For example, if the plist file +/// is defaultSamples.plist, call: +/// [[FIRRemoteConfig remoteConfig] setDefaultsFromPlistFileName:@"defaultSamples"]; +/// @param aNamespace The namespace where the default config is set. +- (void)setDefaultsFromPlistFileName:(nullable NSString *)fileName + namespace:(nullable NSString *)aNamespace + NS_SWIFT_NAME(setDefaults(fromPlist:namespace:)) + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig setDefaultsFromPlistFileName:] instead."); + +/// Returns the default value of a given key and a given namespace from the default config. +/// +/// @param key The parameter key of default config. +/// @return Returns the default value of the specified key and namespace. Returns +/// nil if the key or namespace doesn't exist in the default config. +- (nullable FIRRemoteConfigValue *)defaultValueForKey:(nullable NSString *)key; + +/// Returns the default value of a given key and a given namespace from the default config. +/// +/// @param key The parameter key of default config. +/// @param aNamespace The namespace of default config. +/// @return Returns the default value of the specified key and namespace. Returns +/// nil if the key or namespace doesn't exist in the default config. +- (nullable FIRRemoteConfigValue *)defaultValueForKey:(nullable NSString *)key + namespace:(nullable NSString *)aNamespace + DEPRECATED_MSG_ATTRIBUTE("Use -[FIRRemoteConfig defaultValueForKey:] instead."); + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig.h new file mode 100644 index 00000000..9ae8cea4 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig.h @@ -0,0 +1,17 @@ +/* + * 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 "FIRRemoteConfig.h" diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigConstants.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigConstants.h new file mode 100644 index 00000000..5a29aabb --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigConstants.h @@ -0,0 +1,58 @@ +/* + * 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 <Foundation/Foundation.h> + +#define RCN_SEC_PER_MIN 60 +#define RCN_MSEC_PER_SEC 1000 + +/// Key prefix applied to all the packages (bundle IDs) in internal metadata. +static NSString *const RCNInternalMetadataAllPackagesPrefix = @"all_packages"; + +/// HTTP connection default timeout in seconds. +static const NSTimeInterval RCNHTTPDefaultConnectionTimeout = 60; +/// Default duration of how long config data lasts to stay fresh. +static const NSTimeInterval RCNDefaultMinimumFetchInterval = 43200; + +/// Label for serial queue for read/write lock on ivars. +static const char *RCNRemoteConfigQueueLabel = "com.google.GoogleConfigService.FIRRemoteConfig"; + +/// Constants for key names in the fetch response. +/// Key that includes an array of template entries. +static NSString *const RCNFetchResponseKeyEntries = @"entries"; +/// Key that includes data for experiment descriptions in ABT. +static NSString *const RCNFetchResponseKeyExperimentDescriptions = @"experimentDescriptions"; +/// Error key. +static NSString *const RCNFetchResponseKeyError = @"error"; +/// Error code. +static NSString *const RCNFetchResponseKeyErrorCode = @"code"; +/// Error status. +static NSString *const RCNFetchResponseKeyErrorStatus = @"status"; +/// Error message. +static NSString *const RCNFetchResponseKeyErrorMessage = @"message"; +/// The current state of the backend template. +static NSString *const RCNFetchResponseKeyState = @"state"; +/// Default state (when not set). +static NSString *const RCNFetchResponseKeyStateUnspecified = @"INSTANCE_STATE_UNSPECIFIED"; +/// Config key/value map and/or ABT experiment list differs from last fetch. +/// TODO: Migrate to the new HTTP error codes once available in the backend. b/117182055 +static NSString *const RCNFetchResponseKeyStateUpdate = @"UPDATE"; +/// No template fetched. +static NSString *const RCNFetchResponseKeyStateNoTemplate = @"NO_TEMPLATE"; +/// Config key/value map and ABT experiment list both match last fetch. +static NSString *const RCNFetchResponseKeyStateNoChange = @"NO_CHANGE"; +/// Template found, but evaluates to empty (e.g. all keys omitted). +static NSString *const RCNFetchResponseKeyStateEmptyConfig = @"EMPTY_CONFIG"; diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.h new file mode 100644 index 00000000..cc83c2eb --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.h @@ -0,0 +1,60 @@ +/* + * 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 <Foundation/Foundation.h> + +typedef NS_ENUM(NSInteger, RCNDBSource) { + RCNDBSourceActive, + RCNDBSourceDefault, + RCNDBSourceFetched, +}; + +@class RCNConfigDBManager; + +/// This class handles all the config content that is fetched from the server, cached in local +/// config or persisted in database. +@interface RCNConfigContent : NSObject +/// Shared Singleton Instance ++ (instancetype)sharedInstance; + +/// Fetched config (aka pending config) data that is latest data from server that might or might +/// not be applied. +@property(nonatomic, readonly, copy) NSDictionary *fetchedConfig; +/// Active config that is available to external users; +@property(nonatomic, readonly, copy) NSDictionary *activeConfig; +/// Local default config that is provided by external users; +@property(nonatomic, readonly, copy) NSDictionary *defaultConfig; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer; +- (instancetype)initWithDBManager:(RCNConfigDBManager *)DBManager NS_DESIGNATED_INITIALIZER; + +/// Returns true if initalization succeeded. +- (BOOL)initializationSuccessful; + +/// Update config content from fetch response in JSON format. +- (void)updateConfigContentWithResponse:(NSDictionary *)response + forNamespace:(NSString *)FIRNamespace; + +/// Copy from a given dictionary to one of the data source. +/// @param fromDictionary The data to copy from. +/// @param source The data source to copy to(pending/active/default). +- (void)copyFromDictionary:(NSDictionary *)fromDictionary + toSource:(RCNDBSource)source + forNamespace:(NSString *)FIRNamespace; + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.m new file mode 100644 index 00000000..dc7c796c --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.m @@ -0,0 +1,336 @@ +/* + * 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 "FirebaseRemoteConfig/Sources/RCNConfigContent.h" + +#import <FirebaseRemoteConfig/FIRRemoteConfig.h> +#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDefines.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h" + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" + +@implementation RCNConfigContent { + /// Active config data that is currently used. + NSMutableDictionary *_activeConfig; + /// Pending config (aka Fetched config) data that is latest data from server that might or might + /// not be applied. + NSMutableDictionary *_fetchedConfig; + /// Default config provided by user. + NSMutableDictionary *_defaultConfig; + /// DBManager + RCNConfigDBManager *_DBManager; + /// Current bundle identifier; + NSString *_bundleIdentifier; + /// Dispatch semaphore to block all config reads until we have read from the database. This only + /// potentially blocks on the first read. Should be a no-wait for all subsequent reads once we + /// have data read into memory from the database. + dispatch_semaphore_t _configLoadFromDBSemaphore; + /// Boolean indicating if initial DB load of fetched,active and default config has succeeded. + BOOL _isConfigLoadFromDBCompleted; + /// Boolean indicating that the load from database has initiated at least once. + BOOL _isDatabaseLoadAlreadyInitiated; +} + +/// Default timeout when waiting to read data from database. +static const NSTimeInterval kDatabaseLoadTimeoutSecs = 30.0; + +/// Singleton instance of RCNConfigContent. ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static RCNConfigContent *sharedInstance; + dispatch_once(&onceToken, ^{ + sharedInstance = + [[RCNConfigContent alloc] initWithDBManager:[RCNConfigDBManager sharedInstance]]; + }); + return sharedInstance; +} + +- (instancetype)init { + NSAssert(NO, @"Invalid initializer."); + return nil; +} + +/// Designated initializer +- (instancetype)initWithDBManager:(RCNConfigDBManager *)DBManager { + self = [super init]; + if (self) { + _activeConfig = [[NSMutableDictionary alloc] init]; + _fetchedConfig = [[NSMutableDictionary alloc] init]; + _defaultConfig = [[NSMutableDictionary alloc] init]; + _bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + if (!_bundleIdentifier) { + FIRLogNotice(kFIRLoggerRemoteConfig, @"I-RCN000038", + @"Main bundle identifier is missing. Remote Config might not work properly."); + _bundleIdentifier = @""; + } + _DBManager = DBManager; + _configLoadFromDBSemaphore = dispatch_semaphore_create(0); + [self loadConfigFromMainTable]; + } + return self; +} + +// Blocking call that returns true/false once database load completes / times out. +// @return Initialization status. +- (BOOL)initializationSuccessful { + RCN_MUST_NOT_BE_MAIN_THREAD(); + BOOL isDatabaseLoadSuccessful = [self checkAndWaitForInitialDatabaseLoad]; + return isDatabaseLoadSuccessful; +} + +#pragma mark - update +/// This function is for copying dictionary when user set up a default config or when user clicks +/// activate. For now the DBSource can only be Active or Default. +- (void)copyFromDictionary:(NSDictionary *)fromDict + toSource:(RCNDBSource)DBSource + forNamespace:(NSString *)FIRNamespace { + // Make sure database load has completed. + [self checkAndWaitForInitialDatabaseLoad]; + NSMutableDictionary *toDict; + if (!fromDict) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000007", + @"The source dictionary to copy from does not exist."); + return; + } + FIRRemoteConfigSource source = FIRRemoteConfigSourceRemote; + switch (DBSource) { + case RCNDBSourceDefault: + toDict = _defaultConfig; + source = FIRRemoteConfigSourceDefault; + break; + case RCNDBSourceFetched: + FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000008", + @"This shouldn't happen. Destination dictionary should never be pending type."); + return; + case RCNDBSourceActive: + toDict = _activeConfig; + source = FIRRemoteConfigSourceRemote; + [toDict removeObjectForKey:FIRNamespace]; + break; + default: + toDict = _activeConfig; + source = FIRRemoteConfigSourceRemote; + [toDict removeObjectForKey:FIRNamespace]; + break; + } + + // Completely wipe out DB first. + [_DBManager deleteRecordFromMainTableWithNamespace:FIRNamespace + bundleIdentifier:_bundleIdentifier + fromSource:DBSource]; + + toDict[FIRNamespace] = [[NSMutableDictionary alloc] init]; + NSDictionary *config = fromDict[FIRNamespace]; + for (NSString *key in config) { + if (DBSource == FIRRemoteConfigSourceDefault) { + NSObject *value = config[key]; + NSData *valueData; + if ([value isKindOfClass:[NSData class]]) { + valueData = (NSData *)value; + } else if ([value isKindOfClass:[NSString class]]) { + valueData = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding]; + } else if ([value isKindOfClass:[NSNumber class]]) { + NSString *strValue = [(NSNumber *)value stringValue]; + valueData = [(NSString *)strValue dataUsingEncoding:NSUTF8StringEncoding]; + } else if ([value isKindOfClass:[NSDate class]]) { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; + NSString *strValue = [dateFormatter stringFromDate:(NSDate *)value]; + valueData = [(NSString *)strValue dataUsingEncoding:NSUTF8StringEncoding]; + } else { + continue; + } + toDict[FIRNamespace][key] = [[FIRRemoteConfigValue alloc] initWithData:valueData + source:source]; + NSArray *values = @[ _bundleIdentifier, FIRNamespace, key, valueData ]; + [self updateMainTableWithValues:values fromSource:DBSource]; + } else { + FIRRemoteConfigValue *value = config[key]; + toDict[FIRNamespace][key] = [[FIRRemoteConfigValue alloc] initWithData:value.dataValue + source:source]; + NSArray *values = @[ _bundleIdentifier, FIRNamespace, key, value.dataValue ]; + [self updateMainTableWithValues:values fromSource:DBSource]; + } + } +} + +- (void)updateConfigContentWithResponse:(NSDictionary *)response + forNamespace:(NSString *)currentNamespace { + // Make sure database load has completed. + [self checkAndWaitForInitialDatabaseLoad]; + NSString *state = response[RCNFetchResponseKeyState]; + + if (!state) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000049", @"State field in fetch response is nil."); + return; + } + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000059", + @"Updating config content from Response for namespace:%@ with state: %@", + currentNamespace, response[RCNFetchResponseKeyState]); + + if ([state isEqualToString:RCNFetchResponseKeyStateNoChange]) { + [self handleNoChangeStateForConfigNamespace:currentNamespace]; + return; + } + + /// Handle empty config state + if ([state isEqualToString:RCNFetchResponseKeyStateEmptyConfig]) { + [self handleEmptyConfigStateForConfigNamespace:currentNamespace]; + return; + } + + /// Handle no template state. + if ([state isEqualToString:RCNFetchResponseKeyStateNoTemplate]) { + [self handleNoTemplateStateForConfigNamespace:currentNamespace]; + return; + } + + /// Handle update state + if ([state isEqualToString:RCNFetchResponseKeyStateUpdate]) { + [self handleUpdateStateForConfigNamespace:currentNamespace + withEntries:response[RCNFetchResponseKeyEntries]]; + return; + } +} + +#pragma mark State handling +- (void)handleNoChangeStateForConfigNamespace:(NSString *)currentNamespace { + if (!_fetchedConfig[currentNamespace]) { + _fetchedConfig[currentNamespace] = [[NSMutableDictionary alloc] init]; + } +} + +- (void)handleEmptyConfigStateForConfigNamespace:(NSString *)currentNamespace { + if (_fetchedConfig[currentNamespace]) { + [_fetchedConfig[currentNamespace] removeAllObjects]; + } else { + // If namespace has empty status and it doesn't exist in _fetchedConfig, we will + // still add an entry for that namespace. Even if it will not be persisted in database. + // TODO: Add generics for all collection types. + _fetchedConfig[currentNamespace] = [[NSMutableDictionary alloc] init]; + } + [_DBManager deleteRecordFromMainTableWithNamespace:currentNamespace + bundleIdentifier:_bundleIdentifier + fromSource:RCNDBSourceFetched]; +} + +- (void)handleNoTemplateStateForConfigNamespace:(NSString *)currentNamespace { + // Remove the namespace. + [_fetchedConfig removeObjectForKey:currentNamespace]; + [_DBManager deleteRecordFromMainTableWithNamespace:currentNamespace + bundleIdentifier:_bundleIdentifier + fromSource:RCNDBSourceFetched]; +} +- (void)handleUpdateStateForConfigNamespace:(NSString *)currentNamespace + withEntries:(NSDictionary *)entries { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000058", @"Update config in DB for namespace:%@", + currentNamespace); + // Clear before updating + [_DBManager deleteRecordFromMainTableWithNamespace:currentNamespace + bundleIdentifier:_bundleIdentifier + fromSource:RCNDBSourceFetched]; + if ([_fetchedConfig objectForKey:currentNamespace]) { + [_fetchedConfig[currentNamespace] removeAllObjects]; + } else { + _fetchedConfig[currentNamespace] = [[NSMutableDictionary alloc] init]; + } + + // Store the fetched config values. + for (NSString *key in entries) { + NSData *valueData = [entries[key] dataUsingEncoding:NSUTF8StringEncoding]; + if (!valueData) { + continue; + } + _fetchedConfig[currentNamespace][key] = + [[FIRRemoteConfigValue alloc] initWithData:valueData source:FIRRemoteConfigSourceRemote]; + NSArray *values = @[ _bundleIdentifier, currentNamespace, key, valueData ]; + [self updateMainTableWithValues:values fromSource:RCNDBSourceFetched]; + } +} + +#pragma mark - database + +/// This method is only meant to be called at init time. The underlying logic will need to be +/// revaluated if the assumption changes at a later time. +- (void)loadConfigFromMainTable { + if (!_DBManager) { + return; + } + + NSAssert(!_isDatabaseLoadAlreadyInitiated, @"Database load has already been initiated"); + _isDatabaseLoadAlreadyInitiated = true; + + [_DBManager + loadMainWithBundleIdentifier:_bundleIdentifier + completionHandler:^(BOOL success, NSDictionary *fetchedConfig, + NSDictionary *activeConfig, NSDictionary *defaultConfig) { + self->_fetchedConfig = [fetchedConfig mutableCopy]; + self->_activeConfig = [activeConfig mutableCopy]; + self->_defaultConfig = [defaultConfig mutableCopy]; + dispatch_semaphore_signal(self->_configLoadFromDBSemaphore); + }]; +} + +/// Update the current config result to main table. +/// @param values Values in a row to write to the table. +/// @param source The source the config data is coming from. It determines which table to write to. +- (void)updateMainTableWithValues:(NSArray *)values fromSource:(RCNDBSource)source { + [_DBManager insertMainTableWithValues:values fromSource:source completionHandler:nil]; +} +#pragma mark - getter/setter +- (NSDictionary *)fetchedConfig { + /// If this is the first time reading the fetchedConfig, we might still be reading it from the + /// database. + [self checkAndWaitForInitialDatabaseLoad]; + return _fetchedConfig; +} + +- (NSDictionary *)activeConfig { + /// If this is the first time reading the activeConfig, we might still be reading it from the + /// database. + [self checkAndWaitForInitialDatabaseLoad]; + return _activeConfig; +} + +- (NSDictionary *)defaultConfig { + /// If this is the first time reading the fetchedConfig, we might still be reading it from the + /// database. + [self checkAndWaitForInitialDatabaseLoad]; + return _defaultConfig; +} + +/// We load the database async at init time. Block all further calls to active/fetched/default +/// configs until load is done. +/// @return Database load completion status. +- (BOOL)checkAndWaitForInitialDatabaseLoad { + /// Wait on semaphore until done. This should be a no-op for subsequent calls. + if (!_isConfigLoadFromDBCompleted) { + long result = dispatch_semaphore_wait( + _configLoadFromDBSemaphore, + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDatabaseLoadTimeoutSecs * NSEC_PER_SEC))); + if (result != 0) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000048", + @"Timed out waiting for fetched config to be loaded from DB"); + return false; + } + _isConfigLoadFromDBCompleted = true; + } + return true; +} + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h new file mode 100644 index 00000000..540a2b78 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h @@ -0,0 +1,121 @@ +/* + * 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 <Foundation/Foundation.h> + +#import "FirebaseRemoteConfig/Sources/RCNConfigContent.h" + +typedef NS_ENUM(NSInteger, RCNUpdateOption) { + RCNUpdateOptionApplyTime, + RCNUpdateOptionDefaultTime, + RCNUpdateOptionFetchStatus, +}; + +/// Column names in metadata table +static NSString *const RCNKeyBundleIdentifier = @"bundle_identifier"; +static NSString *const RCNKeyFetchTime = @"fetch_time"; +static NSString *const RCNKeyDigestPerNamespace = @"digest_per_ns"; +static NSString *const RCNKeyDeviceContext = @"device_context"; +static NSString *const RCNKeyAppContext = @"app_context"; +static NSString *const RCNKeySuccessFetchTime = @"success_fetch_time"; +static NSString *const RCNKeyFailureFetchTime = @"failure_fetch_time"; +static NSString *const RCNKeyLastFetchStatus = @"last_fetch_status"; +static NSString *const RCNKeyLastFetchError = @"last_fetch_error"; +static NSString *const RCNKeyLastApplyTime = @"last_apply_time"; +static NSString *const RCNKeyLastSetDefaultsTime = @"last_set_defaults_time"; + +/// Persist config data in sqlite database on device. Managing data read/write from/to database. +@interface RCNConfigDBManager : NSObject +/// Shared Singleton Instance ++ (instancetype)sharedInstance; + +/// Database Operation Completion callback. +/// @param success Decide whether the DB operation succeeds. +/// @param result Return operation result data. +typedef void (^RCNDBCompletion)(BOOL success, NSDictionary *result); + +/// Database Load Operation Completion callback. +/// @param success Decide whether the DB operation succeeds. +/// @param fetchedConfig Return fetchedConfig loaded from DB +/// @param activeConfig Return activeConfig loaded from DB +/// @param defaultConfig Return defaultConfig loaded from DB +typedef void (^RCNDBLoadCompletion)(BOOL success, + NSDictionary *fetchedConfig, + NSDictionary *activeConfig, + NSDictionary *defaultConfig); + +/// Returns the current version of the Remote Config database. ++ (NSString *)remoteConfigPathForDatabase; + +/// Load config content from main table to cached memory during app start. +- (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier + completionHandler:(RCNDBLoadCompletion)handler; +/// Load config settings from metadata table to cached memory during app start. Config settings +/// include success/failure fetch times, device contenxt, app context, etc. +- (NSDictionary *)loadMetadataWithBundleIdentifier:(NSString *)bundleIdentifier; +/// Load internal metadata from internal metadata table, such as customized HTTP connection/read +/// timeout, throttling time interval and number limit of throttling, etc. +/// This call needs to be blocking to ensure throttling works during apps starts. +- (NSDictionary *)loadInternalMetadataTable; +/// Load experiment from experiment table. +/// @param handler The callback when reading from DB is complete. +- (void)loadExperimentWithCompletionHandler:(RCNDBCompletion)handler; + +/// Insert a record in metadata table. +/// @param columnNameToValue The column name and its value to be inserted in metadata table. +/// @param handler The callback. +- (void)insertMetadataTableWithValues:(NSDictionary *)columnNameToValue + completionHandler:(RCNDBCompletion)handler; +/// Insert a record in main table. +/// @param values Values to be inserted. +- (void)insertMainTableWithValues:(NSArray *)values + fromSource:(RCNDBSource)source + completionHandler:(RCNDBCompletion)handler; +/// Insert a record in internal metadata table. +/// @param values Values to be inserted. +- (void)insertInternalMetadataTableWithValues:(NSArray *)values + completionHandler:(RCNDBCompletion)handler; +/// Insert exepriment data in experiment table. +/// @param key The key of experiment data belongs to, which are defined in +/// RCNConfigDefines.h. +/// @param value The value that experiment. +/// @param handler The callback. +- (void)insertExperimentTableWithKey:(NSString *)key + value:(NSData *)value + completionHandler:(RCNDBCompletion)handler; + +- (void)updateMetadataWithOption:(RCNUpdateOption)option + values:(NSArray *)values + completionHandler:(RCNDBCompletion)handler; +/// Clear the record of given namespace and package name +/// before updating the table. +- (void)deleteRecordFromMainTableWithNamespace:(NSString *)namespace_p + bundleIdentifier:(NSString *)bundleIdentifier + fromSource:(RCNDBSource)source; +/// Remove all the records of given package name from metadata/internal metadata DB before updating +/// new values from response. +- (void)deleteRecordWithBundleIdentifier:(NSString *)bundlerIdentifier + isInternalDB:(BOOL)isInternalDB; +/// Remove all the records from a config content table. +- (void)deleteAllRecordsFromTableWithSource:(RCNDBSource)source; + +/// Remove all the records from experiment table with given key. +/// @param key The key of experiment data belongs to, which are defined in RCNConfigDefines.h. +- (void)deleteExperimentTableForKey:(NSString *)key; + +/// Returns true if this a new install of the Config database. +- (BOOL)isNewDatabase; +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m new file mode 100644 index 00000000..b3c704de --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m @@ -0,0 +1,1045 @@ +/* + * 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 <sqlite3.h> + +#import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDefines.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h" + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" + +/// Using macro for securely preprocessing string concatenation in query before runtime. +#define RCNTableNameMain "main" +#define RCNTableNameMainActive "main_active" +#define RCNTableNameMainDefault "main_default" +#define RCNTableNameMetadata "fetch_metadata" +#define RCNTableNameInternalMetadata "internal_metadata" +#define RCNTableNameExperiment "experiment" + +static BOOL gIsNewDatabase; +/// SQLite file name in versions 0, 1 and 2. +static NSString *const RCNDatabaseName = @"RemoteConfig.sqlite3"; +/// The application support sub-directory that the Remote Config database resides in. +static NSString *const RCNRemoteConfigApplicationSupportSubDirectory = @"Google/RemoteConfig"; + +/// Remote Config database path for deprecated V0 version. +static NSString *RemoteConfigPathForOldDatabaseV0() { + NSArray *dirPaths = + NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *docPath = dirPaths.firstObject; + return [docPath stringByAppendingPathComponent:RCNDatabaseName]; +} + +/// Remote Config database path for current database. +static NSString *RemoteConfigPathForDatabase(void) { + NSArray *dirPaths = + NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *appSupportPath = dirPaths.firstObject; + NSArray *components = + @[ appSupportPath, RCNRemoteConfigApplicationSupportSubDirectory, RCNDatabaseName ]; + return [NSString pathWithComponents:components]; +} + +static BOOL RemoteConfigAddSkipBackupAttributeToItemAtPath(NSString *filePathString) { + NSURL *URL = [NSURL fileURLWithPath:filePathString]; + assert([[NSFileManager defaultManager] fileExistsAtPath:[URL path]]); + + NSError *error = nil; + BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES] + forKey:NSURLIsExcludedFromBackupKey + error:&error]; + if (!success) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000017", @"Error excluding %@ from backup %@.", + [URL lastPathComponent], error); + } + return success; +} + +static BOOL RemoteConfigCreateFilePathIfNotExist(NSString *filePath) { + if (!filePath || !filePath.length) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000018", + @"Failed to create subdirectory for an empty file path."); + return NO; + } + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:filePath]) { + gIsNewDatabase = YES; + NSError *error; + [fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent] + withIntermediateDirectories:YES + attributes:nil + error:&error]; + if (error) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000019", + @"Failed to create subdirectory for database file: %@.", error); + return NO; + } + } + return YES; +} + +static NSArray *RemoteConfigMetadataTableColumnsInOrder() { + return @[ + RCNKeyBundleIdentifier, RCNKeyFetchTime, RCNKeyDigestPerNamespace, RCNKeyDeviceContext, + RCNKeyAppContext, RCNKeySuccessFetchTime, RCNKeyFailureFetchTime, RCNKeyLastFetchStatus, + RCNKeyLastFetchError, RCNKeyLastApplyTime, RCNKeyLastSetDefaultsTime + ]; +} + +@interface RCNConfigDBManager () { + /// Database storing all the config information. + sqlite3 *_database; + /// Serial queue for database read/write operations. + dispatch_queue_t _databaseOperationQueue; +} +@end + +@implementation RCNConfigDBManager + ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static RCNConfigDBManager *sharedInstance; + dispatch_once(&onceToken, ^{ + sharedInstance = [[RCNConfigDBManager alloc] init]; + }); + return sharedInstance; +} + +/// Returns the current version of the Remote Config database. ++ (NSString *)remoteConfigPathForDatabase { + return RemoteConfigPathForDatabase(); +} + +- (instancetype)init { + self = [super init]; + if (self) { + _databaseOperationQueue = + dispatch_queue_create("com.google.GoogleConfigService.database", DISPATCH_QUEUE_SERIAL); + [self createOrOpenDatabase]; + } + return self; +} + +#pragma mark - database +- (void)migrateV1NamespaceToV2Namespace { + for (int table = 0; table < 3; table++) { + NSString *tableName = @"" RCNTableNameMain; + switch (table) { + case 1: + tableName = @"" RCNTableNameMainActive; + break; + case 2: + tableName = @"" RCNTableNameMainDefault; + break; + default: + break; + } + NSString *SQLString = [NSString + stringWithFormat:@"SELECT namespace FROM %@ WHERE namespace NOT LIKE '%%:%%'", tableName]; + const char *SQL = [SQLString UTF8String]; + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return; + } + NSMutableArray<NSString *> *namespaceArray = [[NSMutableArray alloc] init]; + while (sqlite3_step(statement) == SQLITE_ROW) { + NSString *configNamespace = + [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)]; + [namespaceArray addObject:configNamespace]; + } + sqlite3_finalize(statement); + + // Update. + for (NSString *namespaceToUpdate in namespaceArray) { + NSString *newNamespace = + [NSString stringWithFormat:@"%@:%@", namespaceToUpdate, kFIRDefaultAppName]; + NSString *updateSQLString = + [NSString stringWithFormat:@"UPDATE %@ SET namespace = ? WHERE namespace = ?", tableName]; + const char *updateSQL = [updateSQLString UTF8String]; + sqlite3_stmt *updateStatement = [self prepareSQL:updateSQL]; + if (!updateStatement) { + return; + } + NSArray<NSString *> *updateParams = @[ newNamespace, namespaceToUpdate ]; + [self bindStringsToStatement:updateStatement stringArray:updateParams]; + + int result = sqlite3_step(updateStatement); + if (result != SQLITE_DONE) { + [self logErrorWithSQL:SQL finalizeStatement:updateStatement returnValue:NO]; + return; + } + sqlite3_finalize(updateStatement); + } + } +} + +- (void)createOrOpenDatabase { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + NSString *oldV0DBPath = RemoteConfigPathForOldDatabaseV0(); + // Backward Compatibility + if ([[NSFileManager defaultManager] fileExistsAtPath:oldV0DBPath]) { + FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000009", + @"Old database V0 exists, removed it and replace with the new one."); + [strongSelf removeDatabase:oldV0DBPath]; + } + NSString *dbPath = [RCNConfigDBManager remoteConfigPathForDatabase]; + FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000062", @"Loading database at path %@", dbPath); + const char *databasePath = dbPath.UTF8String; + + // Create or open database path. + if (!RemoteConfigCreateFilePathIfNotExist(dbPath)) { + return; + } + int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FILEPROTECTION_COMPLETE | + SQLITE_OPEN_FULLMUTEX; + if (sqlite3_open_v2(databasePath, &strongSelf->_database, flags, NULL) == SQLITE_OK) { + // Always try to create table if not exists for backward compatibility. + if (![strongSelf createTableSchema]) { + // Remove database before fail. + [strongSelf removeDatabase:dbPath]; + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000010", @"Failed to create table."); + // Create a new database if existing database file is corrupted. + if (!RemoteConfigCreateFilePathIfNotExist(dbPath)) { + return; + } + if (sqlite3_open_v2(databasePath, &strongSelf->_database, flags, NULL) == SQLITE_OK) { + if (![strongSelf createTableSchema]) { + // Remove database before fail. + [strongSelf removeDatabase:dbPath]; + // If it failed again, there's nothing we can do here. + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000010", @"Failed to create table."); + } else { + // Exclude the app data used from iCloud backup. + RemoteConfigAddSkipBackupAttributeToItemAtPath(dbPath); + } + } else { + [strongSelf logDatabaseError]; + } + } else { + // DB file already exists. Migrate any V1 namespace column entries to V2 fully qualified + // 'namespace:FIRApp' entries. + [self migrateV1NamespaceToV2Namespace]; + // Exclude the app data used from iCloud backup. + RemoteConfigAddSkipBackupAttributeToItemAtPath(dbPath); + } + } else { + [strongSelf logDatabaseError]; + } + }); +} + +- (BOOL)createTableSchema { + RCN_MUST_NOT_BE_MAIN_THREAD(); + static const char *createTableMain = + "create TABLE IF NOT EXISTS " RCNTableNameMain + " (_id INTEGER PRIMARY KEY, bundle_identifier TEXT, namespace TEXT, key TEXT, value BLOB)"; + + static const char *createTableMainActive = + "create TABLE IF NOT EXISTS " RCNTableNameMainActive + " (_id INTEGER PRIMARY KEY, bundle_identifier TEXT, namespace TEXT, key TEXT, value BLOB)"; + + static const char *createTableMainDefault = + "create TABLE IF NOT EXISTS " RCNTableNameMainDefault + " (_id INTEGER PRIMARY KEY, bundle_identifier TEXT, namespace TEXT, key TEXT, value BLOB)"; + + static const char *createTableMetadata = + "create TABLE IF NOT EXISTS " RCNTableNameMetadata + " (_id INTEGER PRIMARY KEY, bundle_identifier" + " TEXT, fetch_time INTEGER, digest_per_ns BLOB, device_context BLOB, app_context BLOB, " + "success_fetch_time BLOB, failure_fetch_time BLOB, last_fetch_status INTEGER, " + "last_fetch_error INTEGER, last_apply_time INTEGER, last_set_defaults_time INTEGER)"; + + static const char *createTableInternalMetadata = + "create TABLE IF NOT EXISTS " RCNTableNameInternalMetadata + " (_id INTEGER PRIMARY KEY, key TEXT, value BLOB)"; + + static const char *createTableExperiment = "create TABLE IF NOT EXISTS " RCNTableNameExperiment + " (_id INTEGER PRIMARY KEY, key TEXT, value BLOB)"; + + return [self executeQuery:createTableMain] && [self executeQuery:createTableMainActive] && + [self executeQuery:createTableMainDefault] && [self executeQuery:createTableMetadata] && + [self executeQuery:createTableInternalMetadata] && + [self executeQuery:createTableExperiment]; +} + +- (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_sync(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + if (sqlite3_close(strongSelf->_database) != SQLITE_OK) { + [self logDatabaseError]; + } + strongSelf->_database = nil; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *error; + if (![fileManager removeItemAtPath:path error:&error]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000011", + @"Failed to remove database at path %@ for error %@.", path, error); + } + }); +} + +- (void)removeDatabase:(NSString *)path { + if (sqlite3_close(_database) != SQLITE_OK) { + [self logDatabaseError]; + } + _database = nil; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *error; + if (![fileManager removeItemAtPath:path error:&error]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000011", + @"Failed to remove database at path %@ for error %@.", path, error); + } +} + +#pragma mark - execute +- (BOOL)executeQuery:(const char *)SQL { + RCN_MUST_NOT_BE_MAIN_THREAD(); + char *error; + if (sqlite3_exec(_database, SQL, nil, nil, &error) != SQLITE_OK) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000012", @"Failed to execute query with error %s.", + error); + return NO; + } + return YES; +} + +#pragma mark - insert +- (void)insertMetadataTableWithValues:(NSDictionary *)columnNameToValue + completionHandler:(RCNDBCompletion)handler { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + BOOL success = [weakSelf insertMetadataTableWithValues:columnNameToValue]; + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(success, nil); + }); + } + }); +} + +- (BOOL)insertMetadataTableWithValues:(NSDictionary *)columnNameToValue { + RCN_MUST_NOT_BE_MAIN_THREAD(); + static const char *SQL = + "INSERT INTO " RCNTableNameMetadata + " (bundle_identifier, fetch_time, digest_per_ns, device_context, " + "app_context, success_fetch_time, failure_fetch_time, last_fetch_status, " + "last_fetch_error, last_apply_time, last_set_defaults_time) values (?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?)"; + + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + [self logErrorWithSQL:SQL finalizeStatement:nil returnValue:NO]; + return NO; + } + + NSArray *columns = RemoteConfigMetadataTableColumnsInOrder(); + int index = 0; + for (NSString *columnName in columns) { + if ([columnName isEqualToString:RCNKeyBundleIdentifier]) { + NSString *value = columnNameToValue[columnName]; + if (![self bindStringToStatement:statement index:++index string:value]) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + } else if ([columnName isEqualToString:RCNKeyFetchTime] || + [columnName isEqualToString:RCNKeyLastApplyTime] || + [columnName isEqualToString:RCNKeyLastSetDefaultsTime]) { + double value = [columnNameToValue[columnName] doubleValue]; + if (sqlite3_bind_double(statement, ++index, value) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + } else if ([columnName isEqualToString:RCNKeyLastFetchStatus] || + [columnName isEqualToString:RCNKeyLastFetchError]) { + int value = [columnNameToValue[columnName] intValue]; + if (sqlite3_bind_int(statement, ++index, value) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + } else { + NSData *data = columnNameToValue[columnName]; + if (sqlite3_bind_blob(statement, ++index, data.bytes, (int)data.length, NULL) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + } + } + if (sqlite3_step(statement) != SQLITE_DONE) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + sqlite3_finalize(statement); + return YES; +} + +- (void)insertMainTableWithValues:(NSArray *)values + fromSource:(RCNDBSource)source + completionHandler:(RCNDBCompletion)handler { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + BOOL success = [weakSelf insertMainTableWithValues:values fromSource:source]; + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(success, nil); + }); + } + }); +} + +- (BOOL)insertMainTableWithValues:(NSArray *)values fromSource:(RCNDBSource)source { + RCN_MUST_NOT_BE_MAIN_THREAD(); + if (values.count != 4) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000013", + @"Failed to insert config record. Wrong number of give parameters, current " + @"number is %ld, correct number is 4.", + (long)values.count); + return NO; + } + const char *SQL = "INSERT INTO " RCNTableNameMain + " (bundle_identifier, namespace, key, value) values (?, ?, ?, ?)"; + if (source == RCNDBSourceDefault) { + SQL = "INSERT INTO " RCNTableNameMainDefault + " (bundle_identifier, namespace, key, value) values (?, ?, ?, ?)"; + } else if (source == RCNDBSourceActive) { + SQL = "INSERT INTO " RCNTableNameMainActive + " (bundle_identifier, namespace, key, value) values (?, ?, ?, ?)"; + } + + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return NO; + } + + NSString *aString = values[0]; + if (![self bindStringToStatement:statement index:1 string:aString]) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + aString = values[1]; + if (![self bindStringToStatement:statement index:2 string:aString]) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + aString = values[2]; + if (![self bindStringToStatement:statement index:3 string:aString]) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + NSData *blobData = values[3]; + if (sqlite3_bind_blob(statement, 4, blobData.bytes, (int)blobData.length, NULL) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + if (sqlite3_step(statement) != SQLITE_DONE) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + sqlite3_finalize(statement); + return YES; +} + +- (void)insertInternalMetadataTableWithValues:(NSArray *)values + completionHandler:(RCNDBCompletion)handler { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + BOOL success = [weakSelf insertInternalMetadataWithValues:values]; + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(success, nil); + }); + } + }); +} + +- (BOOL)insertInternalMetadataWithValues:(NSArray *)values { + RCN_MUST_NOT_BE_MAIN_THREAD(); + if (values.count != 2) { + return NO; + } + const char *SQL = + "INSERT OR REPLACE INTO " RCNTableNameInternalMetadata " (key, value) values (?, ?)"; + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return NO; + } + NSString *aString = values[0]; + if (![self bindStringToStatement:statement index:1 string:aString]) { + [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + return NO; + } + NSData *blobData = values[1]; + if (sqlite3_bind_blob(statement, 2, blobData.bytes, (int)blobData.length, NULL) != SQLITE_OK) { + [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + return NO; + } + if (sqlite3_step(statement) != SQLITE_DONE) { + [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + return NO; + } + sqlite3_finalize(statement); + return YES; +} + +- (void)insertExperimentTableWithKey:(NSString *)key + value:(NSData *)serializedValue + completionHandler:(RCNDBCompletion)handler { + dispatch_async(_databaseOperationQueue, ^{ + BOOL success = [self insertExperimentTableWithKey:key value:serializedValue]; + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(success, nil); + }); + } + }); +} + +- (BOOL)insertExperimentTableWithKey:(NSString *)key value:(NSData *)dataValue { + if ([key isEqualToString:@RCNExperimentTableKeyMetadata]) { + return [self updateExperimentMetadata:dataValue]; + } + + RCN_MUST_NOT_BE_MAIN_THREAD(); + const char *SQL = "INSERT INTO " RCNTableNameExperiment " (key, value) values (?, ?)"; + + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return NO; + } + + if (![self bindStringToStatement:statement index:1 string:key]) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + + if (sqlite3_bind_blob(statement, 2, dataValue.bytes, (int)dataValue.length, NULL) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + + if (sqlite3_step(statement) != SQLITE_DONE) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + sqlite3_finalize(statement); + return YES; +} + +- (BOOL)updateExperimentMetadata:(NSData *)dataValue { + RCN_MUST_NOT_BE_MAIN_THREAD(); + const char *SQL = "INSERT OR REPLACE INTO " RCNTableNameExperiment + " (_id, key, value) values ((SELECT _id from " RCNTableNameExperiment + " WHERE key = ?), ?, ?)"; + + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return NO; + } + + if (![self bindStringToStatement:statement index:1 string:@RCNExperimentTableKeyMetadata]) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + + if (![self bindStringToStatement:statement index:2 string:@RCNExperimentTableKeyMetadata]) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + if (sqlite3_bind_blob(statement, 3, dataValue.bytes, (int)dataValue.length, NULL) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + + if (sqlite3_step(statement) != SQLITE_DONE) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + sqlite3_finalize(statement); + return YES; +} + +#pragma mark - update + +- (void)updateMetadataWithOption:(RCNUpdateOption)option + values:(NSArray *)values + completionHandler:(RCNDBCompletion)handler { + dispatch_async(_databaseOperationQueue, ^{ + BOOL success = [self updateMetadataTableWithOption:option andValues:values]; + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(success, nil); + }); + } + }); +} + +- (BOOL)updateMetadataTableWithOption:(RCNUpdateOption)option andValues:(NSArray *)values { + RCN_MUST_NOT_BE_MAIN_THREAD(); + static const char *SQL = + "UPDATE " RCNTableNameMetadata " (last_fetch_status, last_fetch_error, last_apply_time, " + "last_set_defaults_time) values (?, ?, ?, ?)"; + if (option == RCNUpdateOptionFetchStatus) { + SQL = "UPDATE " RCNTableNameMetadata " SET last_fetch_status = ?, last_fetch_error = ?"; + } else if (option == RCNUpdateOptionApplyTime) { + SQL = "UPDATE " RCNTableNameMetadata " SET last_apply_time = ?"; + } else if (option == RCNUpdateOptionDefaultTime) { + SQL = "UPDATE " RCNTableNameMetadata " SET last_set_defaults_time = ?"; + } else { + return NO; + } + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return NO; + } + + int index = 0; + if ((option == RCNUpdateOptionApplyTime || option == RCNUpdateOptionDefaultTime) && + values.count == 1) { + double value = [values[0] doubleValue]; + if (sqlite3_bind_double(statement, ++index, value) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + } else if (option == RCNUpdateOptionFetchStatus && values.count == 2) { + int value = [values[0] intValue]; + if (sqlite3_bind_int(statement, ++index, value) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + value = [values[1] intValue]; + if (sqlite3_bind_int(statement, ++index, value) != SQLITE_OK) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + } + if (sqlite3_step(statement) != SQLITE_DONE) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + sqlite3_finalize(statement); + return YES; +} +#pragma mark - read from DB + +- (NSDictionary *)loadMetadataWithBundleIdentifier:(NSString *)bundleIdentifier { + __block NSDictionary *metadataTableResult; + __weak RCNConfigDBManager *weakSelf = self; + dispatch_sync(_databaseOperationQueue, ^{ + metadataTableResult = [weakSelf loadMetadataTableWithBundleIdentifier:bundleIdentifier]; + }); + if (metadataTableResult) { + return metadataTableResult; + } + return [[NSDictionary alloc] init]; +} + +- (NSMutableDictionary *)loadMetadataTableWithBundleIdentifier:(NSString *)bundleIdentifier { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + const char *SQL = + "SELECT bundle_identifier, fetch_time, digest_per_ns, device_context, app_context, " + "success_fetch_time, failure_fetch_time , last_fetch_status, " + "last_fetch_error, last_apply_time, last_set_defaults_time FROM " RCNTableNameMetadata + " WHERE bundle_identifier = ?"; + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return nil; + } + + NSArray *params = @[ bundleIdentifier ]; + [self bindStringsToStatement:statement stringArray:params]; + + while (sqlite3_step(statement) == SQLITE_ROW) { + NSString *dbBundleIdentifier = + [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)]; + + if (dbBundleIdentifier && ![dbBundleIdentifier isEqualToString:bundleIdentifier]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000014", + @"Load Metadata from table error: Wrong package name %@, should be %@.", + dbBundleIdentifier, bundleIdentifier); + return nil; + } + + double fetchTime = sqlite3_column_double(statement, 1); + NSData *digestPerNamespace = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 2) + length:sqlite3_column_bytes(statement, 2)]; + NSData *deviceContext = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 3) + length:sqlite3_column_bytes(statement, 3)]; + NSData *appContext = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 4) + length:sqlite3_column_bytes(statement, 4)]; + NSData *successTimeDigest = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 5) + length:sqlite3_column_bytes(statement, 5)]; + NSData *failureTimeDigest = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 6) + length:sqlite3_column_bytes(statement, 6)]; + + int lastFetchStatus = sqlite3_column_int(statement, 7); + int lastFetchFailReason = sqlite3_column_int(statement, 8); + double lastApplyTimestamp = sqlite3_column_double(statement, 9); + double lastSetDefaultsTimestamp = sqlite3_column_double(statement, 10); + + NSError *error; + NSMutableDictionary *deviceContextDict = nil; + if (deviceContext) { + deviceContextDict = [NSJSONSerialization JSONObjectWithData:deviceContext + options:NSJSONReadingMutableContainers + error:&error]; + } + + NSMutableDictionary *appContextDict = nil; + if (appContext) { + appContextDict = [NSJSONSerialization JSONObjectWithData:appContext + options:NSJSONReadingMutableContainers + error:&error]; + } + + NSMutableDictionary<NSString *, id> *digestPerNamespaceDictionary = nil; + if (digestPerNamespace) { + digestPerNamespaceDictionary = + [NSJSONSerialization JSONObjectWithData:digestPerNamespace + options:NSJSONReadingMutableContainers + error:&error]; + } + + NSMutableArray *successTimes = nil; + if (successTimeDigest) { + successTimes = [NSJSONSerialization JSONObjectWithData:successTimeDigest + options:NSJSONReadingMutableContainers + error:&error]; + } + + NSMutableArray *failureTimes = nil; + if (failureTimeDigest) { + failureTimes = [NSJSONSerialization JSONObjectWithData:failureTimeDigest + options:NSJSONReadingMutableContainers + error:&error]; + } + + dict[RCNKeyBundleIdentifier] = bundleIdentifier; + dict[RCNKeyFetchTime] = @(fetchTime); + dict[RCNKeyDigestPerNamespace] = digestPerNamespaceDictionary; + dict[RCNKeyDeviceContext] = deviceContextDict; + dict[RCNKeyAppContext] = appContextDict; + dict[RCNKeySuccessFetchTime] = successTimes; + dict[RCNKeyFailureFetchTime] = failureTimes; + dict[RCNKeyLastFetchStatus] = @(lastFetchStatus); + dict[RCNKeyLastFetchError] = @(lastFetchFailReason); + dict[RCNKeyLastApplyTime] = @(lastApplyTimestamp); + dict[RCNKeyLastSetDefaultsTime] = @(lastSetDefaultsTimestamp); + + break; + } + sqlite3_finalize(statement); + return dict; +} + +- (void)loadExperimentWithCompletionHandler:(RCNDBCompletion)handler { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + NSMutableArray *experimentPayloads = + [strongSelf loadExperimentTableFromKey:@RCNExperimentTableKeyPayload]; + if (!experimentPayloads) { + experimentPayloads = [[NSMutableArray alloc] init]; + } + + NSMutableDictionary *experimentMetadata; + NSMutableArray *experiments = + [strongSelf loadExperimentTableFromKey:@RCNExperimentTableKeyMetadata]; + // There should be only one entry for experiment metadata. + if (experiments.count > 0) { + NSError *error; + experimentMetadata = [NSJSONSerialization JSONObjectWithData:experiments[0] + options:NSJSONReadingMutableContainers + error:&error]; + } + if (!experimentMetadata) { + experimentMetadata = [[NSMutableDictionary alloc] init]; + } + + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler( + YES, @{ + @RCNExperimentTableKeyPayload : [experimentPayloads copy], + @RCNExperimentTableKeyMetadata : [experimentMetadata copy] + }); + }); + } + }); +} + +- (NSMutableArray<NSData *> *)loadExperimentTableFromKey:(NSString *)key { + RCN_MUST_NOT_BE_MAIN_THREAD(); + + NSMutableArray *results = [[NSMutableArray alloc] init]; + const char *SQL = "SELECT value FROM " RCNTableNameExperiment " WHERE key = ?"; + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return nil; + } + + NSArray *params = @[ key ]; + [self bindStringsToStatement:statement stringArray:params]; + NSData *experimentData; + while (sqlite3_step(statement) == SQLITE_ROW) { + experimentData = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 0) + length:sqlite3_column_bytes(statement, 0)]; + if (experimentData) { + [results addObject:experimentData]; + } + } + + sqlite3_finalize(statement); + return results; +} + +- (NSDictionary *)loadInternalMetadataTable { + __block NSMutableDictionary *internalMetadataTableResult; + __weak RCNConfigDBManager *weakSelf = self; + dispatch_sync(_databaseOperationQueue, ^{ + internalMetadataTableResult = [weakSelf loadInternalMetadataTableInternal]; + }); + return internalMetadataTableResult; +} + +- (NSMutableDictionary *)loadInternalMetadataTableInternal { + NSMutableDictionary *internalMetadata = [[NSMutableDictionary alloc] init]; + const char *SQL = "SELECT key, value FROM " RCNTableNameInternalMetadata; + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return nil; + } + + while (sqlite3_step(statement) == SQLITE_ROW) { + NSString *key = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)]; + + NSData *dataValue = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 1) + length:sqlite3_column_bytes(statement, 1)]; + internalMetadata[key] = dataValue; + } + sqlite3_finalize(statement); + return internalMetadata; +} + +/// This method is only meant to be called at init time. The underlying logic will need to be +/// revaluated if the assumption changes at a later time. +- (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier + completionHandler:(RCNDBLoadCompletion)handler { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + __block NSDictionary *fetchedConfig = + [strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier + fromSource:RCNDBSourceFetched]; + __block NSDictionary *activeConfig = + [strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier + fromSource:RCNDBSourceActive]; + __block NSDictionary *defaultConfig = + [strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier + fromSource:RCNDBSourceDefault]; + if (handler) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + fetchedConfig = fetchedConfig ? fetchedConfig : [[NSDictionary alloc] init]; + activeConfig = activeConfig ? activeConfig : [[NSDictionary alloc] init]; + defaultConfig = defaultConfig ? defaultConfig : [[NSDictionary alloc] init]; + handler(YES, fetchedConfig, activeConfig, defaultConfig); + }); + } + }); +} + +- (NSMutableDictionary *)loadMainTableWithBundleIdentifier:(NSString *)bundleIdentifier + fromSource:(RCNDBSource)source { + NSMutableDictionary *namespaceToConfig = [[NSMutableDictionary alloc] init]; + const char *SQL = "SELECT bundle_identifier, namespace, key, value FROM " RCNTableNameMain + " WHERE bundle_identifier = ?"; + if (source == RCNDBSourceDefault) { + SQL = "SELECT bundle_identifier, namespace, key, value FROM " RCNTableNameMainDefault + " WHERE bundle_identifier = ?"; + } else if (source == RCNDBSourceActive) { + SQL = "SELECT bundle_identifier, namespace, key, value FROM " RCNTableNameMainActive + " WHERE bundle_identifier = ?"; + } + NSArray *params = @[ bundleIdentifier ]; + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return nil; + } + [self bindStringsToStatement:statement stringArray:params]; + + while (sqlite3_step(statement) == SQLITE_ROW) { + NSString *configNamespace = + [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 1)]; + NSString *key = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 2)]; + NSData *value = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 3) + length:sqlite3_column_bytes(statement, 3)]; + if (!namespaceToConfig[configNamespace]) { + namespaceToConfig[configNamespace] = [[NSMutableDictionary alloc] init]; + } + + if (source == RCNDBSourceDefault) { + namespaceToConfig[configNamespace][key] = + [[FIRRemoteConfigValue alloc] initWithData:value source:FIRRemoteConfigSourceDefault]; + } else { + namespaceToConfig[configNamespace][key] = + [[FIRRemoteConfigValue alloc] initWithData:value source:FIRRemoteConfigSourceRemote]; + } + } + sqlite3_finalize(statement); + return namespaceToConfig; +} + +#pragma mark - delete +- (void)deleteRecordFromMainTableWithNamespace:(NSString *)namespace_p + bundleIdentifier:(NSString *)bundleIdentifier + fromSource:(RCNDBSource)source { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + NSArray *params = @[ bundleIdentifier, namespace_p ]; + const char *SQL = + "DELETE FROM " RCNTableNameMain " WHERE bundle_identifier = ? and namespace = ?"; + if (source == RCNDBSourceDefault) { + SQL = "DELETE FROM " RCNTableNameMainDefault " WHERE bundle_identifier = ? and namespace = ?"; + } else if (source == RCNDBSourceActive) { + SQL = "DELETE FROM " RCNTableNameMainActive " WHERE bundle_identifier = ? and namespace = ?"; + } + [strongSelf executeQuery:SQL withParams:params]; + }); +} + +- (void)deleteRecordWithBundleIdentifier:(NSString *)bundleIdentifier + isInternalDB:(BOOL)isInternalDB { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + const char *SQL = "DELETE FROM " RCNTableNameInternalMetadata " WHERE key LIKE ?"; + if (!isInternalDB) { + SQL = "DELETE FROM " RCNTableNameMetadata " WHERE bundle_identifier = ?"; + } + NSArray *params = @[ bundleIdentifier ]; + [strongSelf executeQuery:SQL withParams:params]; + }); +} + +- (void)deleteAllRecordsFromTableWithSource:(RCNDBSource)source { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + const char *SQL = "DELETE FROM " RCNTableNameMain; + if (source == RCNDBSourceDefault) { + SQL = "DELETE FROM " RCNTableNameMainDefault; + } else if (source == RCNDBSourceActive) { + SQL = "DELETE FROM " RCNTableNameMainActive; + } + [strongSelf executeQuery:SQL]; + }); +} + +- (void)deleteExperimentTableForKey:(NSString *)key { + __weak RCNConfigDBManager *weakSelf = self; + dispatch_async(_databaseOperationQueue, ^{ + RCNConfigDBManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + NSArray *params = @[ key ]; + const char *SQL = "DELETE FROM " RCNTableNameExperiment " WHERE key = ?"; + [strongSelf executeQuery:SQL withParams:params]; + }); +} + +#pragma mark - helper +- (BOOL)executeQuery:(const char *)SQL withParams:(NSArray *)params { + RCN_MUST_NOT_BE_MAIN_THREAD(); + sqlite3_stmt *statement = [self prepareSQL:SQL]; + if (!statement) { + return NO; + } + + [self bindStringsToStatement:statement stringArray:params]; + if (sqlite3_step(statement) != SQLITE_DONE) { + return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + } + sqlite3_finalize(statement); + return YES; +} + +/// Params only accept TEXT format string. +- (BOOL)bindStringsToStatement:(sqlite3_stmt *)statement stringArray:(NSArray *)array { + int index = 1; + for (NSString *param in array) { + if (![self bindStringToStatement:statement index:index string:param]) { + return [self logErrorWithSQL:nil finalizeStatement:statement returnValue:NO]; + } + index++; + } + return YES; +} + +- (BOOL)bindStringToStatement:(sqlite3_stmt *)statement index:(int)index string:(NSString *)value { + if (sqlite3_bind_text(statement, index, [value UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) { + return [self logErrorWithSQL:nil finalizeStatement:statement returnValue:NO]; + } + return YES; +} + +- (sqlite3_stmt *)prepareSQL:(const char *)SQL { + sqlite3_stmt *statement = nil; + if (sqlite3_prepare_v2(_database, SQL, -1, &statement, NULL) != SQLITE_OK) { + [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO]; + return nil; + } + return statement; +} + +- (NSString *)errorMessage { + return [NSString stringWithFormat:@"%s", sqlite3_errmsg(_database)]; +} + +- (int)errorCode { + return sqlite3_errcode(_database); +} + +- (void)logDatabaseError { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000015", @"Error message: %@. Error code: %d.", + [self errorMessage], [self errorCode]); +} + +- (BOOL)logErrorWithSQL:(const char *)SQL + finalizeStatement:(sqlite3_stmt *)statement + returnValue:(BOOL)returnValue { + if (SQL) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000016", @"Failed with SQL: %s.", SQL); + } + [self logDatabaseError]; + + if (statement) { + sqlite3_finalize(statement); + } + + return returnValue; +} + +- (BOOL)isNewDatabase { + return gIsNewDatabase; +} + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDefines.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDefines.h new file mode 100644 index 00000000..9181355b --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDefines.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef RCNConfigDefines_h +#define RCNConfigDefines_h + +#if defined(DEBUG) +#define RCN_MUST_NOT_BE_MAIN_THREAD() \ + do { \ + NSAssert(![NSThread isMainThread], @"Must not be executing on the main thread."); \ + } while (0); +#else +#define RCN_MUST_NOT_BE_MAIN_THREAD() \ + do { \ + } while (0); +#endif + +#define RCNExperimentTableKeyPayload "experiment_payload" +#define RCNExperimentTableKeyMetadata "experiment_metadata" + +#endif diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.h new file mode 100644 index 00000000..34779d42 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.h @@ -0,0 +1,37 @@ +/* + * 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 <Foundation/Foundation.h> + +@class FIRExperimentController; +@class RCNConfigDBManager; + +/// Handles experiment information update and persistence. +@interface RCNConfigExperiment : NSObject + +/// Designated initializer; +- (instancetype)initWithDBManager:(RCNConfigDBManager *)DBManager + experimentController:(FIRExperimentController *)controller NS_DESIGNATED_INITIALIZER; + +/// Use `initWithDBManager:` instead. +- (instancetype)init NS_UNAVAILABLE; + +/// Update/Persist experiment information from config fetch response. +- (void)updateExperimentsWithResponse:(NSArray<NSDictionary<NSString *, id> *> *)response; + +/// Update experiments to Firebase Analytics when activateFetched happens. +- (void)updateExperiments; +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.m new file mode 100644 index 00000000..0c4c3cd2 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.m @@ -0,0 +1,165 @@ +/* + * 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 "FirebaseRemoteConfig/Sources/RCNConfigExperiment.h" + +#import <FirebaseABTesting/ABTExperimentPayload.h> +#import <FirebaseABTesting/FIRExperimentController.h> +#import <FirebaseABTesting/FIRLifecycleEvents.h> +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDefines.h" + +static NSString *const kExperimentMetadataKeyLastStartTime = @"last_experiment_start_time"; + +static NSString *const kServiceOrigin = @"frc"; +static NSString *const kMethodNameLatestStartTime = + @"latestExperimentStartTimestampBetweenTimestamp:andPayloads:"; + +@interface RCNConfigExperiment () +@property(nonatomic, strong) + NSMutableArray<NSData *> *experimentPayloads; ///< Experiment payloads. +@property(nonatomic, strong) + NSMutableDictionary<NSString *, id> *experimentMetadata; ///< Experiment metadata +@property(nonatomic, strong) RCNConfigDBManager *DBManager; ///< Database Manager. +@property(nonatomic, strong) FIRExperimentController *experimentController; +@property(nonatomic, strong) NSDateFormatter *experimentStartTimeDateFormatter; +@end + +@implementation RCNConfigExperiment +/// Designated initializer +- (instancetype)initWithDBManager:(RCNConfigDBManager *)DBManager + experimentController:(FIRExperimentController *)controller { + self = [super init]; + if (self) { + _experimentPayloads = [[NSMutableArray alloc] init]; + _experimentMetadata = [[NSMutableDictionary alloc] init]; + _experimentStartTimeDateFormatter = [[NSDateFormatter alloc] init]; + [_experimentStartTimeDateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"]; + [_experimentStartTimeDateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; + // Locale needs to be hardcoded. See + // https://developer.apple.com/library/ios/#qa/qa1480/_index.html for more details. + [_experimentStartTimeDateFormatter + setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]]; + [_experimentStartTimeDateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; + + _DBManager = DBManager; + _experimentController = controller; + [self loadExperimentFromTable]; + } + return self; +} + +- (void)loadExperimentFromTable { + if (!_DBManager) { + return; + } + __weak RCNConfigExperiment *weakSelf = self; + RCNDBCompletion completionHandler = ^(BOOL success, NSDictionary<NSString *, id> *result) { + RCNConfigExperiment *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + if (result[@RCNExperimentTableKeyPayload]) { + [strongSelf->_experimentPayloads removeAllObjects]; + for (NSData *experiment in result[@RCNExperimentTableKeyPayload]) { + if (![NSJSONSerialization isValidJSONObject:experiment]) { + FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000031", + @"Experiment payload could not be parsed as JSON."); + } else { + [strongSelf->_experimentPayloads addObject:experiment]; + } + } + } + if (result[@RCNExperimentTableKeyMetadata]) { + strongSelf->_experimentMetadata = [result[@RCNExperimentTableKeyMetadata] mutableCopy]; + } + }; + [_DBManager loadExperimentWithCompletionHandler:completionHandler]; +} + +- (void)updateExperimentsWithResponse:(NSArray<NSDictionary<NSString *, id> *> *)response { + // cache fetched experiment payloads. + [_experimentPayloads removeAllObjects]; + [_DBManager deleteExperimentTableForKey:@RCNExperimentTableKeyPayload]; + + for (NSDictionary<NSString *, id> *experiment in response) { + NSError *error; + NSData *JSONPayload = [NSJSONSerialization dataWithJSONObject:experiment + options:kNilOptions + error:&error]; + if (!JSONPayload || error) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000030", + @"Invalid experiment payload to be serialized."); + } else { + [_experimentPayloads addObject:JSONPayload]; + [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyPayload + value:JSONPayload + completionHandler:nil]; + } + } +} + +- (void)updateExperiments { + FIRLifecycleEvents *lifecycleEvent = [[FIRLifecycleEvents alloc] init]; + + // Get the last experiment start time prior to the latest payload. + NSTimeInterval lastStartTime = + [_experimentMetadata[kExperimentMetadataKeyLastStartTime] doubleValue]; + + // Update the last experiment start time with the latest payload. + [self updateExperimentStartTime]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self.experimentController + updateExperimentsWithServiceOrigin:kServiceOrigin + events:lifecycleEvent + policy:ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest + lastStartTime:lastStartTime + payloads:_experimentPayloads]; +#pragma clang diagnostic pop +} + +- (void)updateExperimentStartTime { + NSTimeInterval existingLastStartTime = + [_experimentMetadata[kExperimentMetadataKeyLastStartTime] doubleValue]; + + NSTimeInterval latestStartTime = + [self latestStartTimeWithExistingLastStartTime:existingLastStartTime]; + + _experimentMetadata[kExperimentMetadataKeyLastStartTime] = @(latestStartTime); + + if (![NSJSONSerialization isValidJSONObject:_experimentMetadata]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000028", + @"Invalid fetched experiment metadata to be serialized."); + return; + } + NSError *error; + NSData *serializedExperimentMetadata = + [NSJSONSerialization dataWithJSONObject:_experimentMetadata + options:NSJSONWritingPrettyPrinted + error:&error]; + [_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyMetadata + value:serializedExperimentMetadata + completionHandler:nil]; +} + +- (NSTimeInterval)latestStartTimeWithExistingLastStartTime:(NSTimeInterval)existingLastStartTime { + return [self.experimentController + latestExperimentStartTimestampBetweenTimestamp:existingLastStartTime + andPayloads:_experimentPayloads]; +} +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigSettings.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigSettings.m new file mode 100644 index 00000000..46ca22bd --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigSettings.m @@ -0,0 +1,459 @@ +/* + * 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 "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h" + +#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h" +#import "FirebaseRemoteConfig/Sources/RCNDevice.h" +#import "FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h" + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h" + +static NSString *const kRCNGroupPrefix = @"frc.group."; +static NSString *const kRCNUserDefaultsKeyNamelastETag = @"lastETag"; +static NSString *const kRCNUserDefaultsKeyNameLastSuccessfulFetchTime = @"lastSuccessfulFetchTime"; +static const int kRCNExponentialBackoffMinimumInterval = 60 * 2; // 2 mins. +static const int kRCNExponentialBackoffMaximumInterval = 60 * 60 * 4; // 4 hours. + +@interface RCNConfigSettings () { + /// A list of successful fetch timestamps in seconds. + NSMutableArray *_successFetchTimes; + /// A list of failed fetch timestamps in seconds. + NSMutableArray *_failureFetchTimes; + /// Device conditions since last successful fetch from the backend. Device conditions including + /// app + /// version, iOS version, device localte, language, GMP project ID and Game project ID. Used for + /// determing whether to throttle. + NSMutableDictionary *_deviceContext; + /// Custom variables (aka App context digest). This is the pending custom variables request before + /// fetching. + NSMutableDictionary *_customVariables; + /// Cached internal metadata from internal metadata table. It contains customized information such + /// as HTTP connection timeout, HTTP read timeout, success/failure throttling rate and time + /// interval. Client has the default value of each parameters, they are only saved in + /// internalMetadata if they have been customize by developers. + NSMutableDictionary *_internalMetadata; + /// Last fetch status. + FIRRemoteConfigFetchStatus _lastFetchStatus; + /// Last fetch Error. + FIRRemoteConfigError _lastFetchError; + /// The time of last apply timestamp. + NSTimeInterval _lastApplyTimeInterval; + /// The time of last setDefaults timestamp. + NSTimeInterval _lastSetDefaultsTimeInterval; + /// The database manager. + RCNConfigDBManager *_DBManager; + // The namespace for this instance. + NSString *_FIRNamespace; + // The Google App ID of the configured FIRApp. + NSString *_googleAppID; + /// The user defaults manager scoped to this RC instance of FIRApp and namespace. + RCNUserDefaultsManager *_userDefaultsManager; + /// The timestamp of last eTag update. + NSTimeInterval _lastETagUpdateTime; +} +@end + +@implementation RCNConfigSettings + +- (instancetype)initWithDatabaseManager:(RCNConfigDBManager *)manager + namespace:(NSString *)FIRNamespace + firebaseAppName:(NSString *)appName + googleAppID:(NSString *)googleAppID { + self = [super init]; + if (self) { + _FIRNamespace = FIRNamespace; + _googleAppID = googleAppID; + _bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + if (!_bundleIdentifier) { + FIRLogNotice(kFIRLoggerRemoteConfig, @"I-RCN000038", + @"Main bundle identifier is missing. Remote Config might not work properly."); + _bundleIdentifier = @""; + } + _minimumFetchInterval = RCNDefaultMinimumFetchInterval; + _deviceContext = [[NSMutableDictionary alloc] init]; + _customVariables = [[NSMutableDictionary alloc] init]; + _successFetchTimes = [[NSMutableArray alloc] init]; + _failureFetchTimes = [[NSMutableArray alloc] init]; + _DBManager = manager; + + _internalMetadata = [[_DBManager loadInternalMetadataTable] mutableCopy]; + if (!_internalMetadata) { + _internalMetadata = [[NSMutableDictionary alloc] init]; + } + _userDefaultsManager = [[RCNUserDefaultsManager alloc] initWithAppName:appName + bundleID:_bundleIdentifier + namespace:_FIRNamespace]; + + // Check if the config database is new. If so, clear the configs saved in userDefaults. + if ([_DBManager isNewDatabase]) { + FIRLogNotice(kFIRLoggerRemoteConfig, @"I-RCN000072", + @"New config database created. Resetting user defaults."); + [_userDefaultsManager resetUserDefaults]; + } + + _isFetchInProgress = NO; + } + return self; +} + +#pragma mark - read from / update userDefaults +- (NSString *)lastETag { + return [_userDefaultsManager lastETag]; +} + +- (void)setLastETag:(NSString *)lastETag { + [self setLastETagUpdateTime:[[NSDate date] timeIntervalSince1970]]; + [_userDefaultsManager setLastETag:lastETag]; +} + +- (void)setLastETagUpdateTime:(NSTimeInterval)lastETagUpdateTime { + [_userDefaultsManager setLastETagUpdateTime:lastETagUpdateTime]; +} + +- (NSTimeInterval)lastFetchTimeInterval { + return _userDefaultsManager.lastFetchTime; +} + +- (NSTimeInterval)lastETagUpdateTime { + return _userDefaultsManager.lastETagUpdateTime; +} + +// TODO: Update logic for app extensions as required. +- (void)updateLastFetchTimeInterval:(NSTimeInterval)lastFetchTimeInterval { + _userDefaultsManager.lastFetchTime = lastFetchTimeInterval; +} + +#pragma mark - load from DB +- (NSDictionary *)loadConfigFromMetadataTable { + NSDictionary *metadata = [[_DBManager loadMetadataWithBundleIdentifier:_bundleIdentifier] copy]; + if (metadata) { + // TODO: Remove (all metadata in general) once ready to + // migrate to user defaults completely. + if (metadata[RCNKeyDeviceContext]) { + self->_deviceContext = [metadata[RCNKeyDeviceContext] mutableCopy]; + } + if (metadata[RCNKeyAppContext]) { + self->_customVariables = [metadata[RCNKeyAppContext] mutableCopy]; + } + if (metadata[RCNKeySuccessFetchTime]) { + self->_successFetchTimes = [metadata[RCNKeySuccessFetchTime] mutableCopy]; + } + if (metadata[RCNKeyFailureFetchTime]) { + self->_failureFetchTimes = [metadata[RCNKeyFailureFetchTime] mutableCopy]; + } + if (metadata[RCNKeyLastFetchStatus]) { + self->_lastFetchStatus = + (FIRRemoteConfigFetchStatus)[metadata[RCNKeyLastFetchStatus] intValue]; + } + if (metadata[RCNKeyLastFetchError]) { + self->_lastFetchError = (FIRRemoteConfigError)[metadata[RCNKeyLastFetchError] intValue]; + } + if (metadata[RCNKeyLastApplyTime]) { + self->_lastApplyTimeInterval = [metadata[RCNKeyLastApplyTime] doubleValue]; + } + if (metadata[RCNKeyLastFetchStatus]) { + self->_lastSetDefaultsTimeInterval = [metadata[RCNKeyLastSetDefaultsTime] doubleValue]; + } + } + return metadata; +} + +#pragma mark - update DB/cached + +// Update internal metadata content to cache and DB. +- (void)updateInternalContentWithResponse:(NSDictionary *)response { + // Remove all the keys with current pakcage name. + [_DBManager deleteRecordWithBundleIdentifier:_bundleIdentifier isInternalDB:YES]; + + for (NSString *key in _internalMetadata.allKeys) { + if ([key hasPrefix:_bundleIdentifier]) { + [_internalMetadata removeObjectForKey:key]; + } + } + + for (NSString *entry in response) { + NSData *val = [response[entry] dataUsingEncoding:NSUTF8StringEncoding]; + NSArray *values = @[ entry, val ]; + _internalMetadata[entry] = response[entry]; + [self updateInternalMetadataTableWithValues:values]; + } +} + +- (void)updateInternalMetadataTableWithValues:(NSArray *)values { + [_DBManager insertInternalMetadataTableWithValues:values completionHandler:nil]; +} + +/// If the last fetch was not successful, update the (exponential backoff) period that we wait until +/// fetching again. Any subsequent fetch requests will be checked and allowed only if past this +/// throttle end time. +- (void)updateExponentialBackoffTime { + // If not in exponential backoff mode, reset the retry interval. + if (_lastFetchStatus == FIRRemoteConfigFetchStatusSuccess) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000057", + @"Throttling: Entering exponential backoff mode."); + _exponentialBackoffRetryInterval = kRCNExponentialBackoffMinimumInterval; + } else { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000057", + @"Throttling: Updating throttling interval."); + // Double the retry interval until we hit the truncated exponential backoff. More info here: + // https://cloud.google.com/storage/docs/exponential-backoff + _exponentialBackoffRetryInterval = + ((_exponentialBackoffRetryInterval * 2) < kRCNExponentialBackoffMaximumInterval) + ? _exponentialBackoffRetryInterval * 2 + : _exponentialBackoffRetryInterval; + } + + // Randomize the next retry interval. + int randomPlusMinusInterval = ((arc4random() % 2) == 0) ? -1 : 1; + NSTimeInterval randomizedRetryInterval = + _exponentialBackoffRetryInterval + + (0.5 * _exponentialBackoffRetryInterval * randomPlusMinusInterval); + _exponentialBackoffThrottleEndTime = + [[NSDate date] timeIntervalSince1970] + randomizedRetryInterval; +} + +- (void)updateMetadataWithFetchSuccessStatus:(BOOL)fetchSuccess { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000056", @"Updating metadata with fetch result."); + if (!fetchSuccess) { + [self updateExponentialBackoffTime]; + } + + [self updateFetchTimeWithSuccessFetch:fetchSuccess]; + _lastFetchStatus = + fetchSuccess ? FIRRemoteConfigFetchStatusSuccess : FIRRemoteConfigFetchStatusFailure; + _lastFetchError = fetchSuccess ? FIRRemoteConfigErrorUnknown : FIRRemoteConfigErrorInternalError; + if (fetchSuccess) { + [self updateLastFetchTimeInterval:[[NSDate date] timeIntervalSince1970]]; + // Note: We expect the googleAppID to always be available. + _deviceContext = FIRRemoteConfigDeviceContextWithProjectIdentifier(_googleAppID); + } + + [self updateMetadataTable]; +} + +- (void)updateFetchTimeWithSuccessFetch:(BOOL)isSuccessfulFetch { + NSTimeInterval epochTimeInterval = [[NSDate date] timeIntervalSince1970]; + if (isSuccessfulFetch) { + [_successFetchTimes addObject:@(epochTimeInterval)]; + } else { + [_failureFetchTimes addObject:@(epochTimeInterval)]; + } +} + +- (void)updateMetadataTable { + [_DBManager deleteRecordWithBundleIdentifier:_bundleIdentifier isInternalDB:NO]; + NSError *error; + // Objects to be serialized cannot be invalid. + if (!_bundleIdentifier) { + return; + } + if (![NSJSONSerialization isValidJSONObject:_customVariables]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000028", + @"Invalid custom variables to be serialized."); + return; + } + if (![NSJSONSerialization isValidJSONObject:_deviceContext]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000029", + @"Invalid device context to be serialized."); + return; + } + + if (![NSJSONSerialization isValidJSONObject:_successFetchTimes]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000031", + @"Invalid success fetch times to be serialized."); + return; + } + if (![NSJSONSerialization isValidJSONObject:_failureFetchTimes]) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000032", + @"Invalid failure fetch times to be serialized."); + return; + } + NSData *serializedAppContext = [NSJSONSerialization dataWithJSONObject:_customVariables + options:NSJSONWritingPrettyPrinted + error:&error]; + NSData *serializedDeviceContext = + [NSJSONSerialization dataWithJSONObject:_deviceContext + options:NSJSONWritingPrettyPrinted + error:&error]; + // The digestPerNamespace is not used and only meant for backwards DB compatibility. + NSData *serializedDigestPerNamespace = + [NSJSONSerialization dataWithJSONObject:@{} options:NSJSONWritingPrettyPrinted error:&error]; + NSData *serializedSuccessTime = [NSJSONSerialization dataWithJSONObject:_successFetchTimes + options:NSJSONWritingPrettyPrinted + error:&error]; + NSData *serializedFailureTime = [NSJSONSerialization dataWithJSONObject:_failureFetchTimes + options:NSJSONWritingPrettyPrinted + error:&error]; + + if (!serializedDigestPerNamespace || !serializedDeviceContext || !serializedAppContext || + !serializedSuccessTime || !serializedFailureTime) { + return; + } + + NSDictionary *columnNameToValue = @{ + RCNKeyBundleIdentifier : _bundleIdentifier, + RCNKeyFetchTime : @(self.lastFetchTimeInterval), + RCNKeyDigestPerNamespace : serializedDigestPerNamespace, + RCNKeyDeviceContext : serializedDeviceContext, + RCNKeyAppContext : serializedAppContext, + RCNKeySuccessFetchTime : serializedSuccessTime, + RCNKeyFailureFetchTime : serializedFailureTime, + RCNKeyLastFetchStatus : [NSString stringWithFormat:@"%ld", (long)_lastFetchStatus], + RCNKeyLastFetchError : [NSString stringWithFormat:@"%ld", (long)_lastFetchError], + RCNKeyLastApplyTime : @(_lastApplyTimeInterval), + RCNKeyLastSetDefaultsTime : @(_lastSetDefaultsTimeInterval) + }; + + [_DBManager insertMetadataTableWithValues:columnNameToValue completionHandler:nil]; +} + +#pragma mark - fetch request + +/// Returns a fetch request with the latest device and config change. +/// Whenever user issues a fetch api call, collect the latest request. +- (NSString *)nextRequestWithUserProperties:(NSDictionary *)userProperties { + // Note: We only set user properties as mentioned in the new REST API Design doc + NSString *ret = [NSString stringWithFormat:@"{"]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@"app_instance_id:'%@'", + _configInstallationsIdentifier]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", app_instance_id_token:'%@'", + _configInstallationsToken]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", app_id:'%@'", _googleAppID]]; + + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", country_code:'%@'", + FIRRemoteConfigDeviceCountry()]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", language_code:'%@'", + FIRRemoteConfigDeviceLocale()]]; + ret = [ret + stringByAppendingString:[NSString stringWithFormat:@", platform_version:'%@'", + [GULAppEnvironmentUtil systemVersion]]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", time_zone:'%@'", + FIRRemoteConfigTimezone()]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", package_name:'%@'", + _bundleIdentifier]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", app_version:'%@'", + FIRRemoteConfigAppVersion()]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", app_build:'%@'", + FIRRemoteConfigAppBuildVersion()]]; + ret = [ret stringByAppendingString:[NSString stringWithFormat:@", sdk_version:'%@'", + FIRRemoteConfigPodVersion()]]; + + if (userProperties && userProperties.count > 0) { + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userProperties + options:0 + error:&error]; + if (!error) { + ret = [ret + stringByAppendingString:[NSString + stringWithFormat:@", analytics_user_properties:%@", + [[NSString alloc] + initWithData:jsonData + encoding:NSUTF8StringEncoding]]]; + } + } + ret = [ret stringByAppendingString:@"}"]; + return ret; +} + +#pragma mark - getter/setter + +- (void)setLastFetchError:(FIRRemoteConfigError)lastFetchError { + if (_lastFetchError != lastFetchError) { + _lastFetchError = lastFetchError; + [_DBManager updateMetadataWithOption:RCNUpdateOptionFetchStatus + values:@[ @(_lastFetchStatus), @(_lastFetchError) ] + completionHandler:nil]; + } +} + +- (NSArray *)successFetchTimes { + return [_successFetchTimes copy]; +} + +- (NSArray *)failureFetchTimes { + return [_failureFetchTimes copy]; +} + +- (NSDictionary *)customVariables { + return [_customVariables copy]; +} + +- (NSDictionary *)internalMetadata { + return [_internalMetadata copy]; +} + +- (NSDictionary *)deviceContext { + return [_deviceContext copy]; +} + +- (void)setCustomVariables:(NSDictionary *)customVariables { + _customVariables = [[NSMutableDictionary alloc] initWithDictionary:customVariables]; + [self updateMetadataTable]; +} + +- (void)setMinimumFetchInterval:(NSTimeInterval)minimumFetchInterval { + if (minimumFetchInterval < 0) { + _minimumFetchInterval = 0; + } else { + _minimumFetchInterval = minimumFetchInterval; + } +} + +- (void)setFetchTimeout:(NSTimeInterval)fetchTimeout { + if (fetchTimeout <= 0) { + _fetchTimeout = RCNHTTPDefaultConnectionTimeout; + } else { + _fetchTimeout = fetchTimeout; + } +} + +- (void)setLastApplyTimeInterval:(NSTimeInterval)lastApplyTimestamp { + _lastApplyTimeInterval = lastApplyTimestamp; + [_DBManager updateMetadataWithOption:RCNUpdateOptionApplyTime + values:@[ @(lastApplyTimestamp) ] + completionHandler:nil]; +} + +- (void)setLastSetDefaultsTimeInterval:(NSTimeInterval)lastSetDefaultsTimestamp { + _lastSetDefaultsTimeInterval = lastSetDefaultsTimestamp; + [_DBManager updateMetadataWithOption:RCNUpdateOptionDefaultTime + values:@[ @(lastSetDefaultsTimestamp) ] + completionHandler:nil]; +} + +#pragma mark Throttling + +- (BOOL)hasMinimumFetchIntervalElapsed:(NSTimeInterval)minimumFetchInterval { + if (self.lastFetchTimeInterval == 0) return YES; + + // Check if last config fetch is within minimum fetch interval in seconds. + NSTimeInterval diffInSeconds = [[NSDate date] timeIntervalSince1970] - self.lastFetchTimeInterval; + return diffInSeconds > minimumFetchInterval; +} + +- (BOOL)shouldThrottle { + NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; + return ((self.lastFetchTimeInterval > 0) && + (_lastFetchStatus != FIRRemoteConfigFetchStatusSuccess) && + (_exponentialBackoffThrottleEndTime - now > 0)); +} + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h new file mode 100644 index 00000000..28d2d693 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h @@ -0,0 +1,25 @@ +/* + * 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 <FirebaseRemoteConfig/FIRRemoteConfig.h> + +@interface FIRRemoteConfigValue () +@property(nonatomic, readwrite, assign) FIRRemoteConfigSource source; + +/// Designated initializer. +- (instancetype)initWithData:(NSData *)data + source:(FIRRemoteConfigSource)source NS_DESIGNATED_INITIALIZER; +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConstants3P.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConstants3P.m new file mode 100644 index 00000000..687d5878 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConstants3P.m @@ -0,0 +1,20 @@ +/* + * 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 <FirebaseRemoteConfig/FIRRemoteConfig.h> + +/// Firebase Remote Config service default namespace. +NSString *const FIRNamespaceGoogleMobilePlatform = @"firebase"; diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.h new file mode 100644 index 00000000..15697e3c --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.h @@ -0,0 +1,57 @@ +/* + * 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 <Foundation/Foundation.h> + +typedef NS_ENUM(NSInteger, RCNDeviceModel) { + RCNDeviceModelOther, + RCNDeviceModelPhone, + RCNDeviceModelTablet, + RCNDeviceModelTV, + RCNDeviceModelGlass, + RCNDeviceModelCar, + RCNDeviceModelWearable, +}; + +/// CocoaPods SDK version +NSString *FIRRemoteConfigPodVersion(void); + +/// App version. +NSString *FIRRemoteConfigAppVersion(void); + +/// App build version +NSString *FIRRemoteConfigAppBuildVersion(void); + +/// Device country, in lowercase. +NSString *FIRRemoteConfigDeviceCountry(void); + +/// Device locale, in language_country format, e.g. en_US. +NSString *FIRRemoteConfigDeviceLocale(void); + +/// Device subtype. +RCNDeviceModel FIRRemoteConfigDeviceSubtype(void); + +/// Device timezone. +NSString *FIRRemoteConfigTimezone(void); + +/// Update device context to the given dictionary. +NSMutableDictionary *FIRRemoteConfigDeviceContextWithProjectIdentifier( + NSString *GMPProjectIdentifier); + +/// Check whether client has changed device context, including app version, +/// iOS version, device country etc. This is used to determine whether to throttle. +BOOL FIRRemoteConfigHasDeviceContextChanged(NSDictionary *deviceContext, + NSString *GMPProjectIdentifier); diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.m new file mode 100644 index 00000000..cc5b8c12 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.m @@ -0,0 +1,240 @@ +/* + * 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 "FirebaseRemoteConfig/Sources/RCNDevice.h" + +#import <sys/utsname.h> + +#import "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h" +#import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h" + +#define STR(x) STR_EXPAND(x) +#define STR_EXPAND(x) #x + +static NSString *const RCNDeviceContextKeyVersion = @"app_version"; +static NSString *const RCNDeviceContextKeyBuild = @"app_build"; +static NSString *const RCNDeviceContextKeyOSVersion = @"os_version"; +static NSString *const RCNDeviceContextKeyDeviceLocale = @"device_locale"; +static NSString *const RCNDeviceContextKeyLocaleLanguage = @"locale_language"; +static NSString *const RCNDeviceContextKeyGMPProjectIdentifier = @"GMP_project_Identifier"; + +NSString *FIRRemoteConfigAppVersion() { + return [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; +} + +NSString *FIRRemoteConfigAppBuildVersion() { + return [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; +} + +NSString *FIRRemoteConfigPodVersion() { + return [NSString stringWithUTF8String:STR(FIRRemoteConfig_VERSION)]; +} + +RCNDeviceModel FIRRemoteConfigDeviceSubtype() { + NSString *model = [GULAppEnvironmentUtil deviceModel]; + if ([model hasPrefix:@"iPhone"]) { + return RCNDeviceModelPhone; + } + if ([model isEqualToString:@"iPad"]) { + return RCNDeviceModelTablet; + } + return RCNDeviceModelOther; +} + +NSString *FIRRemoteConfigDeviceCountry() { + return [[[NSLocale currentLocale] objectForKey:NSLocaleCountryCode] lowercaseString]; +} + +NSDictionary<NSString *, NSArray *> *FIRRemoteConfigFirebaseLocaleMap() { + return @{ + // Albanian + @"sq" : @[ @"sq_AL" ], + // Belarusian + @"be" : @[ @"be_BY" ], + // Bulgarian + @"bg" : @[ @"bg_BG" ], + // Catalan + @"ca" : @[ @"ca", @"ca_ES" ], + // Croatian + @"hr" : @[ @"hr", @"hr_HR" ], + // Czech + @"cs" : @[ @"cs", @"cs_CZ" ], + // Danish + @"da" : @[ @"da", @"da_DK" ], + // Estonian + @"et" : @[ @"et_EE" ], + // Finnish + @"fi" : @[ @"fi", @"fi_FI" ], + // Hebrew + @"he" : @[ @"he", @"iw_IL" ], + // Hindi + @"hi" : @[ @"hi_IN" ], + // Hungarian + @"hu" : @[ @"hu", @"hu_HU" ], + // Icelandic + @"is" : @[ @"is_IS" ], + // Indonesian + @"id" : @[ @"id", @"in_ID", @"id_ID" ], + // Irish + @"ga" : @[ @"ga_IE" ], + // Korean + @"ko" : @[ @"ko", @"ko_KR", @"ko-KR" ], + // Latvian + @"lv" : @[ @"lv_LV" ], + // Lithuanian + @"lt" : @[ @"lt_LT" ], + // Macedonian + @"mk" : @[ @"mk_MK" ], + // Malay + @"ms" : @[ @"ms_MY" ], + // Maltese + @"mt" : @[ @"mt_MT" ], + // Polish + @"pl" : @[ @"pl", @"pl_PL", @"pl-PL" ], + // Romanian + @"ro" : @[ @"ro", @"ro_RO" ], + // Russian + @"ru" : @[ @"ru_RU", @"ru", @"ru_BY", @"ru_KZ", @"ru-RU" ], + // Slovak + @"sk" : @[ @"sk", @"sk_SK" ], + // Slovenian + @"sl" : @[ @"sl_SI" ], + // Swedish + @"sv" : @[ @"sv", @"sv_SE", @"sv-SE" ], + // Turkish + @"tr" : @[ @"tr", @"tr-TR", @"tr_TR" ], + // Ukrainian + @"uk" : @[ @"uk", @"uk_UA" ], + // Vietnamese + @"vi" : @[ @"vi", @"vi_VN" ], + // The following are groups of locales or locales that sub-divide a + // language). + // Arabic + @"ar" : @[ + @"ar", @"ar_DZ", @"ar_BH", @"ar_EG", @"ar_IQ", @"ar_JO", @"ar_KW", + @"ar_LB", @"ar_LY", @"ar_MA", @"ar_OM", @"ar_QA", @"ar_SA", @"ar_SD", + @"ar_SY", @"ar_TN", @"ar_AE", @"ar_YE", @"ar_GB", @"ar-IQ", @"ar_US" + ], + // Simplified Chinese + @"zh_Hans" : @[ @"zh_CN", @"zh_SG", @"zh-Hans" ], + // Traditional Chinese + // Remove zh_HK until console added to the list. Otherwise client sends + // zh_HK and server/console falls back to zh. + // @"zh_Hant" : @[ @"zh_HK", @"zh_TW", @"zh-Hant", @"zh-HK", @"zh-TW" ], + @"zh_Hant" : @[ @"zh_TW", @"zh-Hant", @"zh-TW" ], + // Dutch + @"nl" : @[ @"nl", @"nl_BE", @"nl_NL", @"nl-NL" ], + // English + @"en" : @[ + @"en", @"en_AU", @"en_CA", @"en_IN", @"en_IE", @"en_MT", @"en_NZ", @"en_PH", + @"en_SG", @"en_ZA", @"en_GB", @"en_US", @"en_AE", @"en-AE", @"en_AS", @"en-AU", + @"en_BD", @"en-CA", @"en_EG", @"en_ES", @"en_GB", @"en-GB", @"en_HK", @"en_ID", + @"en-IN", @"en_NG", @"en-PH", @"en_PK", @"en-SG", @"en-US" + ], + // French + @"fr" : + @[ @"fr", @"fr_BE", @"fr_CA", @"fr_FR", @"fr_LU", @"fr_CH", @"fr-CA", @"fr-FR", @"fr_MA" ], + // German + @"de" : @[ @"de", @"de_AT", @"de_DE", @"de_LU", @"de_CH", @"de-DE" ], + // Greek + @"el" : @[ @"el", @"el_CY", @"el_GR" ], + // Italian + @"it" : @[ @"it", @"it_IT", @"it_CH", @"it-IT" ], + // Japanese + @"ja" : @[ @"ja", @"ja_JP", @"ja_JP_JP", @"ja-JP" ], + // Norwegian + @"no" : @[ @"nb", @"no_NO", @"no_NO_NY", @"nb_NO" ], + // Brazilian Portuguese + @"pt_BR" : @[ @"pt_BR", @"pt-BR" ], + // European Portuguese + @"pt_PT" : @[ @"pt", @"pt_PT", @"pt-PT" ], + // Serbian + @"sr" : @[ @"sr_BA", @"sr_ME", @"sr_RS", @"sr_Latn_BA", @"sr_Latn_ME", @"sr_Latn_RS" ], + // European Spanish + @"es_ES" : @[ @"es", @"es_ES", @"es-ES" ], + // Mexican Spanish + @"es_MX" : @[ @"es-MX", @"es_MX", @"es_US", @"es-US" ], + // Latin American Spanish + @"es_419" : @[ + @"es_AR", @"es_BO", @"es_CL", @"es_CO", @"es_CR", @"es_DO", @"es_EC", + @"es_SV", @"es_GT", @"es_HN", @"es_NI", @"es_PA", @"es_PY", @"es_PE", + @"es_PR", @"es_UY", @"es_VE", @"es-AR", @"es-CL", @"es-CO" + ], + // Thai + @"th" : @[ @"th", @"th_TH", @"th_TH_TH" ], + }; +} + +NSArray<NSString *> *FIRRemoteConfigAppManagerLocales() { + NSMutableArray *locales = [NSMutableArray array]; + NSDictionary<NSString *, NSArray *> *localesMap = FIRRemoteConfigFirebaseLocaleMap(); + for (NSString *key in localesMap) { + [locales addObjectsFromArray:localesMap[key]]; + } + return locales; +} +NSString *FIRRemoteConfigDeviceLocale() { + NSArray<NSString *> *locales = FIRRemoteConfigAppManagerLocales(); + NSArray<NSString *> *preferredLocalizations = + [NSBundle preferredLocalizationsFromArray:locales + forPreferences:[NSLocale preferredLanguages]]; + NSString *legalDocsLanguage = [preferredLocalizations firstObject]; + // Use en as the default language + return legalDocsLanguage ? legalDocsLanguage : @"en"; +} + +NSString *FIRRemoteConfigTimezone() { + NSTimeZone *timezone = [NSTimeZone systemTimeZone]; + return timezone.name; +} + +NSMutableDictionary *FIRRemoteConfigDeviceContextWithProjectIdentifier( + NSString *GMPProjectIdentifier) { + NSMutableDictionary *deviceContext = [[NSMutableDictionary alloc] init]; + deviceContext[RCNDeviceContextKeyVersion] = FIRRemoteConfigAppVersion(); + deviceContext[RCNDeviceContextKeyBuild] = FIRRemoteConfigAppBuildVersion(); + deviceContext[RCNDeviceContextKeyOSVersion] = [GULAppEnvironmentUtil systemVersion]; + deviceContext[RCNDeviceContextKeyDeviceLocale] = FIRRemoteConfigDeviceLocale(); + // NSDictionary setObjectForKey will fail if there's no GMP project ID, must check ahead. + if (GMPProjectIdentifier) { + deviceContext[RCNDeviceContextKeyGMPProjectIdentifier] = GMPProjectIdentifier; + } + return deviceContext; +} + +BOOL FIRRemoteConfigHasDeviceContextChanged(NSDictionary *deviceContext, + NSString *GMPProjectIdentifier) { + if (![deviceContext[RCNDeviceContextKeyVersion] isEqual:FIRRemoteConfigAppVersion()]) { + return YES; + } + if (![deviceContext[RCNDeviceContextKeyBuild] isEqual:FIRRemoteConfigAppBuildVersion()]) { + return YES; + } + if (![deviceContext[RCNDeviceContextKeyOSVersion] + isEqual:[GULAppEnvironmentUtil systemVersion]]) { + return YES; + } + if (![deviceContext[RCNDeviceContextKeyDeviceLocale] isEqual:FIRRemoteConfigDeviceLocale()]) { + return YES; + } + // GMP project id is optional. + if (deviceContext[RCNDeviceContextKeyGMPProjectIdentifier] && + ![deviceContext[RCNDeviceContextKeyGMPProjectIdentifier] isEqual:GMPProjectIdentifier]) { + return YES; + } + return NO; +} diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNFetch.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNFetch.m new file mode 100644 index 00000000..250e2318 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNFetch.m @@ -0,0 +1,568 @@ +/* + * 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 "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h" + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h" +#import "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigContent.h" +#import "FirebaseRemoteConfig/Sources/RCNConfigExperiment.h" +#import "FirebaseRemoteConfig/Sources/RCNDevice.h" +#import "GoogleUtilities/NSData+zlib/Private/GULNSDataInternal.h" + +#ifdef RCN_STAGING_SERVER +static NSString *const kServerURLDomain = + @"https://staging-firebaseremoteconfig.sandbox.googleapis.com"; +#else +static NSString *const kServerURLDomain = @"https://firebaseremoteconfig.googleapis.com"; +#endif + +static NSString *const kServerURLVersion = @"/v1"; +static NSString *const kServerURLProjects = @"/projects/"; +static NSString *const kServerURLNamespaces = @"/namespaces/"; +static NSString *const kServerURLQuery = @":fetch?"; +static NSString *const kServerURLKey = @"key="; +static NSString *const kRequestJSONKeyAppID = @"app_id"; + +static NSString *const kHTTPMethodPost = @"POST"; ///< HTTP request method config fetch using +static NSString *const kContentTypeHeaderName = @"Content-Type"; ///< HTTP Header Field Name +static NSString *const kContentEncodingHeaderName = + @"Content-Encoding"; ///< HTTP Header Field Name +static NSString *const kAcceptEncodingHeaderName = @"Accept-Encoding"; ///< HTTP Header Field Name +static NSString *const kETagHeaderName = @"etag"; ///< HTTP Header Field Name +static NSString *const kIfNoneMatchETagHeaderName = @"if-none-match"; ///< HTTP Header Field Name +static NSString *const kInstallationsAuthTokenHeaderName = @"x-goog-firebase-installations-auth"; +// Sends the bundle ID. Refer to b/130301479 for details. +static NSString *const kiOSBundleIdentifierHeaderName = + @"X-Ios-Bundle-Identifier"; ///< HTTP Header Field Name + +/// Config HTTP request content type proto buffer +static NSString *const kContentTypeValueJSON = @"application/json"; + +/// HTTP status codes. Ref: https://cloud.google.com/apis/design/errors#error_retries +static NSInteger const kRCNFetchResponseHTTPStatusCodeOK = 200; +static NSInteger const kRCNFetchResponseHTTPStatusTooManyRequests = 429; +static NSInteger const kRCNFetchResponseHTTPStatusCodeInternalError = 500; +static NSInteger const kRCNFetchResponseHTTPStatusCodeServiceUnavailable = 503; +static NSInteger const kRCNFetchResponseHTTPStatusCodeGatewayTimeout = 504; + +// Deprecated error code previously from FirebaseCore +static const NSInteger sFIRErrorCodeConfigFailed = -114; + +#pragma mark - RCNConfig + +@implementation RCNConfigFetch { + RCNConfigContent *_content; + RCNConfigSettings *_settings; + id<FIRAnalyticsInterop> _analytics; + RCNConfigExperiment *_experiment; + dispatch_queue_t _lockQueue; /// Guard the read/write operation. + NSURLSession *_fetchSession; /// Managed internally by the fetch instance. + NSString *_FIRNamespace; + FIROptions *_options; +} + +- (instancetype)init { + NSAssert(NO, @"Invalid initializer."); + return nil; +} + +/// Designated initializer +- (instancetype)initWithContent:(RCNConfigContent *)content + DBManager:(RCNConfigDBManager *)DBManager + settings:(RCNConfigSettings *)settings + analytics:(nullable id<FIRAnalyticsInterop>)analytics + experiment:(RCNConfigExperiment *)experiment + queue:(dispatch_queue_t)queue + namespace:(NSString *)FIRNamespace + options:(FIROptions *)options { + self = [super init]; + if (self) { + _FIRNamespace = FIRNamespace; + _settings = settings; + _analytics = analytics; + _experiment = experiment; + _lockQueue = queue; + _content = content; + _fetchSession = [self newFetchSession]; + _options = options; + } + return self; +} + +/// Force a new NSURLSession creation for updated config. +- (void)recreateNetworkSession { + if (_fetchSession) { + [_fetchSession invalidateAndCancel]; + } + _fetchSession = [self newFetchSession]; +} + +/// Return the current session. (Tests). +- (NSURLSession *)currentNetworkSession { + return _fetchSession; +} + +- (void)dealloc { + [_fetchSession invalidateAndCancel]; +} + +#pragma mark - Fetch Config API + +- (void)fetchConfigWithExpirationDuration:(NSTimeInterval)expirationDuration + completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler { + // Note: We expect the googleAppID to always be available. + BOOL hasDeviceContextChanged = + FIRRemoteConfigHasDeviceContextChanged(_settings.deviceContext, _options.googleAppID); + + __weak RCNConfigFetch *weakSelf = self; + RCNConfigFetch *fetchWithExpirationSelf = weakSelf; + dispatch_async(fetchWithExpirationSelf->_lockQueue, ^{ + RCNConfigFetch *strongSelf = fetchWithExpirationSelf; + + // Check whether we are outside of the minimum fetch interval. + if (![strongSelf->_settings hasMinimumFetchIntervalElapsed:expirationDuration] && + !hasDeviceContextChanged) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000051", @"Returning cached data."); + return [strongSelf reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusSuccess + withError:nil]; + } + + // Check if a fetch is already in progress. + if (strongSelf->_settings.isFetchInProgress) { + // Check if we have some fetched data. + if (strongSelf->_settings.lastFetchTimeInterval > 0) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000052", + @"A fetch is already in progress. Using previous fetch results."); + return [strongSelf reportCompletionOnHandler:completionHandler + withStatus:strongSelf->_settings.lastFetchStatus + withError:nil]; + } else { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000053", + @"A fetch is already in progress. Ignoring duplicate request."); + NSError *error = + [NSError errorWithDomain:FIRRemoteConfigErrorDomain + code:sFIRErrorCodeConfigFailed + userInfo:@{ + NSLocalizedDescriptionKey : + @"FetchError: Duplicate request while the previous one is pending" + }]; + return [strongSelf reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusFailure + withError:error]; + } + } + + // Check whether cache data is within throttle limit. + if ([strongSelf->_settings shouldThrottle] && !hasDeviceContextChanged) { + // Must set lastFetchStatus before FailReason. + strongSelf->_settings.lastFetchStatus = FIRRemoteConfigFetchStatusThrottled; + strongSelf->_settings.lastFetchError = FIRRemoteConfigErrorThrottled; + NSTimeInterval throttledEndTime = strongSelf->_settings.exponentialBackoffThrottleEndTime; + + NSError *error = + [NSError errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorThrottled + userInfo:@{ + FIRRemoteConfigThrottledEndTimeInSecondsKey : @(throttledEndTime) + }]; + return [strongSelf reportCompletionOnHandler:completionHandler + withStatus:strongSelf->_settings.lastFetchStatus + withError:error]; + } + strongSelf->_settings.isFetchInProgress = YES; + [strongSelf refreshInstallationsTokenWithCompletionHandler:completionHandler]; + }); +} + +#pragma mark - Fetch helpers + +- (NSString *)FIRAppNameFromFullyQualifiedNamespace { + return [[_FIRNamespace componentsSeparatedByString:@":"] lastObject]; +} +/// Refresh installation ID token before fetching config. installation ID is now mandatory for fetch +/// requests to work.(b/14751422). +- (void)refreshInstallationsTokenWithCompletionHandler: + (FIRRemoteConfigFetchCompletion)completionHandler { + FIRInstallations *installations = [FIRInstallations + installationsWithApp:[FIRApp appNamed:[self FIRAppNameFromFullyQualifiedNamespace]]]; + if (!installations || !_options.GCMSenderID) { + NSString *errorDescription = @"Failed to get GCMSenderID"; + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000074", @"%@", + [NSString stringWithFormat:@"%@", errorDescription]); + self->_settings.isFetchInProgress = NO; + return [self + reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusFailure + withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{ + NSLocalizedDescriptionKey : errorDescription + }]]; + } + FIRInstallationsTokenHandler installationsTokenHandler = ^( + FIRInstallationsAuthTokenResult *tokenResult, NSError *error) { + if (!tokenResult || !tokenResult.authToken || error) { + NSString *errorDescription = + [NSString stringWithFormat:@"Failed to get installations token. Error : %@.", error]; + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000073", @"%@", + [NSString stringWithFormat:@"%@", errorDescription]); + self->_settings.isFetchInProgress = NO; + return [self + reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusFailure + withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{ + NSLocalizedDescriptionKey : errorDescription + }]]; + } + + // We have a valid token. Get the backing installationID. + __weak RCNConfigFetch *weakSelf = self; + [installations installationIDWithCompletion:^(NSString *_Nullable identifier, + NSError *_Nullable error) { + RCNConfigFetch *strongSelf = weakSelf; + + // Dispatch to the RC serial queue to update settings on the queue. + dispatch_async(strongSelf->_lockQueue, ^{ + RCNConfigFetch *strongSelfQueue = weakSelf; + + // Update config settings with the IID and token. + strongSelfQueue->_settings.configInstallationsToken = tokenResult.authToken; + strongSelfQueue->_settings.configInstallationsIdentifier = identifier; + + if (!identifier || error) { + NSString *errorDescription = + [NSString stringWithFormat:@"Error getting iid : %@.", error]; + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000055", @"%@", + [NSString stringWithFormat:@"%@", errorDescription]); + strongSelfQueue->_settings.isFetchInProgress = NO; + return [strongSelfQueue + reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusFailure + withError:[NSError + errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{ + NSLocalizedDescriptionKey : errorDescription + }]]; + } + + FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000022", @"Success to get iid : %@.", + strongSelfQueue->_settings.configInstallationsIdentifier); + [strongSelf doFetchCall:completionHandler]; + }); + }]; + }; + + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000039", @"Starting requesting token."); + [installations authTokenWithCompletion:installationsTokenHandler]; +} + +- (void)doFetchCall:(FIRRemoteConfigFetchCompletion)completionHandler { + [self getAnalyticsUserPropertiesWithCompletionHandler:^(NSDictionary *userProperties) { + dispatch_async(self->_lockQueue, ^{ + [self fetchWithUserProperties:userProperties completionHandler:completionHandler]; + }); + }]; +} + +- (void)getAnalyticsUserPropertiesWithCompletionHandler: + (FIRAInteropUserPropertiesCallback)completionHandler { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000060", @"Fetch with user properties completed."); + id<FIRAnalyticsInterop> analytics = self->_analytics; + if (analytics == nil) { + completionHandler(@{}); + } else { + [analytics getUserPropertiesWithCallback:completionHandler]; + } +} + +- (void)reportCompletionOnHandler:(FIRRemoteConfigFetchCompletion)completionHandler + withStatus:(FIRRemoteConfigFetchStatus)status + withError:(NSError *)error { + if (completionHandler) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(status, error); + }); + } +} + +- (void)fetchWithUserProperties:(NSDictionary *)userProperties + completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000061", @"Fetch with user properties initiated."); + + NSString *postRequestString = [_settings nextRequestWithUserProperties:userProperties]; + + // Get POST request content. + NSData *content = [postRequestString dataUsingEncoding:NSUTF8StringEncoding]; + NSError *compressionError; + NSData *compressedContent = [NSData gul_dataByGzippingData:content error:&compressionError]; + if (compressionError) { + NSString *errString = [NSString stringWithFormat:@"Failed to compress the config request."]; + FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000033", @"%@", errString); + + self->_settings.isFetchInProgress = NO; + return [self + reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusFailure + withError:[NSError + errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{NSLocalizedDescriptionKey : errString}]]; + } + + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000040", @"Start config fetch."); + __weak RCNConfigFetch *weakSelf = self; + RCNConfigFetcherCompletion fetcherCompletion = ^(NSData *data, NSURLResponse *response, + NSError *error) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000050", + @"config fetch completed. Error: %@ StatusCode: %ld", (error ? error : @"nil"), + (long)[((NSHTTPURLResponse *)response) statusCode]); + + // The fetch has completed. + self->_settings.isFetchInProgress = NO; + + RCNConfigFetch *fetcherCompletionSelf = weakSelf; + if (!fetcherCompletionSelf) { + return; + }; + + dispatch_async(fetcherCompletionSelf->_lockQueue, ^{ + RCNConfigFetch *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + NSInteger statusCode = [((NSHTTPURLResponse *)response) statusCode]; + + if (error || (statusCode != kRCNFetchResponseHTTPStatusCodeOK)) { + // Update metadata about fetch failure. + [strongSelf->_settings updateMetadataWithFetchSuccessStatus:NO]; + if (error) { + if (strongSelf->_settings.lastFetchStatus == FIRRemoteConfigFetchStatusSuccess) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000025", + @"RCN Fetch failure: %@. Using cached config result.", error); + } else { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000026", + @"RCN Fetch failure: %@. No cached config result.", error); + } + } + if (statusCode != kRCNFetchResponseHTTPStatusCodeOK) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000026", + @"RCN Fetch failure. Response http error code: %ld", (long)statusCode); + // Response error code 429, 500, 503 will trigger exponential backoff mode. + if (statusCode == kRCNFetchResponseHTTPStatusTooManyRequests || + statusCode == kRCNFetchResponseHTTPStatusCodeInternalError || + statusCode == kRCNFetchResponseHTTPStatusCodeServiceUnavailable || + statusCode == kRCNFetchResponseHTTPStatusCodeGatewayTimeout) { + if ([strongSelf->_settings shouldThrottle]) { + // Must set lastFetchStatus before FailReason. + strongSelf->_settings.lastFetchStatus = FIRRemoteConfigFetchStatusThrottled; + strongSelf->_settings.lastFetchError = FIRRemoteConfigErrorThrottled; + NSTimeInterval throttledEndTime = + strongSelf->_settings.exponentialBackoffThrottleEndTime; + + NSError *error = [NSError + errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorThrottled + userInfo:@{ + FIRRemoteConfigThrottledEndTimeInSecondsKey : @(throttledEndTime) + }]; + return [strongSelf reportCompletionOnHandler:completionHandler + withStatus:strongSelf->_settings.lastFetchStatus + withError:error]; + } + } // Response error code 429, 500, 503 + } // StatusCode != kRCNFetchResponseHTTPStatusCodeOK + // Return back the received error. + // Must set lastFetchStatus before setting Fetch Error. + strongSelf->_settings.lastFetchStatus = FIRRemoteConfigFetchStatusFailure; + strongSelf->_settings.lastFetchError = FIRRemoteConfigErrorInternalError; + NSDictionary<NSErrorUserInfoKey, id> *userInfo = @{ + NSLocalizedDescriptionKey : + (error ? [error localizedDescription] + : [NSString + stringWithFormat:@"Internal Error. Status code: %ld", (long)statusCode]) + }; + return [strongSelf + reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusFailure + withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:userInfo]]; + } + + // Fetch was successful. Check if we have data. + NSError *retError; + if (!data) { + FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000043", @"RCN Fetch: No data in fetch response"); + return [strongSelf reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusSuccess + withError:nil]; + } + + // Config fetch succeeded. + // JSONObjectWithData is always expected to return an NSDictionary in our case + NSMutableDictionary *fetchedConfig = + [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableContainers + error:&retError]; + if (retError) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000042", + @"RCN Fetch failure: %@. Could not parse response data as JSON", error); + } + + // Check and log if we received an error from the server + if (fetchedConfig && fetchedConfig.count == 1 && fetchedConfig[RCNFetchResponseKeyError]) { + NSString *errStr = [NSString stringWithFormat:@"RCN Fetch Failure: Server returned error:"]; + NSDictionary *errDict = fetchedConfig[RCNFetchResponseKeyError]; + if (errDict[RCNFetchResponseKeyErrorCode]) { + errStr = [errStr + stringByAppendingString:[NSString + stringWithFormat:@"code: %@", + errDict[RCNFetchResponseKeyErrorCode]]]; + } + if (errDict[RCNFetchResponseKeyErrorStatus]) { + errStr = [errStr stringByAppendingString: + [NSString stringWithFormat:@". Status: %@", + errDict[RCNFetchResponseKeyErrorStatus]]]; + } + if (errDict[RCNFetchResponseKeyErrorMessage]) { + errStr = + [errStr stringByAppendingString: + [NSString stringWithFormat:@". Message: %@", + errDict[RCNFetchResponseKeyErrorMessage]]]; + } + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000044", @"%@.", errStr); + return [strongSelf + reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusFailure + withError:[NSError + errorWithDomain:FIRRemoteConfigErrorDomain + code:FIRRemoteConfigErrorInternalError + userInfo:@{NSLocalizedDescriptionKey : errStr}]]; + } + + // Add the fetched config to the database. + if (fetchedConfig) { + // Update config content to cache and DB. + [self->_content updateConfigContentWithResponse:fetchedConfig + forNamespace:self->_FIRNamespace]; + // Update experiments. + [strongSelf->_experiment + updateExperimentsWithResponse:fetchedConfig[RCNFetchResponseKeyExperimentDescriptions]]; + } else { + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000063", + @"Empty response with no fetched config."); + } + + // We had a successful fetch. Update the current eTag in settings if different. + NSString *latestETag = ((NSHTTPURLResponse *)response).allHeaderFields[kETagHeaderName]; + if (!self->_settings.lastETag || !([self->_settings.lastETag isEqualToString:latestETag])) { + self->_settings.lastETag = latestETag; + } + + [self->_settings updateMetadataWithFetchSuccessStatus:YES]; + return [strongSelf reportCompletionOnHandler:completionHandler + withStatus:FIRRemoteConfigFetchStatusSuccess + withError:nil]; + }); + }; + + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000061", @"Making remote config fetch."); + + NSURLSessionDataTask *dataTask = [self URLSessionDataTaskWithContent:compressedContent + completionHandler:fetcherCompletion]; + [dataTask resume]; +} + +- (NSString *)constructServerURL { + NSString *serverURLStr = [[NSString alloc] initWithString:kServerURLDomain]; + serverURLStr = [serverURLStr stringByAppendingString:kServerURLVersion]; + + if (_options.projectID) { + serverURLStr = [serverURLStr stringByAppendingString:kServerURLProjects]; + serverURLStr = [serverURLStr stringByAppendingString:_options.projectID]; + } else { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000070", + @"Missing `projectID` from `FirebaseOptions`, please ensure the configured " + @"`FirebaseApp` is configured with `FirebaseOptions` that contains a `projectID`."); + } + + serverURLStr = [serverURLStr stringByAppendingString:kServerURLNamespaces]; + + // Get the namespace from the fully qualified namespace string of "namespace:FIRAppName". + NSString *namespace = + [_FIRNamespace substringToIndex:[_FIRNamespace rangeOfString:@":"].location]; + serverURLStr = [serverURLStr stringByAppendingString:namespace]; + serverURLStr = [serverURLStr stringByAppendingString:kServerURLQuery]; + + if (_options.APIKey) { + serverURLStr = [serverURLStr stringByAppendingString:kServerURLKey]; + serverURLStr = [serverURLStr stringByAppendingString:_options.APIKey]; + } else { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000071", + @"Missing `APIKey` from `FirebaseOptions`, please ensure the configured " + @"`FirebaseApp` is configured with `FirebaseOptions` that contains an `APIKey`."); + } + + return serverURLStr; +} + +- (NSURLSession *)newFetchSession { + NSURLSessionConfiguration *config = + [[NSURLSessionConfiguration defaultSessionConfiguration] copy]; + config.timeoutIntervalForRequest = _settings.fetchTimeout; + config.timeoutIntervalForResource = _settings.fetchTimeout; + NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; + return session; +} + +- (NSURLSessionDataTask *)URLSessionDataTaskWithContent:(NSData *)content + completionHandler: + (RCNConfigFetcherCompletion)fetcherCompletion { + NSURL *URL = [NSURL URLWithString:[self constructServerURL]]; + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000046", @"%@", + [NSString stringWithFormat:@"Making config request: %@", [URL absoluteString]]); + + NSTimeInterval timeoutInterval = _fetchSession.configuration.timeoutIntervalForResource; + NSMutableURLRequest *URLRequest = + [[NSMutableURLRequest alloc] initWithURL:URL + cachePolicy:NSURLRequestReloadIgnoringLocalCacheData + timeoutInterval:timeoutInterval]; + URLRequest.HTTPMethod = kHTTPMethodPost; + [URLRequest setValue:kContentTypeValueJSON forHTTPHeaderField:kContentTypeHeaderName]; + [URLRequest setValue:_settings.configInstallationsToken + forHTTPHeaderField:kInstallationsAuthTokenHeaderName]; + [URLRequest setValue:[[NSBundle mainBundle] bundleIdentifier] + forHTTPHeaderField:kiOSBundleIdentifierHeaderName]; + [URLRequest setValue:@"gzip" forHTTPHeaderField:kContentEncodingHeaderName]; + [URLRequest setValue:@"gzip" forHTTPHeaderField:kAcceptEncodingHeaderName]; + // Set the eTag from the last successful fetch, if available. + if (_settings.lastETag) { + [URLRequest setValue:_settings.lastETag forHTTPHeaderField:kIfNoneMatchETagHeaderName]; + } + [URLRequest setHTTPBody:content]; + + return [_fetchSession dataTaskWithRequest:URLRequest completionHandler:fetcherCompletion]; +} + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h new file mode 100644 index 00000000..020d651e --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h @@ -0,0 +1,55 @@ +/* + * 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface RCNUserDefaultsManager : NSObject + +/// The last eTag received from the backend. +@property(nonatomic, assign) NSString *lastETag; +/// The time of the last eTag update. +@property(nonatomic, assign) NSTimeInterval lastETagUpdateTime; +/// The time of the last successful fetch. +@property(nonatomic, assign) NSTimeInterval lastFetchTime; +/// The time of the last successful fetch. +@property(nonatomic, assign) NSString *lastFetchStatus; +/// Boolean indicating if the last (one or more) fetch(es) was/were unsuccessful, in which case we +/// are in an exponential backoff mode. +@property(nonatomic, assign) BOOL isClientThrottledWithExponentialBackoff; +/// Time when the next request can be made while being throttled. +@property(nonatomic, assign) NSTimeInterval throttleEndTime; +/// The retry interval increases exponentially for cumulative fetch failures. Refer to +/// go/rc-client-throttling for details. +@property(nonatomic, assign) NSTimeInterval currentThrottlingRetryIntervalSeconds; + +/// Designated initializer. +- (instancetype)initWithAppName:(NSString *)appName + bundleID:(NSString *)bundleIdentifier + namespace:(NSString *)firebaseNamespace NS_DESIGNATED_INITIALIZER; + +// NOLINTBEGIN +/// Use `initWithAppName:bundleID:namespace:` instead. +- (instancetype)init + __attribute__((unavailable("Use `initWithAppName:bundleID:namespace:` instead."))); +// NOLINTEND + +/// Delete all saved userdefaults for this instance. +- (void)resetUserDefaults; +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m new file mode 100644 index 00000000..edd716c2 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m @@ -0,0 +1,233 @@ +/* + * 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 "FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h" +#import <FirebaseRemoteConfig/FIRRemoteConfig.h> +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" + +static NSString *const kRCNGroupPrefix = @"group"; +static NSString *const kRCNGroupSuffix = @"firebase"; +static NSString *const kRCNUserDefaultsKeyNamelastETag = @"lastETag"; +static NSString *const kRCNUserDefaultsKeyNamelastETagUpdateTime = @"lastETagUpdateTime"; +static NSString *const kRCNUserDefaultsKeyNameLastSuccessfulFetchTime = @"lastSuccessfulFetchTime"; +static NSString *const kRCNUserDefaultsKeyNamelastFetchStatus = @"lastFetchStatus"; +static NSString *const kRCNUserDefaultsKeyNameIsClientThrottled = + @"isClientThrottledWithExponentialBackoff"; +static NSString *const kRCNUserDefaultsKeyNameThrottleEndTime = @"throttleEndTime"; +static NSString *const kRCNUserDefaultsKeyNamecurrentThrottlingRetryInterval = + @"currentThrottlingRetryInterval"; + +@interface RCNUserDefaultsManager () { + /// User Defaults instance for this bundleID. NSUserDefaults is guaranteed to be thread-safe. + NSUserDefaults *_userDefaults; + /// The suite name for this user defaults instance. It is a combination of a prefix and the + /// bundleID. This is because you cannot use just the bundleID of the current app as the suite + /// name when initializing user defaults. + NSString *_userDefaultsSuiteName; + /// The FIRApp that this instance is scoped within. + NSString *_firebaseAppName; + /// The Firebase Namespace that this instance is scoped within. + NSString *_firebaseNamespace; + /// The bundleID of the app. In case of an extension, this will be the bundleID of the parent app. + NSString *_bundleIdentifier; +} + +@end + +@implementation RCNUserDefaultsManager + +#pragma mark Initializers. + +/// Designated initializer. +- (instancetype)initWithAppName:(NSString *)appName + bundleID:(NSString *)bundleIdentifier + namespace:(NSString *)firebaseNamespace { + self = [super init]; + if (self) { + _firebaseAppName = appName; + _bundleIdentifier = bundleIdentifier; + NSInteger location = [firebaseNamespace rangeOfString:@":"].location; + if (location == NSNotFound) { + FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000064", + @"Error: Namespace %@ is not fully qualified app:namespace.", firebaseNamespace); + _firebaseNamespace = firebaseNamespace; + } else { + _firebaseNamespace = [firebaseNamespace substringToIndex:location]; + } + + // Initialize the user defaults with a prefix and the bundleID. For app extensions, this will be + // the bundleID of the app extension. + _userDefaults = + [RCNUserDefaultsManager sharedUserDefaultsForBundleIdentifier:_bundleIdentifier]; + } + + return self; +} + ++ (NSUserDefaults *)sharedUserDefaultsForBundleIdentifier:(NSString *)bundleIdentifier { + static dispatch_once_t onceToken; + static NSUserDefaults *sharedInstance; + dispatch_once(&onceToken, ^{ + NSString *userDefaultsSuiteName = + [RCNUserDefaultsManager userDefaultsSuiteNameForBundleIdentifier:bundleIdentifier]; + sharedInstance = [[NSUserDefaults alloc] initWithSuiteName:userDefaultsSuiteName]; + }); + return sharedInstance; +} + ++ (NSString *)userDefaultsSuiteNameForBundleIdentifier:(NSString *)bundleIdentifier { + NSString *suiteName = + [NSString stringWithFormat:@"%@.%@.%@", kRCNGroupPrefix, bundleIdentifier, kRCNGroupSuffix]; + return suiteName; +} + +#pragma mark Public properties. + +- (NSString *)lastETag { + return [[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNamelastETag]; +} + +- (void)setLastETag:(NSString *)lastETag { + if (lastETag) { + [self setInstanceUserDefaultsValue:lastETag forKey:kRCNUserDefaultsKeyNamelastETag]; + } +} + +- (NSTimeInterval)lastETagUpdateTime { + NSNumber *lastETagUpdateTime = + [[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNamelastETagUpdateTime]; + return lastETagUpdateTime.doubleValue; +} + +- (void)setLastETagUpdateTime:(NSTimeInterval)lastETagUpdateTime { + if (lastETagUpdateTime) { + [self setInstanceUserDefaultsValue:@(lastETagUpdateTime) + forKey:kRCNUserDefaultsKeyNamelastETagUpdateTime]; + } +} + +- (NSTimeInterval)lastFetchTime { + NSNumber *lastFetchTime = + [[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNameLastSuccessfulFetchTime]; + return lastFetchTime.doubleValue; +} + +- (void)setLastFetchTime:(NSTimeInterval)lastFetchTime { + [self setInstanceUserDefaultsValue:@(lastFetchTime) + forKey:kRCNUserDefaultsKeyNameLastSuccessfulFetchTime]; +} + +- (NSString *)lastFetchStatus { + return [[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNamelastFetchStatus]; +} + +- (void)setLastFetchStatus:(NSString *)lastFetchStatus { + if (lastFetchStatus) { + [self setInstanceUserDefaultsValue:lastFetchStatus + forKey:kRCNUserDefaultsKeyNamelastFetchStatus]; + } +} + +- (BOOL)isClientThrottledWithExponentialBackoff { + NSNumber *isClientThrottled = + [[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNameIsClientThrottled]; + return isClientThrottled.boolValue; +} + +- (void)setIsClientThrottledWithExponentialBackoff:(BOOL)isClientThrottled { + [self setInstanceUserDefaultsValue:@(isClientThrottled) + forKey:kRCNUserDefaultsKeyNameIsClientThrottled]; +} + +- (NSTimeInterval)throttleEndTime { + NSNumber *throttleEndTime = + [[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNameThrottleEndTime]; + return throttleEndTime.doubleValue; +} + +- (void)setThrottleEndTime:(NSTimeInterval)throttleEndTime { + [self setInstanceUserDefaultsValue:@(throttleEndTime) + forKey:kRCNUserDefaultsKeyNameThrottleEndTime]; +} + +- (NSTimeInterval)currentThrottlingRetryIntervalSeconds { + NSNumber *throttleEndTime = [[self instanceUserDefaults] + objectForKey:kRCNUserDefaultsKeyNamecurrentThrottlingRetryInterval]; + return throttleEndTime.doubleValue; +} + +- (void)setCurrentThrottlingRetryIntervalSeconds:(NSTimeInterval)throttlingRetryIntervalSeconds { + [self setInstanceUserDefaultsValue:@(throttlingRetryIntervalSeconds) + forKey:kRCNUserDefaultsKeyNamecurrentThrottlingRetryInterval]; +} + +#pragma mark Public methods. +- (void)resetUserDefaults { + [self resetInstanceUserDefaults]; +} + +#pragma mark Private methods. + +// There is a nested hierarchy for the userdefaults as follows: +// [FIRAppName][FIRNamespaceName][Key] +- (nonnull NSDictionary *)appUserDefaults { + NSString *appPath = _firebaseAppName; + NSDictionary *appDict = [_userDefaults valueForKeyPath:appPath]; + if (!appDict) { + appDict = [[NSDictionary alloc] init]; + } + return appDict; +} + +// Search for the user defaults for this (app, namespace) instance using the valueForKeyPath method. +- (nonnull NSDictionary *)instanceUserDefaults { + NSString *appNamespacePath = + [NSString stringWithFormat:@"%@.%@", _firebaseAppName, _firebaseNamespace]; + NSDictionary *appNamespaceDict = [_userDefaults valueForKeyPath:appNamespacePath]; + + if (!appNamespaceDict) { + appNamespaceDict = [[NSMutableDictionary alloc] init]; + } + return appNamespaceDict; +} + +// Update users defaults for just this (app, namespace) instance. +- (void)setInstanceUserDefaultsValue:(NSObject *)value forKey:(NSString *)key { + @synchronized(_userDefaults) { + NSMutableDictionary *appUserDefaults = [[self appUserDefaults] mutableCopy]; + NSMutableDictionary *appNamespaceUserDefaults = [[self instanceUserDefaults] mutableCopy]; + [appNamespaceUserDefaults setObject:value forKey:key]; + [appUserDefaults setObject:appNamespaceUserDefaults forKey:_firebaseNamespace]; + [_userDefaults setObject:appUserDefaults forKey:_firebaseAppName]; + // We need to synchronize to have this value updated for the extension. + [_userDefaults synchronize]; + } +} + +// Delete any existing userdefaults for this instance. +- (void)resetInstanceUserDefaults { + @synchronized(_userDefaults) { + NSMutableDictionary *appUserDefaults = [[self appUserDefaults] mutableCopy]; + NSMutableDictionary *appNamespaceUserDefaults = [[self instanceUserDefaults] mutableCopy]; + [appNamespaceUserDefaults removeAllObjects]; + [appUserDefaults setObject:appNamespaceUserDefaults forKey:_firebaseNamespace]; + [_userDefaults setObject:appUserDefaults forKey:_firebaseAppName]; + // We need to synchronize to have this value updated for the extension. + [_userDefaults synchronize]; + } +} + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h new file mode 100644 index 00000000..2fb16226 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h @@ -0,0 +1,47 @@ +/* + * 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> + +@interface GULAppEnvironmentUtil : NSObject + +/// Indicates whether the app is from Apple Store or not. Returns NO if the app is on simulator, +/// development environment or sideloaded. ++ (BOOL)isFromAppStore; + +/// Indicates whether the app is a Testflight app. Returns YES if the app has sandbox receipt. +/// Returns NO otherwise. ++ (BOOL)isAppStoreReceiptSandbox; + +/// Indicates whether the app is on simulator or not at runtime depending on the device +/// architecture. ++ (BOOL)isSimulator; + +/// The current device model. Returns an empty string if device model cannot be retrieved. ++ (NSString *)deviceModel; + +/// The current operating system version. Returns an empty string if the system version cannot be +/// retrieved. ++ (NSString *)systemVersion; + +/// Indicates whether it is running inside an extension or an app. ++ (BOOL)isAppExtension; + +/// @return Returns @YES when is run on iOS version greater or equal to 7.0 ++ (BOOL)isIOS7OrHigher DEPRECATED_MSG_ATTRIBUTE( + "Always `YES` because only iOS 8 and higher supported. The method will be removed."); + +@end diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULHeartbeatDateStorage.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULHeartbeatDateStorage.h new file mode 100644 index 00000000..9432dfc0 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULHeartbeatDateStorage.h @@ -0,0 +1,49 @@ +/* + * 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/// Stores either a date or a dictionary to a specified file. +@interface GULHeartbeatDateStorage : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +@property(nonatomic, readonly) NSURL *fileURL; + +/** + * Default initializer. + * @param fileName The name of the file to store the date information. + * exist, it will be created if needed. + */ +- (instancetype)initWithFileName:(NSString *)fileName; + +/** + * Reads the date from the specified file for the given tag. + * @return Returns date if exists, otherwise `nil`. + */ +- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag; + +/** + * Saves the date for the specified tag in the specified file. + * @return YES on success, NO otherwise. + */ +- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULKeychainStorage.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULKeychainStorage.h new file mode 100644 index 00000000..dc01a836 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULKeychainStorage.h @@ -0,0 +1,79 @@ +/* + * 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 <Foundation/Foundation.h> + +@class FBLPromise<ValueType>; + +NS_ASSUME_NONNULL_BEGIN + +/// The class provides a convenient abstraction on top of the iOS Keychain API to save data. +@interface GULKeychainStorage : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** Initializes the keychain storage with Keychain Service name. + * @param service A Keychain Service name that will be used to store and retrieve objects. See also + * `kSecAttrService`. + */ +- (instancetype)initWithService:(NSString *)service; + +/** + * Get an object by key. + * @param key The key. + * @param objectClass The expected object class required by `NSSecureCoding`. + * @param accessGroup The Keychain Access Group. + * + * @return Returns a promise. It is resolved with an object stored by key if exists. It is resolved + * with `nil` when the object not found. It fails on a Keychain error. + */ +- (FBLPromise<id<NSSecureCoding>> *)getObjectForKey:(NSString *)key + objectClass:(Class)objectClass + accessGroup:(nullable NSString *)accessGroup; + +/** + * Saves the given object by the given key. + * @param object The object to store. + * @param key The key to store the object. If there is an existing object by the key, it will be + * overridden. + * @param accessGroup The Keychain Access Group. + * + * @return Returns which is resolved with `[NSNull null]` on success. + */ +- (FBLPromise<NSNull *> *)setObject:(id<NSSecureCoding>)object + forKey:(NSString *)key + accessGroup:(nullable NSString *)accessGroup; + +/** + * Removes the object by the given key. + * @param key The key to store the object. If there is an existing object by the key, it will be + * overridden. + * @param accessGroup The Keychain Access Group. + * + * @return Returns which is resolved with `[NSNull null]` on success. + */ +- (FBLPromise<NSNull *> *)removeObjectForKey:(NSString *)key + accessGroup:(nullable NSString *)accessGroup; + +#if TARGET_OS_OSX +/// If not `nil`, then only this keychain will be used to save and read data (see +/// `kSecMatchSearchList` and `kSecUseKeychain`. It is mostly intended to be used by unit tests. +@property(nonatomic, nullable) SecKeychainRef keychainRef; +#endif // TARGET_OSX + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULKeychainUtils.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULKeychainUtils.h new file mode 100644 index 00000000..de4bef2f --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULKeychainUtils.h @@ -0,0 +1,61 @@ +/* + * 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString *const kGULKeychainUtilsErrorDomain; + +/// Helper functions to access Keychain. +@interface GULKeychainUtils : NSObject + +/** Fetches a keychain item data matching to the provided query. + * @param query A dictionary with Keychain query parameters. See docs for `SecItemCopyMatching` for + * details. + * @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be + * assigned with an error if there is. + * @returns Data for the first Keychain Item matching the provided query or `nil` if there is not + * such an item (`outError` will be `nil` in this case) or an error occurred. + */ ++ (nullable NSData *)getItemWithQuery:(NSDictionary *)query + error:(NSError *_Nullable *_Nullable)outError; + +/** Stores data to a Keychain Item matching to the provided query. An existing Keychain Item + * matching the query parameters will be updated or a new will be created. + * @param item A Keychain Item data to store. + * @param query A dictionary with Keychain query parameters. See docs for `SecItemAdd` and + * `SecItemUpdate` for details. + * @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be + * assigned with an error if there is. + * @returns `YES` when data was successfully stored, `NO` otherwise. + */ ++ (BOOL)setItem:(NSData *)item + withQuery:(NSDictionary *)query + error:(NSError *_Nullable *_Nullable)outError; + +/** Removes a Keychain Item matching to the provided query. + * @param query A dictionary with Keychain query parameters. See docs for `SecItemDelete` for + * details. + * @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be + * assigned with an error if there is. + * @returns `YES` if the item was removed successfully or doesn't exist, `NO` otherwise. + */ ++ (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULSecureCoding.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULSecureCoding.h new file mode 100644 index 00000000..8484b395 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/Environment/Private/GULSecureCoding.h @@ -0,0 +1,36 @@ +// 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** The class wraps `NSKeyedArchiver` and `NSKeyedUnarchiver` API to provide a unified secure coding + * methods for iOS versions before and after 11. + */ +@interface GULSecureCoding : NSObject + ++ (nullable id)unarchivedObjectOfClasses:(NSSet<Class> *)classes + fromData:(NSData *)data + error:(NSError **)outError; + ++ (nullable id)unarchivedObjectOfClass:(Class)class + fromData:(NSData *)data + error:(NSError **)outError; + ++ (nullable NSData *)archivedDataWithRootObject:(id<NSCoding>)object error:(NSError **)outError; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/NSData+zlib/Private/GULNSDataInternal.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/NSData+zlib/Private/GULNSDataInternal.h new file mode 100644 index 00000000..903589d5 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/GoogleUtilities/NSData+zlib/Private/GULNSDataInternal.h @@ -0,0 +1,22 @@ +// 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. + +// An umbrella header, for any other libraries in this repo to access Firebase Public and Private +// headers. Any package manager complexity should be handled here. + +#if SWIFT_PACKAGE +@import GoogleUtilities_NSData; +#else +#import <GoogleUtilities/GULNSData+zlib.h> +#endif diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInterop.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInterop.h new file mode 100644 index 00000000..6581b536 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInterop.h @@ -0,0 +1,66 @@ +/* + * 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> + +@protocol FIRAnalyticsInteropListener; + +NS_ASSUME_NONNULL_BEGIN + +/// Block typedef callback parameter to getUserPropertiesWithCallback:. +typedef void (^FIRAInteropUserPropertiesCallback)(NSDictionary<NSString *, id> *userProperties); + +/// Connector for bridging communication between Firebase SDKs and FirebaseAnalytics API. +@protocol FIRAnalyticsInterop + +/// Sets user property when trigger event is logged. This API is only available in the SDK. +- (void)setConditionalUserProperty:(NSDictionary<NSString *, id> *)conditionalUserProperty; + +/// Clears user property if set. +- (void)clearConditionalUserProperty:(NSString *)userPropertyName + forOrigin:(NSString *)origin + clearEventName:(NSString *)clearEventName + clearEventParameters:(NSDictionary<NSString *, NSString *> *)clearEventParameters; + +/// Returns currently set user properties. +- (NSArray<NSDictionary<NSString *, NSString *> *> *)conditionalUserProperties:(NSString *)origin + propertyNamePrefix: + (NSString *)propertyNamePrefix; + +/// Returns the maximum number of user properties. +- (NSInteger)maxUserProperties:(NSString *)origin; + +/// Returns the user properties to a callback function. +- (void)getUserPropertiesWithCallback:(FIRAInteropUserPropertiesCallback)callback; + +/// Logs events. +- (void)logEventWithOrigin:(NSString *)origin + name:(NSString *)name + parameters:(nullable NSDictionary<NSString *, id> *)parameters; + +/// Sets user property. +- (void)setUserPropertyWithOrigin:(NSString *)origin name:(NSString *)name value:(id)value; + +/// Registers an Analytics listener for the given origin. +- (void)registerAnalyticsListener:(id<FIRAnalyticsInteropListener>)listener + withOrigin:(NSString *)origin; + +/// Unregisters an Analytics listener for the given origin. +- (void)unregisterAnalyticsListenerWithOrigin:(NSString *)origin; + +@end + +NS_ASSUME_NONNULL_END diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInteropListener.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInteropListener.h new file mode 100644 index 00000000..45cde550 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInteropListener.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/// Handles events and messages from Analytics. +@protocol FIRAnalyticsInteropListener <NSObject> + +/// Triggers when an Analytics event happens for the registered origin with +/// `FIRAnalyticsInterop`s `registerAnalyticsListener:withOrigin:`. +- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters; + +@end
\ No newline at end of file diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropEventNames.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropEventNames.h new file mode 100644 index 00000000..efc54ab2 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropEventNames.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/// @file FIRInteropEventNames.h + +#import <Foundation/Foundation.h> + +/// Notification open event name. +static NSString *const kFIRIEventNotificationOpen = @"_no"; + +/// Notification foreground event name. +static NSString *const kFIRIEventNotificationForeground = @"_nf"; + +/// Campaign event name. +static NSString *const kFIRIEventFirebaseCampaign = @"_cmp"; diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropParameterNames.h b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropParameterNames.h new file mode 100644 index 00000000..ae440bec --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropParameterNames.h @@ -0,0 +1,73 @@ +/* + * 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> + +/// @file FIRInteropParameterNames.h +/// +/// Predefined event parameter names used by Firebase. This file is a subset of the +/// FirebaseAnalytics FIRParameterNames.h public header. +/// +/// The origin of your traffic, such as an Ad network (for example, google) or partner (urban +/// airship). Identify the advertiser, site, publication, etc. that is sending traffic to your +/// property. Highly recommended (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterSource : @"InMobi", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRIParameterSource NS_SWIFT_NAME(AnalyticsParameterSource) = @"source"; + +/// The advertising or marketing medium, for example: cpc, banner, email, push. Highly recommended +/// (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterMedium : @"email", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRIParameterMedium NS_SWIFT_NAME(AnalyticsParameterMedium) = @"medium"; + +/// The individual campaign name, slogan, promo code, etc. Some networks have pre-defined macro to +/// capture campaign information, otherwise can be populated by developer. Highly Recommended +/// (NSString). +/// <pre> +/// NSDictionary *params = @{ +/// kFIRParameterCampaign : @"winter_promotion", +/// // ... +/// }; +/// </pre> +static NSString *const kFIRIParameterCampaign NS_SWIFT_NAME(AnalyticsParameterCampaign) = + @"campaign"; + +/// Message identifier. +static NSString *const kFIRIParameterMessageIdentifier = @"_nmid"; + +/// Message name. +static NSString *const kFIRIParameterMessageName = @"_nmn"; + +/// Message send time. +static NSString *const kFIRIParameterMessageTime = @"_nmt"; + +/// Message device time. +static NSString *const kFIRIParameterMessageDeviceTime = @"_ndt"; + +/// Topic message. +static NSString *const kFIRIParameterTopic = @"_nt"; + +/// Stores the message_id of the last notification opened by the app. +static NSString *const kFIRIUserPropertyLastNotification = @"_ln"; diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/LICENSE b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/LICENSE @@ -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. diff --git a/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/README.md b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/README.md new file mode 100644 index 00000000..1d9f0f67 --- /dev/null +++ b/StoneIsland/platforms/ios/Pods/FirebaseRemoteConfig/README.md @@ -0,0 +1,298 @@ +[](https://cocoapods.org/pods/Firebase) +[](https://cocoapods.org/pods/Firebase) +[](https://cocoapods.org/pods/Firebase) + +[![Actions Status][gh-abtesting-badge]][gh-actions] +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-crashlytics-badge]][gh-actions] +[![Actions Status][gh-database-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-functions-badge]][gh-actions] +[![Actions Status][gh-inappmessaging-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-remoteconfig-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. + +The repository also includes GoogleUtilities source. The +[GoogleUtilities](GoogleUtilities/README.md) pod is +a set of utilities used by Firebase and other Google products. + +Firebase is an app development platform with tools to help you build, grow and +monetize your app. More information about Firebase can be found at +[https://firebase.google.com](https://firebase.google.com). + +## Installation + +See the three subsections for details about three different installation methods. +1. [Standard pod install](README.md#standard-pod-install) +1. [Installing from the GitHub repo](README.md#installing-from-github) +1. [Experimental Carthage](README.md#carthage-ios-only) + +### Standard pod install + +Go to +[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup). + +### Installing from GitHub + +For releases starting with 5.0.0, the source for each release is also deployed +to CocoaPods master and available via standard +[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod). + +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 of the 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: +``` +pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master' +pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master' +``` + +To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do: + +``` +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 are at +[Carthage](Carthage.md). + +### Rome + +Instructions for installing binary frameworks via +[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). + +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + +## Development + +To develop Firebase software in this repository, ensure that you have at least +the following software: + + * Xcode 10.3 (or later) + * CocoaPods 1.7.2 (or later) + * [CocoaPods generate](https://github.com/square/cocoapods-generate) + +For the pod that you want to develop: + +`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.md](Firestore/README.md). + +### 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 + +### Adding a New Firebase Pod + +See [AddNewPod.md](AddNewPod.md). + +### Managing Headers and Imports + +See [HeadersImports.md](HeadersImports.md). + +### Code Formatting + +To ensure that the code is formatted consistently, run the script +[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh) +before creating a PR. + +Travis will verify that any code changes are done in a style compliant way. Install +`clang-format` and `swiftformat`: + +``` +brew install clang-format +brew install swiftformat +``` + +### Running Unit Tests + +Select a scheme and press Command-u to build a component and run its unit tests. + +#### Viewing Code Coverage (Deprecated) + +First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. + +After running the `AllUnitTests_iOS` scheme in Xcode, execute +`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output` +at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results. + +### Running Sample Apps +In order to run the sample apps and integration tests, you'll need valid +`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist +files without real values, but 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. + +## Specific Component Instructions +See the sections below for any special instructions for those components. + +### Firebase Auth + +If you're doing specific Firebase Auth development, see +[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 Storage + +To run the Storage Integration tests, follow the instructions in +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). + +#### Push Notifications + +Push notifications can only be delivered to specially provisioned App IDs in the developer portal. +In order to actually 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. +In order to receive push notifications, you'll have to follow the steps above and run the app on a +physical device. + +## Community Supported Efforts + +We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are +very grateful! We'd like to empower as many developers as we can to be able to use Firebase and +participate in the Firebase community. + +### tvOS, macOS, watchOS and Catalyst +Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on +tvOS, macOS, watchOS and Catalyst. + +For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). + +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this +repository is actively developed primarily for iOS. While we can catch basic unit test issues with +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or 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 macOS/tvOS/watchOS/Catalyst. +**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. + +To install, add a subset of the following to the Podfile: + +``` +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet +pod 'Firebase/Messaging' +pod 'Firebase/RemoteConfig' # No watchOS support yet +pod 'Firebase/Storage' +``` + +#### Additional Catalyst Notes + +* FirebaseAuth and FirebaseMessaging require adding `Keychain Sharing Capability` +to Build Settings. +* FirebaseFirestore requires signing the +[gRPC Resource target](https://github.com/firebase/firebase-ios-sdk/issues/3500#issuecomment-518741681). + +## Roadmap + +See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source +plans and directions. + +## Contributing + +See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase +iOS SDK. + +## License + +The contents of this repository is 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/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg +[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg +[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg |
