pod firebase相关的依赖包
This commit is contained in:
parent
db9e06d91c
commit
90fec41d2d
@ -46,5 +46,5 @@ pod 'Firebase/AnalyticsWithoutAdIdSupport'
|
|||||||
pod 'FirebaseAuth'
|
pod 'FirebaseAuth'
|
||||||
pod 'FirebaseFirestore'
|
pod 'FirebaseFirestore'
|
||||||
pod 'FacebookCore'
|
pod 'FacebookCore'
|
||||||
|
pod 'FirebaseRemoteConfig'
|
||||||
end
|
end
|
||||||
|
|||||||
@ -836,6 +836,8 @@ PODS:
|
|||||||
- FirebaseAnalytics/WithoutAdIdSupport (~> 10.23.0)
|
- FirebaseAnalytics/WithoutAdIdSupport (~> 10.23.0)
|
||||||
- Firebase/CoreOnly (10.23.0):
|
- Firebase/CoreOnly (10.23.0):
|
||||||
- FirebaseCore (= 10.23.0)
|
- FirebaseCore (= 10.23.0)
|
||||||
|
- FirebaseABTesting (10.24.0):
|
||||||
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseAnalytics/WithoutAdIdSupport (10.23.0):
|
- FirebaseAnalytics/WithoutAdIdSupport (10.23.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 10.0)
|
||||||
@ -895,6 +897,14 @@ PODS:
|
|||||||
- GoogleUtilities/Environment (~> 7.8)
|
- GoogleUtilities/Environment (~> 7.8)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.8)
|
- GoogleUtilities/UserDefaults (~> 7.8)
|
||||||
- PromisesObjC (~> 2.1)
|
- PromisesObjC (~> 2.1)
|
||||||
|
- FirebaseRemoteConfig (10.24.0):
|
||||||
|
- FirebaseABTesting (~> 10.0)
|
||||||
|
- FirebaseCore (~> 10.0)
|
||||||
|
- FirebaseInstallations (~> 10.0)
|
||||||
|
- FirebaseRemoteConfigInterop (~> 10.23)
|
||||||
|
- FirebaseSharedSwift (~> 10.0)
|
||||||
|
- GoogleUtilities/Environment (~> 7.8)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
||||||
- FirebaseRemoteConfigInterop (10.23.0)
|
- FirebaseRemoteConfigInterop (10.23.0)
|
||||||
- FirebaseSessions (10.23.0):
|
- FirebaseSessions (10.23.0):
|
||||||
- FirebaseCore (~> 10.5)
|
- FirebaseCore (~> 10.5)
|
||||||
@ -1051,6 +1061,7 @@ DEPENDENCIES:
|
|||||||
- FirebaseAuth
|
- FirebaseAuth
|
||||||
- FirebaseCrashlytics
|
- FirebaseCrashlytics
|
||||||
- FirebaseFirestore
|
- FirebaseFirestore
|
||||||
|
- FirebaseRemoteConfig
|
||||||
- LLCycleScrollView
|
- LLCycleScrollView
|
||||||
- SnapKit
|
- SnapKit
|
||||||
- SVProgressHUD
|
- SVProgressHUD
|
||||||
@ -1065,6 +1076,7 @@ SPEC REPOS:
|
|||||||
- FacebookCore
|
- FacebookCore
|
||||||
- FBSDKCoreKit
|
- FBSDKCoreKit
|
||||||
- Firebase
|
- Firebase
|
||||||
|
- FirebaseABTesting
|
||||||
- FirebaseAnalytics
|
- FirebaseAnalytics
|
||||||
- FirebaseAppCheckInterop
|
- FirebaseAppCheckInterop
|
||||||
- FirebaseAuth
|
- FirebaseAuth
|
||||||
@ -1075,6 +1087,7 @@ SPEC REPOS:
|
|||||||
- FirebaseFirestore
|
- FirebaseFirestore
|
||||||
- FirebaseFirestoreInternal
|
- FirebaseFirestoreInternal
|
||||||
- FirebaseInstallations
|
- FirebaseInstallations
|
||||||
|
- FirebaseRemoteConfig
|
||||||
- FirebaseRemoteConfigInterop
|
- FirebaseRemoteConfigInterop
|
||||||
- FirebaseSessions
|
- FirebaseSessions
|
||||||
- FirebaseSharedSwift
|
- FirebaseSharedSwift
|
||||||
@ -1103,6 +1116,7 @@ SPEC CHECKSUMS:
|
|||||||
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
||||||
FBSDKCoreKit: 1d5acf7c9d7a2f92bb1a242dc60cae5b7adb91df
|
FBSDKCoreKit: 1d5acf7c9d7a2f92bb1a242dc60cae5b7adb91df
|
||||||
Firebase: 333ec7c6b12fa09c77b5162cda6b862102211d50
|
Firebase: 333ec7c6b12fa09c77b5162cda6b862102211d50
|
||||||
|
FirebaseABTesting: 4431c2c56ac6e56f463b9cab05cc111078639f99
|
||||||
FirebaseAnalytics: 45f6e2e5ef8ccbb90be73ae983c3b20fa78837f7
|
FirebaseAnalytics: 45f6e2e5ef8ccbb90be73ae983c3b20fa78837f7
|
||||||
FirebaseAppCheckInterop: a1955ce8c30f38f87e7d091630e871e91154d65d
|
FirebaseAppCheckInterop: a1955ce8c30f38f87e7d091630e871e91154d65d
|
||||||
FirebaseAuth: 22eb85d3853141de7062bfabc131aa7d6335cade
|
FirebaseAuth: 22eb85d3853141de7062bfabc131aa7d6335cade
|
||||||
@ -1113,6 +1127,7 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseFirestore: 3478b0580f6c16d895460611b4fcec93955f4717
|
FirebaseFirestore: 3478b0580f6c16d895460611b4fcec93955f4717
|
||||||
FirebaseFirestoreInternal: 627b23f682c1c2aad38ba1345ed3ca6574c5a89c
|
FirebaseFirestoreInternal: 627b23f682c1c2aad38ba1345ed3ca6574c5a89c
|
||||||
FirebaseInstallations: 42d6ead4605d6eafb3b6683674e80e18eb6f2c35
|
FirebaseInstallations: 42d6ead4605d6eafb3b6683674e80e18eb6f2c35
|
||||||
|
FirebaseRemoteConfig: 95dddc50496b37eef199dadce850d5652b534b43
|
||||||
FirebaseRemoteConfigInterop: cbc87ffa4932719a7911a08e94510f18f026f5a7
|
FirebaseRemoteConfigInterop: cbc87ffa4932719a7911a08e94510f18f026f5a7
|
||||||
FirebaseSessions: f06853e30f99fe42aa511014d7ee6c8c319f08a3
|
FirebaseSessions: f06853e30f99fe42aa511014d7ee6c8c319f08a3
|
||||||
FirebaseSharedSwift: c92645b392db3c41a83a0aa967de16f8bad25568
|
FirebaseSharedSwift: c92645b392db3c41a83a0aa967de16f8bad25568
|
||||||
@ -1133,6 +1148,6 @@ SPEC CHECKSUMS:
|
|||||||
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
||||||
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
||||||
|
|
||||||
PODFILE CHECKSUM: 348e68c1ad5662db981acd0c0690ba09e3436445
|
PODFILE CHECKSUM: d9797551bca99266176914268501259fabf3ff32
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.15.2
|
||||||
|
|||||||
79
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTConditionalUserPropertyController.h
generated
Normal file
79
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTConditionalUserPropertyController.h
generated
Normal file
@ -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>
|
||||||
|
#import "FirebaseABTesting/Sources/Private/ABTExperimentPayload.h"
|
||||||
|
|
||||||
|
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class FIRLifecycleEvents;
|
||||||
|
|
||||||
|
/// This class dynamically calls Firebase Analytics API to collect or update experiments
|
||||||
|
/// information.
|
||||||
|
/// The experiment in Firebase Analytics is named as conditional user property (CUP) object defined
|
||||||
|
/// in FIRAConditionalUserProperty.h.
|
||||||
|
@interface ABTConditionalUserPropertyController : NSObject
|
||||||
|
|
||||||
|
/// Returns the ABTConditionalUserPropertyController singleton.
|
||||||
|
+ (instancetype)sharedInstanceWithAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
|
||||||
|
|
||||||
|
/// Returns the list of currently set experiments from Firebase Analytics for the provided origin.
|
||||||
|
- (NSArray *)experimentsWithOrigin:(NSString *)origin;
|
||||||
|
|
||||||
|
/// Returns the experiment ID from Firebase Analytics given an experiment object. Returns empty
|
||||||
|
/// string if can't find Firebase Analytics service.
|
||||||
|
- (NSString *)experimentIDOfExperiment:(nullable id)experiment;
|
||||||
|
|
||||||
|
/// Returns the variant ID from Firebase Analytics given an experiment object. Returns empty string
|
||||||
|
/// if can't find Firebase Analytics service.
|
||||||
|
- (NSString *)variantIDOfExperiment:(nullable id)experiment;
|
||||||
|
|
||||||
|
/// Returns whether the experiment is the same as the one in the provided payload.
|
||||||
|
- (BOOL)isExperiment:(id)experiment theSameAsPayload:(ABTExperimentPayload *)payload;
|
||||||
|
|
||||||
|
/// Clears the experiment in Firebase Analytics.
|
||||||
|
/// @param experimentID Experiment ID to clear.
|
||||||
|
/// @param variantID Variant ID to clear.
|
||||||
|
/// @param origin Impacted originating service, it is defined at Firebase Analytics
|
||||||
|
/// FIREventOrigins.h.
|
||||||
|
/// @param payload Payload to overwrite event name in events. DO NOT use payload's experiment
|
||||||
|
/// ID and variant ID as the experiment to clear.
|
||||||
|
/// @param events Events name for clearing the experiment.
|
||||||
|
- (void)clearExperiment:(NSString *)experimentID
|
||||||
|
variantID:(NSString *)variantID
|
||||||
|
withOrigin:(NSString *)origin
|
||||||
|
payload:(nullable ABTExperimentPayload *)payload
|
||||||
|
events:(FIRLifecycleEvents *)events;
|
||||||
|
|
||||||
|
/// Sets the experiment in Firebase Analytics.
|
||||||
|
/// @param origin Impacted originating service, it is defined at Firebase Analytics
|
||||||
|
/// FIREventOrigins.h.
|
||||||
|
/// @param payload Payload to overwrite event name in events. DO NOT use payload's experiment
|
||||||
|
/// ID and variant ID as the experiment to set.
|
||||||
|
/// @param events Events name for setting the experiment.
|
||||||
|
/// @param policy Overflow policy when the number of experiments is over the limit.
|
||||||
|
- (void)setExperimentWithOrigin:(NSString *)origin
|
||||||
|
payload:(ABTExperimentPayload *)payload
|
||||||
|
events:(FIRLifecycleEvents *)events
|
||||||
|
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unavailable. Use sharedInstanceWithAnalytics: instead.
|
||||||
|
*/
|
||||||
|
- (instancetype)init __attribute__((unavailable("Use +sharedInstanceWithAnalytics: instead.")));
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
288
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTConditionalUserPropertyController.m
generated
Normal file
288
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTConditionalUserPropertyController.m
generated
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
// 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 "FirebaseABTesting/Sources/ABTConditionalUserPropertyController.h"
|
||||||
|
|
||||||
|
#import "FirebaseABTesting/Sources/ABTConstants.h"
|
||||||
|
#import "FirebaseABTesting/Sources/Public/FirebaseABTesting/FIRLifecycleEvents.h"
|
||||||
|
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
||||||
|
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
|
||||||
|
|
||||||
|
@implementation ABTConditionalUserPropertyController {
|
||||||
|
dispatch_queue_t _analyticOperationQueue;
|
||||||
|
id<FIRAnalyticsInterop> _Nullable _analytics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the ABTConditionalUserPropertyController singleton.
|
||||||
|
+ (instancetype)sharedInstanceWithAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
|
||||||
|
static ABTConditionalUserPropertyController *sharedInstance = nil;
|
||||||
|
static dispatch_once_t onceToken = 0;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
sharedInstance = [[ABTConditionalUserPropertyController alloc] initWithAnalytics:analytics];
|
||||||
|
});
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_analyticOperationQueue =
|
||||||
|
dispatch_queue_create("com.google.FirebaseABTesting.analytics", DISPATCH_QUEUE_SERIAL);
|
||||||
|
_analytics = analytics;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - experiments proxy methods on Firebase Analytics
|
||||||
|
|
||||||
|
- (NSArray *)experimentsWithOrigin:(NSString *)origin {
|
||||||
|
return [_analytics conditionalUserProperties:origin propertyNamePrefix:@""];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearExperiment:(NSString *)experimentID
|
||||||
|
variantID:(NSString *)variantID
|
||||||
|
withOrigin:(NSString *)origin
|
||||||
|
payload:(ABTExperimentPayload *)payload
|
||||||
|
events:(FIRLifecycleEvents *)events {
|
||||||
|
// Payload always overwrite event names.
|
||||||
|
NSString *clearExperimentEventName = events.clearExperimentEventName;
|
||||||
|
if (payload && payload.clearEventToLog && payload.clearEventToLog.length) {
|
||||||
|
clearExperimentEventName = payload.clearEventToLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_analytics clearConditionalUserProperty:experimentID
|
||||||
|
forOrigin:origin
|
||||||
|
clearEventName:clearExperimentEventName
|
||||||
|
clearEventParameters:@{experimentID : variantID}];
|
||||||
|
|
||||||
|
FIRLogDebug(kFIRLoggerABTesting, @"I-ABT000015", @"Clear Experiment ID %@, variant ID %@.",
|
||||||
|
experimentID, variantID);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setExperimentWithOrigin:(NSString *)origin
|
||||||
|
payload:(ABTExperimentPayload *)payload
|
||||||
|
events:(FIRLifecycleEvents *)events
|
||||||
|
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy {
|
||||||
|
NSInteger maxNumOfExperiments = [self maxNumberOfExperimentsOfOrigin:origin];
|
||||||
|
if (maxNumOfExperiments < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear experiments if overflow
|
||||||
|
NSArray *experiments = [self experimentsWithOrigin:origin];
|
||||||
|
if (!experiments) {
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000003",
|
||||||
|
@"Failed to get conditional user properties from Firebase Analytics.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.experimentId == nil) {
|
||||||
|
// When doing experiment test on devices, the payload could be empty. Returning here to prevent
|
||||||
|
// app crash.
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000020", @"Experiment Id in payload is empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxNumOfExperiments <= experiments.count) {
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicy overflowPolicy =
|
||||||
|
[self overflowPolicyWithPayload:payload originalPolicy:policy];
|
||||||
|
id experimentToClear = experiments.firstObject;
|
||||||
|
if (overflowPolicy == ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest &&
|
||||||
|
experimentToClear) {
|
||||||
|
NSString *expID = [self experimentIDOfExperiment:experimentToClear];
|
||||||
|
NSString *varID = [self variantIDOfExperiment:experimentToClear];
|
||||||
|
|
||||||
|
[self clearExperiment:expID variantID:varID withOrigin:origin payload:payload events:events];
|
||||||
|
FIRLogDebug(kFIRLoggerABTesting, @"I-ABT000016",
|
||||||
|
@"Clear experiment ID %@ variant ID %@ due to "
|
||||||
|
@"overflow policy.",
|
||||||
|
expID, varID);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
FIRLogDebug(kFIRLoggerABTesting, @"I-ABT000017",
|
||||||
|
@"Experiment ID %@ variant ID %@ won't be set due to "
|
||||||
|
@"overflow policy.",
|
||||||
|
payload.experimentId, payload.variantId);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear experiment if other variant ID exists.
|
||||||
|
NSString *experimentID = payload.experimentId;
|
||||||
|
NSString *variantID = payload.variantId;
|
||||||
|
for (id experiment in experiments) {
|
||||||
|
NSString *expID = [self experimentIDOfExperiment:experiment];
|
||||||
|
NSString *varID = [self variantIDOfExperiment:experiment];
|
||||||
|
if ([expID isEqualToString:experimentID] && ![varID isEqualToString:variantID]) {
|
||||||
|
FIRLogDebug(kFIRLoggerABTesting, @"I-ABT000018",
|
||||||
|
@"Clear experiment ID %@ with variant ID %@ because "
|
||||||
|
@"only one variant ID can be existed "
|
||||||
|
@"at any time.",
|
||||||
|
expID, varID);
|
||||||
|
[self clearExperiment:expID variantID:varID withOrigin:origin payload:payload events:events];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set experiment
|
||||||
|
NSDictionary<NSString *, id> *experiment = [self createExperimentFromOrigin:origin
|
||||||
|
payload:payload
|
||||||
|
events:events];
|
||||||
|
|
||||||
|
[_analytics setConditionalUserProperty:experiment];
|
||||||
|
|
||||||
|
FIRLogDebug(kFIRLoggerABTesting, @"I-ABT000019",
|
||||||
|
@"Set conditional user property, experiment ID %@ with "
|
||||||
|
@"variant ID %@ triggered event %@.",
|
||||||
|
experimentID, variantID, payload.triggerEvent);
|
||||||
|
|
||||||
|
// Log setEvent (experiment lifecycle event to be set when an experiment is set)
|
||||||
|
[self logEventWithOrigin:origin payload:payload events:events];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableDictionary<NSString *, id> *)createExperimentFromOrigin:(NSString *)origin
|
||||||
|
payload:(ABTExperimentPayload *)payload
|
||||||
|
events:(FIRLifecycleEvents *)events {
|
||||||
|
NSMutableDictionary<NSString *, id> *experiment = [[NSMutableDictionary alloc] init];
|
||||||
|
NSString *experimentID = payload.experimentId;
|
||||||
|
NSString *variantID = payload.variantId;
|
||||||
|
|
||||||
|
NSDictionary *eventParams = @{experimentID : variantID};
|
||||||
|
|
||||||
|
[experiment setValue:origin forKey:kABTExperimentDictionaryOriginKey];
|
||||||
|
|
||||||
|
NSTimeInterval creationTimestamp = (double)(payload.experimentStartTimeMillis / ABT_MSEC_PER_SEC);
|
||||||
|
[experiment setValue:@(creationTimestamp) forKey:kABTExperimentDictionaryCreationTimestampKey];
|
||||||
|
[experiment setValue:experimentID forKey:kABTExperimentDictionaryExperimentIDKey];
|
||||||
|
[experiment setValue:variantID forKey:kABTExperimentDictionaryVariantIDKey];
|
||||||
|
|
||||||
|
// For the experiment to be immediately activated/triggered, its trigger event must be null.
|
||||||
|
// Double check if payload's trigger event is empty string, it must be set to null to trigger.
|
||||||
|
if (payload && payload.triggerEvent && payload.triggerEvent.length) {
|
||||||
|
[experiment setValue:payload.triggerEvent forKey:kABTExperimentDictionaryTriggeredEventNameKey];
|
||||||
|
} else {
|
||||||
|
[experiment setValue:nil forKey:kABTExperimentDictionaryTriggeredEventNameKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set timeout event name and params.
|
||||||
|
NSString *timeoutEventName = events.timeoutExperimentEventName;
|
||||||
|
if (payload && payload.timeoutEventToLog && payload.timeoutEventToLog.length) {
|
||||||
|
timeoutEventName = payload.timeoutEventToLog;
|
||||||
|
}
|
||||||
|
NSDictionary<NSString *, id> *timeoutEvent = [self eventDictionaryWithOrigin:origin
|
||||||
|
eventName:timeoutEventName
|
||||||
|
params:eventParams];
|
||||||
|
[experiment setValue:timeoutEvent forKey:kABTExperimentDictionaryTimedOutEventKey];
|
||||||
|
|
||||||
|
// Set trigger timeout information on how long to wait for trigger event.
|
||||||
|
NSTimeInterval triggerTimeout = (double)(payload.triggerTimeoutMillis / ABT_MSEC_PER_SEC);
|
||||||
|
[experiment setValue:@(triggerTimeout) forKey:kABTExperimentDictionaryTriggerTimeoutKey];
|
||||||
|
|
||||||
|
// Set activate event name and params.
|
||||||
|
NSString *activateEventName = events.activateExperimentEventName;
|
||||||
|
if (payload && payload.activateEventToLog && payload.activateEventToLog.length) {
|
||||||
|
activateEventName = payload.activateEventToLog;
|
||||||
|
}
|
||||||
|
NSDictionary<NSString *, id> *triggeredEvent = [self eventDictionaryWithOrigin:origin
|
||||||
|
eventName:activateEventName
|
||||||
|
params:eventParams];
|
||||||
|
[experiment setValue:triggeredEvent forKey:kABTExperimentDictionaryTriggeredEventKey];
|
||||||
|
|
||||||
|
// Set time to live information for how long the experiment lasts.
|
||||||
|
NSTimeInterval timeToLive = (double)(payload.timeToLiveMillis / ABT_MSEC_PER_SEC);
|
||||||
|
[experiment setValue:@(timeToLive) forKey:kABTExperimentDictionaryTimeToLiveKey];
|
||||||
|
|
||||||
|
// Set expired event name and params.
|
||||||
|
NSString *expiredEventName = events.expireExperimentEventName;
|
||||||
|
if (payload && payload.ttlExpiryEventToLog && payload.ttlExpiryEventToLog.length) {
|
||||||
|
expiredEventName = payload.ttlExpiryEventToLog;
|
||||||
|
}
|
||||||
|
NSDictionary<NSString *, id> *expiredEvent = [self eventDictionaryWithOrigin:origin
|
||||||
|
eventName:expiredEventName
|
||||||
|
params:eventParams];
|
||||||
|
[experiment setValue:expiredEvent forKey:kABTExperimentDictionaryExpiredEventKey];
|
||||||
|
return experiment;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary<NSString *, id> *)
|
||||||
|
eventDictionaryWithOrigin:(nonnull NSString *)origin
|
||||||
|
eventName:(nonnull NSString *)eventName
|
||||||
|
params:(nonnull NSDictionary<NSString *, NSString *> *)params {
|
||||||
|
return @{
|
||||||
|
kABTEventDictionaryOriginKey : origin,
|
||||||
|
kABTEventDictionaryNameKey : eventName,
|
||||||
|
kABTEventDictionaryTimestampKey : @([NSDate date].timeIntervalSince1970),
|
||||||
|
kABTEventDictionaryParametersKey : params
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - experiment properties
|
||||||
|
- (NSString *)experimentIDOfExperiment:(id)experiment {
|
||||||
|
if (!experiment) {
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
return [experiment valueForKey:kABTExperimentDictionaryExperimentIDKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)variantIDOfExperiment:(id)experiment {
|
||||||
|
if (!experiment) {
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
return [experiment valueForKey:kABTExperimentDictionaryVariantIDKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)maxNumberOfExperimentsOfOrigin:(NSString *)origin {
|
||||||
|
if (!_analytics) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return [_analytics maxUserProperties:origin];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - analytics internal methods
|
||||||
|
|
||||||
|
- (void)logEventWithOrigin:(NSString *)origin
|
||||||
|
payload:(ABTExperimentPayload *)payload
|
||||||
|
events:(FIRLifecycleEvents *)events {
|
||||||
|
NSString *setExperimentEventName = events.setExperimentEventName;
|
||||||
|
if (payload && payload.setEventToLog && payload.setEventToLog.length) {
|
||||||
|
setExperimentEventName = payload.setEventToLog;
|
||||||
|
}
|
||||||
|
NSDictionary<NSString *, NSString *> *params;
|
||||||
|
params = payload.experimentId ? @{payload.experimentId : payload.variantId} : @{};
|
||||||
|
[_analytics logEventWithOrigin:origin name:setExperimentEventName parameters:params];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - helper
|
||||||
|
|
||||||
|
- (BOOL)isExperiment:(id)experiment theSameAsPayload:(ABTExperimentPayload *)payload {
|
||||||
|
NSString *experimentID = [self experimentIDOfExperiment:experiment];
|
||||||
|
NSString *variantID = [self variantIDOfExperiment:experiment];
|
||||||
|
return [experimentID isEqualToString:payload.experimentId] &&
|
||||||
|
[variantID isEqualToString:payload.variantId];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ABTExperimentPayloadExperimentOverflowPolicy)
|
||||||
|
overflowPolicyWithPayload:(ABTExperimentPayload *)payload
|
||||||
|
originalPolicy:(ABTExperimentPayloadExperimentOverflowPolicy)originalPolicy {
|
||||||
|
if ([payload overflowPolicyIsValid]) {
|
||||||
|
return payload.overflowPolicy;
|
||||||
|
}
|
||||||
|
if (originalPolicy == ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest ||
|
||||||
|
originalPolicy == ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest) {
|
||||||
|
return originalPolicy;
|
||||||
|
}
|
||||||
|
return ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
50
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTConstants.h
generated
Normal file
50
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTConstants.h
generated
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// 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/Extension/FIRLogger.h"
|
||||||
|
|
||||||
|
#define ABT_MSEC_PER_SEC 1000ull
|
||||||
|
|
||||||
|
#pragma mark - Keys for experiment dictionaries.
|
||||||
|
|
||||||
|
static NSString *const kABTExperimentDictionaryCreationTimestampKey = @"creationTimestamp";
|
||||||
|
static NSString *const kABTExperimentDictionaryExperimentIDKey = @"name";
|
||||||
|
static NSString *const kABTExperimentDictionaryExpiredEventKey = @"expiredEvent";
|
||||||
|
static NSString *const kABTExperimentDictionaryOriginKey = @"origin";
|
||||||
|
static NSString *const kABTExperimentDictionaryTimedOutEventKey = @"timedOutEvent";
|
||||||
|
static NSString *const kABTExperimentDictionaryTimeToLiveKey = @"timeToLive";
|
||||||
|
static NSString *const kABTExperimentDictionaryTriggeredEventKey = @"triggeredEvent";
|
||||||
|
static NSString *const kABTExperimentDictionaryTriggeredEventNameKey = @"triggerEventName";
|
||||||
|
static NSString *const kABTExperimentDictionaryTriggerTimeoutKey = @"triggerTimeout";
|
||||||
|
static NSString *const kABTExperimentDictionaryVariantIDKey = @"value";
|
||||||
|
|
||||||
|
#pragma mark - Keys for event dictionaries.
|
||||||
|
|
||||||
|
static NSString *const kABTEventDictionaryNameKey = @"name";
|
||||||
|
static NSString *const kABTEventDictionaryOriginKey = @"origin";
|
||||||
|
static NSString *const kABTEventDictionaryParametersKey = @"parameters";
|
||||||
|
static NSString *const kABTEventDictionaryTimestampKey = @"timestamp";
|
||||||
|
|
||||||
|
#pragma mark - Errors
|
||||||
|
|
||||||
|
static NSString *const kABTErrorDomain = @"com.google.abtesting";
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, ABTInternalErrorCode) {
|
||||||
|
kABTInternalErrorFailedToFetchConditionalUserProperties = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma mark - Logger Service String
|
||||||
|
|
||||||
|
extern FIRLoggerService kFIRLoggerABTesting;
|
||||||
151
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTExperimentPayload.m
generated
Normal file
151
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/ABTExperimentPayload.m
generated
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "FirebaseABTesting/Sources/Private/ABTExperimentPayload.h"
|
||||||
|
|
||||||
|
static NSString *const kExperimentPayloadKeyExperimentID = @"experimentId";
|
||||||
|
static NSString *const kExperimentPayloadKeyVariantID = @"variantId";
|
||||||
|
|
||||||
|
// Start time can either be a date string or integer (milliseconds since 1970).
|
||||||
|
static NSString *const kExperimentPayloadKeyExperimentStartTime = @"experimentStartTime";
|
||||||
|
static NSString *const kExperimentPayloadKeyExperimentStartTimeMillis =
|
||||||
|
@"experimentStartTimeMillis";
|
||||||
|
static NSString *const kExperimentPayloadKeyTriggerEvent = @"triggerEvent";
|
||||||
|
static NSString *const kExperimentPayloadKeyTriggerTimeoutMillis = @"triggerTimeoutMillis";
|
||||||
|
static NSString *const kExperimentPayloadKeyTimeToLiveMillis = @"timeToLiveMillis";
|
||||||
|
static NSString *const kExperimentPayloadKeySetEventToLog = @"setEventToLog";
|
||||||
|
static NSString *const kExperimentPayloadKeyActivateEventToLog = @"activateEventToLog";
|
||||||
|
static NSString *const kExperimentPayloadKeyClearEventToLog = @"clearEventToLog";
|
||||||
|
static NSString *const kExperimentPayloadKeyTimeoutEventToLog = @"timeoutEventToLog";
|
||||||
|
static NSString *const kExperimentPayloadKeyTTLExpiryEventToLog = @"ttlExpiryEventToLog";
|
||||||
|
|
||||||
|
static NSString *const kExperimentPayloadKeyOverflowPolicy = @"overflowPolicy";
|
||||||
|
static NSString *const kExperimentPayloadValueDiscardOldestOverflowPolicy = @"DISCARD_OLDEST";
|
||||||
|
static NSString *const kExperimentPayloadValueIgnoreNewestOverflowPolicy = @"IGNORE_NEWEST";
|
||||||
|
|
||||||
|
static NSString *const kExperimentPayloadKeyOngoingExperiments = @"ongoingExperiments";
|
||||||
|
|
||||||
|
@implementation ABTExperimentLite
|
||||||
|
|
||||||
|
- (instancetype)initWithExperimentId:(NSString *)experimentId {
|
||||||
|
if (self = [super init]) {
|
||||||
|
_experimentId = experimentId;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ABTExperimentPayload
|
||||||
|
|
||||||
|
+ (NSDateFormatter *)experimentStartTimeFormatter {
|
||||||
|
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||||
|
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
|
||||||
|
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
|
||||||
|
// Locale needs to be hardcoded. See
|
||||||
|
// https://developer.apple.com/library/ios/#qa/qa1480/_index.html for more details.
|
||||||
|
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
|
||||||
|
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
|
||||||
|
return dateFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nullable instancetype)parseFromData:(NSData *)data {
|
||||||
|
NSError *error;
|
||||||
|
NSDictionary *experimentDictionary =
|
||||||
|
[NSJSONSerialization JSONObjectWithData:data
|
||||||
|
options:NSJSONReadingAllowFragments
|
||||||
|
error:&error];
|
||||||
|
if (error != nil) {
|
||||||
|
return nil;
|
||||||
|
} else {
|
||||||
|
return [[ABTExperimentPayload alloc] initWithDictionary:experimentDictionary];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dictionary {
|
||||||
|
if (self = [super init]) {
|
||||||
|
_experimentId = dictionary[kExperimentPayloadKeyExperimentID];
|
||||||
|
_variantId = dictionary[kExperimentPayloadKeyVariantID];
|
||||||
|
_triggerEvent = dictionary[kExperimentPayloadKeyTriggerEvent];
|
||||||
|
_setEventToLog = dictionary[kExperimentPayloadKeySetEventToLog];
|
||||||
|
_activateEventToLog = dictionary[kExperimentPayloadKeyActivateEventToLog];
|
||||||
|
_clearEventToLog = dictionary[kExperimentPayloadKeyClearEventToLog];
|
||||||
|
_timeoutEventToLog = dictionary[kExperimentPayloadKeyTimeoutEventToLog];
|
||||||
|
_ttlExpiryEventToLog = dictionary[kExperimentPayloadKeyTTLExpiryEventToLog];
|
||||||
|
|
||||||
|
// Experiment start time can either be in the form of a date string or milliseconds since 1970.
|
||||||
|
if (dictionary[kExperimentPayloadKeyExperimentStartTime]) {
|
||||||
|
// Convert from date string.
|
||||||
|
NSDate *experimentStartTime = [[[self class] experimentStartTimeFormatter]
|
||||||
|
dateFromString:dictionary[kExperimentPayloadKeyExperimentStartTime]];
|
||||||
|
_experimentStartTimeMillis =
|
||||||
|
[@([experimentStartTime timeIntervalSince1970] * 1000) longLongValue];
|
||||||
|
} else if (dictionary[kExperimentPayloadKeyExperimentStartTimeMillis]) {
|
||||||
|
// Simply store milliseconds.
|
||||||
|
_experimentStartTimeMillis =
|
||||||
|
[dictionary[kExperimentPayloadKeyExperimentStartTimeMillis] longLongValue];
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
_triggerTimeoutMillis = [dictionary[kExperimentPayloadKeyTriggerTimeoutMillis] longLongValue];
|
||||||
|
_timeToLiveMillis = [dictionary[kExperimentPayloadKeyTimeToLiveMillis] longLongValue];
|
||||||
|
|
||||||
|
// Overflow policy can be an integer, or string e.g. "DISCARD_OLDEST" or "IGNORE_NEWEST".
|
||||||
|
if ([dictionary[kExperimentPayloadKeyOverflowPolicy] isKindOfClass:[NSString class]]) {
|
||||||
|
// If it's a string, pick against the preset string values.
|
||||||
|
NSString *policy = dictionary[kExperimentPayloadKeyOverflowPolicy];
|
||||||
|
if ([policy isEqualToString:kExperimentPayloadValueDiscardOldestOverflowPolicy]) {
|
||||||
|
_overflowPolicy = ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
|
||||||
|
} else if ([policy isEqualToString:kExperimentPayloadValueIgnoreNewestOverflowPolicy]) {
|
||||||
|
_overflowPolicy = ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest;
|
||||||
|
} else {
|
||||||
|
_overflowPolicy = ABTExperimentPayloadExperimentOverflowPolicyUnrecognizedValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_overflowPolicy = [dictionary[kExperimentPayloadKeyOverflowPolicy] intValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableArray<ABTExperimentLite *> *ongoingExperiments = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
NSArray<NSDictionary<NSString *, NSString *> *> *ongoingExperimentsArray =
|
||||||
|
dictionary[kExperimentPayloadKeyOngoingExperiments];
|
||||||
|
|
||||||
|
for (NSDictionary<NSString *, NSString *> *experimentDictionary in ongoingExperimentsArray) {
|
||||||
|
NSString *experimentId = experimentDictionary[kExperimentPayloadKeyExperimentID];
|
||||||
|
if (experimentId) {
|
||||||
|
ABTExperimentLite *liteExperiment =
|
||||||
|
[[ABTExperimentLite alloc] initWithExperimentId:experimentId];
|
||||||
|
[ongoingExperiments addObject:liteExperiment];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ongoingExperiments = [ongoingExperiments copy];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearTriggerEvent {
|
||||||
|
_triggerEvent = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)overflowPolicyIsValid {
|
||||||
|
return self.overflowPolicy == ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest ||
|
||||||
|
self.overflowPolicy == ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setOverflowPolicy:(ABTExperimentPayloadExperimentOverflowPolicy)overflowPolicy {
|
||||||
|
_overflowPolicy = overflowPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
313
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/FIRExperimentController.m
generated
Normal file
313
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/FIRExperimentController.m
generated
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
// 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 "FirebaseABTesting/Sources/Public/FirebaseABTesting/FIRExperimentController.h"
|
||||||
|
|
||||||
|
#import "FirebaseABTesting/Sources/ABTConditionalUserPropertyController.h"
|
||||||
|
#import "FirebaseABTesting/Sources/ABTConstants.h"
|
||||||
|
#import "FirebaseABTesting/Sources/Private/ABTExperimentPayload.h"
|
||||||
|
#import "FirebaseABTesting/Sources/Public/FirebaseABTesting/FIRLifecycleEvents.h"
|
||||||
|
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
||||||
|
|
||||||
|
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
|
||||||
|
|
||||||
|
/// Logger Service String.
|
||||||
|
FIRLoggerService kFIRLoggerABTesting = @"[FirebaseABTesting]";
|
||||||
|
|
||||||
|
/// Default experiment overflow policy.
|
||||||
|
const ABTExperimentPayloadExperimentOverflowPolicy FIRDefaultExperimentOverflowPolicy =
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
|
||||||
|
|
||||||
|
/// Deserialize the experiment payloads.
|
||||||
|
ABTExperimentPayload *ABTDeserializeExperimentPayload(NSData *payload) {
|
||||||
|
// Verify that we have a JSON object.
|
||||||
|
NSError *error;
|
||||||
|
id JSONObject = [NSJSONSerialization JSONObjectWithData:payload options:kNilOptions error:&error];
|
||||||
|
if (JSONObject == nil) {
|
||||||
|
FIRLogError(kFIRLoggerABTesting, @"I-ABT000001", @"Failed to parse experiment payload: %@",
|
||||||
|
error.debugDescription);
|
||||||
|
}
|
||||||
|
return [ABTExperimentPayload parseFromData:payload];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of experiments to be set given the payloads and current list of experiments from
|
||||||
|
/// Firebase Analytics. If an experiment is in payloads but not in experiments, it should be set to
|
||||||
|
/// Firebase Analytics.
|
||||||
|
NSArray<ABTExperimentPayload *> *ABTExperimentsToSetFromPayloads(
|
||||||
|
NSArray<NSData *> *payloads,
|
||||||
|
NSArray<NSDictionary<NSString *, NSString *> *> *experiments,
|
||||||
|
id<FIRAnalyticsInterop> _Nullable analytics) {
|
||||||
|
NSArray<NSData *> *payloadsCopy = [payloads copy];
|
||||||
|
NSArray *experimentsCopy = [experiments copy];
|
||||||
|
NSMutableArray *experimentsToSet = [[NSMutableArray alloc] init];
|
||||||
|
ABTConditionalUserPropertyController *controller =
|
||||||
|
[ABTConditionalUserPropertyController sharedInstanceWithAnalytics:analytics];
|
||||||
|
|
||||||
|
// Check if the experiment is in payloads but not in experiments.
|
||||||
|
for (NSData *payload in payloadsCopy) {
|
||||||
|
ABTExperimentPayload *experimentPayload = ABTDeserializeExperimentPayload(payload);
|
||||||
|
if (!experimentPayload) {
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000002",
|
||||||
|
@"Either payload is not set or it cannot be deserialized.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isExperimentSet = NO;
|
||||||
|
for (id experiment in experimentsCopy) {
|
||||||
|
if ([controller isExperiment:experiment theSameAsPayload:experimentPayload]) {
|
||||||
|
isExperimentSet = YES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isExperimentSet) {
|
||||||
|
[experimentsToSet addObject:experimentPayload];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [experimentsToSet copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of experiments to be cleared given the payloads and current list of
|
||||||
|
/// experiments from Firebase Analytics. If an experiment is in experiments but not in payloads, it
|
||||||
|
/// should be cleared in Firebase Analytics.
|
||||||
|
NSArray *ABTExperimentsToClearFromPayloads(
|
||||||
|
NSArray<NSData *> *payloads,
|
||||||
|
NSArray<NSDictionary<NSString *, NSString *> *> *experiments,
|
||||||
|
id<FIRAnalyticsInterop> _Nullable analytics) {
|
||||||
|
NSMutableArray *experimentsToClear = [[NSMutableArray alloc] init];
|
||||||
|
ABTConditionalUserPropertyController *controller =
|
||||||
|
[ABTConditionalUserPropertyController sharedInstanceWithAnalytics:analytics];
|
||||||
|
|
||||||
|
// Check if the experiment is in experiments but not payloads.
|
||||||
|
for (id experiment in [experiments copy]) {
|
||||||
|
BOOL doesExperimentNoLongerExist = YES;
|
||||||
|
for (NSData *payload in [payloads copy]) {
|
||||||
|
ABTExperimentPayload *experimentPayload = ABTDeserializeExperimentPayload(payload);
|
||||||
|
if (!experimentPayload) {
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000002",
|
||||||
|
@"Either payload is not set or it cannot be deserialized.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([controller isExperiment:experiment theSameAsPayload:experimentPayload]) {
|
||||||
|
doesExperimentNoLongerExist = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doesExperimentNoLongerExist) {
|
||||||
|
[experimentsToClear addObject:experiment];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return experimentsToClear;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABT doesn't provide any functionality to other components,
|
||||||
|
// so it provides a private, empty protocol that it conforms to and use it for registration.
|
||||||
|
|
||||||
|
@protocol FIRABTInstanceProvider
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface FIRExperimentController () <FIRABTInstanceProvider, FIRLibrary>
|
||||||
|
@property(nonatomic, readwrite, strong) id<FIRAnalyticsInterop> _Nullable analytics;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FIRExperimentController
|
||||||
|
|
||||||
|
+ (void)load {
|
||||||
|
[FIRApp registerInternalLibrary:(Class<FIRLibrary>)self withName:@"fire-abt"];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nonnull NSArray<FIRComponent *> *)componentsToRegister {
|
||||||
|
FIRDependency *analyticsDep = [FIRDependency dependencyWithProtocol:@protocol(FIRAnalyticsInterop)
|
||||||
|
isRequired:NO];
|
||||||
|
FIRComponentCreationBlock creationBlock =
|
||||||
|
^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
|
||||||
|
// Ensure it's cached so it returns the same instance every time ABTesting is called.
|
||||||
|
*isCacheable = YES;
|
||||||
|
id<FIRAnalyticsInterop> analytics = FIR_COMPONENT(FIRAnalyticsInterop, container);
|
||||||
|
return [[FIRExperimentController alloc] initWithAnalytics:analytics];
|
||||||
|
};
|
||||||
|
FIRComponent *abtProvider = [FIRComponent componentWithProtocol:@protocol(FIRABTInstanceProvider)
|
||||||
|
instantiationTiming:FIRInstantiationTimingLazy
|
||||||
|
dependencies:@[ analyticsDep ]
|
||||||
|
creationBlock:creationBlock];
|
||||||
|
|
||||||
|
return @[ abtProvider ];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
_analytics = analytics;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (FIRExperimentController *)sharedInstance {
|
||||||
|
FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here.
|
||||||
|
id<FIRABTInstanceProvider> instance = FIR_COMPONENT(FIRABTInstanceProvider, defaultApp.container);
|
||||||
|
|
||||||
|
// We know the instance coming from the container is a FIRExperimentController instance, cast it.
|
||||||
|
return (FIRExperimentController *)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateExperimentsWithServiceOrigin:(NSString *)origin
|
||||||
|
events:(FIRLifecycleEvents *)events
|
||||||
|
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy
|
||||||
|
lastStartTime:(NSTimeInterval)lastStartTime
|
||||||
|
payloads:(NSArray<NSData *> *)payloads
|
||||||
|
completionHandler:
|
||||||
|
(nullable void (^)(NSError *_Nullable error))completionHandler {
|
||||||
|
FIRExperimentController *__weak weakSelf = self;
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||||
|
FIRExperimentController *strongSelf = weakSelf;
|
||||||
|
[strongSelf updateExperimentConditionalUserPropertiesWithServiceOrigin:origin
|
||||||
|
events:events
|
||||||
|
policy:policy
|
||||||
|
lastStartTime:lastStartTime
|
||||||
|
payloads:payloads
|
||||||
|
completionHandler:completionHandler];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)
|
||||||
|
updateExperimentConditionalUserPropertiesWithServiceOrigin:(NSString *)origin
|
||||||
|
events:(FIRLifecycleEvents *)events
|
||||||
|
policy:
|
||||||
|
(ABTExperimentPayloadExperimentOverflowPolicy)
|
||||||
|
policy
|
||||||
|
lastStartTime:(NSTimeInterval)lastStartTime
|
||||||
|
payloads:(NSArray<NSData *> *)payloads
|
||||||
|
completionHandler:
|
||||||
|
(nullable void (^)(NSError *_Nullable error))
|
||||||
|
completionHandler {
|
||||||
|
ABTConditionalUserPropertyController *controller =
|
||||||
|
[ABTConditionalUserPropertyController sharedInstanceWithAnalytics:_analytics];
|
||||||
|
|
||||||
|
// Get the list of expriments from Firebase Analytics.
|
||||||
|
NSArray *experiments = [controller experimentsWithOrigin:origin];
|
||||||
|
if (!experiments) {
|
||||||
|
NSString *errorDescription =
|
||||||
|
@"Failed to get conditional user properties from Firebase Analytics.";
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000003", @"%@", errorDescription);
|
||||||
|
|
||||||
|
if (completionHandler) {
|
||||||
|
completionHandler([NSError
|
||||||
|
errorWithDomain:kABTErrorDomain
|
||||||
|
code:kABTInternalErrorFailedToFetchConditionalUserProperties
|
||||||
|
userInfo:@{NSLocalizedDescriptionKey : errorDescription}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSArray<ABTExperimentPayload *> *experimentsToSet =
|
||||||
|
ABTExperimentsToSetFromPayloads(payloads, experiments, _analytics);
|
||||||
|
NSArray<NSDictionary<NSString *, NSString *> *> *experimentsToClear =
|
||||||
|
ABTExperimentsToClearFromPayloads(payloads, experiments, _analytics);
|
||||||
|
|
||||||
|
for (id experiment in experimentsToClear) {
|
||||||
|
NSString *experimentID = [controller experimentIDOfExperiment:experiment];
|
||||||
|
NSString *variantID = [controller variantIDOfExperiment:experiment];
|
||||||
|
[controller clearExperiment:experimentID
|
||||||
|
variantID:variantID
|
||||||
|
withOrigin:origin
|
||||||
|
payload:nil
|
||||||
|
events:events];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ABTExperimentPayload *experimentPayload in experimentsToSet) {
|
||||||
|
if (experimentPayload.experimentStartTimeMillis > lastStartTime * ABT_MSEC_PER_SEC) {
|
||||||
|
[controller setExperimentWithOrigin:origin
|
||||||
|
payload:experimentPayload
|
||||||
|
events:events
|
||||||
|
policy:policy];
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000008",
|
||||||
|
@"Set Experiment ID %@, variant ID %@ to Firebase Analytics.",
|
||||||
|
experimentPayload.experimentId, experimentPayload.variantId);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000009",
|
||||||
|
@"Not setting experiment ID %@, variant ID %@ due to the last update time %lld.",
|
||||||
|
experimentPayload.experimentId, experimentPayload.variantId,
|
||||||
|
(long)lastStartTime * ABT_MSEC_PER_SEC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completionHandler) {
|
||||||
|
completionHandler(nil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTimeInterval)latestExperimentStartTimestampBetweenTimestamp:(NSTimeInterval)timestamp
|
||||||
|
andPayloads:(NSArray<NSData *> *)payloads {
|
||||||
|
for (NSData *payload in [payloads copy]) {
|
||||||
|
ABTExperimentPayload *experimentPayload = ABTDeserializeExperimentPayload(payload);
|
||||||
|
if (!experimentPayload) {
|
||||||
|
FIRLogInfo(kFIRLoggerABTesting, @"I-ABT000002",
|
||||||
|
@"Either payload is not set or it cannot be deserialized.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (experimentPayload.experimentStartTimeMillis > timestamp * ABT_MSEC_PER_SEC) {
|
||||||
|
timestamp = (double)(experimentPayload.experimentStartTimeMillis / ABT_MSEC_PER_SEC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)validateRunningExperimentsForServiceOrigin:(NSString *)origin
|
||||||
|
runningExperimentPayloads:(NSArray<ABTExperimentPayload *> *)payloads {
|
||||||
|
ABTConditionalUserPropertyController *controller =
|
||||||
|
[ABTConditionalUserPropertyController sharedInstanceWithAnalytics:_analytics];
|
||||||
|
|
||||||
|
FIRLifecycleEvents *lifecycleEvents = [[FIRLifecycleEvents alloc] init];
|
||||||
|
|
||||||
|
// Get the list of experiments from Firebase Analytics.
|
||||||
|
NSArray<NSDictionary<NSString *, NSString *> *> *activeExperiments =
|
||||||
|
[controller experimentsWithOrigin:origin];
|
||||||
|
|
||||||
|
NSMutableSet *runningExperimentIDs = [NSMutableSet setWithCapacity:payloads.count];
|
||||||
|
for (ABTExperimentPayload *payload in payloads) {
|
||||||
|
[runningExperimentIDs addObject:payload.experimentId];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NSDictionary<NSString *, NSString *> *activeExperimentDictionary in activeExperiments) {
|
||||||
|
NSString *experimentID = activeExperimentDictionary[@"name"];
|
||||||
|
if (![runningExperimentIDs containsObject:experimentID]) {
|
||||||
|
NSString *variantID = activeExperimentDictionary[@"value"];
|
||||||
|
|
||||||
|
[controller clearExperiment:experimentID
|
||||||
|
variantID:variantID
|
||||||
|
withOrigin:origin
|
||||||
|
payload:nil
|
||||||
|
events:lifecycleEvents];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)activateExperiment:(ABTExperimentPayload *)experimentPayload
|
||||||
|
forServiceOrigin:(NSString *)origin {
|
||||||
|
ABTConditionalUserPropertyController *controller =
|
||||||
|
[ABTConditionalUserPropertyController sharedInstanceWithAnalytics:_analytics];
|
||||||
|
|
||||||
|
FIRLifecycleEvents *lifecycleEvents = [[FIRLifecycleEvents alloc] init];
|
||||||
|
|
||||||
|
// Ensure that trigger event is nil, which will immediately set the experiment to active.
|
||||||
|
[experimentPayload clearTriggerEvent];
|
||||||
|
|
||||||
|
[controller setExperimentWithOrigin:origin
|
||||||
|
payload:experimentPayload
|
||||||
|
events:lifecycleEvents
|
||||||
|
policy:experimentPayload.overflowPolicy];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
88
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/FIRLifecycleEvents.m
generated
Normal file
88
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/FIRLifecycleEvents.m
generated
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// 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 "FirebaseABTesting/Sources/Public/FirebaseABTesting/FIRLifecycleEvents.h"
|
||||||
|
|
||||||
|
#import "FirebaseABTesting/Sources/Public/FirebaseABTesting/FIRExperimentController.h"
|
||||||
|
|
||||||
|
/// Default name of the analytics event to be logged when an experiment is set.
|
||||||
|
NSString *const FIRSetExperimentEventName = @"_exp_set";
|
||||||
|
/// Default name of the analytics event to be logged when an experiment is activated.
|
||||||
|
NSString *const FIRActivateExperimentEventName = @"_exp_activate";
|
||||||
|
/// Default name of the analytics event to be logged when an experiment is cleared.
|
||||||
|
NSString *const FIRClearExperimentEventName = @"_exp_clear";
|
||||||
|
/// Default name of the analytics event to be logged when an experiment times out for being
|
||||||
|
/// activated.
|
||||||
|
NSString *const FIRTimeoutExperimentEventName = @"_exp_timeout";
|
||||||
|
/// Default name of the analytics event to be logged when an experiment is expired as it reaches the
|
||||||
|
/// end of TTL.
|
||||||
|
NSString *const FIRExpireExperimentEventName = @"_exp_expire";
|
||||||
|
/// Prefix for lifecycle event names.
|
||||||
|
static NSString *const kLifecycleEventPrefix = @"_";
|
||||||
|
|
||||||
|
@implementation FIRLifecycleEvents
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_setExperimentEventName = FIRSetExperimentEventName;
|
||||||
|
_activateExperimentEventName = FIRActivateExperimentEventName;
|
||||||
|
_clearExperimentEventName = FIRClearExperimentEventName;
|
||||||
|
_timeoutExperimentEventName = FIRTimeoutExperimentEventName;
|
||||||
|
_expireExperimentEventName = FIRExpireExperimentEventName;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setSetExperimentEventName:(NSString *)setExperimentEventName {
|
||||||
|
if (setExperimentEventName && [setExperimentEventName hasPrefix:kLifecycleEventPrefix]) {
|
||||||
|
_setExperimentEventName = setExperimentEventName;
|
||||||
|
} else {
|
||||||
|
_setExperimentEventName = FIRSetExperimentEventName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setActivateExperimentEventName:(NSString *)activateExperimentEventName {
|
||||||
|
if (activateExperimentEventName &&
|
||||||
|
[activateExperimentEventName hasPrefix:kLifecycleEventPrefix]) {
|
||||||
|
_activateExperimentEventName = activateExperimentEventName;
|
||||||
|
} else {
|
||||||
|
_activateExperimentEventName = FIRActivateExperimentEventName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setClearExperimentEventName:(NSString *)clearExperimentEventName {
|
||||||
|
if (clearExperimentEventName && [clearExperimentEventName hasPrefix:kLifecycleEventPrefix]) {
|
||||||
|
_clearExperimentEventName = clearExperimentEventName;
|
||||||
|
} else {
|
||||||
|
_clearExperimentEventName = FIRClearExperimentEventName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setTimeoutExperimentEventName:(NSString *)timeoutExperimentEventName {
|
||||||
|
if (timeoutExperimentEventName && [timeoutExperimentEventName hasPrefix:kLifecycleEventPrefix]) {
|
||||||
|
_timeoutExperimentEventName = timeoutExperimentEventName;
|
||||||
|
} else {
|
||||||
|
_timeoutExperimentEventName = FIRTimeoutExperimentEventName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setExpireExperimentEventName:(NSString *)expireExperimentEventName {
|
||||||
|
if (expireExperimentEventName && [_timeoutExperimentEventName hasPrefix:kLifecycleEventPrefix]) {
|
||||||
|
_expireExperimentEventName = expireExperimentEventName;
|
||||||
|
} else {
|
||||||
|
_expireExperimentEventName = FIRExpireExperimentEventName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
96
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Private/ABTExperimentPayload.h
generated
Normal file
96
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Private/ABTExperimentPayload.h
generated
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// Policy for handling the case where there's an overflow of experiments for an installation
|
||||||
|
/// instance.
|
||||||
|
typedef NS_ENUM(int32_t, ABTExperimentPayloadExperimentOverflowPolicy) {
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyUnrecognizedValue = 999,
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyUnspecified = 0,
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest = 1,
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface ABTExperimentLite : NSObject
|
||||||
|
@property(nonatomic, readonly, copy) NSString *experimentId;
|
||||||
|
|
||||||
|
- (instancetype)initWithExperimentId:(NSString *)experimentId NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface ABTExperimentPayload : NSObject
|
||||||
|
|
||||||
|
/// Unique identifier for this experiment.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *experimentId;
|
||||||
|
|
||||||
|
/// Unique identifier for the variant to which an installation instance has been assigned.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *variantId;
|
||||||
|
|
||||||
|
/// Epoch time that represents when the experiment was started.
|
||||||
|
@property(nonatomic, readonly) int64_t experimentStartTimeMillis;
|
||||||
|
|
||||||
|
/// The event that triggers this experiment into ON state.
|
||||||
|
@property(nonatomic, nullable, readonly, copy) NSString *triggerEvent;
|
||||||
|
|
||||||
|
/// Duration in milliseconds for which the experiment can stay in STANDBY state (un-triggered).
|
||||||
|
@property(nonatomic, readonly) int64_t triggerTimeoutMillis;
|
||||||
|
|
||||||
|
/// Duration in milliseconds for which the experiment can stay in ON state (triggered).
|
||||||
|
@property(nonatomic, readonly) int64_t timeToLiveMillis;
|
||||||
|
|
||||||
|
/// The event logged when impact service sets the experiment.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *setEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment goes to the ON state.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *activateEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment is cleared.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *clearEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment times out after `triggerTimeoutMillis` milliseconds.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *timeoutEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment times out after `timeToLiveMillis` milliseconds.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *ttlExpiryEventToLog;
|
||||||
|
|
||||||
|
@property(nonatomic, readonly) ABTExperimentPayloadExperimentOverflowPolicy overflowPolicy;
|
||||||
|
|
||||||
|
/// A list of all other ongoing (started, and not yet stopped) experiments at the time this
|
||||||
|
/// experiment was started. Does not include this experiment; only the others.
|
||||||
|
@property(nonatomic, readonly) NSArray<ABTExperimentLite *> *ongoingExperiments;
|
||||||
|
|
||||||
|
/// Parses an ABTExperimentPayload directly from JSON data.
|
||||||
|
/// @param data JSON object as NSData. Must be reconstructible as an NSDictionary<NSString* , id>.
|
||||||
|
+ (nullable instancetype)parseFromData:(NSData *)data;
|
||||||
|
|
||||||
|
/// Initializes an ABTExperimentPayload from a dictionary with experiment metadata.
|
||||||
|
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dictionary
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/// Clears the trigger event associated with this payload.
|
||||||
|
- (void)clearTriggerEvent;
|
||||||
|
|
||||||
|
/// Checks if the overflow policy is a valid enum object.
|
||||||
|
- (BOOL)overflowPolicyIsValid;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
20
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Private/FirebaseABTestingInternal.h
generated
Normal file
20
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Private/FirebaseABTestingInternal.h
generated
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#import <FirebaseABTesting/FirebaseABTesting.h>
|
||||||
|
|
||||||
|
#import "FirebaseABTesting/Sources/Private/ABTExperimentPayload.h"
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
// 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 ABTExperimentPayload;
|
||||||
|
|
||||||
|
// Forward declaration to avoid importing into the module header
|
||||||
|
typedef NS_ENUM(int32_t, ABTExperimentPayloadExperimentOverflowPolicy);
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class FIRLifecycleEvents;
|
||||||
|
|
||||||
|
/// The default experiment overflow policy, that is to discard the experiment with the oldest start
|
||||||
|
/// time when users start the experiment on the web console.
|
||||||
|
extern const ABTExperimentPayloadExperimentOverflowPolicy FIRDefaultExperimentOverflowPolicy;
|
||||||
|
|
||||||
|
/// This class is for Firebase services to handle experiments updates to Firebase Analytics.
|
||||||
|
/// Experiments can be set, cleared and updated through this controller.
|
||||||
|
NS_SWIFT_NAME(ExperimentController)
|
||||||
|
@interface FIRExperimentController : NSObject
|
||||||
|
|
||||||
|
/// Returns the FIRExperimentController singleton.
|
||||||
|
+ (FIRExperimentController *)sharedInstance;
|
||||||
|
|
||||||
|
/// Updates the list of experiments with an optional completion handler. Experiments already
|
||||||
|
/// existing in payloads are not affected, whose state and payload is preserved. This method
|
||||||
|
/// compares whether the experiments have changed or not by their variant ID. This runs in a
|
||||||
|
/// background queue and calls the completion handler when finished executing.
|
||||||
|
/// @param origin The originating service affected by the experiment.
|
||||||
|
/// @param events A list of event names to be used for logging experiment lifecycle events,
|
||||||
|
/// if they are not defined in the payload.
|
||||||
|
/// @param policy The policy to handle new experiments when slots are full.
|
||||||
|
/// @param lastStartTime The last known experiment start timestamp for this affected service.
|
||||||
|
/// (Timestamps are specified by the number of seconds from 00:00:00 UTC on 1
|
||||||
|
/// January 1970.).
|
||||||
|
/// @param payloads List of experiment metadata.
|
||||||
|
/// @param completionHandler Code to be executed after experiments are updated in the background
|
||||||
|
/// thread.
|
||||||
|
- (void)updateExperimentsWithServiceOrigin:(NSString *)origin
|
||||||
|
events:(FIRLifecycleEvents *)events
|
||||||
|
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy
|
||||||
|
lastStartTime:(NSTimeInterval)lastStartTime
|
||||||
|
payloads:(NSArray<NSData *> *)payloads
|
||||||
|
completionHandler:
|
||||||
|
(nullable void (^)(NSError *_Nullable error))completionHandler;
|
||||||
|
|
||||||
|
/// Returns the latest experiment start timestamp given a current latest timestamp and a list of
|
||||||
|
/// experiment payloads. Timestamps are specified by the number of seconds from 00:00:00 UTC on 1
|
||||||
|
/// January 1970.
|
||||||
|
/// @param timestamp Current latest experiment start timestamp. If not known, affected service
|
||||||
|
/// should specify -1;
|
||||||
|
/// @param payloads List of experiment metadata.
|
||||||
|
- (NSTimeInterval)latestExperimentStartTimestampBetweenTimestamp:(NSTimeInterval)timestamp
|
||||||
|
andPayloads:(NSArray<NSData *> *)payloads;
|
||||||
|
|
||||||
|
/// Expires experiments that aren't in the list of running experiment payloads.
|
||||||
|
/// @param origin The originating service affected by the experiment.
|
||||||
|
/// @param payloads The list of valid, running experiments.
|
||||||
|
- (void)validateRunningExperimentsForServiceOrigin:(NSString *)origin
|
||||||
|
runningExperimentPayloads:(NSArray<ABTExperimentPayload *> *)payloads;
|
||||||
|
|
||||||
|
/// Directly sets a given experiment to be active.
|
||||||
|
/// @param experimentPayload The payload for the experiment that should be activated.
|
||||||
|
/// @param origin The originating service affected by the experiment.
|
||||||
|
- (void)activateExperiment:(ABTExperimentPayload *)experimentPayload
|
||||||
|
forServiceOrigin:(NSString *)origin;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
/// Default event name for when an experiment is set.
|
||||||
|
extern NSString *const FIRSetExperimentEventName NS_SWIFT_NAME(DefaultSetExperimentEventName);
|
||||||
|
/// Default event name for when an experiment is activated.
|
||||||
|
// clang-format off
|
||||||
|
// clang-format12 will merge lines and exceed 100 character limit.
|
||||||
|
extern NSString *const FIRActivateExperimentEventName
|
||||||
|
NS_SWIFT_NAME(DefaultActivateExperimentEventName);
|
||||||
|
/// Default event name for when an experiment is cleared.
|
||||||
|
extern NSString *const FIRClearExperimentEventName NS_SWIFT_NAME(DefaultClearExperimentEventName);
|
||||||
|
/// Default event name for when an experiment times out for being activated.
|
||||||
|
extern NSString *const FIRTimeoutExperimentEventName
|
||||||
|
NS_SWIFT_NAME(DefaultTimeoutExperimentEventName);
|
||||||
|
// clang-format on
|
||||||
|
/// Default event name for when an experiment is expired as it reaches the end of TTL.
|
||||||
|
extern NSString *const FIRExpireExperimentEventName NS_SWIFT_NAME(DefaultExpireExperimentEventName);
|
||||||
|
|
||||||
|
/// An Experiment Lifecycle Event Object that specifies the name of the experiment event to be
|
||||||
|
/// logged by Firebase Analytics.
|
||||||
|
NS_SWIFT_NAME(LifecycleEvents)
|
||||||
|
@interface FIRLifecycleEvents : NSObject
|
||||||
|
|
||||||
|
/// Event name for when an experiment is set. It defaults to `SetExperimentEventName` and can be
|
||||||
|
/// overridden. If experiment payload has a valid string of this field, always use
|
||||||
|
/// experiment payload.
|
||||||
|
@property(nonatomic, copy) NSString *setExperimentEventName;
|
||||||
|
|
||||||
|
/// Event name for when an experiment is activated. It defaults to `ActivateExperimentEventName`
|
||||||
|
/// and can be overridden. If experiment payload has a valid string of this field, always use
|
||||||
|
/// experiment payload.
|
||||||
|
@property(nonatomic, copy) NSString *activateExperimentEventName;
|
||||||
|
|
||||||
|
/// Event name for when an experiment is cleared. It is default to `ClearExperimentEventName` and
|
||||||
|
/// can be overridden. If experiment payload has a valid string of this field, always use experiment
|
||||||
|
/// payload.
|
||||||
|
@property(nonatomic, copy) NSString *clearExperimentEventName;
|
||||||
|
/// Event name for when an experiment is timeout from being STANDBY. It is default to
|
||||||
|
/// `TimeoutExperimentEventName` and can be overridden. If experiment payload has a valid string
|
||||||
|
/// of this field, always use experiment payload.
|
||||||
|
@property(nonatomic, copy) NSString *timeoutExperimentEventName;
|
||||||
|
|
||||||
|
/// Event name when an experiment is expired when it reaches the end of its TTL.
|
||||||
|
/// It is default to `ExpireExperimentEventName` and can be overridden. If experiment payload has a
|
||||||
|
/// valid string of this field, always use experiment payload.
|
||||||
|
@property(nonatomic, copy) NSString *expireExperimentEventName;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
16
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Public/FirebaseABTesting/FirebaseABTesting.h
generated
Executable file
16
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Public/FirebaseABTesting/FirebaseABTesting.h
generated
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
// 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 "FIRExperimentController.h"
|
||||||
|
#import "FIRLifecycleEvents.h"
|
||||||
18
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Resources/PrivacyInfo.xcprivacy
generated
Normal file
18
SwiftProject/Pods/FirebaseABTesting/FirebaseABTesting/Sources/Resources/PrivacyInfo.xcprivacy
generated
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
||||||
159
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRAppInternal.h
generated
Normal file
159
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRAppInternal.h
generated
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FirebaseCore/FIRApp.h>
|
||||||
|
|
||||||
|
@class FIRComponentContainer;
|
||||||
|
@class FIRHeartbeatLogger;
|
||||||
|
@protocol FIRLibrary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal interface to `FirebaseApp`. This is meant for first-party integrators, who need to
|
||||||
|
* receive `FirebaseApp` notifications, log info about the success or failure of their
|
||||||
|
* configuration, and access other internal functionality of `FirebaseApp`.
|
||||||
|
*/
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, FIRConfigType) {
|
||||||
|
FIRConfigTypeCore = 1,
|
||||||
|
FIRConfigTypeSDK = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern NSString *const kFIRDefaultAppName;
|
||||||
|
extern NSString *const kFIRAppReadyToConfigureSDKNotification;
|
||||||
|
extern NSString *const kFIRAppDeleteNotification;
|
||||||
|
extern NSString *const kFIRAppIsDefaultAppKey;
|
||||||
|
extern NSString *const kFIRAppNameKey;
|
||||||
|
extern NSString *const kFIRGoogleAppIDKey;
|
||||||
|
extern NSString *const kFirebaseCoreErrorDomain;
|
||||||
|
|
||||||
|
/** The `UserDefaults` suite name for `FirebaseCore`, for those storage locations that use it. */
|
||||||
|
extern NSString *const kFirebaseCoreDefaultsSuiteName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format string for the `UserDefaults` key used for storing the data collection enabled flag.
|
||||||
|
* This includes formatting to append the `FirebaseApp`'s name.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The plist key used for storing the data collection enabled flag.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotification
|
||||||
|
@brief The name of the @c NotificationCenter notification which is posted when the auth state
|
||||||
|
changes (e.g. a new token has been produced, a user logs in or out). The object parameter of
|
||||||
|
the notification is a dictionary possibly containing the key:
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not
|
||||||
|
contain this key it indicates a sign-out event took place.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotification;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotificationTokenKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the new access token.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotificationAppKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the FirebaseApp associated with the auth instance.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotificationUIDKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the new user's UID (or nil if there is no longer a user signed in).
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
|
||||||
|
|
||||||
|
@interface FIRApp ()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag indicating if this is the default app (has the default app name).
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isDefaultApp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The container of interop SDKs for this app.
|
||||||
|
*/
|
||||||
|
@property(nonatomic) FIRComponentContainer *container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The heartbeat logger associated with this app.
|
||||||
|
*
|
||||||
|
* Firebase apps have a 1:1 relationship with heartbeat loggers.
|
||||||
|
*/
|
||||||
|
@property(readonly) FIRHeartbeatLogger *heartbeatLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the default app is configured without trying to configure it.
|
||||||
|
*/
|
||||||
|
+ (BOOL)isDefaultAppConfigured;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given third-party library with the given version number to be reported for
|
||||||
|
* analytics.
|
||||||
|
*
|
||||||
|
* @param name Name of the library.
|
||||||
|
* @param version Version of the library.
|
||||||
|
*/
|
||||||
|
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given internal library to be reported for analytics.
|
||||||
|
*
|
||||||
|
* @param library Optional parameter for component registration.
|
||||||
|
* @param name Name of the library.
|
||||||
|
*/
|
||||||
|
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
|
||||||
|
withName:(nonnull NSString *)name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given internal library with the given version number to be reported for
|
||||||
|
* analytics. This should only be used for non-Firebase libraries that have their own versioning
|
||||||
|
* scheme.
|
||||||
|
*
|
||||||
|
* @param library Optional parameter for component registration.
|
||||||
|
* @param name Name of the library.
|
||||||
|
* @param version Version of the library.
|
||||||
|
*/
|
||||||
|
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
|
||||||
|
withName:(nonnull NSString *)name
|
||||||
|
withVersion:(nonnull NSString *)version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A concatenated string representing all the third-party libraries and version numbers.
|
||||||
|
*/
|
||||||
|
+ (NSString *)firebaseUserAgent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used by the unit tests in each SDK to reset `FirebaseApp`. This method is thread unsafe.
|
||||||
|
*/
|
||||||
|
+ (void)resetApps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used by the unit tests in each SDK to set customized options.
|
||||||
|
*/
|
||||||
|
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
91
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRComponent.h
generated
Normal file
91
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRComponent.h
generated
Normal file
@ -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
|
||||||
45
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRComponentContainer.h
generated
Normal file
45
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRComponentContainer.h
generated
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// A type-safe macro to retrieve a component from a container. This should be used to retrieve
|
||||||
|
/// components instead of using the container directly.
|
||||||
|
#define FIR_COMPONENT(type, container) \
|
||||||
|
[FIRComponentType<id<type>> instanceForProtocol:@protocol(type) inContainer:container]
|
||||||
|
|
||||||
|
@class FIRApp;
|
||||||
|
|
||||||
|
/// A container that holds different components that are registered via the
|
||||||
|
/// `registerAsComponentRegistrant` call. These classes should conform to `ComponentRegistrant`
|
||||||
|
/// in order to properly register components for Core.
|
||||||
|
NS_SWIFT_NAME(FirebaseComponentContainer)
|
||||||
|
@interface FIRComponentContainer : NSObject
|
||||||
|
|
||||||
|
/// A weak reference to the app that an instance of the container belongs to.
|
||||||
|
@property(nonatomic, weak, readonly) FIRApp *app;
|
||||||
|
|
||||||
|
// TODO: See if we can get improved type safety here.
|
||||||
|
/// A Swift only API for fetching an instance since the top macro isn't available.
|
||||||
|
- (nullable id)__instanceForProtocol:(Protocol *)protocol NS_SWIFT_NAME(instance(for:));
|
||||||
|
|
||||||
|
/// Unavailable. Use the `container` property on `FirebaseApp`.
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
35
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRComponentType.h
generated
Normal file
35
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRComponentType.h
generated
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class FIRComponentContainer;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// Do not use directly. A placeholder type in order to provide a macro that will warn users of
|
||||||
|
/// mis-matched protocols.
|
||||||
|
NS_SWIFT_NAME(ComponentType)
|
||||||
|
@interface FIRComponentType<__covariant T> : NSObject
|
||||||
|
|
||||||
|
/// Do not use directly. A factory method to retrieve an instance that provides a specific
|
||||||
|
/// functionality.
|
||||||
|
+ (nullable T)instanceForProtocol:(Protocol *)protocol
|
||||||
|
inContainer:(FIRComponentContainer *)container;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
45
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRDependency.h
generated
Normal file
45
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRDependency.h
generated
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// A 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 `init(protocol:isRequired:)` with true 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 `init(withProtocol:isRequired:)` instead.
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
90
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRHeartbeatLogger.h
generated
Normal file
90
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRHeartbeatLogger.h
generated
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2021 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
@class FIRHeartbeatsPayload;
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// Enum representing different daily heartbeat codes.
|
||||||
|
/// This enum is only used by clients using platform logging V1. This is because
|
||||||
|
/// the V1 payload only supports a single daily heartbeat.
|
||||||
|
typedef NS_ENUM(NSInteger, FIRDailyHeartbeatCode) {
|
||||||
|
/// Represents the absence of a daily heartbeat.
|
||||||
|
FIRDailyHeartbeatCodeNone = 0,
|
||||||
|
/// Represents the presence of a daily heartbeat.
|
||||||
|
FIRDailyHeartbeatCodeSome = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
@protocol FIRHeartbeatLoggerProtocol <NSObject>
|
||||||
|
|
||||||
|
/// Asynchronously logs a heartbeat.
|
||||||
|
- (void)log;
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
/// Flushes heartbeats from storage into a structured payload of heartbeats.
|
||||||
|
- (FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload;
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// Gets the heartbeat code for today.
|
||||||
|
- (FIRDailyHeartbeatCode)heartbeatCodeForToday;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
/// Returns a nullable string header value from a given heartbeats payload.
|
||||||
|
///
|
||||||
|
/// This API returns `nil` when the given heartbeats payload is considered empty.
|
||||||
|
///
|
||||||
|
/// @param heartbeatsPayload The heartbeats payload.
|
||||||
|
NSString *_Nullable FIRHeaderValueFromHeartbeatsPayload(FIRHeartbeatsPayload *heartbeatsPayload);
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// A thread safe, synchronized object that logs and flushes platform logging info.
|
||||||
|
@interface FIRHeartbeatLogger : NSObject <FIRHeartbeatLoggerProtocol>
|
||||||
|
|
||||||
|
/// Designated initializer.
|
||||||
|
///
|
||||||
|
/// @param appID The app ID that this heartbeat logger corresponds to.
|
||||||
|
- (instancetype)initWithAppID:(NSString *)appID;
|
||||||
|
|
||||||
|
/// Asynchronously logs a new heartbeat corresponding to the Firebase User Agent, if needed.
|
||||||
|
///
|
||||||
|
/// @note This API is thread-safe.
|
||||||
|
- (void)log;
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
/// Flushes heartbeats from storage into a structured payload of heartbeats.
|
||||||
|
///
|
||||||
|
/// This API is for clients using platform logging V2.
|
||||||
|
///
|
||||||
|
/// @note This API is thread-safe.
|
||||||
|
/// @return A payload of heartbeats.
|
||||||
|
- (FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload;
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// Gets today's corresponding heartbeat code.
|
||||||
|
///
|
||||||
|
/// This API is for clients using platform logging V1.
|
||||||
|
///
|
||||||
|
/// @note This API is thread-safe.
|
||||||
|
/// @return Heartbeat code indicating whether or not there is an unsent global heartbeat.
|
||||||
|
- (FIRDailyHeartbeatCode)heartbeatCodeForToday;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
44
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRLibrary.h
generated
Normal file
44
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRLibrary.h
generated
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FIRLibrary_h
|
||||||
|
#define FIRLibrary_h
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class FIRApp;
|
||||||
|
@class FIRComponent;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// Provide an interface to register a library for userAgent logging and availability to others.
|
||||||
|
NS_SWIFT_NAME(Library)
|
||||||
|
@protocol FIRLibrary
|
||||||
|
|
||||||
|
/// Returns one or more Components that will be registered in
|
||||||
|
/// FirebaseApp and participate in dependency resolution and injection.
|
||||||
|
+ (NSArray<FIRComponent *> *)componentsToRegister;
|
||||||
|
|
||||||
|
@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 */
|
||||||
193
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRLogger.h
generated
Normal file
193
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIRLogger.h
generated
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import <FirebaseCore/FIRLoggerLevel.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Firebase services used in Firebase logger.
|
||||||
|
*/
|
||||||
|
typedef NSString *const FIRLoggerService;
|
||||||
|
|
||||||
|
extern FIRLoggerService kFIRLoggerAnalytics;
|
||||||
|
extern FIRLoggerService kFIRLoggerCrash;
|
||||||
|
extern FIRLoggerService kFIRLoggerCore;
|
||||||
|
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 true, the logging level for Analytics will be set to FirebaseLoggerLevelDebug.
|
||||||
|
* Enabling the debug mode has no effect if the app is running from App Store.
|
||||||
|
* (required) analytics debug mode flag.
|
||||||
|
*/
|
||||||
|
void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current FIRLoggerLevel.
|
||||||
|
*/
|
||||||
|
FIRLoggerLevel FIRGetLoggerLevel(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the default logging level of FirebaseLoggerLevelNotice to a user-specified level.
|
||||||
|
* The default level cannot be set above FirebaseLoggerLevelNotice if the app is running from App
|
||||||
|
* Store. (required) log level (one of the FirebaseLoggerLevel enum values).
|
||||||
|
*/
|
||||||
|
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified logger level is loggable given the current settings.
|
||||||
|
* (required) log level (one of the FirebaseLoggerLevel enum values).
|
||||||
|
* (required) whether or not this function is called from the Analytics component.
|
||||||
|
*/
|
||||||
|
BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message to the Xcode console and the device log. If running from AppStore, will
|
||||||
|
* not log any messages with a level higher than FirebaseLoggerLevelNotice to avoid log spamming.
|
||||||
|
* (required) log level (one of the FirebaseLoggerLevel enum values).
|
||||||
|
* (required) service name of type FirebaseLoggerService.
|
||||||
|
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
* three-character service identifier and a six digit integer message ID that is unique
|
||||||
|
* within the service.
|
||||||
|
* An example of the message code is @"I-COR000001".
|
||||||
|
* (required) message string which can be a format string.
|
||||||
|
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
extern void FIRLogBasic(FIRLoggerLevel level,
|
||||||
|
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 FirebaseLoggerService.
|
||||||
|
* (required) message code starting from "I-" which means iOS, followed by a capitalized
|
||||||
|
* three-character service identifier and a six digit integer message ID that is unique
|
||||||
|
* within the service.
|
||||||
|
* An example of the message code is @"I-COR000001".
|
||||||
|
* See go/firebase-log-proposal for details.
|
||||||
|
* (required) message string which can be a format string.
|
||||||
|
* (optional) the list of arguments to substitute into the format string.
|
||||||
|
* Example usage:
|
||||||
|
* FirebaseLogError(kFirebaseLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
|
||||||
|
*/
|
||||||
|
extern void FIRLogError(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);
|
||||||
|
|
||||||
|
// TODO: Come up with a better logging scheme for Swift.
|
||||||
|
/**
|
||||||
|
* Logs a debug message to the Xcode console and the device log. If running from AppStore, will
|
||||||
|
* not log any messages with a level higher than FirebaseLoggerLevelNotice to avoid log spamming.
|
||||||
|
* This function is intended to be used by Swift clients that do not support variadic parameters.
|
||||||
|
*
|
||||||
|
* @param service The service name of type `FirebaseLoggerService`.
|
||||||
|
* @param messageCode The mesage 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".
|
||||||
|
* @param message The message string.
|
||||||
|
*/
|
||||||
|
extern void FIRLogDebugSwift(FIRLoggerService service, NSString *messageCode, NSString *message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a warning message to the Xcode console and the device log. If running from AppStore, will
|
||||||
|
* not log any messages with a level higher than FirebaseLoggerLevelNotice to avoid log spamming.
|
||||||
|
* This function is intended to be used by Swift clients that do not support variadic parameters.
|
||||||
|
*
|
||||||
|
* @param service The service name of type `FirebaseLoggerService`.
|
||||||
|
* @param messageCode The mesage 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".
|
||||||
|
* @param message The message string.
|
||||||
|
*/
|
||||||
|
extern void FIRLogWarningSwift(FIRLoggerService service, NSString *messageCode, NSString *message);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
NS_SWIFT_NAME(FirebaseLogger)
|
||||||
|
@interface FIRLoggerWrapper : NSObject
|
||||||
|
|
||||||
|
/// Logs a given message at a given log level. This API is effectively a wrapper for the
|
||||||
|
/// `FIRLogBasic` C API.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - level: The log level to use (defined by `FirebaseLoggerLevel` enum values).
|
||||||
|
/// - service: The service name of type `FirebaseLoggerService`.
|
||||||
|
/// - code: The mesage code. Starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
/// three-character service identifier and a six digit integer message ID that is unique within
|
||||||
|
/// the service. An example of the message code is @"I-COR000001".
|
||||||
|
/// - message: Formatted string to be used as the log's message.
|
||||||
|
/// - args: 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;
|
||||||
|
|
||||||
|
/// Logs a given message at a given log level.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - level: The log level to use (defined by `FirebaseLoggerLevel` enum values).
|
||||||
|
/// - service: The service name of type `FirebaseLoggerService`.
|
||||||
|
/// - code: The mesage code. Starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
/// three-character service identifier and a six digit integer message ID that is unique within
|
||||||
|
/// the service. An example of the message code is @"I-COR000001".
|
||||||
|
/// - message: Formatted string to be used as the log's message.
|
||||||
|
/// - args: Arguments list obtained from calling `va_start`, used when message is a format string.
|
||||||
|
+ (void)logWithLevel:(FIRLoggerLevel)level
|
||||||
|
service:(FIRLoggerService)service
|
||||||
|
code:(NSString *)code
|
||||||
|
message:(NSString *)message
|
||||||
|
__attribute__((__swift_name__("log(level:service:code:message:)")));
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
106
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIROptionsInternal.h
generated
Normal file
106
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FIROptionsInternal.h
generated
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FirebaseCore/FIROptions.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys for the strings in the plist file.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRAPIKey;
|
||||||
|
extern NSString *const kFIRTrackingID;
|
||||||
|
extern NSString *const kFIRGoogleAppID;
|
||||||
|
extern NSString *const kFIRClientID;
|
||||||
|
extern NSString *const kFIRGCMSenderID;
|
||||||
|
extern NSString *const kFIRAndroidClientID;
|
||||||
|
extern NSString *const kFIRDatabaseURL;
|
||||||
|
extern NSString *const kFIRStorageBucket;
|
||||||
|
extern NSString *const kFIRBundleID;
|
||||||
|
extern NSString *const kFIRProjectID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys for the plist file name
|
||||||
|
*/
|
||||||
|
extern NSString *const kServiceInfoFileName;
|
||||||
|
|
||||||
|
extern NSString *const kServiceInfoFileType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This header file exposes the initialization of FirebaseOptions to internal use.
|
||||||
|
*/
|
||||||
|
@interface FIROptions ()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `resetDefaultOptions` and `initInternalWithOptionsDictionary` are exposed only for unit tests.
|
||||||
|
*/
|
||||||
|
+ (void)resetDefaultOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the options with dictionary. The above strings are the keys of the dictionary.
|
||||||
|
* This is the designated initializer.
|
||||||
|
*/
|
||||||
|
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `defaultOptions` and `defaultOptionsDictionary` are exposed in order to be used in FirebaseApp
|
||||||
|
* and other first party services.
|
||||||
|
*/
|
||||||
|
+ (FIROptions *)defaultOptions;
|
||||||
|
|
||||||
|
+ (NSDictionary *)defaultOptionsDictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at
|
||||||
|
* runtime.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionExplicitlySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless
|
||||||
|
* explicitly disabled in GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Analytics Collection was completely disabled. If true, then
|
||||||
|
* isAnalyticsCollectionEnabled will be false.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version ID of the client library, e.g. @"1100000".
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly, copy) NSString *libraryVersionID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag indicating whether this object was constructed with the values in the default plist
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
@property(nonatomic) BOOL usingOptionsFromDefaultPlist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
|
||||||
|
* GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isMeasurementEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not editing is locked. This should occur after `FirebaseOptions` has been set on a
|
||||||
|
* `FirebaseApp`.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
|
||||||
|
|
||||||
|
@end
|
||||||
25
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FirebaseCoreInternal.h
generated
Normal file
25
SwiftProject/Pods/FirebaseABTesting/FirebaseCore/Extension/FirebaseCoreInternal.h
generated
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
@import FirebaseCore;
|
||||||
|
|
||||||
|
#import "FIRAppInternal.h"
|
||||||
|
#import "FIRComponent.h"
|
||||||
|
#import "FIRComponentContainer.h"
|
||||||
|
#import "FIRComponentType.h"
|
||||||
|
#import "FIRDependency.h"
|
||||||
|
#import "FIRHeartbeatLogger.h"
|
||||||
|
#import "FIRLibrary.h"
|
||||||
|
#import "FIRLogger.h"
|
||||||
|
#import "FIROptionsInternal.h"
|
||||||
68
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRAnalyticsInterop.h
generated
Normal file
68
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRAnalyticsInterop.h
generated
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 `getUserProperties(with:)`.
|
||||||
|
typedef void (^FIRAInteropUserPropertiesCallback)(NSDictionary<NSString *, id> *userProperties)
|
||||||
|
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
|
||||||
|
|
||||||
|
/// Connector for bridging communication between Firebase SDKs and FirebaseAnalytics APIs.
|
||||||
|
@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:
|
||||||
|
(void (^)(NSDictionary<NSString *, id> *userProperties))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
|
||||||
24
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRAnalyticsInteropListener.h
generated
Normal file
24
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRAnalyticsInteropListener.h
generated
Normal file
@ -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
|
||||||
|
/// FirebaseAnalyticsInterop`s `registerAnalyticsListener(_:withOrigin:)`.
|
||||||
|
- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters;
|
||||||
|
|
||||||
|
@end
|
||||||
28
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRInteropEventNames.h
generated
Normal file
28
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRInteropEventNames.h
generated
Normal file
@ -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";
|
||||||
73
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRInteropParameterNames.h
generated
Normal file
73
SwiftProject/Pods/FirebaseABTesting/Interop/Analytics/Public/FIRInteropParameterNames.h
generated
Normal file
@ -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 (String).
|
||||||
|
/// <pre>
|
||||||
|
/// let 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
|
||||||
|
/// (String).
|
||||||
|
/// <pre>
|
||||||
|
/// let 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
|
||||||
|
/// (String).
|
||||||
|
/// <pre>
|
||||||
|
/// let 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";
|
||||||
202
SwiftProject/Pods/FirebaseABTesting/LICENSE
generated
Normal file
202
SwiftProject/Pods/FirebaseABTesting/LICENSE
generated
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
297
SwiftProject/Pods/FirebaseABTesting/README.md
generated
Normal file
297
SwiftProject/Pods/FirebaseABTesting/README.md
generated
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://cocoapods.org/pods/Firebase">
|
||||||
|
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=CocoaPods"/>
|
||||||
|
</a>
|
||||||
|
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||||
|
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=Swift%20Package%20Index&color=red"/>
|
||||||
|
</a>
|
||||||
|
<a href="https://cocoapods.org/pods/Firebase">
|
||||||
|
<img src="https://img.shields.io/github/license/Firebase/firebase-ios-sdk?style=flat"/>
|
||||||
|
</a><br/>
|
||||||
|
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||||
|
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dplatforms"/>
|
||||||
|
</a>
|
||||||
|
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||||
|
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dswift-versions"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# Firebase Apple Open Source Development
|
||||||
|
|
||||||
|
This repository contains the source code for all Apple platform Firebase SDKs except FirebaseAnalytics.
|
||||||
|
|
||||||
|
Firebase is an app development platform with tools to help you build, grow, and
|
||||||
|
monetize your app. More information about Firebase can be found on the
|
||||||
|
[official Firebase website](https://firebase.google.com).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
See the subsections below for details about the different installation methods. Where
|
||||||
|
available, it's recommended to install any libraries with a `Swift` suffix to get the
|
||||||
|
best experience when writing your app in Swift.
|
||||||
|
|
||||||
|
1. [Standard pod install](#standard-pod-install)
|
||||||
|
2. [Swift Package Manager](#swift-package-manager)
|
||||||
|
3. [Installing from the GitHub repo](#installing-from-github)
|
||||||
|
4. [Experimental Carthage](#carthage-ios-only)
|
||||||
|
|
||||||
|
### Standard pod install
|
||||||
|
|
||||||
|
For instructions on the standard pod install, visit:
|
||||||
|
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
|
||||||
|
|
||||||
|
### Swift Package Manager
|
||||||
|
|
||||||
|
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
|
||||||
|
found in the [SwiftPackageManager.md](SwiftPackageManager.md) Markdown file.
|
||||||
|
|
||||||
|
### Installing from GitHub
|
||||||
|
|
||||||
|
These instructions can be used to access the Firebase repo at other branches,
|
||||||
|
tags, or commits.
|
||||||
|
|
||||||
|
#### Background
|
||||||
|
|
||||||
|
See [the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
|
||||||
|
for instructions and options about overriding pod source locations.
|
||||||
|
|
||||||
|
#### Accessing Firebase Source Snapshots
|
||||||
|
|
||||||
|
All official releases are tagged in this repo and available via CocoaPods. To access a local
|
||||||
|
source snapshot or unreleased branch, use Podfile directives like the following:
|
||||||
|
|
||||||
|
To access FirebaseFirestore via a branch:
|
||||||
|
```ruby
|
||||||
|
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
|
||||||
|
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
|
||||||
|
```
|
||||||
|
|
||||||
|
To access FirebaseMessaging via a checked-out version of the firebase-ios-sdk repo:
|
||||||
|
```ruby
|
||||||
|
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
|
||||||
|
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Carthage (iOS only)
|
||||||
|
|
||||||
|
Instructions for the experimental Carthage distribution can be found at
|
||||||
|
[Carthage.md](Carthage.md).
|
||||||
|
|
||||||
|
### Using Firebase from a Framework or a library
|
||||||
|
|
||||||
|
For details on using Firebase from a Framework or a library, refer to [firebase_in_libraries.md](docs/firebase_in_libraries.md).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To develop Firebase software in this repository, ensure that you have at least
|
||||||
|
the following software:
|
||||||
|
|
||||||
|
* Xcode 14.1 (or later)
|
||||||
|
|
||||||
|
CocoaPods is still the canonical way to develop, but much of the repo now supports
|
||||||
|
development with Swift Package Manager.
|
||||||
|
|
||||||
|
### CocoaPods
|
||||||
|
|
||||||
|
Install the following:
|
||||||
|
* CocoaPods 1.12.0 (or later)
|
||||||
|
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
|
||||||
|
|
||||||
|
For the pod that you want to develop:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: If the CocoaPods cache is out of date, you may need to run
|
||||||
|
`pod repo update` before the `pod gen` command.
|
||||||
|
|
||||||
|
Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
|
||||||
|
those platforms. Since 10.2, Xcode does not properly handle multi-platform
|
||||||
|
CocoaPods workspaces.
|
||||||
|
|
||||||
|
Firestore has a self-contained Xcode project. See
|
||||||
|
[Firestore/README](Firestore/README.md) Markdown file.
|
||||||
|
|
||||||
|
#### Development for Catalyst
|
||||||
|
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
|
||||||
|
* Check the Mac box in the App-iOS Build Settings
|
||||||
|
* Sign the App in the Settings Signing & Capabilities tab
|
||||||
|
* Click Pods in the Project Manager
|
||||||
|
* Add Signing to the iOS host app and unit test targets
|
||||||
|
* Select the Unit-unit scheme
|
||||||
|
* Run it to build and test
|
||||||
|
|
||||||
|
Alternatively, disable signing in each target:
|
||||||
|
* Go to Build Settings tab
|
||||||
|
* Click `+`
|
||||||
|
* Select `Add User-Defined Setting`
|
||||||
|
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
|
||||||
|
|
||||||
|
### Swift Package Manager
|
||||||
|
* To enable test schemes: `./scripts/setup_spm_tests.sh`
|
||||||
|
* `open Package.swift` or double click `Package.swift` in Finder.
|
||||||
|
* Xcode will open the project
|
||||||
|
* Choose a scheme for a library to build or test suite to run
|
||||||
|
* Choose a target platform by selecting the run destination along with the scheme
|
||||||
|
|
||||||
|
### Adding a New Firebase Pod
|
||||||
|
|
||||||
|
Refer to [AddNewPod](AddNewPod.md) Markdown file for details.
|
||||||
|
|
||||||
|
### Managing Headers and Imports
|
||||||
|
|
||||||
|
For information about managing headers and imports, see [HeadersImports](HeadersImports.md) Markdown file.
|
||||||
|
|
||||||
|
### Code Formatting
|
||||||
|
|
||||||
|
To ensure that the code is formatted consistently, run the script
|
||||||
|
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/main/scripts/check.sh)
|
||||||
|
before creating a pull request (PR).
|
||||||
|
|
||||||
|
GitHub Actions will verify that any code changes are done in a style-compliant
|
||||||
|
way. Install `clang-format` and `mint`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
brew install clang-format@18
|
||||||
|
brew install mint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Unit Tests
|
||||||
|
|
||||||
|
Select a scheme and press Command-u to build a component and run its unit tests.
|
||||||
|
|
||||||
|
### Running Sample Apps
|
||||||
|
To run the sample apps and integration tests, you'll need a valid
|
||||||
|
`GoogleService-Info.plist
|
||||||
|
` file. The Firebase Xcode project contains dummy plist
|
||||||
|
files without real values, but they can be replaced with real plist files. To get your own
|
||||||
|
`GoogleService-Info.plist` files:
|
||||||
|
|
||||||
|
1. Go to the [Firebase Console](https://console.firebase.google.com/)
|
||||||
|
2. Create a new Firebase project, if you don't already have one
|
||||||
|
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
|
||||||
|
identifier (e.g., `com.google.Database-Example`)
|
||||||
|
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
|
||||||
|
|
||||||
|
### Coverage Report Generation
|
||||||
|
|
||||||
|
For coverage report generation instructions, see [scripts/code_coverage_report/README](scripts/code_coverage_report/README.md) Markdown file.
|
||||||
|
|
||||||
|
## Specific Component Instructions
|
||||||
|
See the sections below for any special instructions for those components.
|
||||||
|
|
||||||
|
### Firebase Auth
|
||||||
|
|
||||||
|
For specific Firebase Auth development, refer to the [Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about
|
||||||
|
building and running the FirebaseAuth pod along with various samples and tests.
|
||||||
|
|
||||||
|
### Firebase Database
|
||||||
|
|
||||||
|
The Firebase Database Integration tests can be run against a locally running Database Emulator
|
||||||
|
or against a production instance.
|
||||||
|
|
||||||
|
To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
|
||||||
|
running the integration test.
|
||||||
|
|
||||||
|
To run against a production instance, provide a valid `GoogleServices-Info.plist` and copy it to
|
||||||
|
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||||
|
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
|
||||||
|
running.
|
||||||
|
|
||||||
|
### Firebase Dynamic Links
|
||||||
|
|
||||||
|
Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
|
||||||
|
|
||||||
|
Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
|
||||||
|
|
||||||
|
### Firebase Performance Monitoring
|
||||||
|
|
||||||
|
For specific Firebase Performance Monitoring development, see
|
||||||
|
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
|
||||||
|
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
|
||||||
|
integrating Performance with the dev test App.
|
||||||
|
|
||||||
|
### Firebase Storage
|
||||||
|
|
||||||
|
To run the Storage Integration tests, follow the instructions in
|
||||||
|
[StorageIntegration.swift](FirebaseStorage/Tests/Integration/StorageIntegration.swift).
|
||||||
|
|
||||||
|
#### Push Notifications
|
||||||
|
|
||||||
|
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
|
||||||
|
In order to test receiving push notifications, you will need to:
|
||||||
|
|
||||||
|
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
|
||||||
|
account and enable that App ID for push notifications.
|
||||||
|
2. You'll also need to
|
||||||
|
[upload your APNs Provider Authentication Key or certificate to the
|
||||||
|
Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
|
||||||
|
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
|
||||||
|
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
|
||||||
|
|
||||||
|
#### iOS Simulator
|
||||||
|
|
||||||
|
The iOS Simulator cannot register for remote notifications and will not receive push notifications.
|
||||||
|
To receive push notifications, follow the steps above and run the app on a physical device.
|
||||||
|
|
||||||
|
## Building with Firebase on Apple platforms
|
||||||
|
|
||||||
|
Firebase provides official beta support for macOS, Catalyst, and tvOS. visionOS and watchOS
|
||||||
|
are community supported. Thanks to community contributions for many of the multi-platform PRs.
|
||||||
|
|
||||||
|
At this time, most of Firebase's products are available across Apple platforms. There are still
|
||||||
|
a few gaps, especially on visionOS and watchOS. For details about the current support matrix, see
|
||||||
|
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
|
||||||
|
in Firebase's documentation.
|
||||||
|
|
||||||
|
### visionOS
|
||||||
|
|
||||||
|
Where supported, visionOS works as expected with the exception of Firestore via Swift Package
|
||||||
|
Manager where it is required to use the source distribution.
|
||||||
|
|
||||||
|
To enable the Firestore source distribution, quit Xcode and open the desired
|
||||||
|
project from the command line with the `FIREBASE_SOURCE_FIRESTORE` environment
|
||||||
|
variable: `open --env FIREBASE_SOURCE_FIRESTORE /path/to/project.xcodeproj`.
|
||||||
|
To go back to using the binary distribution of Firestore, quit Xcode and open
|
||||||
|
Xcode like normal, without the environment variable.
|
||||||
|
|
||||||
|
### watchOS
|
||||||
|
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
|
||||||
|
work on watchOS. See the [Independent Watch App Sample](Example/watchOSSample).
|
||||||
|
|
||||||
|
Keep in mind that watchOS is not officially supported by Firebase. While we can catch basic unit
|
||||||
|
test issues with GitHub Actions, there may be some changes where the SDK no longer works as expected
|
||||||
|
on watchOS. If you encounter this, please
|
||||||
|
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
|
||||||
|
|
||||||
|
During app setup in the console, you may get to a step that mentions something like "Checking if the
|
||||||
|
app has communicated with our servers". This relies on Analytics and will not work on watchOS.
|
||||||
|
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
|
||||||
|
|
||||||
|
#### Additional Crashlytics Notes
|
||||||
|
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
|
||||||
|
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
|
||||||
|
|
||||||
|
## Combine
|
||||||
|
Thanks to contributions from the community, _FirebaseCombineSwift_ contains support for Apple's Combine
|
||||||
|
framework. This module is currently under development and not yet supported for use in production
|
||||||
|
environments. For more details, please refer to the [docs](FirebaseCombineSwift/README.md).
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
See [Roadmap](ROADMAP.md) for more about the Firebase Apple SDK Open Source
|
||||||
|
plans and directions.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
|
||||||
|
Apple SDK.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The contents of this repository are licensed under the
|
||||||
|
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
|
||||||
|
Your use of Firebase is governed by the
|
||||||
|
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
|
||||||
96
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseABTesting/Sources/Private/ABTExperimentPayload.h
generated
Normal file
96
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseABTesting/Sources/Private/ABTExperimentPayload.h
generated
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// Policy for handling the case where there's an overflow of experiments for an installation
|
||||||
|
/// instance.
|
||||||
|
typedef NS_ENUM(int32_t, ABTExperimentPayloadExperimentOverflowPolicy) {
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyUnrecognizedValue = 999,
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyUnspecified = 0,
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest = 1,
|
||||||
|
ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface ABTExperimentLite : NSObject
|
||||||
|
@property(nonatomic, readonly, copy) NSString *experimentId;
|
||||||
|
|
||||||
|
- (instancetype)initWithExperimentId:(NSString *)experimentId NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface ABTExperimentPayload : NSObject
|
||||||
|
|
||||||
|
/// Unique identifier for this experiment.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *experimentId;
|
||||||
|
|
||||||
|
/// Unique identifier for the variant to which an installation instance has been assigned.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *variantId;
|
||||||
|
|
||||||
|
/// Epoch time that represents when the experiment was started.
|
||||||
|
@property(nonatomic, readonly) int64_t experimentStartTimeMillis;
|
||||||
|
|
||||||
|
/// The event that triggers this experiment into ON state.
|
||||||
|
@property(nonatomic, nullable, readonly, copy) NSString *triggerEvent;
|
||||||
|
|
||||||
|
/// Duration in milliseconds for which the experiment can stay in STANDBY state (un-triggered).
|
||||||
|
@property(nonatomic, readonly) int64_t triggerTimeoutMillis;
|
||||||
|
|
||||||
|
/// Duration in milliseconds for which the experiment can stay in ON state (triggered).
|
||||||
|
@property(nonatomic, readonly) int64_t timeToLiveMillis;
|
||||||
|
|
||||||
|
/// The event logged when impact service sets the experiment.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *setEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment goes to the ON state.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *activateEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment is cleared.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *clearEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment times out after `triggerTimeoutMillis` milliseconds.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *timeoutEventToLog;
|
||||||
|
|
||||||
|
/// The event logged when an experiment times out after `timeToLiveMillis` milliseconds.
|
||||||
|
@property(nonatomic, readonly, copy) NSString *ttlExpiryEventToLog;
|
||||||
|
|
||||||
|
@property(nonatomic, readonly) ABTExperimentPayloadExperimentOverflowPolicy overflowPolicy;
|
||||||
|
|
||||||
|
/// A list of all other ongoing (started, and not yet stopped) experiments at the time this
|
||||||
|
/// experiment was started. Does not include this experiment; only the others.
|
||||||
|
@property(nonatomic, readonly) NSArray<ABTExperimentLite *> *ongoingExperiments;
|
||||||
|
|
||||||
|
/// Parses an ABTExperimentPayload directly from JSON data.
|
||||||
|
/// @param data JSON object as NSData. Must be reconstructible as an NSDictionary<NSString* , id>.
|
||||||
|
+ (nullable instancetype)parseFromData:(NSData *)data;
|
||||||
|
|
||||||
|
/// Initializes an ABTExperimentPayload from a dictionary with experiment metadata.
|
||||||
|
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dictionary
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/// Clears the trigger event associated with this payload.
|
||||||
|
- (void)clearTriggerEvent;
|
||||||
|
|
||||||
|
/// Checks if the overflow policy is a valid enum object.
|
||||||
|
- (BOOL)overflowPolicyIsValid;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
20
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseABTesting/Sources/Private/FirebaseABTestingInternal.h
generated
Normal file
20
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseABTesting/Sources/Private/FirebaseABTestingInternal.h
generated
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#import <FirebaseABTesting/FirebaseABTesting.h>
|
||||||
|
|
||||||
|
#import "FirebaseABTesting/Sources/Private/ABTExperimentPayload.h"
|
||||||
159
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRAppInternal.h
generated
Normal file
159
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRAppInternal.h
generated
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FirebaseCore/FIRApp.h>
|
||||||
|
|
||||||
|
@class FIRComponentContainer;
|
||||||
|
@class FIRHeartbeatLogger;
|
||||||
|
@protocol FIRLibrary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal interface to `FirebaseApp`. This is meant for first-party integrators, who need to
|
||||||
|
* receive `FirebaseApp` notifications, log info about the success or failure of their
|
||||||
|
* configuration, and access other internal functionality of `FirebaseApp`.
|
||||||
|
*/
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, FIRConfigType) {
|
||||||
|
FIRConfigTypeCore = 1,
|
||||||
|
FIRConfigTypeSDK = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern NSString *const kFIRDefaultAppName;
|
||||||
|
extern NSString *const kFIRAppReadyToConfigureSDKNotification;
|
||||||
|
extern NSString *const kFIRAppDeleteNotification;
|
||||||
|
extern NSString *const kFIRAppIsDefaultAppKey;
|
||||||
|
extern NSString *const kFIRAppNameKey;
|
||||||
|
extern NSString *const kFIRGoogleAppIDKey;
|
||||||
|
extern NSString *const kFirebaseCoreErrorDomain;
|
||||||
|
|
||||||
|
/** The `UserDefaults` suite name for `FirebaseCore`, for those storage locations that use it. */
|
||||||
|
extern NSString *const kFirebaseCoreDefaultsSuiteName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format string for the `UserDefaults` key used for storing the data collection enabled flag.
|
||||||
|
* This includes formatting to append the `FirebaseApp`'s name.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The plist key used for storing the data collection enabled flag.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotification
|
||||||
|
@brief The name of the @c NotificationCenter notification which is posted when the auth state
|
||||||
|
changes (e.g. a new token has been produced, a user logs in or out). The object parameter of
|
||||||
|
the notification is a dictionary possibly containing the key:
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not
|
||||||
|
contain this key it indicates a sign-out event took place.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotification;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotificationTokenKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the new access token.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotificationAppKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the FirebaseApp associated with the auth instance.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey;
|
||||||
|
|
||||||
|
/** @var FirebaseAuthStateDidChangeInternalNotificationUIDKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FirebaseAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the new user's UID (or nil if there is no longer a user signed in).
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
|
||||||
|
|
||||||
|
@interface FIRApp ()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag indicating if this is the default app (has the default app name).
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isDefaultApp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The container of interop SDKs for this app.
|
||||||
|
*/
|
||||||
|
@property(nonatomic) FIRComponentContainer *container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The heartbeat logger associated with this app.
|
||||||
|
*
|
||||||
|
* Firebase apps have a 1:1 relationship with heartbeat loggers.
|
||||||
|
*/
|
||||||
|
@property(readonly) FIRHeartbeatLogger *heartbeatLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the default app is configured without trying to configure it.
|
||||||
|
*/
|
||||||
|
+ (BOOL)isDefaultAppConfigured;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given third-party library with the given version number to be reported for
|
||||||
|
* analytics.
|
||||||
|
*
|
||||||
|
* @param name Name of the library.
|
||||||
|
* @param version Version of the library.
|
||||||
|
*/
|
||||||
|
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given internal library to be reported for analytics.
|
||||||
|
*
|
||||||
|
* @param library Optional parameter for component registration.
|
||||||
|
* @param name Name of the library.
|
||||||
|
*/
|
||||||
|
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
|
||||||
|
withName:(nonnull NSString *)name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given internal library with the given version number to be reported for
|
||||||
|
* analytics. This should only be used for non-Firebase libraries that have their own versioning
|
||||||
|
* scheme.
|
||||||
|
*
|
||||||
|
* @param library Optional parameter for component registration.
|
||||||
|
* @param name Name of the library.
|
||||||
|
* @param version Version of the library.
|
||||||
|
*/
|
||||||
|
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
|
||||||
|
withName:(nonnull NSString *)name
|
||||||
|
withVersion:(nonnull NSString *)version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A concatenated string representing all the third-party libraries and version numbers.
|
||||||
|
*/
|
||||||
|
+ (NSString *)firebaseUserAgent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used by the unit tests in each SDK to reset `FirebaseApp`. This method is thread unsafe.
|
||||||
|
*/
|
||||||
|
+ (void)resetApps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used by the unit tests in each SDK to set customized options.
|
||||||
|
*/
|
||||||
|
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
91
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRComponent.h
generated
Normal file
91
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRComponent.h
generated
Normal file
@ -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
|
||||||
45
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRComponentContainer.h
generated
Normal file
45
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRComponentContainer.h
generated
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// A type-safe macro to retrieve a component from a container. This should be used to retrieve
|
||||||
|
/// components instead of using the container directly.
|
||||||
|
#define FIR_COMPONENT(type, container) \
|
||||||
|
[FIRComponentType<id<type>> instanceForProtocol:@protocol(type) inContainer:container]
|
||||||
|
|
||||||
|
@class FIRApp;
|
||||||
|
|
||||||
|
/// A container that holds different components that are registered via the
|
||||||
|
/// `registerAsComponentRegistrant` call. These classes should conform to `ComponentRegistrant`
|
||||||
|
/// in order to properly register components for Core.
|
||||||
|
NS_SWIFT_NAME(FirebaseComponentContainer)
|
||||||
|
@interface FIRComponentContainer : NSObject
|
||||||
|
|
||||||
|
/// A weak reference to the app that an instance of the container belongs to.
|
||||||
|
@property(nonatomic, weak, readonly) FIRApp *app;
|
||||||
|
|
||||||
|
// TODO: See if we can get improved type safety here.
|
||||||
|
/// A Swift only API for fetching an instance since the top macro isn't available.
|
||||||
|
- (nullable id)__instanceForProtocol:(Protocol *)protocol NS_SWIFT_NAME(instance(for:));
|
||||||
|
|
||||||
|
/// Unavailable. Use the `container` property on `FirebaseApp`.
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
35
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRComponentType.h
generated
Normal file
35
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRComponentType.h
generated
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class FIRComponentContainer;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// Do not use directly. A placeholder type in order to provide a macro that will warn users of
|
||||||
|
/// mis-matched protocols.
|
||||||
|
NS_SWIFT_NAME(ComponentType)
|
||||||
|
@interface FIRComponentType<__covariant T> : NSObject
|
||||||
|
|
||||||
|
/// Do not use directly. A factory method to retrieve an instance that provides a specific
|
||||||
|
/// functionality.
|
||||||
|
+ (nullable T)instanceForProtocol:(Protocol *)protocol
|
||||||
|
inContainer:(FIRComponentContainer *)container;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
45
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRDependency.h
generated
Normal file
45
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRDependency.h
generated
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// A 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 `init(protocol:isRequired:)` with true 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 `init(withProtocol:isRequired:)` instead.
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
90
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRHeartbeatLogger.h
generated
Normal file
90
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRHeartbeatLogger.h
generated
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2021 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
@class FIRHeartbeatsPayload;
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// Enum representing different daily heartbeat codes.
|
||||||
|
/// This enum is only used by clients using platform logging V1. This is because
|
||||||
|
/// the V1 payload only supports a single daily heartbeat.
|
||||||
|
typedef NS_ENUM(NSInteger, FIRDailyHeartbeatCode) {
|
||||||
|
/// Represents the absence of a daily heartbeat.
|
||||||
|
FIRDailyHeartbeatCodeNone = 0,
|
||||||
|
/// Represents the presence of a daily heartbeat.
|
||||||
|
FIRDailyHeartbeatCodeSome = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
@protocol FIRHeartbeatLoggerProtocol <NSObject>
|
||||||
|
|
||||||
|
/// Asynchronously logs a heartbeat.
|
||||||
|
- (void)log;
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
/// Flushes heartbeats from storage into a structured payload of heartbeats.
|
||||||
|
- (FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload;
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// Gets the heartbeat code for today.
|
||||||
|
- (FIRDailyHeartbeatCode)heartbeatCodeForToday;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
/// Returns a nullable string header value from a given heartbeats payload.
|
||||||
|
///
|
||||||
|
/// This API returns `nil` when the given heartbeats payload is considered empty.
|
||||||
|
///
|
||||||
|
/// @param heartbeatsPayload The heartbeats payload.
|
||||||
|
NSString *_Nullable FIRHeaderValueFromHeartbeatsPayload(FIRHeartbeatsPayload *heartbeatsPayload);
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// A thread safe, synchronized object that logs and flushes platform logging info.
|
||||||
|
@interface FIRHeartbeatLogger : NSObject <FIRHeartbeatLoggerProtocol>
|
||||||
|
|
||||||
|
/// Designated initializer.
|
||||||
|
///
|
||||||
|
/// @param appID The app ID that this heartbeat logger corresponds to.
|
||||||
|
- (instancetype)initWithAppID:(NSString *)appID;
|
||||||
|
|
||||||
|
/// Asynchronously logs a new heartbeat corresponding to the Firebase User Agent, if needed.
|
||||||
|
///
|
||||||
|
/// @note This API is thread-safe.
|
||||||
|
- (void)log;
|
||||||
|
|
||||||
|
#ifndef FIREBASE_BUILD_CMAKE
|
||||||
|
/// Flushes heartbeats from storage into a structured payload of heartbeats.
|
||||||
|
///
|
||||||
|
/// This API is for clients using platform logging V2.
|
||||||
|
///
|
||||||
|
/// @note This API is thread-safe.
|
||||||
|
/// @return A payload of heartbeats.
|
||||||
|
- (FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload;
|
||||||
|
#endif // FIREBASE_BUILD_CMAKE
|
||||||
|
|
||||||
|
/// Gets today's corresponding heartbeat code.
|
||||||
|
///
|
||||||
|
/// This API is for clients using platform logging V1.
|
||||||
|
///
|
||||||
|
/// @note This API is thread-safe.
|
||||||
|
/// @return Heartbeat code indicating whether or not there is an unsent global heartbeat.
|
||||||
|
- (FIRDailyHeartbeatCode)heartbeatCodeForToday;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
44
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRLibrary.h
generated
Normal file
44
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRLibrary.h
generated
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FIRLibrary_h
|
||||||
|
#define FIRLibrary_h
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class FIRApp;
|
||||||
|
@class FIRComponent;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// Provide an interface to register a library for userAgent logging and availability to others.
|
||||||
|
NS_SWIFT_NAME(Library)
|
||||||
|
@protocol FIRLibrary
|
||||||
|
|
||||||
|
/// Returns one or more Components that will be registered in
|
||||||
|
/// FirebaseApp and participate in dependency resolution and injection.
|
||||||
|
+ (NSArray<FIRComponent *> *)componentsToRegister;
|
||||||
|
|
||||||
|
@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 */
|
||||||
193
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRLogger.h
generated
Normal file
193
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIRLogger.h
generated
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import <FirebaseCore/FIRLoggerLevel.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Firebase services used in Firebase logger.
|
||||||
|
*/
|
||||||
|
typedef NSString *const FIRLoggerService;
|
||||||
|
|
||||||
|
extern FIRLoggerService kFIRLoggerAnalytics;
|
||||||
|
extern FIRLoggerService kFIRLoggerCrash;
|
||||||
|
extern FIRLoggerService kFIRLoggerCore;
|
||||||
|
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 true, the logging level for Analytics will be set to FirebaseLoggerLevelDebug.
|
||||||
|
* Enabling the debug mode has no effect if the app is running from App Store.
|
||||||
|
* (required) analytics debug mode flag.
|
||||||
|
*/
|
||||||
|
void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current FIRLoggerLevel.
|
||||||
|
*/
|
||||||
|
FIRLoggerLevel FIRGetLoggerLevel(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the default logging level of FirebaseLoggerLevelNotice to a user-specified level.
|
||||||
|
* The default level cannot be set above FirebaseLoggerLevelNotice if the app is running from App
|
||||||
|
* Store. (required) log level (one of the FirebaseLoggerLevel enum values).
|
||||||
|
*/
|
||||||
|
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified logger level is loggable given the current settings.
|
||||||
|
* (required) log level (one of the FirebaseLoggerLevel enum values).
|
||||||
|
* (required) whether or not this function is called from the Analytics component.
|
||||||
|
*/
|
||||||
|
BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message to the Xcode console and the device log. If running from AppStore, will
|
||||||
|
* not log any messages with a level higher than FirebaseLoggerLevelNotice to avoid log spamming.
|
||||||
|
* (required) log level (one of the FirebaseLoggerLevel enum values).
|
||||||
|
* (required) service name of type FirebaseLoggerService.
|
||||||
|
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
* three-character service identifier and a six digit integer message ID that is unique
|
||||||
|
* within the service.
|
||||||
|
* An example of the message code is @"I-COR000001".
|
||||||
|
* (required) message string which can be a format string.
|
||||||
|
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
extern void FIRLogBasic(FIRLoggerLevel level,
|
||||||
|
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 FirebaseLoggerService.
|
||||||
|
* (required) message code starting from "I-" which means iOS, followed by a capitalized
|
||||||
|
* three-character service identifier and a six digit integer message ID that is unique
|
||||||
|
* within the service.
|
||||||
|
* An example of the message code is @"I-COR000001".
|
||||||
|
* See go/firebase-log-proposal for details.
|
||||||
|
* (required) message string which can be a format string.
|
||||||
|
* (optional) the list of arguments to substitute into the format string.
|
||||||
|
* Example usage:
|
||||||
|
* FirebaseLogError(kFirebaseLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
|
||||||
|
*/
|
||||||
|
extern void FIRLogError(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);
|
||||||
|
|
||||||
|
// TODO: Come up with a better logging scheme for Swift.
|
||||||
|
/**
|
||||||
|
* Logs a debug message to the Xcode console and the device log. If running from AppStore, will
|
||||||
|
* not log any messages with a level higher than FirebaseLoggerLevelNotice to avoid log spamming.
|
||||||
|
* This function is intended to be used by Swift clients that do not support variadic parameters.
|
||||||
|
*
|
||||||
|
* @param service The service name of type `FirebaseLoggerService`.
|
||||||
|
* @param messageCode The mesage 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".
|
||||||
|
* @param message The message string.
|
||||||
|
*/
|
||||||
|
extern void FIRLogDebugSwift(FIRLoggerService service, NSString *messageCode, NSString *message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a warning message to the Xcode console and the device log. If running from AppStore, will
|
||||||
|
* not log any messages with a level higher than FirebaseLoggerLevelNotice to avoid log spamming.
|
||||||
|
* This function is intended to be used by Swift clients that do not support variadic parameters.
|
||||||
|
*
|
||||||
|
* @param service The service name of type `FirebaseLoggerService`.
|
||||||
|
* @param messageCode The mesage 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".
|
||||||
|
* @param message The message string.
|
||||||
|
*/
|
||||||
|
extern void FIRLogWarningSwift(FIRLoggerService service, NSString *messageCode, NSString *message);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
NS_SWIFT_NAME(FirebaseLogger)
|
||||||
|
@interface FIRLoggerWrapper : NSObject
|
||||||
|
|
||||||
|
/// Logs a given message at a given log level. This API is effectively a wrapper for the
|
||||||
|
/// `FIRLogBasic` C API.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - level: The log level to use (defined by `FirebaseLoggerLevel` enum values).
|
||||||
|
/// - service: The service name of type `FirebaseLoggerService`.
|
||||||
|
/// - code: The mesage code. Starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
/// three-character service identifier and a six digit integer message ID that is unique within
|
||||||
|
/// the service. An example of the message code is @"I-COR000001".
|
||||||
|
/// - message: Formatted string to be used as the log's message.
|
||||||
|
/// - args: 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;
|
||||||
|
|
||||||
|
/// Logs a given message at a given log level.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - level: The log level to use (defined by `FirebaseLoggerLevel` enum values).
|
||||||
|
/// - service: The service name of type `FirebaseLoggerService`.
|
||||||
|
/// - code: The mesage code. Starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
/// three-character service identifier and a six digit integer message ID that is unique within
|
||||||
|
/// the service. An example of the message code is @"I-COR000001".
|
||||||
|
/// - message: Formatted string to be used as the log's message.
|
||||||
|
/// - args: Arguments list obtained from calling `va_start`, used when message is a format string.
|
||||||
|
+ (void)logWithLevel:(FIRLoggerLevel)level
|
||||||
|
service:(FIRLoggerService)service
|
||||||
|
code:(NSString *)code
|
||||||
|
message:(NSString *)message
|
||||||
|
__attribute__((__swift_name__("log(level:service:code:message:)")));
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
106
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIROptionsInternal.h
generated
Normal file
106
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FIROptionsInternal.h
generated
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FirebaseCore/FIROptions.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys for the strings in the plist file.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRAPIKey;
|
||||||
|
extern NSString *const kFIRTrackingID;
|
||||||
|
extern NSString *const kFIRGoogleAppID;
|
||||||
|
extern NSString *const kFIRClientID;
|
||||||
|
extern NSString *const kFIRGCMSenderID;
|
||||||
|
extern NSString *const kFIRAndroidClientID;
|
||||||
|
extern NSString *const kFIRDatabaseURL;
|
||||||
|
extern NSString *const kFIRStorageBucket;
|
||||||
|
extern NSString *const kFIRBundleID;
|
||||||
|
extern NSString *const kFIRProjectID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys for the plist file name
|
||||||
|
*/
|
||||||
|
extern NSString *const kServiceInfoFileName;
|
||||||
|
|
||||||
|
extern NSString *const kServiceInfoFileType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This header file exposes the initialization of FirebaseOptions to internal use.
|
||||||
|
*/
|
||||||
|
@interface FIROptions ()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `resetDefaultOptions` and `initInternalWithOptionsDictionary` are exposed only for unit tests.
|
||||||
|
*/
|
||||||
|
+ (void)resetDefaultOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the options with dictionary. The above strings are the keys of the dictionary.
|
||||||
|
* This is the designated initializer.
|
||||||
|
*/
|
||||||
|
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `defaultOptions` and `defaultOptionsDictionary` are exposed in order to be used in FirebaseApp
|
||||||
|
* and other first party services.
|
||||||
|
*/
|
||||||
|
+ (FIROptions *)defaultOptions;
|
||||||
|
|
||||||
|
+ (NSDictionary *)defaultOptionsDictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at
|
||||||
|
* runtime.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionExplicitlySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless
|
||||||
|
* explicitly disabled in GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Analytics Collection was completely disabled. If true, then
|
||||||
|
* isAnalyticsCollectionEnabled will be false.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version ID of the client library, e.g. @"1100000".
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly, copy) NSString *libraryVersionID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag indicating whether this object was constructed with the values in the default plist
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
@property(nonatomic) BOOL usingOptionsFromDefaultPlist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
|
||||||
|
* GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isMeasurementEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not editing is locked. This should occur after `FirebaseOptions` has been set on a
|
||||||
|
* `FirebaseApp`.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
|
||||||
|
|
||||||
|
@end
|
||||||
25
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FirebaseCoreInternal.h
generated
Normal file
25
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseCore/Extension/FirebaseCoreInternal.h
generated
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
@import FirebaseCore;
|
||||||
|
|
||||||
|
#import "FIRAppInternal.h"
|
||||||
|
#import "FIRComponent.h"
|
||||||
|
#import "FIRComponentContainer.h"
|
||||||
|
#import "FIRComponentType.h"
|
||||||
|
#import "FIRDependency.h"
|
||||||
|
#import "FIRHeartbeatLogger.h"
|
||||||
|
#import "FIRLibrary.h"
|
||||||
|
#import "FIRLogger.h"
|
||||||
|
#import "FIROptionsInternal.h"
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#import <FirebaseInstallations/FirebaseInstallations.h>
|
||||||
91
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRConfigValue.m
generated
Normal file
91
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRConfigValue.m
generated
Normal file
@ -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/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
|
||||||
|
|
||||||
|
#import "FirebaseCore/Extension/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
|
||||||
698
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m
generated
Normal file
698
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m
generated
Normal file
@ -0,0 +1,698 @@
|
|||||||
|
/*
|
||||||
|
* 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/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
|
||||||
|
|
||||||
|
#import "FirebaseABTesting/Sources/Private/FirebaseABTestingInternal.h"
|
||||||
|
#import "FirebaseCore/Extension/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/RCNConfigRealtime.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNDevice.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNPersonalization.h"
|
||||||
|
|
||||||
|
/// Remote Config Error Domain.
|
||||||
|
/// TODO: Rename according to obj-c style for constants.
|
||||||
|
NSString *const FIRRemoteConfigErrorDomain = @"com.google.remoteconfig.ErrorDomain";
|
||||||
|
// Remote Config Realtime Error Domain
|
||||||
|
NSString *const FIRRemoteConfigUpdateErrorDomain = @"com.google.remoteconfig.update.ErrorDomain";
|
||||||
|
/// Remote Config Error Info End Time Seconds;
|
||||||
|
NSString *const FIRRemoteConfigThrottledEndTimeInSecondsKey = @"error_throttled_end_time_seconds";
|
||||||
|
/// 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";
|
||||||
|
/// Notification when config is successfully activated
|
||||||
|
const NSNotificationName FIRRemoteConfigActivateNotification =
|
||||||
|
@"FIRRemoteConfigActivateNotification";
|
||||||
|
static NSNotificationName FIRRolloutsStateDidChangeNotificationName =
|
||||||
|
@"FIRRolloutsStateDidChangeNotification";
|
||||||
|
|
||||||
|
/// Listener for the get methods.
|
||||||
|
typedef void (^FIRRemoteConfigListener)(NSString *_Nonnull, NSDictionary *_Nonnull);
|
||||||
|
|
||||||
|
@implementation FIRRemoteConfigSettings
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_minimumFetchInterval = RCNDefaultMinimumFetchInterval;
|
||||||
|
_fetchTimeout = RCNHTTPDefaultConnectionTimeout;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FIRRemoteConfig {
|
||||||
|
/// All the config content.
|
||||||
|
RCNConfigContent *_configContent;
|
||||||
|
RCNConfigDBManager *_DBManager;
|
||||||
|
RCNConfigSettings *_settings;
|
||||||
|
RCNConfigFetch *_configFetch;
|
||||||
|
RCNConfigExperiment *_configExperiment;
|
||||||
|
RCNConfigRealtime *_configRealtime;
|
||||||
|
dispatch_queue_t _queue;
|
||||||
|
NSString *_appName;
|
||||||
|
NSMutableArray *_listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSMutableDictionary<NSString *, NSMutableDictionary<NSString *, FIRRemoteConfig *> *>
|
||||||
|
*RCInstances;
|
||||||
|
|
||||||
|
+ (nonnull FIRRemoteConfig *)remoteConfigWithApp:(FIRApp *_Nonnull)firebaseApp {
|
||||||
|
return [FIRRemoteConfig
|
||||||
|
remoteConfigWithFIRNamespace:FIRRemoteConfigConstants.FIRNamespaceGoogleMobilePlatform
|
||||||
|
app:firebaseApp];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nonnull FIRRemoteConfig *)remoteConfigWithFIRNamespace:(NSString *_Nonnull)firebaseNamespace {
|
||||||
|
if (![FIRApp isDefaultAppConfigured]) {
|
||||||
|
[NSException raise:@"FIRAppNotConfigured"
|
||||||
|
format:@"The default `FirebaseApp` instance must be configured before the "
|
||||||
|
@"default Remote Config instance can be initialized. One way to ensure this "
|
||||||
|
@"is to call `FirebaseApp.configure()` in the App Delegate's "
|
||||||
|
@"`application(_:didFinishLaunchingWithOptions:)` or the `@main` struct's "
|
||||||
|
@"initializer in SwiftUI."];
|
||||||
|
}
|
||||||
|
|
||||||
|
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]) {
|
||||||
|
[NSException raise:@"FIRAppNotConfigured"
|
||||||
|
format:@"The default `FirebaseApp` instance must be configured before the "
|
||||||
|
@"default Remote Config instance can be initialized. One way to ensure this "
|
||||||
|
@"is to call `FirebaseApp.configure()` in the App Delegate's "
|
||||||
|
@"`application(_:didFinishLaunchingWithOptions:)` or the `@main` struct's "
|
||||||
|
@"initializer in SwiftUI."];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [FIRRemoteConfig
|
||||||
|
remoteConfigWithFIRNamespace:FIRRemoteConfigConstants.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];
|
||||||
|
|
||||||
|
_configRealtime = [[RCNConfigRealtime alloc] init:_configFetch
|
||||||
|
settings:_settings
|
||||||
|
namespace:_FIRNamespace
|
||||||
|
options:options];
|
||||||
|
|
||||||
|
[_settings loadConfigFromMetadataTable];
|
||||||
|
|
||||||
|
if (analytics) {
|
||||||
|
_listeners = [[NSMutableArray alloc] init];
|
||||||
|
RCNPersonalization *personalization =
|
||||||
|
[[RCNPersonalization alloc] initWithAnalytics:analytics];
|
||||||
|
[self addListener:^(NSString *key, NSDictionary *config) {
|
||||||
|
[personalization logArmActive:key config:config];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a listener that will be called whenever one of the get methods is called.
|
||||||
|
/// @param listener Function that takes in the parameter key and the config.
|
||||||
|
- (void)addListener:(nonnull FIRRemoteConfigListener)listener {
|
||||||
|
@synchronized(_listeners) {
|
||||||
|
[_listeners addObject:listener];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)callListeners:(NSString *)key config:(NSDictionary *)config {
|
||||||
|
@synchronized(_listeners) {
|
||||||
|
for (FIRRemoteConfigListener listener in _listeners) {
|
||||||
|
dispatch_async(_queue, ^{
|
||||||
|
listener(key, config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 activateWithCompletion:^(BOOL changed, NSError *_Nullable activateError) {
|
||||||
|
if (completionHandler) {
|
||||||
|
FIRRemoteConfigFetchAndActivateStatus status =
|
||||||
|
activateError ? FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData
|
||||||
|
: FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote;
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
completionHandler(status, nil);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
} else if (completionHandler) {
|
||||||
|
FIRRemoteConfigFetchAndActivateStatus status =
|
||||||
|
fetchStatus == FIRRemoteConfigFetchStatusSuccess
|
||||||
|
? FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData
|
||||||
|
: FIRRemoteConfigFetchAndActivateStatusError;
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
completionHandler(status, fetchError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
[self fetchWithCompletionHandler:fetchCompletion];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - activate
|
||||||
|
|
||||||
|
typedef void (^FIRRemoteConfigActivateChangeCompletion)(BOOL changed, NSError *_Nullable error);
|
||||||
|
|
||||||
|
- (void)activateWithCompletion:(FIRRemoteConfigActivateChangeCompletion)completion {
|
||||||
|
__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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[strongSelf->_configContent copyFromDictionary:self->_configContent.fetchedConfig
|
||||||
|
toSource:RCNDBSourceActive
|
||||||
|
forNamespace:self->_FIRNamespace];
|
||||||
|
strongSelf->_settings.lastApplyTimeInterval = [[NSDate date] timeIntervalSince1970];
|
||||||
|
// New config has been activated at this point
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000069", @"Config activated.");
|
||||||
|
[strongSelf->_configContent activatePersonalization];
|
||||||
|
// Update last active template version number in setting and userDefaults.
|
||||||
|
[strongSelf->_settings updateLastActiveTemplateVersion];
|
||||||
|
// Update activeRolloutMetadata
|
||||||
|
[strongSelf->_configContent activateRolloutMetadata:^(BOOL success) {
|
||||||
|
if (success) {
|
||||||
|
[self notifyRolloutsStateChange:strongSelf->_configContent.activeRolloutMetadata
|
||||||
|
versionNumber:strongSelf->_settings.lastActiveTemplateVersion];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Update experiments only for 3p namespace
|
||||||
|
NSString *namespace = [strongSelf->_FIRNamespace
|
||||||
|
substringToIndex:[strongSelf->_FIRNamespace rangeOfString:@":"].location];
|
||||||
|
if ([namespace isEqualToString:FIRRemoteConfigConstants.FIRNamespaceGoogleMobilePlatform]) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self notifyConfigHasActivated];
|
||||||
|
});
|
||||||
|
[strongSelf->_configExperiment updateExperimentsWithHandler:^(NSError *_Nullable error) {
|
||||||
|
if (completion) {
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
completion(YES, nil);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
if (completion) {
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
completion(YES, nil);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dispatch_async(_queue, applyBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)notifyConfigHasActivated {
|
||||||
|
// Need a valid google app name.
|
||||||
|
if (!_appName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The Remote Config Swift SDK will be listening for this notification so it can tell SwiftUI to
|
||||||
|
// update the UI.
|
||||||
|
NSDictionary *appInfoDict = @{kFIRAppNameKey : _appName};
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:FIRRemoteConfigActivateNotification
|
||||||
|
object:self
|
||||||
|
userInfo:appInfoDict];
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRRemoteConfigValue *)defaultValueForFullyQualifiedNamespace:(NSString *)namespace
|
||||||
|
key:(NSString *)key {
|
||||||
|
FIRRemoteConfigValue *value = self->_configContent.defaultConfig[namespace][key];
|
||||||
|
if (!value) {
|
||||||
|
value = [[FIRRemoteConfigValue alloc]
|
||||||
|
initWithData:[NSData data]
|
||||||
|
source:(FIRRemoteConfigSource)FIRRemoteConfigSourceStatic];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Get Config Result
|
||||||
|
|
||||||
|
- (FIRRemoteConfigValue *)objectForKeyedSubscript:(NSString *)key {
|
||||||
|
return [self configValueForKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRRemoteConfigValue *)configValueForKey:(NSString *)key {
|
||||||
|
if (!key) {
|
||||||
|
return [[FIRRemoteConfigValue alloc] initWithData:[NSData data]
|
||||||
|
source:FIRRemoteConfigSourceStatic];
|
||||||
|
}
|
||||||
|
NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
|
||||||
|
__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);
|
||||||
|
}
|
||||||
|
[self callListeners:key
|
||||||
|
config:[self->_configContent getConfigAndMetadataForNamespace:FQNamespace]];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value = [self defaultValueForFullyQualifiedNamespace:FQNamespace key:key];
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRRemoteConfigValue *)configValueForKey:(NSString *)key source:(FIRRemoteConfigSource)source {
|
||||||
|
if (!key) {
|
||||||
|
return [[FIRRemoteConfigValue alloc] initWithData:[NSData data]
|
||||||
|
source:FIRRemoteConfigSourceStatic];
|
||||||
|
}
|
||||||
|
NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
|
||||||
|
|
||||||
|
__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
|
||||||
|
|
||||||
|
/// Last fetch completion time.
|
||||||
|
- (NSDate *)lastFetchTime {
|
||||||
|
__block NSDate *fetchTime;
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
NSTimeInterval lastFetchTime = self->_settings.lastFetchTimeInterval;
|
||||||
|
fetchTime = [NSDate dateWithTimeIntervalSince1970:lastFetchTime];
|
||||||
|
});
|
||||||
|
return fetchTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRRemoteConfigFetchStatus)lastFetchStatus {
|
||||||
|
__block FIRRemoteConfigFetchStatus currentStatus;
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
currentStatus = self->_settings.lastFetchStatus;
|
||||||
|
});
|
||||||
|
return currentStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)allKeysFromSource:(FIRRemoteConfigSource)source {
|
||||||
|
__block NSArray *keys = [[NSArray alloc] init];
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
NSString *FQNamespace = [self fullyQualifiedNamespace:self->_FIRNamespace];
|
||||||
|
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 {
|
||||||
|
__block NSMutableSet *keys = [[NSMutableSet alloc] init];
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
NSString *FQNamespace = [self fullyQualifiedNamespace:self->_FIRNamespace];
|
||||||
|
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 *> *)defaultConfig {
|
||||||
|
NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
|
||||||
|
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 {
|
||||||
|
NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
|
||||||
|
__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 {
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000037",
|
||||||
|
@"The plist file '%@' could not be found by Remote Config.", fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - custom variables
|
||||||
|
|
||||||
|
- (FIRRemoteConfigSettings *)configSettings {
|
||||||
|
__block NSTimeInterval minimumFetchInterval = RCNDefaultMinimumFetchInterval;
|
||||||
|
__block NSTimeInterval fetchTimeout = RCNHTTPDefaultConnectionTimeout;
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
minimumFetchInterval = self->_settings.minimumFetchInterval;
|
||||||
|
fetchTimeout = self->_settings.fetchTimeout;
|
||||||
|
});
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000066",
|
||||||
|
@"Successfully read configSettings. Minimum Fetch Interval:%f, "
|
||||||
|
@"Fetch timeout: %f",
|
||||||
|
minimumFetchInterval, fetchTimeout);
|
||||||
|
FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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. Minimum Fetch Interval:%f, "
|
||||||
|
@"Fetch timeout:%f",
|
||||||
|
configSettings.minimumFetchInterval, configSettings.fetchTimeout);
|
||||||
|
};
|
||||||
|
dispatch_async(_queue, setConfigSettingsBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Realtime
|
||||||
|
|
||||||
|
- (FIRConfigUpdateListenerRegistration *)addOnConfigUpdateListener:
|
||||||
|
(void (^_Nonnull)(FIRRemoteConfigUpdate *update, NSError *_Nullable error))listener {
|
||||||
|
return [self->_configRealtime addConfigUpdateListener:listener];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Rollout
|
||||||
|
|
||||||
|
- (void)addRemoteConfigInteropSubscriber:(id<FIRRolloutsStateSubscriber>)subscriber {
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserverForName:FIRRolloutsStateDidChangeNotificationName
|
||||||
|
object:self
|
||||||
|
queue:nil
|
||||||
|
usingBlock:^(NSNotification *_Nonnull notification) {
|
||||||
|
FIRRolloutsState *rolloutsState =
|
||||||
|
notification.userInfo[FIRRolloutsStateDidChangeNotificationName];
|
||||||
|
[subscriber rolloutsStateDidChange:rolloutsState];
|
||||||
|
}];
|
||||||
|
// Send active rollout metadata stored in persistence while app launched if there is activeConfig
|
||||||
|
NSString *fullyQualifiedNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
|
||||||
|
NSDictionary<NSString *, NSDictionary *> *activeConfig = self->_configContent.activeConfig;
|
||||||
|
if (activeConfig[fullyQualifiedNamespace] && activeConfig[fullyQualifiedNamespace].count > 0) {
|
||||||
|
[self notifyRolloutsStateChange:self->_configContent.activeRolloutMetadata
|
||||||
|
versionNumber:self->_settings.lastActiveTemplateVersion];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)notifyRolloutsStateChange:(NSArray<NSDictionary *> *)rolloutMetadata
|
||||||
|
versionNumber:(NSString *)versionNumber {
|
||||||
|
NSArray<FIRRolloutAssignment *> *rolloutsAssignments =
|
||||||
|
[self rolloutsAssignmentsWith:rolloutMetadata versionNumber:versionNumber];
|
||||||
|
FIRRolloutsState *rolloutsState =
|
||||||
|
[[FIRRolloutsState alloc] initWithAssignmentList:rolloutsAssignments];
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000069",
|
||||||
|
@"Send rollouts state notification with name %@ to RemoteConfigInterop.",
|
||||||
|
FIRRolloutsStateDidChangeNotificationName);
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
postNotificationName:FIRRolloutsStateDidChangeNotificationName
|
||||||
|
object:self
|
||||||
|
userInfo:@{FIRRolloutsStateDidChangeNotificationName : rolloutsState}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<FIRRolloutAssignment *> *)rolloutsAssignmentsWith:
|
||||||
|
(NSArray<NSDictionary *> *)rolloutMetadata
|
||||||
|
versionNumber:(NSString *)versionNumber {
|
||||||
|
NSMutableArray<FIRRolloutAssignment *> *rolloutsAssignments = [[NSMutableArray alloc] init];
|
||||||
|
NSString *FQNamespace = [self fullyQualifiedNamespace:_FIRNamespace];
|
||||||
|
for (NSDictionary *metadata in rolloutMetadata) {
|
||||||
|
NSString *rolloutId = metadata[RCNFetchResponseKeyRolloutID];
|
||||||
|
NSString *variantID = metadata[RCNFetchResponseKeyVariantID];
|
||||||
|
NSArray<NSString *> *affectedParameterKeys = metadata[RCNFetchResponseKeyAffectedParameterKeys];
|
||||||
|
if (rolloutId && variantID && affectedParameterKeys) {
|
||||||
|
for (NSString *key in affectedParameterKeys) {
|
||||||
|
FIRRemoteConfigValue *value = self->_configContent.activeConfig[FQNamespace][key];
|
||||||
|
if (!value) {
|
||||||
|
value = [self defaultValueForFullyQualifiedNamespace:FQNamespace key:key];
|
||||||
|
}
|
||||||
|
FIRRolloutAssignment *assignment =
|
||||||
|
[[FIRRolloutAssignment alloc] initWithRolloutId:rolloutId
|
||||||
|
variantId:variantID
|
||||||
|
templateVersion:[versionNumber longLongValue]
|
||||||
|
parameterKey:key
|
||||||
|
parameterValue:value.stringValue];
|
||||||
|
[rolloutsAssignments addObject:assignment];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rolloutsAssignments;
|
||||||
|
}
|
||||||
|
@end
|
||||||
64
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h
generated
Normal file
64
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h
generated
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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/Extension/FirebaseCoreInternal.h"
|
||||||
|
@import FirebaseRemoteConfigInterop;
|
||||||
|
|
||||||
|
@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, FIRRemoteConfigInterop>
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// Clear all the component instances from the singleton which created previously, this is for
|
||||||
|
/// testing only
|
||||||
|
+ (void)clearAllComponentInstances;
|
||||||
|
|
||||||
|
/// 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
|
||||||
155
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m
generated
Normal file
155
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m
generated
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* 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/Extension/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"
|
||||||
|
|
||||||
|
@implementation FIRRemoteConfigComponent
|
||||||
|
|
||||||
|
// Because Component now need to register two protocols (provider and interop), we need a way to
|
||||||
|
// return the same component instance for both registered protocol, this singleton pattern allow us
|
||||||
|
// to return the same component object for both registration callback.
|
||||||
|
static NSMutableDictionary<NSString *, FIRRemoteConfigComponent *> *_componentInstances = nil;
|
||||||
|
|
||||||
|
+ (FIRRemoteConfigComponent *)getComponentForApp:(FIRApp *)app {
|
||||||
|
@synchronized(_componentInstances) {
|
||||||
|
// need to init the dictionary first
|
||||||
|
if (!_componentInstances) {
|
||||||
|
_componentInstances = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
if (![_componentInstances objectForKey:app.name]) {
|
||||||
|
_componentInstances[app.name] = [[self alloc] initWithApp:app];
|
||||||
|
}
|
||||||
|
return _componentInstances[app.name];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)clearAllComponentInstances {
|
||||||
|
@synchronized(_componentInstances) {
|
||||||
|
[_componentInstances removeAllObjects];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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";
|
||||||
|
} else if (options.projectID.length == 0) {
|
||||||
|
errorPropertyName = @"projectID";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorPropertyName) {
|
||||||
|
NSString *const kFirebaseConfigErrorDomain = @"com.firebase.config";
|
||||||
|
[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"];
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 getComponentForApp:container.app];
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Unlike provider needs to setup a hard dependency on remote config, interop allows an optional
|
||||||
|
// dependency on RC
|
||||||
|
FIRComponent *rcInterop = [FIRComponent
|
||||||
|
componentWithProtocol:@protocol(FIRRemoteConfigInterop)
|
||||||
|
instantiationTiming:FIRInstantiationTimingAlwaysEager
|
||||||
|
dependencies:@[]
|
||||||
|
creationBlock:^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
|
||||||
|
// Cache the component so instances of Remote Config are cached.
|
||||||
|
*isCacheable = YES;
|
||||||
|
return [FIRRemoteConfigComponent getComponentForApp:container.app];
|
||||||
|
}];
|
||||||
|
return @[ rcProvider, rcInterop ];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Remote Config Interop Protocol
|
||||||
|
|
||||||
|
- (void)registerRolloutsStateSubscriber:(id<FIRRolloutsStateSubscriber>)subscriber
|
||||||
|
for:(NSString * _Nonnull)namespace {
|
||||||
|
FIRRemoteConfig *instance = [self remoteConfigForNamespace:namespace];
|
||||||
|
[instance addRemoteConfigInteropSubscriber:subscriber];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
33
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigUpdate.m
generated
Normal file
33
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/FIRRemoteConfigUpdate.m
generated
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
|
||||||
|
|
||||||
|
@implementation FIRRemoteConfigUpdate {
|
||||||
|
NSSet<NSString *> *_updatedKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUpdatedKeys:(NSSet<NSString *> *)updatedKeys {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_updatedKeys = [updatedKeys copy];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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 "RCNConfigSettings.h" // This import is needed to expose settings for the Swift API tests.
|
||||||
|
|
||||||
|
@class FIROptions;
|
||||||
|
@class RCNConfigContent;
|
||||||
|
@class RCNConfigDBManager;
|
||||||
|
@class RCNConfigFetch;
|
||||||
|
@class RCNConfigRealtime;
|
||||||
|
@protocol FIRAnalyticsInterop;
|
||||||
|
@protocol FIRRolloutsStateSubscriber;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class RCNConfigSettings;
|
||||||
|
|
||||||
|
@interface FIRRemoteConfigUpdate ()
|
||||||
|
|
||||||
|
/// Designated initializer.
|
||||||
|
- (instancetype)initWithUpdatedKeys:(NSSet<NSString *> *)updatedKeys;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface FIRRemoteConfig () {
|
||||||
|
NSString *_FIRNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal settings
|
||||||
|
@property(nonatomic, readonly, strong) RCNConfigSettings *settings;
|
||||||
|
|
||||||
|
/// Config settings are custom settings.
|
||||||
|
@property(nonatomic, readwrite, strong, nonnull) RCNConfigFetch *configFetch;
|
||||||
|
|
||||||
|
@property(nonatomic, readwrite, strong, nonnull) RCNConfigRealtime *configRealtime;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// Register RolloutsStateSubcriber to FIRRemoteConfig instance
|
||||||
|
- (void)addRemoteConfigInteropSubscriber:(id<FIRRolloutsStateSubscriber> _Nonnull)subscriber;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
76
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h
generated
Normal file
76
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h
generated
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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/Public/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);
|
||||||
|
|
||||||
|
/// Completion handler invoked after a fetch that contains the updated keys
|
||||||
|
typedef void (^RCNConfigFetchCompletion)(FIRRemoteConfigFetchStatus status,
|
||||||
|
FIRRemoteConfigUpdate *update,
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// Fetches config data immediately, keyed by namespace. Completion block will be called on the main
|
||||||
|
/// queue.
|
||||||
|
/// @param fetchAttemptNumber The number of the fetch attempt.
|
||||||
|
/// @param completionHandler Callback handler.
|
||||||
|
- (void)realtimeFetchConfigWithNoExpirationDuration:(NSInteger)fetchAttemptNumber
|
||||||
|
completionHandler:(RCNConfigFetchCompletion)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;
|
||||||
|
|
||||||
|
/// Provide config template version number for Realtime config client.
|
||||||
|
@property(nonatomic, copy, nonnull) NSString *templateVersionNumber;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
|
@end
|
||||||
152
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h
generated
Normal file
152
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h
generated
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
// The Google App ID of the configured FIRApp.
|
||||||
|
@property(nonatomic, readwrite, copy) NSString *googleAppID;
|
||||||
|
#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;
|
||||||
|
/// Last fetched template version.
|
||||||
|
@property(nonatomic, readwrite, assign) NSString *lastFetchedTemplateVersion;
|
||||||
|
/// Last active template version.
|
||||||
|
@property(nonatomic, readwrite, assign) NSString *lastActiveTemplateVersion;
|
||||||
|
|
||||||
|
#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;
|
||||||
|
/// Returns the current retry interval in seconds set for exponential backoff for the Realtime
|
||||||
|
/// service.
|
||||||
|
@property(nonatomic, readwrite, assign) double realtimeExponentialBackoffRetryInterval;
|
||||||
|
/// Returns the time in seconds until the next request is allowed while in exponential backoff mode
|
||||||
|
/// for the Realtime service.
|
||||||
|
@property(nonatomic, readonly, assign) NSTimeInterval realtimeExponentialBackoffThrottleEndTime;
|
||||||
|
/// Realtime connection attempts.
|
||||||
|
@property(nonatomic, readwrite, assign) int realtimeRetryCount;
|
||||||
|
|
||||||
|
#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
|
||||||
|
templateVersion:(NSString *)templateVersion;
|
||||||
|
|
||||||
|
/// Increases the throttling time. Should only be called if the fetch error indicates a server
|
||||||
|
/// issue.
|
||||||
|
- (void)updateExponentialBackoffTime;
|
||||||
|
|
||||||
|
/// Increases the throttling time for Realtime. Should only be called if the Realtime error
|
||||||
|
/// indicates a server issue.
|
||||||
|
- (void)updateRealtimeExponentialBackoffTime;
|
||||||
|
|
||||||
|
/// Update last active template version from last fetched template version.
|
||||||
|
- (void)updateLastActiveTemplateVersion;
|
||||||
|
|
||||||
|
/// Returns the difference between the Realtime backoff end time and the current time in a
|
||||||
|
/// NSTimeInterval format.
|
||||||
|
- (NSTimeInterval)getRealtimeBackoffInterval;
|
||||||
|
|
||||||
|
/// 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
|
||||||
@ -0,0 +1,360 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener registration returned by `addOnConfigUpdateListener`. Calling its method `remove` stops
|
||||||
|
* the associated listener from receiving config updates and unregisters itself.
|
||||||
|
*
|
||||||
|
* If remove is called and no other listener registrations remain, the connection to the real-time
|
||||||
|
* RC backend is closed. Subsequently calling `addOnConfigUpdateListener` will re-open the
|
||||||
|
* connection.
|
||||||
|
*/
|
||||||
|
NS_SWIFT_NAME(ConfigUpdateListenerRegistration)
|
||||||
|
@interface FIRConfigUpdateListenerRegistration : NSObject
|
||||||
|
/**
|
||||||
|
* Removes the listener associated with this `ConfigUpdateListenerRegistration`. After the
|
||||||
|
* initial call, subsequent calls have no effect.
|
||||||
|
*/
|
||||||
|
- (void)remove;
|
||||||
|
@end
|
||||||
|
|
||||||
|
/// 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_ERROR_ENUM(FIRRemoteConfigErrorDomain, 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);
|
||||||
|
|
||||||
|
/// Remote Config error domain that handles errors for the real-time config update service.
|
||||||
|
extern NSString *const _Nonnull FIRRemoteConfigUpdateErrorDomain NS_SWIFT_NAME(RemoteConfigUpdateErrorDomain);
|
||||||
|
/// Firebase Remote Config real-time config update service error.
|
||||||
|
typedef NS_ERROR_ENUM(FIRRemoteConfigUpdateErrorDomain, FIRRemoteConfigUpdateError){
|
||||||
|
/// Unable to make a connection to the Remote Config backend.
|
||||||
|
FIRRemoteConfigUpdateErrorStreamError = 8001,
|
||||||
|
/// Unable to fetch the latest version of the config.
|
||||||
|
FIRRemoteConfigUpdateErrorNotFetched = 8002,
|
||||||
|
/// The ConfigUpdate message was unparsable.
|
||||||
|
FIRRemoteConfigUpdateErrorMessageInvalid = 8003,
|
||||||
|
/// The Remote Config real-time config update service is unavailable.
|
||||||
|
FIRRemoteConfigUpdateErrorUnavailable = 8004,
|
||||||
|
} NS_SWIFT_NAME(RemoteConfigUpdateError);
|
||||||
|
|
||||||
|
/// 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_UNAVAILABLE("Use Swift's closure syntax instead.");
|
||||||
|
|
||||||
|
/// 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_UNAVAILABLE("Use Swift's closure syntax instead.");
|
||||||
|
|
||||||
|
/// Completion handler invoked upon completion of Remote Config initialization.
|
||||||
|
///
|
||||||
|
/// @param initializationError nil if initialization succeeded.
|
||||||
|
typedef void (^FIRRemoteConfigInitializationCompletion)(NSError *_Nullable initializationError)
|
||||||
|
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
|
||||||
|
|
||||||
|
/// 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_UNAVAILABLE("Use Swift's closure syntax instead.");
|
||||||
|
|
||||||
|
#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, nonnull) 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 `RemoteConfig.fetch(withExpirationDuration:)`. For example, setting
|
||||||
|
/// the expiration duration to 0 in the fetch request will override the `minimumFetchInterval` and
|
||||||
|
/// allow the request to proceed.
|
||||||
|
@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;
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark - FIRRemoteConfigUpdate
|
||||||
|
/// Used by Remote Config real-time config update service, this class represents changes between the
|
||||||
|
/// newly fetched config and the current one. An instance of this class is passed to
|
||||||
|
/// `FIRRemoteConfigUpdateCompletion` when a new config version has been automatically fetched.
|
||||||
|
NS_SWIFT_NAME(RemoteConfigUpdate)
|
||||||
|
@interface FIRRemoteConfigUpdate : NSObject
|
||||||
|
|
||||||
|
/// Parameter keys whose values have been updated from the currently activated values. Includes
|
||||||
|
/// keys that are added, deleted, and whose value, value source, or metadata has changed.
|
||||||
|
@property(nonatomic, readonly, nonnull) NSSet<NSString *> *updatedKeys;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark - FIRRemoteConfig
|
||||||
|
/// Firebase Remote Config class. The class method `remoteConfig()` can be used
|
||||||
|
/// to fetch, activate and read config results and set default config results on the default
|
||||||
|
/// Remote Config instance.
|
||||||
|
NS_SWIFT_NAME(RemoteConfig)
|
||||||
|
@interface FIRRemoteConfig : NSObject <NSFastEnumeration>
|
||||||
|
/// Last successful fetch completion time.
|
||||||
|
@property(nonatomic, readonly, strong, nullable) NSDate *lastFetchTime;
|
||||||
|
/// Last fetch status. The status can be any enumerated value from `RemoteConfigFetchStatus`.
|
||||||
|
@property(nonatomic, readonly, assign) FIRRemoteConfigFetchStatus lastFetchStatus;
|
||||||
|
/// Config settings are custom settings.
|
||||||
|
@property(nonatomic, readwrite, strong, nonnull) FIRRemoteConfigSettings *configSettings;
|
||||||
|
|
||||||
|
/// Returns the `RemoteConfig` 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 `activate()`. When
|
||||||
|
/// you fetch values from the Remote Config server using the default Firebase app, you should use
|
||||||
|
/// this class method to create and reuse a shared instance of `RemoteConfig`.
|
||||||
|
+ (nonnull FIRRemoteConfig *)remoteConfig NS_SWIFT_NAME(remoteConfig());
|
||||||
|
|
||||||
|
/// Returns the `RemoteConfig` 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 `activate())`. When you fetch values
|
||||||
|
/// from the Remote Config Server using the non-default Firebase app, you should use this
|
||||||
|
/// class method to create and reuse shared instance of `RemoteConfig`.
|
||||||
|
+ (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 `activate()` 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
|
||||||
|
/// `Installations.authToken(completion:)`).
|
||||||
|
/// To stop the periodic sync, call `Installations.delete(completion:)`
|
||||||
|
/// 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 `activateWithCompletion:` 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
|
||||||
|
/// `Installations.authToken(completion:)`).
|
||||||
|
/// To stop the periodic sync, call `Installations.delete(completion:)`
|
||||||
|
/// and avoid calling this method again.
|
||||||
|
///
|
||||||
|
/// @param expirationDuration Override the (default or optionally set `minimumFetchInterval`
|
||||||
|
/// property in RemoteConfigSettings) `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
|
||||||
|
/// `Installations.authToken(completion:)`).
|
||||||
|
/// To stop the periodic sync, call `Installations.delete(completion:)`
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
#pragma mark - Get Config
|
||||||
|
/// Enables access to configuration values by using object subscripting syntax.
|
||||||
|
/// For example:
|
||||||
|
/// let config = RemoteConfig.remoteConfig()
|
||||||
|
/// let value = config["yourKey"]
|
||||||
|
/// let boolValue = value.boolValue
|
||||||
|
/// let number = config["yourKey"].numberValue
|
||||||
|
- (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 source from the default namespace.
|
||||||
|
/// @param key Config key.
|
||||||
|
/// @param source Config value source.
|
||||||
|
- (nonnull FIRRemoteConfigValue *)configValueForKey:(nullable NSString *)key
|
||||||
|
source:(FIRRemoteConfigSource)source;
|
||||||
|
|
||||||
|
/// Gets all the parameter keys of a given source from the default namespace.
|
||||||
|
///
|
||||||
|
/// @param source The config data source.
|
||||||
|
/// @return An array of keys under the given source.
|
||||||
|
- (nonnull NSArray<NSString *> *)allKeysFromSource:(FIRRemoteConfigSource)source;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
#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 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 named `defaultSamples.plist`:
|
||||||
|
/// `RemoteConfig.remoteConfig().setDefaults(fromPlist: "defaultSamples")`
|
||||||
|
- (void)setDefaultsFromPlistFileName:(nullable NSString *)fileName
|
||||||
|
NS_SWIFT_NAME(setDefaults(fromPlist:));
|
||||||
|
|
||||||
|
/// Returns the default value of a given key from the default config.
|
||||||
|
///
|
||||||
|
/// @param key The parameter key of default config.
|
||||||
|
/// @return Returns the default value of the specified key. Returns
|
||||||
|
/// nil if the key doesn't exist in the default config.
|
||||||
|
- (nullable FIRRemoteConfigValue *)defaultValueForKey:(nullable NSString *)key;
|
||||||
|
|
||||||
|
#pragma mark - Real-time Config Updates
|
||||||
|
|
||||||
|
/// Completion handler invoked by `addOnConfigUpdateListener` when there is an update to
|
||||||
|
/// the config from the backend.
|
||||||
|
///
|
||||||
|
/// @param configUpdate An instance of `FIRRemoteConfigUpdate` that contains information on which
|
||||||
|
/// key's values have changed.
|
||||||
|
/// @param error Error message on failure.
|
||||||
|
typedef void (^FIRRemoteConfigUpdateCompletion)(FIRRemoteConfigUpdate *_Nullable configUpdate,
|
||||||
|
NSError *_Nullable error)
|
||||||
|
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
|
||||||
|
|
||||||
|
/// Start listening for real-time config updates from the Remote Config backend and automatically
|
||||||
|
/// fetch updates when they're available.
|
||||||
|
///
|
||||||
|
/// If a connection to the Remote Config backend is not already open, calling this method will
|
||||||
|
/// open it. Multiple listeners can be added by calling this method again, but subsequent calls
|
||||||
|
/// re-use the same connection to the backend.
|
||||||
|
///
|
||||||
|
/// Note: Real-time Remote Config requires the Firebase Remote Config Realtime API. See Get started
|
||||||
|
/// with Firebase Remote Config at https://firebase.google.com/docs/remote-config/get-started for
|
||||||
|
/// more information.
|
||||||
|
///
|
||||||
|
/// @param listener The configured listener that is called for every config update.
|
||||||
|
/// @return Returns a registration representing the listener. The registration contains
|
||||||
|
/// a remove method, which can be used to stop receiving updates for the provided listener.
|
||||||
|
- (FIRConfigUpdateListenerRegistration *_Nonnull)addOnConfigUpdateListener:
|
||||||
|
(FIRRemoteConfigUpdateCompletion _Nonnull)listener
|
||||||
|
NS_SWIFT_NAME(addOnConfigUpdateListener(remoteConfigUpdateCompletion:));
|
||||||
|
|
||||||
|
@end
|
||||||
@ -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"
|
||||||
72
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigConstants.h
generated
Normal file
72
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigConstants.h
generated
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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";
|
||||||
|
/// Key that includes data for Personalization metadata.
|
||||||
|
static NSString *const RCNFetchResponseKeyPersonalizationMetadata = @"personalizationMetadata";
|
||||||
|
/// Key that includes data for Rollout metadata.
|
||||||
|
static NSString *const RCNFetchResponseKeyRolloutMetadata = @"rolloutMetadata";
|
||||||
|
/// Key that indicates rollout id in Rollout metadata.
|
||||||
|
static NSString *const RCNFetchResponseKeyRolloutID = @"rolloutId";
|
||||||
|
/// Key that indicates variant id in Rollout metadata.
|
||||||
|
static NSString *const RCNFetchResponseKeyVariantID = @"variantId";
|
||||||
|
/// Key that indicates affected parameter keys in Rollout Metadata.
|
||||||
|
static NSString *const RCNFetchResponseKeyAffectedParameterKeys = @"affectedParameterKeys";
|
||||||
|
/// 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";
|
||||||
|
/// Fetched Template Version key
|
||||||
|
static NSString *const RCNFetchResponseKeyTemplateVersion = @"templateVersion";
|
||||||
|
/// Active Template Version key
|
||||||
|
static NSString *const RCNActiveKeyTemplateVersion = @"activeTemplateVersion";
|
||||||
76
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.h
generated
Normal file
76
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.h
generated
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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/Public/FirebaseRemoteConfig/FIRRemoteConfig.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;
|
||||||
|
/// Active Rollout metadata that is currently used.
|
||||||
|
@property(nonatomic, readonly, copy) NSArray<NSDictionary *> *activeRolloutMetadata;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/// Designated initializer;
|
||||||
|
- (instancetype)initWithDBManager:(RCNConfigDBManager *)DBManager NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
/// Returns true if initialization 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;
|
||||||
|
|
||||||
|
/// Sets the fetched Personalization metadata to active.
|
||||||
|
- (void)activatePersonalization;
|
||||||
|
|
||||||
|
/// Gets the active config and Personalization metadata.
|
||||||
|
- (NSDictionary *)getConfigAndMetadataForNamespace:(NSString *)FIRNamespace;
|
||||||
|
|
||||||
|
/// Sets the fetched rollout metadata to active with a success completion handler.
|
||||||
|
- (void)activateRolloutMetadata:(void (^)(BOOL success))completionHandler;
|
||||||
|
|
||||||
|
/// Returns the updated parameters between fetched and active config.
|
||||||
|
- (FIRRemoteConfigUpdate *)getConfigUpdateForNamespace:(NSString *)FIRNamespace;
|
||||||
|
|
||||||
|
@end
|
||||||
526
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.m
generated
Normal file
526
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigContent.m
generated
Normal file
@ -0,0 +1,526 @@
|
|||||||
|
/*
|
||||||
|
* 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/Sources/Private/FIRRemoteConfig_Private.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Public/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/Extension/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;
|
||||||
|
/// Active Personalization metadata that is currently used.
|
||||||
|
NSDictionary *_activePersonalization;
|
||||||
|
/// Pending Personalization metadata that is latest data from server that might or might not be
|
||||||
|
/// applied.
|
||||||
|
NSDictionary *_fetchedPersonalization;
|
||||||
|
/// Active Rollout metadata that is currently used.
|
||||||
|
NSArray<NSDictionary *> *_activeRolloutMetadata;
|
||||||
|
/// Pending Rollout metadata that is latest data from server that might or might not be applied.
|
||||||
|
NSArray<NSDictionary *> *_fetchedRolloutMetadata;
|
||||||
|
/// DBManager
|
||||||
|
RCNConfigDBManager *_DBManager;
|
||||||
|
/// Current bundle identifier;
|
||||||
|
NSString *_bundleIdentifier;
|
||||||
|
/// Blocks 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_group_t _dispatch_group;
|
||||||
|
/// 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.
|
||||||
|
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];
|
||||||
|
_activePersonalization = [[NSDictionary alloc] init];
|
||||||
|
_fetchedPersonalization = [[NSDictionary alloc] init];
|
||||||
|
_activeRolloutMetadata = [[NSArray alloc] init];
|
||||||
|
_fetchedRolloutMetadata = [[NSArray 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;
|
||||||
|
// Waits for both config and Personalization data to load.
|
||||||
|
_dispatch_group = dispatch_group_create();
|
||||||
|
[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 - 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;
|
||||||
|
|
||||||
|
dispatch_group_enter(_dispatch_group);
|
||||||
|
[_DBManager loadMainWithBundleIdentifier:_bundleIdentifier
|
||||||
|
completionHandler:^(
|
||||||
|
BOOL success, NSDictionary *fetchedConfig, NSDictionary *activeConfig,
|
||||||
|
NSDictionary *defaultConfig, NSDictionary *rolloutMetadata) {
|
||||||
|
self->_fetchedConfig = [fetchedConfig mutableCopy];
|
||||||
|
self->_activeConfig = [activeConfig mutableCopy];
|
||||||
|
self->_defaultConfig = [defaultConfig mutableCopy];
|
||||||
|
self->_fetchedRolloutMetadata =
|
||||||
|
[rolloutMetadata[@RCNRolloutTableKeyFetchedMetadata] copy];
|
||||||
|
self->_activeRolloutMetadata =
|
||||||
|
[rolloutMetadata[@RCNRolloutTableKeyActiveMetadata] copy];
|
||||||
|
dispatch_group_leave(self->_dispatch_group);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// TODO(karenzeng): Refactor personalization to be returned in loadMainWithBundleIdentifier above
|
||||||
|
dispatch_group_enter(_dispatch_group);
|
||||||
|
[_DBManager
|
||||||
|
loadPersonalizationWithCompletionHandler:^(
|
||||||
|
BOOL success, NSDictionary *fetchedPersonalization, NSDictionary *activePersonalization,
|
||||||
|
NSDictionary *defaultConfig, NSDictionary *rolloutMetadata) {
|
||||||
|
self->_fetchedPersonalization = [fetchedPersonalization copy];
|
||||||
|
self->_activePersonalization = [activePersonalization copy];
|
||||||
|
dispatch_group_leave(self->_dispatch_group);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 - 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 if ([value isKindOfClass:[NSArray class]]) {
|
||||||
|
NSError *error;
|
||||||
|
valueData = [NSJSONSerialization dataWithJSONObject:value options:0 error:&error];
|
||||||
|
if (error) {
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000076", @"Invalid array value for key '%@'",
|
||||||
|
key);
|
||||||
|
}
|
||||||
|
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
||||||
|
NSError *error;
|
||||||
|
valueData = [NSJSONSerialization dataWithJSONObject:value options:0 error:&error];
|
||||||
|
if (error) {
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000077",
|
||||||
|
@"Invalid dictionary value for key '%@'", key);
|
||||||
|
}
|
||||||
|
} 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]];
|
||||||
|
[self handleUpdatePersonalization:response[RCNFetchResponseKeyPersonalizationMetadata]];
|
||||||
|
[self handleUpdateRolloutFetchedMetadata:response[RCNFetchResponseKeyRolloutMetadata]];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)activatePersonalization {
|
||||||
|
_activePersonalization = _fetchedPersonalization;
|
||||||
|
[_DBManager insertOrUpdatePersonalizationConfig:_activePersonalization
|
||||||
|
fromSource:RCNDBSourceActive];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)activateRolloutMetadata:(void (^)(BOOL success))completionHandler {
|
||||||
|
_activeRolloutMetadata = _fetchedRolloutMetadata;
|
||||||
|
[_DBManager insertOrUpdateRolloutTableWithKey:@RCNRolloutTableKeyActiveMetadata
|
||||||
|
value:_activeRolloutMetadata
|
||||||
|
completionHandler:^(BOOL success, NSDictionary *result) {
|
||||||
|
completionHandler(success);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleUpdatePersonalization:(NSDictionary *)metadata {
|
||||||
|
if (!metadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_fetchedPersonalization = metadata;
|
||||||
|
[_DBManager insertOrUpdatePersonalizationConfig:metadata fromSource:RCNDBSourceFetched];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleUpdateRolloutFetchedMetadata:(NSArray<NSDictionary *> *)metadata {
|
||||||
|
if (!metadata) {
|
||||||
|
metadata = [[NSArray alloc] init];
|
||||||
|
}
|
||||||
|
_fetchedRolloutMetadata = metadata;
|
||||||
|
[_DBManager insertOrUpdateRolloutTableWithKey:@RCNRolloutTableKeyFetchedMetadata
|
||||||
|
value:metadata
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)activePersonalization {
|
||||||
|
[self checkAndWaitForInitialDatabaseLoad];
|
||||||
|
return _activePersonalization;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSDictionary *> *)activeRolloutMetadata {
|
||||||
|
[self checkAndWaitForInitialDatabaseLoad];
|
||||||
|
return _activeRolloutMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)getConfigAndMetadataForNamespace:(NSString *)FIRNamespace {
|
||||||
|
/// If this is the first time reading the active metadata, we might still be reading it from the
|
||||||
|
/// database.
|
||||||
|
[self checkAndWaitForInitialDatabaseLoad];
|
||||||
|
return @{
|
||||||
|
RCNFetchResponseKeyEntries : _activeConfig[FIRNamespace],
|
||||||
|
RCNFetchResponseKeyPersonalizationMetadata : _activePersonalization
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 until load is done. This should be a no-op for subsequent calls.
|
||||||
|
if (!_isConfigLoadFromDBCompleted) {
|
||||||
|
intptr_t isErrorOrTimeout = dispatch_group_wait(
|
||||||
|
_dispatch_group,
|
||||||
|
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDatabaseLoadTimeoutSecs * NSEC_PER_SEC)));
|
||||||
|
if (isErrorOrTimeout) {
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000048",
|
||||||
|
@"Timed out waiting for fetched config to be loaded from DB");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_isConfigLoadFromDBCompleted = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare fetched config with active config and output what has changed
|
||||||
|
- (FIRRemoteConfigUpdate *)getConfigUpdateForNamespace:(NSString *)FIRNamespace {
|
||||||
|
// TODO: handle diff in experiment metadata
|
||||||
|
|
||||||
|
FIRRemoteConfigUpdate *configUpdate;
|
||||||
|
NSMutableSet<NSString *> *updatedKeys = [[NSMutableSet alloc] init];
|
||||||
|
|
||||||
|
NSDictionary *fetchedConfig =
|
||||||
|
_fetchedConfig[FIRNamespace] ? _fetchedConfig[FIRNamespace] : [[NSDictionary alloc] init];
|
||||||
|
NSDictionary *activeConfig =
|
||||||
|
_activeConfig[FIRNamespace] ? _activeConfig[FIRNamespace] : [[NSDictionary alloc] init];
|
||||||
|
NSDictionary *fetchedP13n = _fetchedPersonalization;
|
||||||
|
NSDictionary *activeP13n = _activePersonalization;
|
||||||
|
NSArray<NSDictionary *> *fetchedRolloutMetadata = _fetchedRolloutMetadata;
|
||||||
|
NSArray<NSDictionary *> *activeRolloutMetadata = _activeRolloutMetadata;
|
||||||
|
|
||||||
|
// add new/updated params
|
||||||
|
for (NSString *key in [fetchedConfig allKeys]) {
|
||||||
|
if (activeConfig[key] == nil ||
|
||||||
|
![[activeConfig[key] stringValue] isEqualToString:[fetchedConfig[key] stringValue]]) {
|
||||||
|
[updatedKeys addObject:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add deleted params
|
||||||
|
for (NSString *key in [activeConfig allKeys]) {
|
||||||
|
if (fetchedConfig[key] == nil) {
|
||||||
|
[updatedKeys addObject:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add params with new/updated p13n metadata
|
||||||
|
for (NSString *key in [fetchedP13n allKeys]) {
|
||||||
|
if (activeP13n[key] == nil || ![activeP13n[key] isEqualToDictionary:fetchedP13n[key]]) {
|
||||||
|
[updatedKeys addObject:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add params with deleted p13n metadata
|
||||||
|
for (NSString *key in [activeP13n allKeys]) {
|
||||||
|
if (fetchedP13n[key] == nil) {
|
||||||
|
[updatedKeys addObject:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary<NSString *, NSDictionary *> *fetchedRollouts =
|
||||||
|
[self getParameterKeyToRolloutMetadata:fetchedRolloutMetadata];
|
||||||
|
NSDictionary<NSString *, NSDictionary *> *activeRollouts =
|
||||||
|
[self getParameterKeyToRolloutMetadata:activeRolloutMetadata];
|
||||||
|
|
||||||
|
// add params with new/updated rollout metadata
|
||||||
|
for (NSString *key in [fetchedRollouts allKeys]) {
|
||||||
|
if (activeRollouts[key] == nil ||
|
||||||
|
![activeRollouts[key] isEqualToDictionary:fetchedRollouts[key]]) {
|
||||||
|
[updatedKeys addObject:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add params with deleted rollout metadata
|
||||||
|
for (NSString *key in [activeRollouts allKeys]) {
|
||||||
|
if (fetchedRollouts[key] == nil) {
|
||||||
|
[updatedKeys addObject:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configUpdate = [[FIRRemoteConfigUpdate alloc] initWithUpdatedKeys:updatedKeys];
|
||||||
|
return configUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary<NSString *, NSDictionary *> *)getParameterKeyToRolloutMetadata:
|
||||||
|
(NSArray<NSDictionary *> *)rolloutMetadata {
|
||||||
|
NSMutableDictionary<NSString *, NSMutableDictionary *> *result =
|
||||||
|
[[NSMutableDictionary alloc] init];
|
||||||
|
for (NSDictionary *metadata in rolloutMetadata) {
|
||||||
|
NSString *rolloutId = metadata[RCNFetchResponseKeyRolloutID];
|
||||||
|
NSString *variantId = metadata[RCNFetchResponseKeyVariantID];
|
||||||
|
NSArray<NSString *> *affectedKeys = metadata[RCNFetchResponseKeyAffectedParameterKeys];
|
||||||
|
if (rolloutId && variantId && affectedKeys) {
|
||||||
|
for (NSString *key in affectedKeys) {
|
||||||
|
if (result[key]) {
|
||||||
|
NSMutableDictionary *rolloutIdToVariantId = result[key];
|
||||||
|
[rolloutIdToVariantId setValue:variantId forKey:rolloutId];
|
||||||
|
} else {
|
||||||
|
NSMutableDictionary *rolloutIdToVariantId = [@{rolloutId : variantId} mutableCopy];
|
||||||
|
[result setValue:rolloutIdToVariantId forKey:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [result copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
142
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h
generated
Normal file
142
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h
generated
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* 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 RCNKeyNamespace = @"namespace";
|
||||||
|
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
|
||||||
|
/// @param rolloutMetadata Return fetched and active RolloutMetadata loaded from DB
|
||||||
|
typedef void (^RCNDBLoadCompletion)(BOOL success,
|
||||||
|
NSDictionary *fetchedConfig,
|
||||||
|
NSDictionary *activeConfig,
|
||||||
|
NSDictionary *defaultConfig,
|
||||||
|
NSDictionary *rolloutMetadata);
|
||||||
|
|
||||||
|
/// 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 for a given namespace 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
|
||||||
|
namespace:(NSString *)namespace;
|
||||||
|
/// 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;
|
||||||
|
/// Load Personalization from table.
|
||||||
|
/// @param handler The callback when reading from DB is complete.
|
||||||
|
- (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)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
|
||||||
|
namespace:(NSString *)namespace
|
||||||
|
values:(NSArray *)values
|
||||||
|
completionHandler:(RCNDBCompletion)handler;
|
||||||
|
|
||||||
|
/// Insert or update the data in Personalization config.
|
||||||
|
- (BOOL)insertOrUpdatePersonalizationConfig:(NSDictionary *)metadata fromSource:(RCNDBSource)source;
|
||||||
|
|
||||||
|
/// Insert rollout metadata in rollout table.
|
||||||
|
/// @param key Key indicating whether rollout metadata is fetched or active and defined in
|
||||||
|
/// RCNConfigDefines.h.
|
||||||
|
/// @param metadataList The metadata info for each rollout entry .
|
||||||
|
/// @param handler The callback.
|
||||||
|
- (void)insertOrUpdateRolloutTableWithKey:(NSString *)key
|
||||||
|
value:(NSArray<NSDictionary *> *)metadataList
|
||||||
|
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 and namespace from metadata/internal metadata DB
|
||||||
|
/// before updating new values from response.
|
||||||
|
- (void)deleteRecordWithBundleIdentifier:(NSString *)bundlerIdentifier
|
||||||
|
namespace:(NSString *)namespace
|
||||||
|
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
|
||||||
1302
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m
generated
Normal file
1302
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDefines.h
generated
Normal file
37
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigDefines.h
generated
Normal file
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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"
|
||||||
|
#define RCNExperimentTableKeyActivePayload "experiment_active_payload"
|
||||||
|
#define RCNRolloutTableKeyActiveMetadata "active_rollout_metadata"
|
||||||
|
#define RCNRolloutTableKeyFetchedMetadata "fetched_rollout_metadata"
|
||||||
|
|
||||||
|
#endif
|
||||||
38
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.h
generated
Normal file
38
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.h
generated
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
- (nonnull instancetype)initWithDBManager:(RCNConfigDBManager *_Nullable)DBManager
|
||||||
|
experimentController:(FIRExperimentController *_Nullable)controller
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
/// Use `initWithDBManager:` instead.
|
||||||
|
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/// Update/Persist experiment information from config fetch response.
|
||||||
|
- (void)updateExperimentsWithResponse:(NSArray<NSDictionary<NSString *, id> *> *_Nullable)response;
|
||||||
|
|
||||||
|
/// Update experiments to Firebase Analytics when `activateWithCompletion:` happens.
|
||||||
|
- (void)updateExperimentsWithHandler:(nullable void (^)(NSError *_Nullable error))handler;
|
||||||
|
@end
|
||||||
200
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.m
generated
Normal file
200
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigExperiment.m
generated
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* 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/Sources/Private/FirebaseABTestingInternal.h"
|
||||||
|
#import "FirebaseCore/Extension/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)
|
||||||
|
NSMutableArray<NSData *> *activeExperimentPayloads; ///< Activated experiment payloads.
|
||||||
|
@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];
|
||||||
|
_activeExperimentPayloads = [[NSMutableArray 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]) {
|
||||||
|
NSError *error;
|
||||||
|
id experimentPayloadJSON = [NSJSONSerialization JSONObjectWithData:experiment
|
||||||
|
options:kNilOptions
|
||||||
|
error:&error];
|
||||||
|
if (!experimentPayloadJSON || error) {
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load activated experiments payload and metadata.
|
||||||
|
if (result[@RCNExperimentTableKeyActivePayload]) {
|
||||||
|
[strongSelf->_activeExperimentPayloads removeAllObjects];
|
||||||
|
for (NSData *experiment in result[@RCNExperimentTableKeyActivePayload]) {
|
||||||
|
NSError *error;
|
||||||
|
id experimentPayloadJSON = [NSJSONSerialization JSONObjectWithData:experiment
|
||||||
|
options:kNilOptions
|
||||||
|
error:&error];
|
||||||
|
if (!experimentPayloadJSON || error) {
|
||||||
|
FIRLogWarning(kFIRLoggerRemoteConfig, @"I-RCN000031",
|
||||||
|
@"Activated experiment payload could not be parsed as JSON.");
|
||||||
|
} else {
|
||||||
|
[strongSelf->_activeExperimentPayloads addObject:experiment];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
[_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)updateExperimentsWithHandler:(void (^)(NSError *_Nullable))handler {
|
||||||
|
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];
|
||||||
|
[self.experimentController
|
||||||
|
updateExperimentsWithServiceOrigin:kServiceOrigin
|
||||||
|
events:lifecycleEvent
|
||||||
|
policy:ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest
|
||||||
|
lastStartTime:lastStartTime
|
||||||
|
payloads:_experimentPayloads
|
||||||
|
completionHandler:handler];
|
||||||
|
|
||||||
|
/// Update activated experiments payload and metadata in DB.
|
||||||
|
[self updateActiveExperimentsInDB];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateActiveExperimentsInDB {
|
||||||
|
/// Put current fetched experiment payloads into activated experiment DB.
|
||||||
|
[_activeExperimentPayloads removeAllObjects];
|
||||||
|
[_DBManager deleteExperimentTableForKey:@RCNExperimentTableKeyActivePayload];
|
||||||
|
for (NSData *experiment in _experimentPayloads) {
|
||||||
|
[_activeExperimentPayloads addObject:experiment];
|
||||||
|
[_DBManager insertExperimentTableWithKey:@RCNExperimentTableKeyActivePayload
|
||||||
|
value:experiment
|
||||||
|
completionHandler:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTimeInterval)latestStartTimeWithExistingLastStartTime:(NSTimeInterval)existingLastStartTime {
|
||||||
|
return [self.experimentController
|
||||||
|
latestExperimentStartTimestampBetweenTimestamp:existingLastStartTime
|
||||||
|
andPayloads:_experimentPayloads];
|
||||||
|
}
|
||||||
|
@end
|
||||||
691
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigFetch.m
generated
Normal file
691
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigFetch.m
generated
Normal file
@ -0,0 +1,691 @@
|
|||||||
|
/*
|
||||||
|
* 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 "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h"
|
||||||
|
|
||||||
|
#import <GoogleUtilities/GULNSData+zlib.h>
|
||||||
|
#import "FirebaseCore/Extension/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 FirebaseRemoteConfigInterop;
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
static NSString *const kFetchTypeHeaderName =
|
||||||
|
@"X-Firebase-RC-Fetch-Type"; ///< Custom Http header key to identify the fetch type
|
||||||
|
static NSString *const kBaseFetchType = @"BASE"; ///< Fetch identifier for Base Fetch
|
||||||
|
static NSString *const kRealtimeFetchType = @"REALTIME"; ///< Fetch identifier for Realtime Fetch
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
#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;
|
||||||
|
NSString *_templateVersionNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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;
|
||||||
|
_templateVersionNumber = [self->_settings lastFetchedTemplateVersion];
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
dispatch_async(_lockQueue, ^{
|
||||||
|
RCNConfigFetch *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.");
|
||||||
|
return [strongSelf reportCompletionOnHandler:completionHandler
|
||||||
|
withStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withError:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
NSString *fetchTypeHeader = [NSString stringWithFormat:@"%@/1", kBaseFetchType];
|
||||||
|
[strongSelf refreshInstallationsTokenWithFetchHeader:fetchTypeHeader
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:nil];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Fetch helpers
|
||||||
|
|
||||||
|
- (void)realtimeFetchConfigWithNoExpirationDuration:(NSInteger)fetchAttemptNumber
|
||||||
|
completionHandler:(RCNConfigFetchCompletion)completionHandler {
|
||||||
|
// Note: We expect the googleAppID to always be available.
|
||||||
|
BOOL hasDeviceContextChanged =
|
||||||
|
FIRRemoteConfigHasDeviceContextChanged(_settings.deviceContext, _options.googleAppID);
|
||||||
|
|
||||||
|
__weak RCNConfigFetch *weakSelf = self;
|
||||||
|
dispatch_async(_lockQueue, ^{
|
||||||
|
RCNConfigFetch *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 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 reportCompletionWithStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withUpdate:nil
|
||||||
|
withError:error
|
||||||
|
completionHandler:nil
|
||||||
|
updateCompletionHandler:completionHandler];
|
||||||
|
}
|
||||||
|
strongSelf->_settings.isFetchInProgress = YES;
|
||||||
|
|
||||||
|
NSString *fetchTypeHeader =
|
||||||
|
[NSString stringWithFormat:@"%@/%ld", kRealtimeFetchType, (long)fetchAttemptNumber];
|
||||||
|
[strongSelf refreshInstallationsTokenWithFetchHeader:fetchTypeHeader
|
||||||
|
completionHandler:nil
|
||||||
|
updateCompletionHandler:completionHandler];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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)refreshInstallationsTokenWithFetchHeader:(NSString *)fetchTypeHeader
|
||||||
|
completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler
|
||||||
|
updateCompletionHandler:(RCNConfigFetchCompletion)updateCompletionHandler {
|
||||||
|
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
|
||||||
|
}]];
|
||||||
|
}
|
||||||
|
|
||||||
|
__weak RCNConfigFetch *weakSelf = self;
|
||||||
|
FIRInstallationsTokenHandler installationsTokenHandler = ^(
|
||||||
|
FIRInstallationsAuthTokenResult *tokenResult, NSError *error) {
|
||||||
|
RCNConfigFetch *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenResult || !tokenResult.authToken || error) {
|
||||||
|
NSString *errorDescription =
|
||||||
|
[NSString stringWithFormat:@"Failed to get installations token. Error : %@.", error];
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000073", @"%@",
|
||||||
|
[NSString stringWithFormat:@"%@", errorDescription]);
|
||||||
|
strongSelf->_settings.isFetchInProgress = NO;
|
||||||
|
|
||||||
|
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||||
|
userInfo[NSLocalizedDescriptionKey] = errorDescription;
|
||||||
|
userInfo[NSUnderlyingErrorKey] = error.userInfo[NSUnderlyingErrorKey];
|
||||||
|
|
||||||
|
return [strongSelf
|
||||||
|
reportCompletionOnHandler:completionHandler
|
||||||
|
withStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain
|
||||||
|
code:FIRRemoteConfigErrorInternalError
|
||||||
|
userInfo:userInfo]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a valid token. Get the backing installationID.
|
||||||
|
[installations installationIDWithCompletion:^(NSString *_Nullable identifier,
|
||||||
|
NSError *_Nullable error) {
|
||||||
|
RCNConfigFetch *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch to the RC serial queue to update settings on the queue.
|
||||||
|
dispatch_async(strongSelf->_lockQueue, ^{
|
||||||
|
RCNConfigFetch *strongSelfQueue = weakSelf;
|
||||||
|
if (strongSelfQueue == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||||
|
userInfo[NSLocalizedDescriptionKey] = errorDescription;
|
||||||
|
userInfo[NSUnderlyingErrorKey] = error.userInfo[NSUnderlyingErrorKey];
|
||||||
|
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000055", @"%@",
|
||||||
|
[NSString stringWithFormat:@"%@", errorDescription]);
|
||||||
|
strongSelfQueue->_settings.isFetchInProgress = NO;
|
||||||
|
return [strongSelfQueue
|
||||||
|
reportCompletionOnHandler:completionHandler
|
||||||
|
withStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain
|
||||||
|
code:FIRRemoteConfigErrorInternalError
|
||||||
|
userInfo:userInfo]];
|
||||||
|
}
|
||||||
|
|
||||||
|
FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000022", @"Success to get iid : %@.",
|
||||||
|
strongSelfQueue->_settings.configInstallationsIdentifier);
|
||||||
|
[strongSelf doFetchCall:fetchTypeHeader
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000039", @"Starting requesting token.");
|
||||||
|
[installations authTokenWithCompletion:installationsTokenHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doFetchCall:(NSString *)fetchTypeHeader
|
||||||
|
completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler
|
||||||
|
updateCompletionHandler:(RCNConfigFetchCompletion)updateCompletionHandler {
|
||||||
|
[self getAnalyticsUserPropertiesWithCompletionHandler:^(NSDictionary *userProperties) {
|
||||||
|
dispatch_async(self->_lockQueue, ^{
|
||||||
|
[self fetchWithUserProperties:userProperties
|
||||||
|
fetchTypeHeader:fetchTypeHeader
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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 {
|
||||||
|
[self reportCompletionWithStatus:status
|
||||||
|
withUpdate:nil
|
||||||
|
withError:error
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reportCompletionWithStatus:(FIRRemoteConfigFetchStatus)status
|
||||||
|
withUpdate:(FIRRemoteConfigUpdate *)update
|
||||||
|
withError:(NSError *)error
|
||||||
|
completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler
|
||||||
|
updateCompletionHandler:(RCNConfigFetchCompletion)updateCompletionHandler {
|
||||||
|
if (completionHandler) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
completionHandler(status, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// if completion handler expects a config update response
|
||||||
|
if (updateCompletionHandler) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
updateCompletionHandler(status, update, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)fetchWithUserProperties:(NSDictionary *)userProperties
|
||||||
|
fetchTypeHeader:(NSString *)fetchTypeHeader
|
||||||
|
completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler
|
||||||
|
updateCompletionHandler:(RCNConfigFetchCompletion)updateCompletionHandler {
|
||||||
|
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);
|
||||||
|
NSError *error = [NSError errorWithDomain:FIRRemoteConfigErrorDomain
|
||||||
|
code:FIRRemoteConfigErrorInternalError
|
||||||
|
userInfo:@{NSLocalizedDescriptionKey : errString}];
|
||||||
|
|
||||||
|
self->_settings.isFetchInProgress = NO;
|
||||||
|
return [self reportCompletionWithStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withUpdate:nil
|
||||||
|
withError:error
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
RCNConfigFetch *fetcherCompletionSelf = weakSelf;
|
||||||
|
if (fetcherCompletionSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The fetch has completed.
|
||||||
|
fetcherCompletionSelf->_settings.isFetchInProgress = NO;
|
||||||
|
|
||||||
|
dispatch_async(fetcherCompletionSelf->_lockQueue, ^{
|
||||||
|
RCNConfigFetch *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSInteger statusCode = [((NSHTTPURLResponse *)response) statusCode];
|
||||||
|
|
||||||
|
if (error || (statusCode != kRCNFetchResponseHTTPStatusCodeOK)) {
|
||||||
|
// Update metadata about fetch failure.
|
||||||
|
[strongSelf->_settings updateMetadataWithFetchSuccessStatus:NO templateVersion:nil];
|
||||||
|
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.
|
||||||
|
// TODO: check error code in helper
|
||||||
|
if (statusCode == kRCNFetchResponseHTTPStatusTooManyRequests ||
|
||||||
|
statusCode == kRCNFetchResponseHTTPStatusCodeInternalError ||
|
||||||
|
statusCode == kRCNFetchResponseHTTPStatusCodeServiceUnavailable ||
|
||||||
|
statusCode == kRCNFetchResponseHTTPStatusCodeGatewayTimeout) {
|
||||||
|
[strongSelf->_settings updateExponentialBackoffTime];
|
||||||
|
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 reportCompletionWithStatus:strongSelf->_settings.lastFetchStatus
|
||||||
|
withUpdate:nil
|
||||||
|
withError:error
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return back the received error.
|
||||||
|
// Must set lastFetchStatus before setting Fetch Error.
|
||||||
|
strongSelf->_settings.lastFetchStatus = FIRRemoteConfigFetchStatusFailure;
|
||||||
|
strongSelf->_settings.lastFetchError = FIRRemoteConfigErrorInternalError;
|
||||||
|
NSMutableDictionary<NSErrorUserInfoKey, id> *userInfo = [NSMutableDictionary dictionary];
|
||||||
|
userInfo[NSUnderlyingErrorKey] = error;
|
||||||
|
userInfo[NSLocalizedDescriptionKey] =
|
||||||
|
error.localizedDescription
|
||||||
|
?: [NSString
|
||||||
|
stringWithFormat:@"Internal Error. Status code: %ld", (long)statusCode];
|
||||||
|
|
||||||
|
return [strongSelf
|
||||||
|
reportCompletionWithStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withUpdate:nil
|
||||||
|
withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain
|
||||||
|
code:FIRRemoteConfigErrorInternalError
|
||||||
|
userInfo:userInfo]
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch was successful. Check if we have data.
|
||||||
|
NSError *retError;
|
||||||
|
if (!data) {
|
||||||
|
FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000043", @"RCN Fetch: No data in fetch response");
|
||||||
|
// There may still be a difference between fetched and active config
|
||||||
|
FIRRemoteConfigUpdate *update =
|
||||||
|
[strongSelf->_content getConfigUpdateForNamespace:strongSelf->_FIRNamespace];
|
||||||
|
return [strongSelf reportCompletionWithStatus:FIRRemoteConfigFetchStatusSuccess
|
||||||
|
withUpdate:update
|
||||||
|
withError:nil
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
NSError *error = [NSError errorWithDomain:FIRRemoteConfigErrorDomain
|
||||||
|
code:FIRRemoteConfigErrorInternalError
|
||||||
|
userInfo:@{NSLocalizedDescriptionKey : errStr}];
|
||||||
|
return [strongSelf reportCompletionWithStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withUpdate:nil
|
||||||
|
withError:error
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the fetched config to the database.
|
||||||
|
if (fetchedConfig) {
|
||||||
|
// Update config content to cache and DB.
|
||||||
|
[strongSelf->_content updateConfigContentWithResponse:fetchedConfig
|
||||||
|
forNamespace:strongSelf->_FIRNamespace];
|
||||||
|
// Update experiments only for 3p namespace
|
||||||
|
NSString *namespace = [strongSelf->_FIRNamespace
|
||||||
|
substringToIndex:[strongSelf->_FIRNamespace rangeOfString:@":"].location];
|
||||||
|
if ([namespace isEqualToString:FIRRemoteConfigConstants.FIRNamespaceGoogleMobilePlatform]) {
|
||||||
|
[strongSelf->_experiment updateExperimentsWithResponse:
|
||||||
|
fetchedConfig[RCNFetchResponseKeyExperimentDescriptions]];
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf->_templateVersionNumber = [strongSelf getTemplateVersionNumber:fetchedConfig];
|
||||||
|
} 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 (!strongSelf->_settings.lastETag ||
|
||||||
|
!([strongSelf->_settings.lastETag isEqualToString:latestETag])) {
|
||||||
|
strongSelf->_settings.lastETag = latestETag;
|
||||||
|
}
|
||||||
|
// Compute config update after successful fetch
|
||||||
|
FIRRemoteConfigUpdate *update =
|
||||||
|
[strongSelf->_content getConfigUpdateForNamespace:strongSelf->_FIRNamespace];
|
||||||
|
|
||||||
|
[strongSelf->_settings
|
||||||
|
updateMetadataWithFetchSuccessStatus:YES
|
||||||
|
templateVersion:strongSelf->_templateVersionNumber];
|
||||||
|
return [strongSelf reportCompletionWithStatus:FIRRemoteConfigFetchStatusSuccess
|
||||||
|
withUpdate:update
|
||||||
|
withError:nil
|
||||||
|
completionHandler:completionHandler
|
||||||
|
updateCompletionHandler:updateCompletionHandler];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000061", @"Making remote config fetch.");
|
||||||
|
|
||||||
|
NSURLSessionDataTask *dataTask = [self URLSessionDataTaskWithContent:compressedContent
|
||||||
|
fetchTypeHeader:fetchTypeHeader
|
||||||
|
completionHandler:fetcherCompletion];
|
||||||
|
[dataTask resume];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)constructServerURL {
|
||||||
|
NSString *serverURLStr = [[NSString alloc] initWithString:kServerURLDomain];
|
||||||
|
serverURLStr = [serverURLStr stringByAppendingString:kServerURLVersion];
|
||||||
|
serverURLStr = [serverURLStr stringByAppendingString:kServerURLProjects];
|
||||||
|
serverURLStr = [serverURLStr stringByAppendingString:_options.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
|
||||||
|
fetchTypeHeader:(NSString *)fetchTypeHeader
|
||||||
|
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];
|
||||||
|
[URLRequest setValue:fetchTypeHeader forHTTPHeaderField:kFetchTypeHeaderName];
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)getTemplateVersionNumber:(NSDictionary *)fetchedConfig {
|
||||||
|
if (fetchedConfig != nil && [fetchedConfig objectForKey:RCNFetchResponseKeyTemplateVersion] &&
|
||||||
|
[[fetchedConfig objectForKey:RCNFetchResponseKeyTemplateVersion]
|
||||||
|
isKindOfClass:[NSString class]]) {
|
||||||
|
return (NSString *)[fetchedConfig objectForKey:RCNFetchResponseKeyTemplateVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
return @"0";
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
40
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigRealtime.h
generated
Normal file
40
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigRealtime.h
generated
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
|
||||||
|
|
||||||
|
@class RCNConfigSettings;
|
||||||
|
|
||||||
|
@interface RCNConfigRealtime : NSObject <NSURLSessionDataDelegate>
|
||||||
|
|
||||||
|
/// Completion handler invoked by config update methods when they get a response from the server.
|
||||||
|
///
|
||||||
|
/// @param error Error message on failure.
|
||||||
|
typedef void (^RCNConfigUpdateCompletion)(FIRRemoteConfigUpdate *_Nullable configUpdate,
|
||||||
|
NSError *_Nullable error);
|
||||||
|
|
||||||
|
- (instancetype _Nonnull)init:(RCNConfigFetch *_Nonnull)configFetch
|
||||||
|
settings:(RCNConfigSettings *_Nonnull)settings
|
||||||
|
namespace:(NSString *_Nonnull)namespace
|
||||||
|
options:(FIROptions *_Nonnull)options;
|
||||||
|
|
||||||
|
- (FIRConfigUpdateListenerRegistration *_Nonnull)addConfigUpdateListener:
|
||||||
|
(RCNConfigUpdateCompletion _Nonnull)listener;
|
||||||
|
- (void)removeConfigUpdateListener:(RCNConfigUpdateCompletion _Nonnull)listener;
|
||||||
|
|
||||||
|
@end
|
||||||
716
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m
generated
Normal file
716
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m
generated
Normal file
@ -0,0 +1,716 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNConfigRealtime.h"
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <GoogleUtilities/GULNSData+zlib.h>
|
||||||
|
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
||||||
|
#import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNDevice.h"
|
||||||
|
|
||||||
|
/// URL params
|
||||||
|
static NSString *const kServerURLDomain = @"https://firebaseremoteconfigrealtime.googleapis.com";
|
||||||
|
static NSString *const kServerURLVersion = @"/v1";
|
||||||
|
static NSString *const kServerURLProjects = @"/projects/";
|
||||||
|
static NSString *const kServerURLNamespaces = @"/namespaces/";
|
||||||
|
static NSString *const kServerURLQuery = @":streamFetchInvalidations?";
|
||||||
|
static NSString *const kServerURLKey = @"key=";
|
||||||
|
|
||||||
|
/// Realtime API enablement
|
||||||
|
static NSString *const kServerForbiddenStatusCode = @"\"code\": 403";
|
||||||
|
|
||||||
|
/// Header names
|
||||||
|
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"; ///< 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
|
||||||
|
|
||||||
|
/// Retryable HTTP status code.
|
||||||
|
static NSInteger const kRCNFetchResponseHTTPStatusOk = 200;
|
||||||
|
static NSInteger const kRCNFetchResponseHTTPStatusClientTimeout = 429;
|
||||||
|
static NSInteger const kRCNFetchResponseHTTPStatusTooManyRequests = 429;
|
||||||
|
static NSInteger const kRCNFetchResponseHTTPStatusCodeBadGateway = 502;
|
||||||
|
static NSInteger const kRCNFetchResponseHTTPStatusCodeServiceUnavailable = 503;
|
||||||
|
static NSInteger const kRCNFetchResponseHTTPStatusCodeGatewayTimeout = 504;
|
||||||
|
|
||||||
|
/// Invalidation message field names.
|
||||||
|
static NSString *const kTemplateVersionNumberKey = @"latestTemplateVersionNumber";
|
||||||
|
static NSString *const kIsFeatureDisabled = @"featureDisabled";
|
||||||
|
|
||||||
|
static NSTimeInterval gTimeoutSeconds = 330;
|
||||||
|
static NSInteger const gFetchAttempts = 3;
|
||||||
|
|
||||||
|
// Retry parameters
|
||||||
|
static NSInteger const gMaxRetries = 7;
|
||||||
|
|
||||||
|
@interface FIRConfigUpdateListenerRegistration ()
|
||||||
|
@property(strong, atomic, nonnull) RCNConfigUpdateCompletion completionHandler;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FIRConfigUpdateListenerRegistration {
|
||||||
|
RCNConfigRealtime *_realtimeClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithClient:(RCNConfigRealtime *)realtimeClient
|
||||||
|
completionHandler:(RCNConfigUpdateCompletion)completionHandler {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_realtimeClient = realtimeClient;
|
||||||
|
_completionHandler = completionHandler;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)remove {
|
||||||
|
[self->_realtimeClient removeConfigUpdateListener:_completionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface RCNConfigRealtime ()
|
||||||
|
|
||||||
|
@property(strong, atomic, nonnull) NSMutableSet<RCNConfigUpdateCompletion> *listeners;
|
||||||
|
@property(strong, atomic, nonnull) dispatch_queue_t realtimeLockQueue;
|
||||||
|
@property(strong, atomic, nonnull) NSNotificationCenter *notificationCenter;
|
||||||
|
|
||||||
|
@property(strong, atomic) NSURLSession *session;
|
||||||
|
@property(strong, atomic) NSURLSessionDataTask *dataTask;
|
||||||
|
@property(strong, atomic) NSMutableURLRequest *request;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCNConfigRealtime {
|
||||||
|
RCNConfigFetch *_configFetch;
|
||||||
|
RCNConfigSettings *_settings;
|
||||||
|
FIROptions *_options;
|
||||||
|
NSString *_namespace;
|
||||||
|
NSInteger _remainingRetryCount;
|
||||||
|
bool _isRequestInProgress;
|
||||||
|
bool _isInBackground;
|
||||||
|
bool _isRealtimeDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init:(RCNConfigFetch *)configFetch
|
||||||
|
settings:(RCNConfigSettings *)settings
|
||||||
|
namespace:(NSString *)namespace
|
||||||
|
options:(FIROptions *)options {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_listeners = [[NSMutableSet alloc] init];
|
||||||
|
_realtimeLockQueue = [RCNConfigRealtime realtimeRemoteConfigSerialQueue];
|
||||||
|
_notificationCenter = [NSNotificationCenter defaultCenter];
|
||||||
|
|
||||||
|
_configFetch = configFetch;
|
||||||
|
_settings = settings;
|
||||||
|
_options = options;
|
||||||
|
_namespace = namespace;
|
||||||
|
|
||||||
|
_remainingRetryCount = MAX(gMaxRetries - [_settings realtimeRetryCount], 1);
|
||||||
|
_isRequestInProgress = false;
|
||||||
|
_isRealtimeDisabled = false;
|
||||||
|
_isInBackground = false;
|
||||||
|
|
||||||
|
[self setUpHttpRequest];
|
||||||
|
[self setUpHttpSession];
|
||||||
|
[self backgroundChangeListener];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Singleton instance of serial queue for queuing all incoming RC calls.
|
||||||
|
+ (dispatch_queue_t)realtimeRemoteConfigSerialQueue {
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
static dispatch_queue_t realtimeRemoteConfigQueue;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
realtimeRemoteConfigQueue =
|
||||||
|
dispatch_queue_create(RCNRemoteConfigQueueLabel, DISPATCH_QUEUE_SERIAL);
|
||||||
|
});
|
||||||
|
return realtimeRemoteConfigQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)propogateErrors:(NSError *)error {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
for (RCNConfigUpdateCompletion listener in strongSelf->_listeners) {
|
||||||
|
listener(nil, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Test Only Helpers
|
||||||
|
|
||||||
|
// TESTING ONLY
|
||||||
|
- (void)triggerListenerForTesting:(void (^_Nonnull)(FIRRemoteConfigUpdate *configUpdate,
|
||||||
|
NSError *_Nullable error))listener {
|
||||||
|
listener([[FIRRemoteConfigUpdate alloc] init], nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Http Helpers
|
||||||
|
|
||||||
|
- (NSString *)constructServerURL {
|
||||||
|
NSString *serverURLStr = [[NSString alloc] initWithString:kServerURLDomain];
|
||||||
|
serverURLStr = [serverURLStr stringByAppendingString:kServerURLVersion];
|
||||||
|
serverURLStr = [serverURLStr stringByAppendingString:kServerURLProjects];
|
||||||
|
serverURLStr = [serverURLStr stringByAppendingString:_options.GCMSenderID];
|
||||||
|
serverURLStr = [serverURLStr stringByAppendingString:kServerURLNamespaces];
|
||||||
|
|
||||||
|
/// Get the namespace from the fully qualified namespace string of "namespace:FIRAppName".
|
||||||
|
NSString *namespace = [_namespace substringToIndex:[_namespace 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)FIRAppNameFromFullyQualifiedNamespace {
|
||||||
|
return [[_namespace componentsSeparatedByString:@":"] lastObject];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reportCompletionOnHandler:(FIRRemoteConfigFetchCompletion)completionHandler
|
||||||
|
withStatus:(FIRRemoteConfigFetchStatus)status
|
||||||
|
withError:(NSError *)error {
|
||||||
|
if (completionHandler) {
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
completionHandler(status, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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]);
|
||||||
|
return [self
|
||||||
|
reportCompletionOnHandler:completionHandler
|
||||||
|
withStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain
|
||||||
|
code:FIRRemoteConfigErrorInternalError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : errorDescription
|
||||||
|
}]];
|
||||||
|
}
|
||||||
|
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
FIRInstallationsTokenHandler installationsTokenHandler = ^(
|
||||||
|
FIRInstallationsAuthTokenResult *tokenResult, NSError *error) {
|
||||||
|
RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenResult || !tokenResult.authToken || error) {
|
||||||
|
NSString *errorDescription =
|
||||||
|
[NSString stringWithFormat:@"Failed to get installations token. Error : %@.", error];
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000073", @"%@",
|
||||||
|
[NSString stringWithFormat:@"%@", errorDescription]);
|
||||||
|
return [strongSelf
|
||||||
|
reportCompletionOnHandler:completionHandler
|
||||||
|
withStatus:FIRRemoteConfigFetchStatusFailure
|
||||||
|
withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain
|
||||||
|
code:FIRRemoteConfigErrorInternalError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : errorDescription
|
||||||
|
}]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We have a valid token. Get the backing installationID.
|
||||||
|
[installations installationIDWithCompletion:^(NSString *_Nullable identifier,
|
||||||
|
NSError *_Nullable error) {
|
||||||
|
RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch to the RC serial queue to update settings on the queue.
|
||||||
|
dispatch_async(strongSelf->_realtimeLockQueue, ^{
|
||||||
|
RCNConfigRealtime *strongSelfQueue = weakSelf;
|
||||||
|
if (strongSelfQueue == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000039", @"Starting requesting token.");
|
||||||
|
[installations authTokenWithCompletion:installationsTokenHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)createRequestBody {
|
||||||
|
[self refreshInstallationsTokenWithCompletionHandler:^(FIRRemoteConfigFetchStatus status,
|
||||||
|
NSError *_Nullable error) {
|
||||||
|
if (status != FIRRemoteConfigFetchStatusSuccess) {
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000013", @"Installation token retrival failed.");
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
[_request setValue:_settings.configInstallationsToken
|
||||||
|
forHTTPHeaderField:kInstallationsAuthTokenHeaderName];
|
||||||
|
if (_settings.lastETag) {
|
||||||
|
[_request setValue:_settings.lastETag forHTTPHeaderField:kIfNoneMatchETagHeaderName];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *namespace = [_namespace substringToIndex:[_namespace rangeOfString:@":"].location];
|
||||||
|
NSString *postBody = [NSString
|
||||||
|
stringWithFormat:@"{project:'%@', namespace:'%@', lastKnownVersionNumber:'%@', appId:'%@', "
|
||||||
|
@"sdkVersion:'%@', appInstanceId:'%@'}",
|
||||||
|
[self->_options GCMSenderID], namespace, _configFetch.templateVersionNumber,
|
||||||
|
_options.googleAppID, FIRRemoteConfigPodVersion(),
|
||||||
|
_settings.configInstallationsIdentifier];
|
||||||
|
NSData *postData = [postBody dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
NSError *compressionError;
|
||||||
|
return [NSData gul_dataByGzippingData:postData error:&compressionError];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates request.
|
||||||
|
- (void)setUpHttpRequest {
|
||||||
|
NSString *address = [self constructServerURL];
|
||||||
|
_request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:address]
|
||||||
|
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||||
|
timeoutInterval:gTimeoutSeconds];
|
||||||
|
[_request setHTTPMethod:kHTTPMethodPost];
|
||||||
|
[_request setValue:@"application/json" forHTTPHeaderField:kContentTypeHeaderName];
|
||||||
|
[_request setValue:@"application/json" forHTTPHeaderField:kAcceptEncodingHeaderName];
|
||||||
|
[_request setValue:@"gzip" forHTTPHeaderField:kContentEncodingHeaderName];
|
||||||
|
[_request setValue:@"true" forHTTPHeaderField:@"X-Google-GFE-Can-Retry"];
|
||||||
|
[_request setValue:[_options APIKey] forHTTPHeaderField:@"X-Goog-Api-Key"];
|
||||||
|
[_request setValue:[[NSBundle mainBundle] bundleIdentifier]
|
||||||
|
forHTTPHeaderField:kiOSBundleIdentifierHeaderName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes call to create session.
|
||||||
|
- (void)setUpHttpSession {
|
||||||
|
NSURLSessionConfiguration *sessionConfig =
|
||||||
|
[[NSURLSessionConfiguration defaultSessionConfiguration] copy];
|
||||||
|
[sessionConfig setTimeoutIntervalForResource:gTimeoutSeconds];
|
||||||
|
[sessionConfig setTimeoutIntervalForRequest:gTimeoutSeconds];
|
||||||
|
_session = [NSURLSession sessionWithConfiguration:sessionConfig
|
||||||
|
delegate:self
|
||||||
|
delegateQueue:[NSOperationQueue mainQueue]];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Retry Helpers
|
||||||
|
|
||||||
|
// Retry mechanism for HTTP connections
|
||||||
|
- (void)retryHTTPConnection {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
if (strongSelf->_isInBackground) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool noRunningConnection =
|
||||||
|
strongSelf->_dataTask == nil || strongSelf->_dataTask.state != NSURLSessionTaskStateRunning;
|
||||||
|
bool canMakeConnection = noRunningConnection && [strongSelf->_listeners count] > 0 &&
|
||||||
|
!strongSelf->_isRealtimeDisabled;
|
||||||
|
if (canMakeConnection && strongSelf->_remainingRetryCount > 0) {
|
||||||
|
NSTimeInterval backOffInterval = self->_settings.getRealtimeBackoffInterval;
|
||||||
|
|
||||||
|
strongSelf->_remainingRetryCount--;
|
||||||
|
[strongSelf->_settings setRealtimeRetryCount:[strongSelf->_settings realtimeRetryCount] + 1];
|
||||||
|
dispatch_time_t executionDelay =
|
||||||
|
dispatch_time(DISPATCH_TIME_NOW, (backOffInterval * NSEC_PER_SEC));
|
||||||
|
dispatch_after(executionDelay, strongSelf->_realtimeLockQueue, ^{
|
||||||
|
[strongSelf beginRealtimeStream];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
NSError *error = [NSError
|
||||||
|
errorWithDomain:FIRRemoteConfigUpdateErrorDomain
|
||||||
|
code:FIRRemoteConfigUpdateErrorStreamError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
@"Unable to connect to the server. Check your connection and try again."
|
||||||
|
}];
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000014", @"Cannot establish connection. Error: %@",
|
||||||
|
error);
|
||||||
|
[self propogateErrors:error];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)backgroundChangeListener {
|
||||||
|
[_notificationCenter addObserver:self
|
||||||
|
selector:@selector(isInForeground)
|
||||||
|
name:@"UIApplicationWillEnterForegroundNotification"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
[_notificationCenter addObserver:self
|
||||||
|
selector:@selector(isInBackground)
|
||||||
|
name:@"UIApplicationDidEnterBackgroundNotification"
|
||||||
|
object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)isInForeground {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
strongSelf->_isInBackground = false;
|
||||||
|
[strongSelf beginRealtimeStream];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)isInBackground {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
[strongSelf pauseRealtimeStream];
|
||||||
|
strongSelf->_isInBackground = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Autofetch Helpers
|
||||||
|
|
||||||
|
- (void)fetchLatestConfig:(NSInteger)remainingAttempts targetVersion:(NSInteger)targetVersion {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
NSInteger attempts = remainingAttempts - 1;
|
||||||
|
|
||||||
|
[strongSelf->_configFetch
|
||||||
|
realtimeFetchConfigWithNoExpirationDuration:gFetchAttempts - attempts
|
||||||
|
completionHandler:^(FIRRemoteConfigFetchStatus status,
|
||||||
|
FIRRemoteConfigUpdate *update,
|
||||||
|
NSError *error) {
|
||||||
|
if (error != nil) {
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000010",
|
||||||
|
@"Failed to retrive config due to fetch error. "
|
||||||
|
@"Error: %@",
|
||||||
|
error);
|
||||||
|
return [self propogateErrors:error];
|
||||||
|
}
|
||||||
|
if (status == FIRRemoteConfigFetchStatusSuccess) {
|
||||||
|
if ([strongSelf->_configFetch.templateVersionNumber
|
||||||
|
integerValue] >= targetVersion) {
|
||||||
|
// only notify listeners if there is a change
|
||||||
|
if ([update updatedKeys].count > 0) {
|
||||||
|
for (RCNConfigUpdateCompletion listener in strongSelf
|
||||||
|
->_listeners) {
|
||||||
|
listener(update, nil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FIRLogDebug(
|
||||||
|
kFIRLoggerRemoteConfig, @"I-RCN000016",
|
||||||
|
@"Fetched config's template version is outdated, "
|
||||||
|
@"re-fetching");
|
||||||
|
[strongSelf autoFetch:attempts targetVersion:targetVersion];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000016",
|
||||||
|
@"Fetched config's template version is "
|
||||||
|
@"outdated, re-fetching");
|
||||||
|
[strongSelf autoFetch:attempts targetVersion:targetVersion];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)scheduleFetch:(NSInteger)remainingAttempts targetVersion:(NSInteger)targetVersion {
|
||||||
|
/// Needs fetch to occur between 0 - 3 seconds. Randomize to not cause DDoS alerts in backend
|
||||||
|
dispatch_time_t executionDelay =
|
||||||
|
dispatch_time(DISPATCH_TIME_NOW, arc4random_uniform(4) * NSEC_PER_SEC);
|
||||||
|
dispatch_after(executionDelay, _realtimeLockQueue, ^{
|
||||||
|
[self fetchLatestConfig:remainingAttempts targetVersion:targetVersion];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform fetch and handle developers callbacks
|
||||||
|
- (void)autoFetch:(NSInteger)remainingAttempts targetVersion:(NSInteger)targetVersion {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
if (remainingAttempts == 0) {
|
||||||
|
NSError *error = [NSError errorWithDomain:FIRRemoteConfigUpdateErrorDomain
|
||||||
|
code:FIRRemoteConfigUpdateErrorNotFetched
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
@"Unable to fetch the latest version of the template."
|
||||||
|
}];
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000011",
|
||||||
|
@"Ran out of fetch attempts, cannot find target config version.");
|
||||||
|
[self propogateErrors:error];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[strongSelf scheduleFetch:remainingAttempts targetVersion:targetVersion];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSURLSession Delegates
|
||||||
|
|
||||||
|
- (void)evaluateStreamResponse:(NSDictionary *)response error:(NSError *)dataError {
|
||||||
|
NSInteger updateTemplateVersion = 1;
|
||||||
|
if (dataError == nil) {
|
||||||
|
if ([response objectForKey:kTemplateVersionNumberKey]) {
|
||||||
|
updateTemplateVersion = [[response objectForKey:kTemplateVersionNumberKey] integerValue];
|
||||||
|
}
|
||||||
|
if ([response objectForKey:kIsFeatureDisabled]) {
|
||||||
|
self->_isRealtimeDisabled = [response objectForKey:kIsFeatureDisabled];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->_isRealtimeDisabled) {
|
||||||
|
[self pauseRealtimeStream];
|
||||||
|
NSError *error = [NSError
|
||||||
|
errorWithDomain:FIRRemoteConfigUpdateErrorDomain
|
||||||
|
code:FIRRemoteConfigUpdateErrorUnavailable
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
@"The server is temporarily unavailable. Try again in a few minutes."
|
||||||
|
}];
|
||||||
|
[self propogateErrors:error];
|
||||||
|
} else {
|
||||||
|
NSInteger clientTemplateVersion = [_configFetch.templateVersionNumber integerValue];
|
||||||
|
if (updateTemplateVersion > clientTemplateVersion) {
|
||||||
|
[self autoFetch:gFetchAttempts targetVersion:updateTemplateVersion];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSError *error =
|
||||||
|
[NSError errorWithDomain:FIRRemoteConfigUpdateErrorDomain
|
||||||
|
code:FIRRemoteConfigUpdateErrorMessageInvalid
|
||||||
|
userInfo:@{NSLocalizedDescriptionKey : @"Unable to parse ConfigUpdate."}];
|
||||||
|
[self propogateErrors:error];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delegate to asynchronously handle every new notification that comes over the wire. Auto-fetches
|
||||||
|
/// and runs callback for each new notification
|
||||||
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
dataTask:(NSURLSessionDataTask *)dataTask
|
||||||
|
didReceiveData:(NSData *)data {
|
||||||
|
NSError *dataError;
|
||||||
|
NSString *strData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
/// If response data contains the API enablement link, return the entire message to the user in
|
||||||
|
/// the form of a error.
|
||||||
|
if ([strData containsString:kServerForbiddenStatusCode]) {
|
||||||
|
NSError *error = [NSError errorWithDomain:FIRRemoteConfigUpdateErrorDomain
|
||||||
|
code:FIRRemoteConfigUpdateErrorStreamError
|
||||||
|
userInfo:@{NSLocalizedDescriptionKey : strData}];
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000021", @"Cannot establish connection. %@", error);
|
||||||
|
[self propogateErrors:error];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSRange endRange = [strData rangeOfString:@"}"];
|
||||||
|
NSRange beginRange = [strData rangeOfString:@"{"];
|
||||||
|
if (beginRange.location != NSNotFound && endRange.location != NSNotFound) {
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000015",
|
||||||
|
@"Received config update message on stream.");
|
||||||
|
NSRange msgRange =
|
||||||
|
NSMakeRange(beginRange.location, endRange.location - beginRange.location + 1);
|
||||||
|
strData = [strData substringWithRange:msgRange];
|
||||||
|
data = [strData dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data
|
||||||
|
options:NSJSONReadingMutableContainers
|
||||||
|
error:&dataError];
|
||||||
|
|
||||||
|
[self evaluateStreamResponse:response error:dataError];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if response code is retryable
|
||||||
|
- (bool)isStatusCodeRetryable:(NSInteger)statusCode {
|
||||||
|
return statusCode == kRCNFetchResponseHTTPStatusClientTimeout ||
|
||||||
|
statusCode == kRCNFetchResponseHTTPStatusTooManyRequests ||
|
||||||
|
statusCode == kRCNFetchResponseHTTPStatusCodeServiceUnavailable ||
|
||||||
|
statusCode == kRCNFetchResponseHTTPStatusCodeBadGateway ||
|
||||||
|
statusCode == kRCNFetchResponseHTTPStatusCodeGatewayTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delegate to handle initial reply from the server
|
||||||
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
dataTask:(NSURLSessionDataTask *)dataTask
|
||||||
|
didReceiveResponse:(NSURLResponse *)response
|
||||||
|
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
|
||||||
|
_isRequestInProgress = false;
|
||||||
|
NSHTTPURLResponse *_httpURLResponse = (NSHTTPURLResponse *)response;
|
||||||
|
NSInteger statusCode = [_httpURLResponse statusCode];
|
||||||
|
|
||||||
|
if (statusCode == 403) {
|
||||||
|
completionHandler(NSURLSessionResponseAllow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusCode != kRCNFetchResponseHTTPStatusOk) {
|
||||||
|
[self->_settings updateRealtimeExponentialBackoffTime];
|
||||||
|
[self pauseRealtimeStream];
|
||||||
|
|
||||||
|
if ([self isStatusCodeRetryable:statusCode]) {
|
||||||
|
[self retryHTTPConnection];
|
||||||
|
} else {
|
||||||
|
NSError *error = [NSError
|
||||||
|
errorWithDomain:FIRRemoteConfigUpdateErrorDomain
|
||||||
|
code:FIRRemoteConfigUpdateErrorStreamError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
[NSString stringWithFormat:@"Unable to connect to the server. Try again in "
|
||||||
|
@"a few minutes. Http Status code: %@",
|
||||||
|
[@(statusCode) stringValue]]
|
||||||
|
}];
|
||||||
|
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000021", @"Cannot establish connection. Error: %@",
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/// on success reset retry parameters
|
||||||
|
_remainingRetryCount = gMaxRetries;
|
||||||
|
[self->_settings setRealtimeRetryCount:0];
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(NSURLSessionResponseAllow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delegate to handle data task completion
|
||||||
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
task:(NSURLSessionTask *)task
|
||||||
|
didCompleteWithError:(NSError *)error {
|
||||||
|
_isRequestInProgress = false;
|
||||||
|
if (error != nil && [error code] != NSURLErrorCancelled) {
|
||||||
|
[self->_settings updateRealtimeExponentialBackoffTime];
|
||||||
|
}
|
||||||
|
[self pauseRealtimeStream];
|
||||||
|
[self retryHTTPConnection];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delegate to handle session invalidation
|
||||||
|
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
|
||||||
|
if (!_isRequestInProgress) {
|
||||||
|
if (error != nil) {
|
||||||
|
[self->_settings updateRealtimeExponentialBackoffTime];
|
||||||
|
}
|
||||||
|
[self pauseRealtimeStream];
|
||||||
|
[self retryHTTPConnection];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Top level methods
|
||||||
|
|
||||||
|
- (void)beginRealtimeStream {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
bool noRunningConnection =
|
||||||
|
strongSelf->_dataTask == nil || strongSelf->_dataTask.state != NSURLSessionTaskStateRunning;
|
||||||
|
bool canMakeConnection = noRunningConnection && [strongSelf->_listeners count] > 0 &&
|
||||||
|
!strongSelf->_isInBackground && !strongSelf->_isRealtimeDisabled;
|
||||||
|
|
||||||
|
if (self->_settings.getRealtimeBackoffInterval > 0) {
|
||||||
|
[self retryHTTPConnection];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canMakeConnection) {
|
||||||
|
strongSelf->_isRequestInProgress = true;
|
||||||
|
NSData *compressedContent = [strongSelf createRequestBody];
|
||||||
|
[strongSelf->_request setHTTPBody:compressedContent];
|
||||||
|
strongSelf->_dataTask = [strongSelf->_session dataTaskWithRequest:strongSelf->_request];
|
||||||
|
[strongSelf->_dataTask resume];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pauseRealtimeStream {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
if (strongSelf->_dataTask != nil) {
|
||||||
|
[strongSelf->_dataTask cancel];
|
||||||
|
strongSelf->_dataTask = nil;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRConfigUpdateListenerRegistration *)addConfigUpdateListener:
|
||||||
|
(void (^_Nonnull)(FIRRemoteConfigUpdate *configUpdate, NSError *_Nullable error))listener {
|
||||||
|
if (listener == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
__block id listenerCopy = listener;
|
||||||
|
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
[strongSelf->_listeners addObject:listenerCopy];
|
||||||
|
[strongSelf beginRealtimeStream];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[FIRConfigUpdateListenerRegistration alloc] initWithClient:self
|
||||||
|
completionHandler:listenerCopy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeConfigUpdateListener:(void (^_Nonnull)(FIRRemoteConfigUpdate *configUpdate,
|
||||||
|
NSError *_Nullable error))listener {
|
||||||
|
__weak RCNConfigRealtime *weakSelf = self;
|
||||||
|
dispatch_async(_realtimeLockQueue, ^{
|
||||||
|
__strong RCNConfigRealtime *strongSelf = weakSelf;
|
||||||
|
[strongSelf->_listeners removeObject:listener];
|
||||||
|
if (strongSelf->_listeners.count == 0) {
|
||||||
|
[strongSelf pauseRealtimeStream];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
537
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigSettings.m
generated
Normal file
537
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigSettings.m
generated
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
/*
|
||||||
|
* 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 <GoogleUtilities/GULAppEnvironmentUtil.h>
|
||||||
|
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
||||||
|
|
||||||
|
static NSString *const kRCNGroupPrefix = @"frc.group.";
|
||||||
|
static NSString *const kRCNUserDefaultsKeyNamelastETag = @"lastETag";
|
||||||
|
static NSString *const kRCNUserDefaultsKeyNameLastSuccessfulFetchTime = @"lastSuccessfulFetchTime";
|
||||||
|
static NSString *const kRCNAnalyticsFirstOpenTimePropertyName = @"_fot";
|
||||||
|
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;
|
||||||
|
_lastFetchedTemplateVersion = [_userDefaultsManager lastFetchedTemplateVersion];
|
||||||
|
_lastActiveTemplateVersion = [_userDefaultsManager lastActiveTemplateVersion];
|
||||||
|
_realtimeExponentialBackoffRetryInterval =
|
||||||
|
[_userDefaultsManager currentRealtimeThrottlingRetryIntervalSeconds];
|
||||||
|
_realtimeExponentialBackoffThrottleEndTime = [_userDefaultsManager realtimeThrottleEndTime];
|
||||||
|
_realtimeRetryCount = [_userDefaultsManager realtimeRetryCount];
|
||||||
|
}
|
||||||
|
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
|
||||||
|
namespace:_FIRNamespace] 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
|
||||||
|
namespace:_FIRNamespace
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the last Realtime stream attempt was not successful, update the (exponential backoff) period
|
||||||
|
/// that we wait until trying again. Any subsequent Realtime requests will be checked and allowed
|
||||||
|
/// only if past this throttle end time.
|
||||||
|
- (void)updateRealtimeExponentialBackoffTime {
|
||||||
|
// If there was only one stream attempt before, reset the retry interval.
|
||||||
|
if (_realtimeRetryCount == 0) {
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000058",
|
||||||
|
@"Throttling: Entering exponential Realtime backoff mode.");
|
||||||
|
_realtimeExponentialBackoffRetryInterval = kRCNExponentialBackoffMinimumInterval;
|
||||||
|
} else {
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000058",
|
||||||
|
@"Throttling: Updating Realtime throttling interval.");
|
||||||
|
// Double the retry interval until we hit the truncated exponential backoff. More info here:
|
||||||
|
// https://cloud.google.com/storage/docs/exponential-backoff
|
||||||
|
_realtimeExponentialBackoffRetryInterval =
|
||||||
|
((_realtimeExponentialBackoffRetryInterval * 2) < kRCNExponentialBackoffMaximumInterval)
|
||||||
|
? _realtimeExponentialBackoffRetryInterval * 2
|
||||||
|
: _realtimeExponentialBackoffRetryInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Randomize the next retry interval.
|
||||||
|
int randomPlusMinusInterval = ((arc4random() % 2) == 0) ? -1 : 1;
|
||||||
|
NSTimeInterval randomizedRetryInterval =
|
||||||
|
_realtimeExponentialBackoffRetryInterval +
|
||||||
|
(0.5 * _realtimeExponentialBackoffRetryInterval * randomPlusMinusInterval);
|
||||||
|
_realtimeExponentialBackoffThrottleEndTime =
|
||||||
|
[[NSDate date] timeIntervalSince1970] + randomizedRetryInterval;
|
||||||
|
|
||||||
|
[_userDefaultsManager setRealtimeThrottleEndTime:_realtimeExponentialBackoffThrottleEndTime];
|
||||||
|
[_userDefaultsManager
|
||||||
|
setCurrentRealtimeThrottlingRetryIntervalSeconds:_realtimeExponentialBackoffRetryInterval];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRealtimeRetryCount:(int)realtimeRetryCount {
|
||||||
|
_realtimeRetryCount = realtimeRetryCount;
|
||||||
|
[_userDefaultsManager setRealtimeRetryCount:_realtimeRetryCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTimeInterval)getRealtimeBackoffInterval {
|
||||||
|
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
|
||||||
|
return _realtimeExponentialBackoffThrottleEndTime - now;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateMetadataWithFetchSuccessStatus:(BOOL)fetchSuccess
|
||||||
|
templateVersion:(NSString *)templateVersion {
|
||||||
|
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000056", @"Updating metadata with fetch result.");
|
||||||
|
[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);
|
||||||
|
_lastFetchedTemplateVersion = templateVersion;
|
||||||
|
[_userDefaultsManager setLastFetchedTemplateVersion:templateVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
[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
|
||||||
|
namespace:_FIRNamespace
|
||||||
|
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,
|
||||||
|
RCNKeyNamespace : _FIRNamespace,
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateLastActiveTemplateVersion {
|
||||||
|
_lastActiveTemplateVersion = _lastFetchedTemplateVersion;
|
||||||
|
[_userDefaultsManager setLastActiveTemplateVersion:_lastActiveTemplateVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
// Extract first open time from user properties and send as a separate field
|
||||||
|
NSNumber *firstOpenTime = userProperties[kRCNAnalyticsFirstOpenTimePropertyName];
|
||||||
|
NSMutableDictionary *remainingUserProperties = [userProperties mutableCopy];
|
||||||
|
if (firstOpenTime != nil) {
|
||||||
|
NSDate *date = [NSDate dateWithTimeIntervalSince1970:([firstOpenTime longValue] / 1000)];
|
||||||
|
NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
|
||||||
|
NSString *firstOpenTimeISOString = [formatter stringFromDate:date];
|
||||||
|
ret = [ret stringByAppendingString:[NSString stringWithFormat:@", first_open_time:'%@'",
|
||||||
|
firstOpenTimeISOString]];
|
||||||
|
|
||||||
|
[remainingUserProperties removeObjectForKey:kRCNAnalyticsFirstOpenTimePropertyName];
|
||||||
|
}
|
||||||
|
if (remainingUserProperties.count > 0) {
|
||||||
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:remainingUserProperties
|
||||||
|
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
|
||||||
|
namespace:_FIRNamespace
|
||||||
|
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
|
||||||
|
namespace:_FIRNamespace
|
||||||
|
values:@[ @(lastApplyTimestamp) ]
|
||||||
|
completionHandler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLastSetDefaultsTimeInterval:(NSTimeInterval)lastSetDefaultsTimestamp {
|
||||||
|
_lastSetDefaultsTimeInterval = lastSetDefaultsTimestamp;
|
||||||
|
[_DBManager updateMetadataWithOption:RCNUpdateOptionDefaultTime
|
||||||
|
namespace:_FIRNamespace
|
||||||
|
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
|
||||||
25
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h
generated
Normal file
25
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h
generated
Normal file
@ -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/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
|
||||||
|
|
||||||
|
@interface FIRRemoteConfigValue ()
|
||||||
|
@property(nonatomic, readwrite, assign) FIRRemoteConfigSource source;
|
||||||
|
|
||||||
|
/// Designated initializer.
|
||||||
|
- (instancetype)initWithData:(NSData *)data
|
||||||
|
source:(FIRRemoteConfigSource)source NS_DESIGNATED_INITIALIZER;
|
||||||
|
@end
|
||||||
21
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConstants3P.m
generated
Normal file
21
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNConstants3P.m
generated
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
|
||||||
|
|
||||||
|
/// Firebase Remote Config service default namespace.
|
||||||
|
/// TODO(doudounan): Change to use this namespace defined in RemoteConfigInterop.
|
||||||
|
NSString *const FIRNamespaceGoogleMobilePlatform = @"firebase";
|
||||||
57
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.h
generated
Normal file
57
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.h
generated
Normal file
@ -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);
|
||||||
241
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.m
generated
Normal file
241
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNDevice.m
generated
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* 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 <GoogleUtilities/GULAppEnvironmentUtil.h>
|
||||||
|
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.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(void) {
|
||||||
|
return [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *FIRRemoteConfigAppBuildVersion(void) {
|
||||||
|
return [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *FIRRemoteConfigPodVersion(void) {
|
||||||
|
return FIRFirebaseVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
RCNDeviceModel FIRRemoteConfigDeviceSubtype(void) {
|
||||||
|
NSString *model = [GULAppEnvironmentUtil deviceModel];
|
||||||
|
if ([model hasPrefix:@"iPhone"]) {
|
||||||
|
return RCNDeviceModelPhone;
|
||||||
|
}
|
||||||
|
if ([model isEqualToString:@"iPad"]) {
|
||||||
|
return RCNDeviceModelTablet;
|
||||||
|
}
|
||||||
|
return RCNDeviceModelOther;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *FIRRemoteConfigDeviceCountry(void) {
|
||||||
|
return [[[NSLocale currentLocale] objectForKey:NSLocaleCountryCode] lowercaseString];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary<NSString *, NSArray *> *FIRRemoteConfigFirebaseLocaleMap(void) {
|
||||||
|
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(void) {
|
||||||
|
NSMutableArray *locales = [NSMutableArray array];
|
||||||
|
NSDictionary<NSString *, NSArray *> *localesMap = FIRRemoteConfigFirebaseLocaleMap();
|
||||||
|
for (NSString *key in localesMap) {
|
||||||
|
[locales addObjectsFromArray:localesMap[key]];
|
||||||
|
}
|
||||||
|
return locales;
|
||||||
|
}
|
||||||
|
NSString *FIRRemoteConfigDeviceLocale(void) {
|
||||||
|
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(void) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
56
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNPersonalization.h
generated
Normal file
56
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNPersonalization.h
generated
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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 "Interop/Analytics/Public/FIRAnalyticsInterop.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
static NSString *const kAnalyticsOriginPersonalization = @"fp";
|
||||||
|
|
||||||
|
static NSString *const kExternalEvent = @"personalization_assignment";
|
||||||
|
static NSString *const kExternalRcParameterParam = @"arm_key";
|
||||||
|
static NSString *const kExternalArmValueParam = @"arm_value";
|
||||||
|
static NSString *const kPersonalizationId = @"personalizationId";
|
||||||
|
static NSString *const kExternalPersonalizationIdParam = @"personalization_id";
|
||||||
|
static NSString *const kArmIndex = @"armIndex";
|
||||||
|
static NSString *const kExternalArmIndexParam = @"arm_index";
|
||||||
|
static NSString *const kGroup = @"group";
|
||||||
|
static NSString *const kExternalGroupParam = @"group";
|
||||||
|
|
||||||
|
static NSString *const kInternalEvent = @"_fpc";
|
||||||
|
static NSString *const kChoiceId = @"choiceId";
|
||||||
|
static NSString *const kInternalChoiceIdParam = @"_fpid";
|
||||||
|
|
||||||
|
@interface RCNPersonalization : NSObject
|
||||||
|
|
||||||
|
/// Analytics connector
|
||||||
|
@property(nonatomic, strong) id<FIRAnalyticsInterop> _Nullable analytics;
|
||||||
|
|
||||||
|
@property(atomic, strong) NSMutableDictionary *loggedChoiceIds;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/// Designated initializer.
|
||||||
|
- (instancetype)initWithAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
/// Called when an arm is pulled from Remote Config. If the arm is personalized, log information to
|
||||||
|
/// Google in another thread.
|
||||||
|
- (void)logArmActive:(NSString *)rcParameter config:(NSDictionary *)config;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
72
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNPersonalization.m
generated
Normal file
72
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNPersonalization.m
generated
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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/RCNPersonalization.h"
|
||||||
|
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h"
|
||||||
|
|
||||||
|
@implementation RCNPersonalization
|
||||||
|
|
||||||
|
- (instancetype)initWithAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self->_analytics = analytics;
|
||||||
|
self->_loggedChoiceIds = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)logArmActive:(NSString *)rcParameter config:(NSDictionary *)config {
|
||||||
|
NSDictionary *ids = config[RCNFetchResponseKeyPersonalizationMetadata];
|
||||||
|
NSDictionary<NSString *, FIRRemoteConfigValue *> *values = config[RCNFetchResponseKeyEntries];
|
||||||
|
if (ids.count < 1 || values.count < 1 || !values[rcParameter]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *metadata = ids[rcParameter];
|
||||||
|
if (!metadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *choiceId = metadata[kChoiceId];
|
||||||
|
if (choiceId == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listeners like logArmActive() are dispatched to a serial queue, so loggedChoiceIds should
|
||||||
|
// contain any previously logged RC parameter / choice ID pairs.
|
||||||
|
if (self->_loggedChoiceIds[rcParameter] == choiceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self->_loggedChoiceIds[rcParameter] = choiceId;
|
||||||
|
|
||||||
|
[self->_analytics logEventWithOrigin:kAnalyticsOriginPersonalization
|
||||||
|
name:kExternalEvent
|
||||||
|
parameters:@{
|
||||||
|
kExternalRcParameterParam : rcParameter,
|
||||||
|
kExternalArmValueParam : values[rcParameter].stringValue,
|
||||||
|
kExternalPersonalizationIdParam : metadata[kPersonalizationId],
|
||||||
|
kExternalArmIndexParam : metadata[kArmIndex],
|
||||||
|
kExternalGroupParam : metadata[kGroup]
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self->_analytics logEventWithOrigin:kAnalyticsOriginPersonalization
|
||||||
|
name:kInternalEvent
|
||||||
|
parameters:@{kInternalChoiceIdParam : choiceId}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
66
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h
generated
Normal file
66
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h
generated
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
/// Time when the next request can be made while being throttled.
|
||||||
|
@property(nonatomic, assign) NSTimeInterval realtimeThrottleEndTime;
|
||||||
|
/// The retry interval increases exponentially for cumulative Realtime failures. Refer to
|
||||||
|
/// go/rc-client-throttling for details.
|
||||||
|
@property(nonatomic, assign) NSTimeInterval currentRealtimeThrottlingRetryIntervalSeconds;
|
||||||
|
/// Realtime retry count.
|
||||||
|
@property(nonatomic, assign) int realtimeRetryCount;
|
||||||
|
/// Last fetched template version.
|
||||||
|
@property(nonatomic, assign) NSString *lastFetchedTemplateVersion;
|
||||||
|
/// Last active template version.
|
||||||
|
@property(nonatomic, assign) NSString *lastActiveTemplateVersion;
|
||||||
|
|
||||||
|
/// 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
|
||||||
313
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m
generated
Normal file
313
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m
generated
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
* 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 "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
|
||||||
|
#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.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";
|
||||||
|
static NSString *const kRCNUserDefaultsKeyNameRealtimeThrottleEndTime = @"throttleRealtimeEndTime";
|
||||||
|
static NSString *const kRCNUserDefaultsKeyNameCurrentRealtimeThrottlingRetryInterval =
|
||||||
|
@"currentRealtimeThrottlingRetryInterval";
|
||||||
|
static NSString *const kRCNUserDefaultsKeyNameRealtimeRetryCount = @"realtimeRetryCount";
|
||||||
|
|
||||||
|
@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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)lastFetchedTemplateVersion {
|
||||||
|
NSDictionary *userDefaults = [self instanceUserDefaults];
|
||||||
|
if ([userDefaults objectForKey:RCNFetchResponseKeyTemplateVersion]) {
|
||||||
|
return [userDefaults objectForKey:RCNFetchResponseKeyTemplateVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
return @"0";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLastFetchedTemplateVersion:(NSString *)templateVersion {
|
||||||
|
if (templateVersion) {
|
||||||
|
[self setInstanceUserDefaultsValue:templateVersion forKey:RCNFetchResponseKeyTemplateVersion];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)lastActiveTemplateVersion {
|
||||||
|
NSDictionary *userDefaults = [self instanceUserDefaults];
|
||||||
|
if ([userDefaults objectForKey:RCNActiveKeyTemplateVersion]) {
|
||||||
|
return [userDefaults objectForKey:RCNActiveKeyTemplateVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
return @"0";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLastActiveTemplateVersion:(NSString *)templateVersion {
|
||||||
|
if (templateVersion) {
|
||||||
|
[self setInstanceUserDefaultsValue:templateVersion forKey:RCNActiveKeyTemplateVersion];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int)realtimeRetryCount {
|
||||||
|
int realtimeRetryCount = 0;
|
||||||
|
if ([[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNameRealtimeRetryCount]) {
|
||||||
|
realtimeRetryCount = [[[self instanceUserDefaults]
|
||||||
|
objectForKey:kRCNUserDefaultsKeyNameRealtimeRetryCount] intValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
return realtimeRetryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRealtimeRetryCount:(int)realtimeRetryCount {
|
||||||
|
[self setInstanceUserDefaultsValue:[NSNumber numberWithInt:realtimeRetryCount]
|
||||||
|
forKey:kRCNUserDefaultsKeyNameRealtimeRetryCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTimeInterval)realtimeThrottleEndTime {
|
||||||
|
NSNumber *realtimeThrottleEndTime = 0;
|
||||||
|
if ([[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNameRealtimeThrottleEndTime]) {
|
||||||
|
realtimeThrottleEndTime =
|
||||||
|
[[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNameRealtimeThrottleEndTime];
|
||||||
|
}
|
||||||
|
return realtimeThrottleEndTime.doubleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRealtimeThrottleEndTime:(NSTimeInterval)throttleEndTime {
|
||||||
|
[self setInstanceUserDefaultsValue:@(throttleEndTime)
|
||||||
|
forKey:kRCNUserDefaultsKeyNameRealtimeThrottleEndTime];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTimeInterval)currentRealtimeThrottlingRetryIntervalSeconds {
|
||||||
|
NSNumber *realtimeThrottleEndTime = 0;
|
||||||
|
if ([[self instanceUserDefaults]
|
||||||
|
objectForKey:kRCNUserDefaultsKeyNameCurrentRealtimeThrottlingRetryInterval]) {
|
||||||
|
realtimeThrottleEndTime = [[self instanceUserDefaults]
|
||||||
|
objectForKey:kRCNUserDefaultsKeyNameCurrentRealtimeThrottlingRetryInterval];
|
||||||
|
}
|
||||||
|
return realtimeThrottleEndTime.doubleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setCurrentRealtimeThrottlingRetryIntervalSeconds:
|
||||||
|
(NSTimeInterval)throttlingRetryIntervalSeconds {
|
||||||
|
[self setInstanceUserDefaultsValue:@(throttlingRetryIntervalSeconds)
|
||||||
|
forKey:kRCNUserDefaultsKeyNameCurrentRealtimeThrottlingRetryInterval];
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
69
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/Codable.swift
generated
Normal file
69
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/Codable.swift
generated
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
@_exported import FirebaseRemoteConfigInternal
|
||||||
|
#endif // SWIFT_PACKAGE
|
||||||
|
import FirebaseSharedSwift
|
||||||
|
|
||||||
|
public enum RemoteConfigValueCodableError: Error {
|
||||||
|
case unsupportedType(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension RemoteConfigValue {
|
||||||
|
/// Extracts a RemoteConfigValue JSON-encoded object and decodes it to the requested type.
|
||||||
|
///
|
||||||
|
/// - Parameter asType: The type to decode the JSON-object to
|
||||||
|
func decoded<Value: Decodable>(asType: Value.Type = Value.self) throws -> Value {
|
||||||
|
if asType == Date.self {
|
||||||
|
throw RemoteConfigValueCodableError
|
||||||
|
.unsupportedType("Date type is not currently supported for " +
|
||||||
|
" Remote Config Value decoding. Please file a feature request")
|
||||||
|
}
|
||||||
|
return try FirebaseDataDecoder()
|
||||||
|
.decode(Value.self, from: FirebaseRemoteConfigValueDecoderHelper(value: self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RemoteConfigCodableError: Error {
|
||||||
|
case invalidSetDefaultsInput(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension RemoteConfig {
|
||||||
|
/// Decodes a struct from the respective Remote Config values.
|
||||||
|
///
|
||||||
|
/// - Parameter asType: The type to decode to.
|
||||||
|
func decoded<Value: Decodable>(asType: Value.Type = Value.self) throws -> Value {
|
||||||
|
let keys = allKeys(from: RemoteConfigSource.default) + allKeys(from: RemoteConfigSource.remote)
|
||||||
|
let config = keys.reduce(into: [String: FirebaseRemoteConfigValueDecoderHelper]()) {
|
||||||
|
$0[$1] = FirebaseRemoteConfigValueDecoderHelper(value: configValue(forKey: $1))
|
||||||
|
}
|
||||||
|
return try FirebaseDataDecoder().decode(Value.self, from: config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets config defaults from an encodable struct.
|
||||||
|
///
|
||||||
|
/// - Parameter value: The object to use to set the defaults.
|
||||||
|
func setDefaults<Value: Encodable>(from value: Value) throws {
|
||||||
|
guard let encoded = try FirebaseDataEncoder().encode(value) as? [String: NSObject] else {
|
||||||
|
throw RemoteConfigCodableError.invalidSetDefaultsInput(
|
||||||
|
"The setDefaults input: \(value), must be a Struct that encodes to a Dictionary"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
setDefaults(encoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
@_exported import FirebaseRemoteConfigInternal
|
||||||
|
#endif // SWIFT_PACKAGE
|
||||||
|
import FirebaseSharedSwift
|
||||||
|
|
||||||
|
/// Implement the FirebaseRemoteConfigValueDecoding protocol for the shared Firebase decoder to
|
||||||
|
/// decode Remote Config Values. It returns the four different kinds of values from
|
||||||
|
/// a RemoteConfigValue object.
|
||||||
|
struct FirebaseRemoteConfigValueDecoderHelper: FirebaseRemoteConfigValueDecoding {
|
||||||
|
let value: RemoteConfigValue
|
||||||
|
|
||||||
|
func numberValue() -> NSNumber {
|
||||||
|
return value.numberValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolValue() -> Bool {
|
||||||
|
return value.boolValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringValue() -> String {
|
||||||
|
return value.stringValue ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func dataValue() -> Data {
|
||||||
|
return value.dataValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func arrayValue() -> [AnyHashable]? {
|
||||||
|
guard let value = value.jsonValue as? [AnyHashable] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func dictionaryValue() -> [String: AnyHashable]? {
|
||||||
|
guard let value = value.jsonValue as? [String: AnyHashable] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
@_exported import FirebaseRemoteConfigInternal
|
||||||
|
#endif // SWIFT_PACKAGE
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// A property wrapper that listens to a Remote Config value.
|
||||||
|
@available(iOS 14.0, macOS 11.0, macCatalyst 14.0, tvOS 14.0, watchOS 7.0, *)
|
||||||
|
@propertyWrapper
|
||||||
|
public struct RemoteConfigProperty<T: Decodable>: DynamicProperty {
|
||||||
|
@StateObject private var configValueObserver: RemoteConfigValueObservable<T>
|
||||||
|
|
||||||
|
/// Remote Config key name for this property
|
||||||
|
public let key: String
|
||||||
|
|
||||||
|
public var wrappedValue: T {
|
||||||
|
configValueObserver.configValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an instance by providing a config key.
|
||||||
|
///
|
||||||
|
/// - Parameter key: key name
|
||||||
|
/// - Parameter fallback: The value to fall back to if the key doesn't exist in remote or default
|
||||||
|
/// configs
|
||||||
|
public init(key: String, fallback: T) {
|
||||||
|
self.key = key
|
||||||
|
|
||||||
|
_configValueObserver = StateObject(
|
||||||
|
wrappedValue: RemoteConfigValueObservable<T>(
|
||||||
|
key: key,
|
||||||
|
fallbackValue: fallback
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
@_exported import FirebaseRemoteConfigInternal
|
||||||
|
#endif // SWIFT_PACKAGE
|
||||||
|
import FirebaseCore
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension Notification.Name {
|
||||||
|
// Listens to FirebaseRemoteConfig SDK if new configs are activated.
|
||||||
|
static let onRemoteConfigActivated = Notification.Name("FIRRemoteConfigActivateNotification")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure this key is consistent with kFIRGoogleAppIDKey in FirebaseCore SDK
|
||||||
|
let FirebaseRemoteConfigAppNameKey = "FIRAppNameKey"
|
||||||
|
|
||||||
|
@available(iOS 14.0, macOS 11.0, macCatalyst 14.0, tvOS 14.0, watchOS 7.0, *)
|
||||||
|
class RemoteConfigValueObservable<T: Decodable>: ObservableObject {
|
||||||
|
@Published var configValue: T
|
||||||
|
private let key: String
|
||||||
|
private let remoteConfig: RemoteConfig
|
||||||
|
private let fallbackValue: T
|
||||||
|
|
||||||
|
init(key: String, fallbackValue: T) {
|
||||||
|
self.key = key
|
||||||
|
remoteConfig = RemoteConfig.remoteConfig()
|
||||||
|
self.fallbackValue = fallbackValue
|
||||||
|
// Initialize with fallback value
|
||||||
|
configValue = fallbackValue
|
||||||
|
// Check cached remote config value
|
||||||
|
do {
|
||||||
|
let configValue: RemoteConfigValue = remoteConfig[key]
|
||||||
|
if configValue.source == .remote || configValue.source == .default {
|
||||||
|
self.configValue = try remoteConfig[key].decoded()
|
||||||
|
} else {
|
||||||
|
self.configValue = fallbackValue
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
configValue = fallbackValue
|
||||||
|
}
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self, selector: #selector(configDidActivate), name: .onRemoteConfigActivated, object: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func configDidActivate(notification: NSNotification) {
|
||||||
|
// This feature is only available in the default app.
|
||||||
|
let appName = notification.userInfo?[FirebaseRemoteConfigAppNameKey] as? String
|
||||||
|
if FirebaseApp.app()?.name != appName {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let configValue: RemoteConfigValue = remoteConfig[key]
|
||||||
|
if configValue.source == .remote {
|
||||||
|
self.configValue = try remoteConfig[key].decoded()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Suppresses a hard failure if decoding failed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/Resources/PrivacyInfo.xcprivacy
generated
Normal file
38
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/Resources/PrivacyInfo.xcprivacy
generated
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>1C8F.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
||||||
30
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/SPMSwiftHeaderWorkaround.swift
generated
Normal file
30
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/SPMSwiftHeaderWorkaround.swift
generated
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2023 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.
|
||||||
|
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
@_exported import FirebaseRemoteConfigInternal
|
||||||
|
|
||||||
|
// This is a trick to force generate a `FirebaseRemoteConfig-Swift.h` header
|
||||||
|
// that re-exports `FirebaseRemoteConfigInternal` for Objective-C clients. It
|
||||||
|
// is important for the below code to reference a Remote Config symbol defined
|
||||||
|
// in Objective-C as that will import the symbol's module
|
||||||
|
// (`FirebaseRemoteConfigInternal`) in the generated header. This allows
|
||||||
|
// Objective-C clients to import Remote Config's Objective-C API using
|
||||||
|
// `@import FirebaseRemoteConfig;`. This API is not needed for Swift clients
|
||||||
|
// and is therefore unavailable in a Swift context.
|
||||||
|
@available(*, unavailable)
|
||||||
|
@objc public extension RemoteConfig {
|
||||||
|
static var __no_op: () -> Void { {} }
|
||||||
|
}
|
||||||
|
#endif // SWIFT_PACKAGE
|
||||||
42
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/Value.swift
generated
Normal file
42
SwiftProject/Pods/FirebaseRemoteConfig/FirebaseRemoteConfig/Swift/Value.swift
generated
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
@_exported import FirebaseRemoteConfigInternal
|
||||||
|
#endif // SWIFT_PACKAGE
|
||||||
|
|
||||||
|
/// Implements subscript overloads to enable Remote Config values to be accessed
|
||||||
|
/// in a type-safe way directly from the current config.
|
||||||
|
public extension RemoteConfig {
|
||||||
|
/// Return a typed RemoteConfigValue for a key.
|
||||||
|
/// - Parameter key: A Remote Config key.
|
||||||
|
/// - Returns: A typed RemoteConfigValue.
|
||||||
|
subscript<T: Decodable>(decodedValue key: String) -> T? {
|
||||||
|
return try? configValue(forKey: key).decoded()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a Dictionary for a RemoteConfig JSON key.
|
||||||
|
/// - Parameter key: A Remote Config key.
|
||||||
|
/// - Returns: A Dictionary representing a RemoteConfig JSON value.
|
||||||
|
subscript(jsonValue key: String) -> [String: AnyHashable]? {
|
||||||
|
guard let value = configValue(forKey: key).jsonValue as? [String: AnyHashable] else {
|
||||||
|
// nil is the historical behavior for failing to extract JSON.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
68
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInterop.h
generated
Normal file
68
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInterop.h
generated
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 `getUserProperties(with:)`.
|
||||||
|
typedef void (^FIRAInteropUserPropertiesCallback)(NSDictionary<NSString *, id> *userProperties)
|
||||||
|
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
|
||||||
|
|
||||||
|
/// Connector for bridging communication between Firebase SDKs and FirebaseAnalytics APIs.
|
||||||
|
@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:
|
||||||
|
(void (^)(NSDictionary<NSString *, id> *userProperties))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
|
||||||
24
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInteropListener.h
generated
Normal file
24
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRAnalyticsInteropListener.h
generated
Normal file
@ -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
|
||||||
|
/// FirebaseAnalyticsInterop`s `registerAnalyticsListener(_:withOrigin:)`.
|
||||||
|
- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters;
|
||||||
|
|
||||||
|
@end
|
||||||
28
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropEventNames.h
generated
Normal file
28
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropEventNames.h
generated
Normal file
@ -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";
|
||||||
73
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropParameterNames.h
generated
Normal file
73
SwiftProject/Pods/FirebaseRemoteConfig/Interop/Analytics/Public/FIRInteropParameterNames.h
generated
Normal file
@ -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 (String).
|
||||||
|
/// <pre>
|
||||||
|
/// let 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
|
||||||
|
/// (String).
|
||||||
|
/// <pre>
|
||||||
|
/// let 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
|
||||||
|
/// (String).
|
||||||
|
/// <pre>
|
||||||
|
/// let 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";
|
||||||
202
SwiftProject/Pods/FirebaseRemoteConfig/LICENSE
generated
Normal file
202
SwiftProject/Pods/FirebaseRemoteConfig/LICENSE
generated
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
297
SwiftProject/Pods/FirebaseRemoteConfig/README.md
generated
Normal file
297
SwiftProject/Pods/FirebaseRemoteConfig/README.md
generated
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://cocoapods.org/pods/Firebase">
|
||||||
|
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=CocoaPods"/>
|
||||||
|
</a>
|
||||||
|
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||||
|
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=Swift%20Package%20Index&color=red"/>
|
||||||
|
</a>
|
||||||
|
<a href="https://cocoapods.org/pods/Firebase">
|
||||||
|
<img src="https://img.shields.io/github/license/Firebase/firebase-ios-sdk?style=flat"/>
|
||||||
|
</a><br/>
|
||||||
|
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||||
|
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dplatforms"/>
|
||||||
|
</a>
|
||||||
|
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
|
||||||
|
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dswift-versions"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# Firebase Apple Open Source Development
|
||||||
|
|
||||||
|
This repository contains the source code for all Apple platform Firebase SDKs except FirebaseAnalytics.
|
||||||
|
|
||||||
|
Firebase is an app development platform with tools to help you build, grow, and
|
||||||
|
monetize your app. More information about Firebase can be found on the
|
||||||
|
[official Firebase website](https://firebase.google.com).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
See the subsections below for details about the different installation methods. Where
|
||||||
|
available, it's recommended to install any libraries with a `Swift` suffix to get the
|
||||||
|
best experience when writing your app in Swift.
|
||||||
|
|
||||||
|
1. [Standard pod install](#standard-pod-install)
|
||||||
|
2. [Swift Package Manager](#swift-package-manager)
|
||||||
|
3. [Installing from the GitHub repo](#installing-from-github)
|
||||||
|
4. [Experimental Carthage](#carthage-ios-only)
|
||||||
|
|
||||||
|
### Standard pod install
|
||||||
|
|
||||||
|
For instructions on the standard pod install, visit:
|
||||||
|
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
|
||||||
|
|
||||||
|
### Swift Package Manager
|
||||||
|
|
||||||
|
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
|
||||||
|
found in the [SwiftPackageManager.md](SwiftPackageManager.md) Markdown file.
|
||||||
|
|
||||||
|
### Installing from GitHub
|
||||||
|
|
||||||
|
These instructions can be used to access the Firebase repo at other branches,
|
||||||
|
tags, or commits.
|
||||||
|
|
||||||
|
#### Background
|
||||||
|
|
||||||
|
See [the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
|
||||||
|
for instructions and options about overriding pod source locations.
|
||||||
|
|
||||||
|
#### Accessing Firebase Source Snapshots
|
||||||
|
|
||||||
|
All official releases are tagged in this repo and available via CocoaPods. To access a local
|
||||||
|
source snapshot or unreleased branch, use Podfile directives like the following:
|
||||||
|
|
||||||
|
To access FirebaseFirestore via a branch:
|
||||||
|
```ruby
|
||||||
|
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
|
||||||
|
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
|
||||||
|
```
|
||||||
|
|
||||||
|
To access FirebaseMessaging via a checked-out version of the firebase-ios-sdk repo:
|
||||||
|
```ruby
|
||||||
|
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
|
||||||
|
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Carthage (iOS only)
|
||||||
|
|
||||||
|
Instructions for the experimental Carthage distribution can be found at
|
||||||
|
[Carthage.md](Carthage.md).
|
||||||
|
|
||||||
|
### Using Firebase from a Framework or a library
|
||||||
|
|
||||||
|
For details on using Firebase from a Framework or a library, refer to [firebase_in_libraries.md](docs/firebase_in_libraries.md).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To develop Firebase software in this repository, ensure that you have at least
|
||||||
|
the following software:
|
||||||
|
|
||||||
|
* Xcode 14.1 (or later)
|
||||||
|
|
||||||
|
CocoaPods is still the canonical way to develop, but much of the repo now supports
|
||||||
|
development with Swift Package Manager.
|
||||||
|
|
||||||
|
### CocoaPods
|
||||||
|
|
||||||
|
Install the following:
|
||||||
|
* CocoaPods 1.12.0 (or later)
|
||||||
|
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
|
||||||
|
|
||||||
|
For the pod that you want to develop:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: If the CocoaPods cache is out of date, you may need to run
|
||||||
|
`pod repo update` before the `pod gen` command.
|
||||||
|
|
||||||
|
Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
|
||||||
|
those platforms. Since 10.2, Xcode does not properly handle multi-platform
|
||||||
|
CocoaPods workspaces.
|
||||||
|
|
||||||
|
Firestore has a self-contained Xcode project. See
|
||||||
|
[Firestore/README](Firestore/README.md) Markdown file.
|
||||||
|
|
||||||
|
#### Development for Catalyst
|
||||||
|
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
|
||||||
|
* Check the Mac box in the App-iOS Build Settings
|
||||||
|
* Sign the App in the Settings Signing & Capabilities tab
|
||||||
|
* Click Pods in the Project Manager
|
||||||
|
* Add Signing to the iOS host app and unit test targets
|
||||||
|
* Select the Unit-unit scheme
|
||||||
|
* Run it to build and test
|
||||||
|
|
||||||
|
Alternatively, disable signing in each target:
|
||||||
|
* Go to Build Settings tab
|
||||||
|
* Click `+`
|
||||||
|
* Select `Add User-Defined Setting`
|
||||||
|
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
|
||||||
|
|
||||||
|
### Swift Package Manager
|
||||||
|
* To enable test schemes: `./scripts/setup_spm_tests.sh`
|
||||||
|
* `open Package.swift` or double click `Package.swift` in Finder.
|
||||||
|
* Xcode will open the project
|
||||||
|
* Choose a scheme for a library to build or test suite to run
|
||||||
|
* Choose a target platform by selecting the run destination along with the scheme
|
||||||
|
|
||||||
|
### Adding a New Firebase Pod
|
||||||
|
|
||||||
|
Refer to [AddNewPod](AddNewPod.md) Markdown file for details.
|
||||||
|
|
||||||
|
### Managing Headers and Imports
|
||||||
|
|
||||||
|
For information about managing headers and imports, see [HeadersImports](HeadersImports.md) Markdown file.
|
||||||
|
|
||||||
|
### Code Formatting
|
||||||
|
|
||||||
|
To ensure that the code is formatted consistently, run the script
|
||||||
|
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/main/scripts/check.sh)
|
||||||
|
before creating a pull request (PR).
|
||||||
|
|
||||||
|
GitHub Actions will verify that any code changes are done in a style-compliant
|
||||||
|
way. Install `clang-format` and `mint`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
brew install clang-format@18
|
||||||
|
brew install mint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Unit Tests
|
||||||
|
|
||||||
|
Select a scheme and press Command-u to build a component and run its unit tests.
|
||||||
|
|
||||||
|
### Running Sample Apps
|
||||||
|
To run the sample apps and integration tests, you'll need a valid
|
||||||
|
`GoogleService-Info.plist
|
||||||
|
` file. The Firebase Xcode project contains dummy plist
|
||||||
|
files without real values, but they can be replaced with real plist files. To get your own
|
||||||
|
`GoogleService-Info.plist` files:
|
||||||
|
|
||||||
|
1. Go to the [Firebase Console](https://console.firebase.google.com/)
|
||||||
|
2. Create a new Firebase project, if you don't already have one
|
||||||
|
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
|
||||||
|
identifier (e.g., `com.google.Database-Example`)
|
||||||
|
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
|
||||||
|
|
||||||
|
### Coverage Report Generation
|
||||||
|
|
||||||
|
For coverage report generation instructions, see [scripts/code_coverage_report/README](scripts/code_coverage_report/README.md) Markdown file.
|
||||||
|
|
||||||
|
## Specific Component Instructions
|
||||||
|
See the sections below for any special instructions for those components.
|
||||||
|
|
||||||
|
### Firebase Auth
|
||||||
|
|
||||||
|
For specific Firebase Auth development, refer to the [Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about
|
||||||
|
building and running the FirebaseAuth pod along with various samples and tests.
|
||||||
|
|
||||||
|
### Firebase Database
|
||||||
|
|
||||||
|
The Firebase Database Integration tests can be run against a locally running Database Emulator
|
||||||
|
or against a production instance.
|
||||||
|
|
||||||
|
To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
|
||||||
|
running the integration test.
|
||||||
|
|
||||||
|
To run against a production instance, provide a valid `GoogleServices-Info.plist` and copy it to
|
||||||
|
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||||
|
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
|
||||||
|
running.
|
||||||
|
|
||||||
|
### Firebase Dynamic Links
|
||||||
|
|
||||||
|
Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
|
||||||
|
|
||||||
|
Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
|
||||||
|
|
||||||
|
### Firebase Performance Monitoring
|
||||||
|
|
||||||
|
For specific Firebase Performance Monitoring development, see
|
||||||
|
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
|
||||||
|
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
|
||||||
|
integrating Performance with the dev test App.
|
||||||
|
|
||||||
|
### Firebase Storage
|
||||||
|
|
||||||
|
To run the Storage Integration tests, follow the instructions in
|
||||||
|
[StorageIntegration.swift](FirebaseStorage/Tests/Integration/StorageIntegration.swift).
|
||||||
|
|
||||||
|
#### Push Notifications
|
||||||
|
|
||||||
|
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
|
||||||
|
In order to test receiving push notifications, you will need to:
|
||||||
|
|
||||||
|
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
|
||||||
|
account and enable that App ID for push notifications.
|
||||||
|
2. You'll also need to
|
||||||
|
[upload your APNs Provider Authentication Key or certificate to the
|
||||||
|
Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
|
||||||
|
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
|
||||||
|
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
|
||||||
|
|
||||||
|
#### iOS Simulator
|
||||||
|
|
||||||
|
The iOS Simulator cannot register for remote notifications and will not receive push notifications.
|
||||||
|
To receive push notifications, follow the steps above and run the app on a physical device.
|
||||||
|
|
||||||
|
## Building with Firebase on Apple platforms
|
||||||
|
|
||||||
|
Firebase provides official beta support for macOS, Catalyst, and tvOS. visionOS and watchOS
|
||||||
|
are community supported. Thanks to community contributions for many of the multi-platform PRs.
|
||||||
|
|
||||||
|
At this time, most of Firebase's products are available across Apple platforms. There are still
|
||||||
|
a few gaps, especially on visionOS and watchOS. For details about the current support matrix, see
|
||||||
|
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
|
||||||
|
in Firebase's documentation.
|
||||||
|
|
||||||
|
### visionOS
|
||||||
|
|
||||||
|
Where supported, visionOS works as expected with the exception of Firestore via Swift Package
|
||||||
|
Manager where it is required to use the source distribution.
|
||||||
|
|
||||||
|
To enable the Firestore source distribution, quit Xcode and open the desired
|
||||||
|
project from the command line with the `FIREBASE_SOURCE_FIRESTORE` environment
|
||||||
|
variable: `open --env FIREBASE_SOURCE_FIRESTORE /path/to/project.xcodeproj`.
|
||||||
|
To go back to using the binary distribution of Firestore, quit Xcode and open
|
||||||
|
Xcode like normal, without the environment variable.
|
||||||
|
|
||||||
|
### watchOS
|
||||||
|
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
|
||||||
|
work on watchOS. See the [Independent Watch App Sample](Example/watchOSSample).
|
||||||
|
|
||||||
|
Keep in mind that watchOS is not officially supported by Firebase. While we can catch basic unit
|
||||||
|
test issues with GitHub Actions, there may be some changes where the SDK no longer works as expected
|
||||||
|
on watchOS. If you encounter this, please
|
||||||
|
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
|
||||||
|
|
||||||
|
During app setup in the console, you may get to a step that mentions something like "Checking if the
|
||||||
|
app has communicated with our servers". This relies on Analytics and will not work on watchOS.
|
||||||
|
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
|
||||||
|
|
||||||
|
#### Additional Crashlytics Notes
|
||||||
|
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
|
||||||
|
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
|
||||||
|
|
||||||
|
## Combine
|
||||||
|
Thanks to contributions from the community, _FirebaseCombineSwift_ contains support for Apple's Combine
|
||||||
|
framework. This module is currently under development and not yet supported for use in production
|
||||||
|
environments. For more details, please refer to the [docs](FirebaseCombineSwift/README.md).
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
See [Roadmap](ROADMAP.md) for more about the Firebase Apple SDK Open Source
|
||||||
|
plans and directions.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
|
||||||
|
Apple SDK.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The contents of this repository are licensed under the
|
||||||
|
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
|
||||||
|
Your use of Firebase is governed by the
|
||||||
|
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
|
||||||
17
SwiftProject/Pods/Manifest.lock
generated
17
SwiftProject/Pods/Manifest.lock
generated
@ -836,6 +836,8 @@ PODS:
|
|||||||
- FirebaseAnalytics/WithoutAdIdSupport (~> 10.23.0)
|
- FirebaseAnalytics/WithoutAdIdSupport (~> 10.23.0)
|
||||||
- Firebase/CoreOnly (10.23.0):
|
- Firebase/CoreOnly (10.23.0):
|
||||||
- FirebaseCore (= 10.23.0)
|
- FirebaseCore (= 10.23.0)
|
||||||
|
- FirebaseABTesting (10.24.0):
|
||||||
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseAnalytics/WithoutAdIdSupport (10.23.0):
|
- FirebaseAnalytics/WithoutAdIdSupport (10.23.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 10.0)
|
||||||
@ -895,6 +897,14 @@ PODS:
|
|||||||
- GoogleUtilities/Environment (~> 7.8)
|
- GoogleUtilities/Environment (~> 7.8)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.8)
|
- GoogleUtilities/UserDefaults (~> 7.8)
|
||||||
- PromisesObjC (~> 2.1)
|
- PromisesObjC (~> 2.1)
|
||||||
|
- FirebaseRemoteConfig (10.24.0):
|
||||||
|
- FirebaseABTesting (~> 10.0)
|
||||||
|
- FirebaseCore (~> 10.0)
|
||||||
|
- FirebaseInstallations (~> 10.0)
|
||||||
|
- FirebaseRemoteConfigInterop (~> 10.23)
|
||||||
|
- FirebaseSharedSwift (~> 10.0)
|
||||||
|
- GoogleUtilities/Environment (~> 7.8)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
||||||
- FirebaseRemoteConfigInterop (10.23.0)
|
- FirebaseRemoteConfigInterop (10.23.0)
|
||||||
- FirebaseSessions (10.23.0):
|
- FirebaseSessions (10.23.0):
|
||||||
- FirebaseCore (~> 10.5)
|
- FirebaseCore (~> 10.5)
|
||||||
@ -1051,6 +1061,7 @@ DEPENDENCIES:
|
|||||||
- FirebaseAuth
|
- FirebaseAuth
|
||||||
- FirebaseCrashlytics
|
- FirebaseCrashlytics
|
||||||
- FirebaseFirestore
|
- FirebaseFirestore
|
||||||
|
- FirebaseRemoteConfig
|
||||||
- LLCycleScrollView
|
- LLCycleScrollView
|
||||||
- SnapKit
|
- SnapKit
|
||||||
- SVProgressHUD
|
- SVProgressHUD
|
||||||
@ -1065,6 +1076,7 @@ SPEC REPOS:
|
|||||||
- FacebookCore
|
- FacebookCore
|
||||||
- FBSDKCoreKit
|
- FBSDKCoreKit
|
||||||
- Firebase
|
- Firebase
|
||||||
|
- FirebaseABTesting
|
||||||
- FirebaseAnalytics
|
- FirebaseAnalytics
|
||||||
- FirebaseAppCheckInterop
|
- FirebaseAppCheckInterop
|
||||||
- FirebaseAuth
|
- FirebaseAuth
|
||||||
@ -1075,6 +1087,7 @@ SPEC REPOS:
|
|||||||
- FirebaseFirestore
|
- FirebaseFirestore
|
||||||
- FirebaseFirestoreInternal
|
- FirebaseFirestoreInternal
|
||||||
- FirebaseInstallations
|
- FirebaseInstallations
|
||||||
|
- FirebaseRemoteConfig
|
||||||
- FirebaseRemoteConfigInterop
|
- FirebaseRemoteConfigInterop
|
||||||
- FirebaseSessions
|
- FirebaseSessions
|
||||||
- FirebaseSharedSwift
|
- FirebaseSharedSwift
|
||||||
@ -1103,6 +1116,7 @@ SPEC CHECKSUMS:
|
|||||||
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
||||||
FBSDKCoreKit: 1d5acf7c9d7a2f92bb1a242dc60cae5b7adb91df
|
FBSDKCoreKit: 1d5acf7c9d7a2f92bb1a242dc60cae5b7adb91df
|
||||||
Firebase: 333ec7c6b12fa09c77b5162cda6b862102211d50
|
Firebase: 333ec7c6b12fa09c77b5162cda6b862102211d50
|
||||||
|
FirebaseABTesting: 4431c2c56ac6e56f463b9cab05cc111078639f99
|
||||||
FirebaseAnalytics: 45f6e2e5ef8ccbb90be73ae983c3b20fa78837f7
|
FirebaseAnalytics: 45f6e2e5ef8ccbb90be73ae983c3b20fa78837f7
|
||||||
FirebaseAppCheckInterop: a1955ce8c30f38f87e7d091630e871e91154d65d
|
FirebaseAppCheckInterop: a1955ce8c30f38f87e7d091630e871e91154d65d
|
||||||
FirebaseAuth: 22eb85d3853141de7062bfabc131aa7d6335cade
|
FirebaseAuth: 22eb85d3853141de7062bfabc131aa7d6335cade
|
||||||
@ -1113,6 +1127,7 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseFirestore: 3478b0580f6c16d895460611b4fcec93955f4717
|
FirebaseFirestore: 3478b0580f6c16d895460611b4fcec93955f4717
|
||||||
FirebaseFirestoreInternal: 627b23f682c1c2aad38ba1345ed3ca6574c5a89c
|
FirebaseFirestoreInternal: 627b23f682c1c2aad38ba1345ed3ca6574c5a89c
|
||||||
FirebaseInstallations: 42d6ead4605d6eafb3b6683674e80e18eb6f2c35
|
FirebaseInstallations: 42d6ead4605d6eafb3b6683674e80e18eb6f2c35
|
||||||
|
FirebaseRemoteConfig: 95dddc50496b37eef199dadce850d5652b534b43
|
||||||
FirebaseRemoteConfigInterop: cbc87ffa4932719a7911a08e94510f18f026f5a7
|
FirebaseRemoteConfigInterop: cbc87ffa4932719a7911a08e94510f18f026f5a7
|
||||||
FirebaseSessions: f06853e30f99fe42aa511014d7ee6c8c319f08a3
|
FirebaseSessions: f06853e30f99fe42aa511014d7ee6c8c319f08a3
|
||||||
FirebaseSharedSwift: c92645b392db3c41a83a0aa967de16f8bad25568
|
FirebaseSharedSwift: c92645b392db3c41a83a0aa967de16f8bad25568
|
||||||
@ -1133,6 +1148,6 @@ SPEC CHECKSUMS:
|
|||||||
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
||||||
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
||||||
|
|
||||||
PODFILE CHECKSUM: 348e68c1ad5662db981acd0c0690ba09e3436445
|
PODFILE CHECKSUM: d9797551bca99266176914268501259fabf3ff32
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.15.2
|
||||||
|
|||||||
80416
SwiftProject/Pods/Pods.xcodeproj/project.pbxproj
generated
80416
SwiftProject/Pods/Pods.xcodeproj/project.pbxproj
generated
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1500"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "E9F5D289F4688880F8AEA528F4783976"
|
||||||
|
BuildableName = "FirebaseABTesting_Privacy.bundle"
|
||||||
|
BlueprintName = "FirebaseABTesting-FirebaseABTesting_Privacy"
|
||||||
|
ReferencedContainer = "container:Pods.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
58
SwiftProject/Pods/Pods.xcodeproj/xcuserdata/aaa.xcuserdatad/xcschemes/FirebaseABTesting.xcscheme
generated
Normal file
58
SwiftProject/Pods/Pods.xcodeproj/xcuserdata/aaa.xcuserdatad/xcschemes/FirebaseABTesting.xcscheme
generated
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1500"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "8F68D031908A0059566798048C48F776"
|
||||||
|
BuildableName = "FirebaseABTesting.framework"
|
||||||
|
BlueprintName = "FirebaseABTesting"
|
||||||
|
ReferencedContainer = "container:Pods.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1500"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "C7B621349CB28ED0EBA4C3E416947AB6"
|
||||||
|
BuildableName = "FirebaseRemoteConfig_Privacy.bundle"
|
||||||
|
BlueprintName = "FirebaseRemoteConfig-FirebaseRemoteConfig_Privacy"
|
||||||
|
ReferencedContainer = "container:Pods.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
58
SwiftProject/Pods/Pods.xcodeproj/xcuserdata/aaa.xcuserdatad/xcschemes/FirebaseRemoteConfig.xcscheme
generated
Normal file
58
SwiftProject/Pods/Pods.xcodeproj/xcuserdata/aaa.xcuserdatad/xcschemes/FirebaseRemoteConfig.xcscheme
generated
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1500"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "51471EE35F2E9E19E51A74944E5ABB7F"
|
||||||
|
BuildableName = "FirebaseRemoteConfig.framework"
|
||||||
|
BlueprintName = "FirebaseRemoteConfig"
|
||||||
|
ReferencedContainer = "container:Pods.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@ -39,6 +39,16 @@
|
|||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>FirebaseABTesting-FirebaseABTesting_Privacy.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
<key>FirebaseABTesting.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
<key>FirebaseAnalytics.xcscheme</key>
|
<key>FirebaseAnalytics.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
@ -129,6 +139,16 @@
|
|||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>FirebaseRemoteConfig-FirebaseRemoteConfig_Privacy.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
<key>FirebaseRemoteConfig.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
<key>FirebaseRemoteConfigInterop.xcscheme</key>
|
<key>FirebaseRemoteConfigInterop.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
|
|||||||
26
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting-Info.plist
generated
Normal file
26
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting-Info.plist
generated
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>10.24.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
5
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting-dummy.m
generated
Normal file
5
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting-dummy.m
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@interface PodsDummy_FirebaseABTesting : NSObject
|
||||||
|
@end
|
||||||
|
@implementation PodsDummy_FirebaseABTesting
|
||||||
|
@end
|
||||||
19
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting-umbrella.h
generated
Normal file
19
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting-umbrella.h
generated
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifdef __OBJC__
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#else
|
||||||
|
#ifndef FOUNDATION_EXPORT
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#define FOUNDATION_EXPORT extern "C"
|
||||||
|
#else
|
||||||
|
#define FOUNDATION_EXPORT extern
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#import "FirebaseABTesting.h"
|
||||||
|
#import "FIRExperimentController.h"
|
||||||
|
#import "FIRLifecycleEvents.h"
|
||||||
|
|
||||||
|
FOUNDATION_EXPORT double FirebaseABTestingVersionNumber;
|
||||||
|
FOUNDATION_EXPORT const unsigned char FirebaseABTestingVersionString[];
|
||||||
|
|
||||||
16
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting.debug.xcconfig
generated
Normal file
16
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting.debug.xcconfig
generated
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||||
|
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting
|
||||||
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC"
|
||||||
|
GCC_C_LANGUAGE_STANDARD = c99
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
HEADER_SEARCH_PATHS = $(inherited) "${PODS_TARGET_SRCROOT}"
|
||||||
|
OTHER_LDFLAGS = $(inherited) -framework "FirebaseCore" -framework "Foundation" -framework "UIKit"
|
||||||
|
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||||
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||||
|
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||||
|
PODS_ROOT = ${SRCROOT}
|
||||||
|
PODS_TARGET_SRCROOT = ${PODS_ROOT}/FirebaseABTesting
|
||||||
|
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||||
|
SKIP_INSTALL = YES
|
||||||
|
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||||
6
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting.modulemap
generated
Normal file
6
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting.modulemap
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
framework module FirebaseABTesting {
|
||||||
|
umbrella header "FirebaseABTesting-umbrella.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
}
|
||||||
16
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting.release.xcconfig
generated
Normal file
16
SwiftProject/Pods/Target Support Files/FirebaseABTesting/FirebaseABTesting.release.xcconfig
generated
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||||
|
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting
|
||||||
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC"
|
||||||
|
GCC_C_LANGUAGE_STANDARD = c99
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
HEADER_SEARCH_PATHS = $(inherited) "${PODS_TARGET_SRCROOT}"
|
||||||
|
OTHER_LDFLAGS = $(inherited) -framework "FirebaseCore" -framework "Foundation" -framework "UIKit"
|
||||||
|
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||||
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||||
|
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||||
|
PODS_ROOT = ${SRCROOT}
|
||||||
|
PODS_TARGET_SRCROOT = ${PODS_ROOT}/FirebaseABTesting
|
||||||
|
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||||
|
SKIP_INSTALL = YES
|
||||||
|
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>10.24.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user