From e95fac6e6d4a561f90e85331e16fba46971d32a0 Mon Sep 17 00:00:00 2001 From: Themis wang Date: Tue, 21 Nov 2023 14:08:32 -0500 Subject: [PATCH] tests --- Crashlytics/Crashlytics/FIRCrashlytics.m | 14 +++++-- .../Interop/RemoteConfigInterop.swift | 14 +++++++ .../Interop/RolloutAssignment.swift | 39 +++++++++++++++++++ .../Interop/RolloutsStateSubscriber.swift | 13 +++++++ .../Sources/FIRRemoteConfig.m | 7 +++- .../Sources/FIRRemoteConfigComponent.h | 14 ++++++- .../Sources/FIRRemoteConfigComponent.m | 23 ++++++++++- .../Sources/Private/FIRRemoteConfig_Private.h | 5 ++- .../Sources/Private/RCNConfigFetch.h | 2 + FirebaseRemoteConfig/Sources/RCNConfigFetch.m | 3 ++ Package.swift | 25 +++++++++--- 11 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 FirebaseRemoteConfig/Interop/RemoteConfigInterop.swift create mode 100644 FirebaseRemoteConfig/Interop/RolloutAssignment.swift create mode 100644 FirebaseRemoteConfig/Interop/RolloutsStateSubscriber.swift diff --git a/Crashlytics/Crashlytics/FIRCrashlytics.m b/Crashlytics/Crashlytics/FIRCrashlytics.m index 4d112cddad43..3d5163e5aa9b 100644 --- a/Crashlytics/Crashlytics/FIRCrashlytics.m +++ b/Crashlytics/Crashlytics/FIRCrashlytics.m @@ -58,6 +58,7 @@ #import @import FirebaseSessions; +@import FirebaseRemoteConfigInterop; #if TARGET_OS_IPHONE #import @@ -104,7 +105,8 @@ - (instancetype)initWithApp:(FIRApp *)app appInfo:(NSDictionary *)appInfo installations:(FIRInstallations *)installations analytics:(id)analytics - sessions:(id)sessions { + sessions:(id)sessions + remoteConfig:(id)remoteConfig { self = [super init]; if (self) { @@ -206,6 +208,10 @@ + (void)load { FIRDependency *sessionsDep = [FIRDependency dependencyWithProtocol:@protocol(FIRSessionsProvider)]; + FIRDependency *remoteConfigDep = + [FIRDependency dependencyWithProtocol:@protocol(FIRRemoteConfigInterop)]; + + // break point on this also with rc creatioBlock and check the order FIRComponentCreationBlock creationBlock = ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) { if (!container.app.isDefaultApp) { @@ -215,6 +221,7 @@ + (void)load { id analytics = FIR_COMPONENT(FIRAnalyticsInterop, container); id sessions = FIR_COMPONENT(FIRSessionsProvider, container); + id remoteConfig = FIR_COMPONENT(FIRRemoteConfigInterop, container); FIRInstallations *installations = [FIRInstallations installationsWithApp:container.app]; @@ -224,13 +231,14 @@ + (void)load { appInfo:NSBundle.mainBundle.infoDictionary installations:installations analytics:analytics - sessions:sessions]; + sessions:sessions + remoteConfig:remoteConfig]; }; FIRComponent *component = [FIRComponent componentWithProtocol:@protocol(FIRCrashlyticsInstanceProvider) instantiationTiming:FIRInstantiationTimingEagerInDefaultApp - dependencies:@[ analyticsDep, sessionsDep ] + dependencies:@[ analyticsDep, sessionsDep, remoteConfigDep ] creationBlock:creationBlock]; return @[ component ]; } diff --git a/FirebaseRemoteConfig/Interop/RemoteConfigInterop.swift b/FirebaseRemoteConfig/Interop/RemoteConfigInterop.swift new file mode 100644 index 000000000000..9a1e2db6ca96 --- /dev/null +++ b/FirebaseRemoteConfig/Interop/RemoteConfigInterop.swift @@ -0,0 +1,14 @@ +// +// File.swift +// +// +// Created by Themis Wang on 2023-11-16. +// + +import Foundation + +@objc(FIRRemoteConfigInterop) +public protocol RemoteConfigInterop { + func registerRolloutsStateSubscriber(_ namespace: String, + subscriber: RolloutsStateSubscriber?) +} diff --git a/FirebaseRemoteConfig/Interop/RolloutAssignment.swift b/FirebaseRemoteConfig/Interop/RolloutAssignment.swift new file mode 100644 index 000000000000..563a3e0b301c --- /dev/null +++ b/FirebaseRemoteConfig/Interop/RolloutAssignment.swift @@ -0,0 +1,39 @@ +// +// File.swift +// +// +// Created by Themis Wang on 2023-11-16. +// + +import Foundation + +@objc(FIRRolloutAssignment) +public class RolloutAssignment: NSObject { + @objc public var rolloutId: String + @objc public var variantId: String + @objc public var templateVersion: String + @objc public var parameterKey: String + @objc public var parameterValue: String + + public init(rolloutId: String, variantId: String, templateVersion: String, parameterKey: String, + parameterValue: String) { + self.rolloutId = rolloutId + self.variantId = variantId + self.templateVersion = templateVersion + self.parameterKey = parameterKey + self.parameterValue = parameterValue + super.init() + } +} + +@objc(FIRRolloutsState) +public class RolloutsState: NSObject { + @objc public var assignments: Set = Set() + + public init(assignmentList: [RolloutAssignment]) { + for assignment in assignmentList { + assignments.insert(assignment) + } + super.init() + } +} diff --git a/FirebaseRemoteConfig/Interop/RolloutsStateSubscriber.swift b/FirebaseRemoteConfig/Interop/RolloutsStateSubscriber.swift new file mode 100644 index 000000000000..22c8df2430c1 --- /dev/null +++ b/FirebaseRemoteConfig/Interop/RolloutsStateSubscriber.swift @@ -0,0 +1,13 @@ +// +// File.swift +// +// +// Created by Themis Wang on 2023-11-16. +// + +import Foundation + +@objc(FIRRolloutsStateSubscriber) +public protocol RolloutsStateSubscriber { + func onRolloutsStateChanged(_ rolloutsState: RolloutsState) +} diff --git a/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m b/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m index 4035d558707a..9a474b3b8c86 100644 --- a/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m +++ b/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m @@ -31,6 +31,8 @@ #import "FirebaseRemoteConfig/Sources/RCNDevice.h" #import "FirebaseRemoteConfig/Sources/RCNPersonalization.h" +@import FirebaseRemoteConfigInterop; + /// Remote Config Error Domain. /// TODO: Rename according to obj-c style for constants. NSString *const FIRRemoteConfigErrorDomain = @"com.google.remoteconfig.ErrorDomain"; @@ -137,7 +139,9 @@ - (instancetype)initWithAppName:(NSString *)appName namespace:(NSString *)FIRNamespace DBManager:(RCNConfigDBManager *)DBManager configContent:(RCNConfigContent *)configContent - analytics:(nullable id)analytics { + analytics:(nullable id)analytics + delegate:(nullable id) + remoteConfigComponentDelegate { self = [super init]; if (self) { _appName = appName; @@ -164,6 +168,7 @@ - (instancetype)initWithAppName:(NSString *)appName DBManager:_DBManager settings:_settings analytics:analytics + delegate:remoteConfigComponentDelegate experiment:_configExperiment queue:_queue namespace:_FIRNamespace diff --git a/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h b/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h index f015ea14974d..3d91b17940f9 100644 --- a/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h +++ b/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.h @@ -17,6 +17,7 @@ #import #import "FirebaseCore/Extension/FirebaseCoreInternal.h" +@import FirebaseRemoteConfigInterop; @class FIRApp; @class FIRRemoteConfig; @@ -35,9 +36,17 @@ NS_ASSUME_NONNULL_BEGIN @end +/// delegate for RC instance to send the updated rollouts state back to Component +@protocol FIRRemoteConfigComponentDelegate +- (void)rolloutsStateDidUpdate:(FIRRolloutsState *)rolloutsState; +@end + /// A concrete implementation for FIRRemoteConfigInterop to create Remote Config instances and /// register with Core's component system. -@interface FIRRemoteConfigComponent : NSObject +@interface FIRRemoteConfigComponent : NSObject /// The FIRApp that instances will be set up with. @property(nonatomic, weak, readonly) FIRApp *app; @@ -45,6 +54,9 @@ NS_ASSUME_NONNULL_BEGIN /// Cached instances of Remote Config objects. @property(nonatomic, strong) NSMutableDictionary *instances; +@property(nonatomic, strong) + NSMutableDictionary> *subscribers; + /// Default method for retrieving a Remote Config instance, or creating one if it doesn't exist. - (FIRRemoteConfig *)remoteConfigForNamespace:(NSString *)remoteConfigNamespace; diff --git a/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m b/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m index e0adc7bccbf5..03a3f02cd0e8 100644 --- a/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m +++ b/FirebaseRemoteConfig/Sources/FIRRemoteConfigComponent.m @@ -22,6 +22,8 @@ #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h" #import "Interop/Analytics/Public/FIRAnalyticsInterop.h" +@import FirebaseRemoteConfigInterop; + @implementation FIRRemoteConfigComponent /// Default method for retrieving a Remote Config instance, or creating one if it doesn't exist. @@ -60,12 +62,14 @@ - (FIRRemoteConfig *)remoteConfigForNamespace:(NSString *)remoteConfigNamespace FIRApp *app = self.app; id 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]; + analytics:analytics + delegate:self]; self.instances[remoteConfigNamespace] = instance; } @@ -78,6 +82,7 @@ - (instancetype)initWithApp:(FIRApp *)app { if (self) { _app = app; _instances = [[NSMutableDictionary alloc] initWithCapacity:1]; + _subscribers = [[NSMutableDictionary alloc] initWithCapacity:1]; } return self; } @@ -95,10 +100,13 @@ + (void)load { + (NSArray *)componentsToRegister { FIRDependency *analyticsDep = [FIRDependency dependencyWithProtocol:@protocol(FIRAnalyticsInterop) isRequired:NO]; + FIRDependency *rcInteropDep = + [FIRDependency dependencyWithProtocol:@protocol(FIRRemoteConfigInterop) isRequired:NO]; + FIRComponent *rcProvider = [FIRComponent componentWithProtocol:@protocol(FIRRemoteConfigProvider) instantiationTiming:FIRInstantiationTimingAlwaysEager - dependencies:@[ analyticsDep ] + dependencies:@[ analyticsDep, rcInteropDep ] creationBlock:^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) { // Cache the component so instances of Remote Config are cached. *isCacheable = YES; @@ -107,4 +115,15 @@ + (void)load { return @[ rcProvider ]; } +- (void)rolloutsStateDidUpdate:(nonnull FIRRolloutsState *)rolloutsState { + // calling the subscriber to update the change + [self.subscribers[@"nameSpace"] onRolloutsStateChanged:rolloutsState]; +} + +- (void)registerRolloutsStateSubscriber:(nonnull NSString *)nameSpace + subscriber:(nullable id)subscriber { + // adding the registered subscriber reference + self.subscribers[nameSpace] = subscriber; +} + @end diff --git a/FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h b/FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h index ef7def6fd9d3..aa2f6bbda988 100644 --- a/FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h +++ b/FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h @@ -22,6 +22,8 @@ @class RCNConfigDBManager; @class RCNConfigFetch; @class RCNConfigRealtime; + +@protocol FIRRemoteConfigComponentDelegate; @protocol FIRAnalyticsInterop; NS_ASSUME_NONNULL_BEGIN @@ -76,7 +78,8 @@ NS_ASSUME_NONNULL_BEGIN namespace:(NSString *)FIRNamespace DBManager:(RCNConfigDBManager *)DBManager configContent:(RCNConfigContent *)configContent - analytics:(nullable id)analytics; + analytics:(nullable id)analytics + delegate:(nullable id)remoteConfig; @end diff --git a/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h b/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h index dbef87d206af..ad368780d44f 100644 --- a/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h +++ b/FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h @@ -24,6 +24,7 @@ @class RCNConfigSettings; @class RCNConfigExperiment; @class RCNConfigDBManager; +@protocol FIRRemoteConfigComponentDelegate; NS_ASSUME_NONNULL_BEGIN @@ -44,6 +45,7 @@ typedef void (^RCNConfigFetchCompletion)(FIRRemoteConfigFetchStatus status, DBManager:(RCNConfigDBManager *)DBManager settings:(RCNConfigSettings *)settings analytics:(nullable id)analytics + delegate:(nullable id)delegate experiment:(nullable RCNConfigExperiment *)experiment queue:(dispatch_queue_t)queue namespace:(NSString *)firebaseNamespace diff --git a/FirebaseRemoteConfig/Sources/RCNConfigFetch.m b/FirebaseRemoteConfig/Sources/RCNConfigFetch.m index c3a0f16ddd89..d50a785ed128 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigFetch.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigFetch.m @@ -73,6 +73,7 @@ @implementation RCNConfigFetch { RCNConfigContent *_content; RCNConfigSettings *_settings; id _analytics; + id _delegate; RCNConfigExperiment *_experiment; dispatch_queue_t _lockQueue; /// Guard the read/write operation. NSURLSession *_fetchSession; /// Managed internally by the fetch instance. @@ -91,6 +92,7 @@ - (instancetype)initWithContent:(RCNConfigContent *)content DBManager:(RCNConfigDBManager *)DBManager settings:(RCNConfigSettings *)settings analytics:(nullable id)analytics + delegate:(nullable id)delegate experiment:(RCNConfigExperiment *)experiment queue:(dispatch_queue_t)queue namespace:(NSString *)FIRNamespace @@ -100,6 +102,7 @@ - (instancetype)initWithContent:(RCNConfigContent *)content _FIRNamespace = FIRNamespace; _settings = settings; _analytics = analytics; + _delegate = delegate; _experiment = experiment; _lockQueue = queue; _content = content; diff --git a/Package.swift b/Package.swift index 661d1a76295d..3cb72a8636b2 100644 --- a/Package.swift +++ b/Package.swift @@ -491,11 +491,16 @@ let package = Package( ), .target( name: "FirebaseCrashlytics", - dependencies: ["FirebaseCore", "FirebaseInstallations", "FirebaseSessions", - .product(name: "GoogleDataTransport", package: "GoogleDataTransport"), - .product(name: "GULEnvironment", package: "GoogleUtilities"), - .product(name: "FBLPromises", package: "Promises"), - .product(name: "nanopb", package: "nanopb")], + dependencies: [ + "FirebaseCore", + "FirebaseInstallations", + "FirebaseSessions", + "FirebaseRemoteConfigInterop", + .product(name: "GoogleDataTransport", package: "GoogleDataTransport"), + .product(name: "GULEnvironment", package: "GoogleUtilities"), + .product(name: "FBLPromises", package: "Promises"), + .product(name: "nanopb", package: "nanopb"), + ], path: "Crashlytics", exclude: [ "run", @@ -957,6 +962,7 @@ let package = Package( "FirebaseCore", "FirebaseABTesting", "FirebaseInstallations", + "FirebaseRemoteConfigInterop", .product(name: "GULNSData", package: "GoogleUtilities"), ], path: "FirebaseRemoteConfig/Sources", @@ -1028,6 +1034,15 @@ let package = Package( .headerSearchPath("../../../"), ] ), + // Internal headers only for consuming from other SDK. + .target( + name: "FirebaseRemoteConfigInterop", + path: "FirebaseRemoteConfig/Interop", + publicHeadersPath: ".", + cSettings: [ + .headerSearchPath("../../"), + ] + ), // MARK: - Firebase Sessions