Skip to content

Commit c0f1c3c

Browse files
authored
[rc-swift] Personalization (#14284)
1 parent 9fb11b8 commit c0f1c3c

File tree

6 files changed

+114
-138
lines changed

6 files changed

+114
-138
lines changed

FirebaseRemoteConfig/Sources/FIRRemoteConfig.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
2323
#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
2424
#import "FirebaseRemoteConfig/Sources/RCNConfigRealtime.h"
25-
#import "FirebaseRemoteConfig/Sources/RCNPersonalization.h"
2625

2726
#import "FirebaseRemoteConfig/FirebaseRemoteConfig-Swift.h"
2827

@@ -182,7 +181,7 @@ - (instancetype)initWithAppName:(NSString *)appName
182181
RCNPersonalization *personalization =
183182
[[RCNPersonalization alloc] initWithAnalytics:analytics];
184183
[self addListener:^(NSString *key, NSDictionary *config) {
185-
[personalization logArmActive:key config:config];
184+
[personalization logArmActiveWithRcParameter:key config:config];
186185
}];
187186
}
188187
}

FirebaseRemoteConfig/Sources/RCNPersonalization.h

Lines changed: 0 additions & 56 deletions
This file was deleted.

FirebaseRemoteConfig/Sources/RCNPersonalization.m

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
17+
// TODO: AnalyticInterop refactor
18+
// import FirebaseAnalyticsInterop
19+
20+
private let kAnalyticsOriginPersonalization = "fp"
21+
private let kExternalEvent = "personalization_assignment"
22+
private let kExternalRcParameterParam = "arm_key"
23+
private let kExternalArmValueParam = "arm_value"
24+
private let kPersonalizationId = "personalizationId"
25+
private let kExternalPersonalizationIdParam = "personalization_id"
26+
private let kArmIndex = "armIndex"
27+
private let kExternalArmIndexParam = "arm_index"
28+
private let kGroup = "group"
29+
private let kExternalGroupParam = "group"
30+
31+
private let kInternalEvent = "_fpc"
32+
private let kChoiceId = "choiceId"
33+
private let kInternalChoiceIdParam = "_fpid"
34+
35+
@objc(RCNPersonalization)
36+
public class Personalization: NSObject {
37+
/// Analytics connector.
38+
var analytics: FIRAnalyticsInterop?
39+
40+
private var loggedChoiceIds = [String: String]()
41+
42+
/// Designated initializer.
43+
@objc public init(analytics: FIRAnalyticsInterop?) {
44+
self.analytics = analytics
45+
super.init()
46+
}
47+
48+
/// Called when an arm is pulled from Remote Config. If the arm is personalized, log information
49+
/// to
50+
/// Google Analytics in another thread.
51+
@objc public func logArmActive(rcParameter: String, config: [String: Any]) {
52+
guard let ids =
53+
config[ConfigConstants.fetchResponseKeyPersonalizationMetadata] as? [String: Any],
54+
let values = config[ConfigConstants.fetchResponseKeyEntries] as? [String: RemoteConfigValue],
55+
let value = values[rcParameter] else {
56+
return
57+
}
58+
59+
guard let metadata = ids[rcParameter] as? [String: AnyHashable],
60+
let choiceId = metadata[kChoiceId] as? String else {
61+
return
62+
}
63+
64+
// Listeners like logArmActive() are dispatched to a serial queue, so loggedChoiceIds should
65+
// contain any previously logged RC parameter / choice ID pairs.
66+
if loggedChoiceIds[rcParameter] == choiceId {
67+
return
68+
}
69+
loggedChoiceIds[rcParameter] = choiceId
70+
71+
analytics?.logEvent(
72+
withOrigin: kAnalyticsOriginPersonalization,
73+
name: kExternalEvent,
74+
parameters: [
75+
kExternalRcParameterParam: rcParameter,
76+
kExternalArmValueParam: value.stringValue,
77+
kExternalPersonalizationIdParam: metadata[kPersonalizationId] ?? "",
78+
// Provide default value if nil
79+
kExternalArmIndexParam: metadata[kArmIndex] ?? "", // Provide default value if nil
80+
kExternalGroupParam: metadata[kGroup] ?? "", // Provide default value if nil
81+
]
82+
)
83+
84+
analytics?.logEvent(withOrigin: kAnalyticsOriginPersonalization,
85+
name: kInternalEvent,
86+
parameters: [kInternalChoiceIdParam: choiceId])
87+
}
88+
}

FirebaseRemoteConfig/SwiftNew/RemoteConfigComponent.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ import FirebaseRemoteConfigInterop
2323
// TODO(ncooke3): Move to another pod.
2424
@objc(AnalyticsInterop) public protocol FIRAnalyticsInterop {
2525
func getUserProperties(callback: @escaping ([String: Any]) -> Void)
26+
func logEvent(withOrigin origin: String,
27+
name: String,
28+
parameters: [String: Any])
2629
}
2730

2831
/// Provides and creates instances of Remote Config based on the namespace provided. Used in the

FirebaseRemoteConfig/Tests/Unit/RCNPersonalizationTest.m

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,26 @@
2323
// #import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h"
2424
#import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
2525
#import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
26-
#import "FirebaseRemoteConfig/Sources/RCNPersonalization.h"
2726
#import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
2827
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
2928

30-
#import "FirebaseRemoteConfig/FirebaseRemoteConfig-Swift.h"
29+
@import FirebaseRemoteConfig;
30+
31+
static NSString *const kAnalyticsOriginPersonalization = @"fp";
32+
33+
static NSString *const kExternalEvent = @"personalization_assignment";
34+
static NSString *const kExternalRcParameterParam = @"arm_key";
35+
static NSString *const kExternalArmValueParam = @"arm_value";
36+
static NSString *const kPersonalizationId = @"personalizationId";
37+
static NSString *const kExternalPersonalizationIdParam = @"personalization_id";
38+
static NSString *const kArmIndex = @"armIndex";
39+
static NSString *const kExternalArmIndexParam = @"arm_index";
40+
static NSString *const kGroup = @"group";
41+
static NSString *const kExternalGroupParam = @"group";
42+
43+
static NSString *const kInternalEvent = @"_fpc";
44+
static NSString *const kChoiceId = @"choiceId";
45+
static NSString *const kInternalChoiceIdParam = @"_fpid";
3146

3247
@interface RCNConfigFetch (ForTest)
3348
- (NSURLSessionDataTask *)URLSessionDataTaskWithContent:(NSData *)content
@@ -118,8 +133,7 @@ - (void)tearDown {
118133

119134
- (void)testNonPersonalizationKey {
120135
[_fakeLogs removeAllObjects];
121-
122-
[_personalization logArmActive:@"key3" config:_configContainer];
136+
[_personalization logArmActiveWithRcParameter:@"key3" config:_configContainer];
123137

124138
OCMVerify(never(),
125139
[_analyticsMock logEventWithOrigin:kAnalyticsOriginPersonalization
@@ -134,7 +148,7 @@ - (void)testNonPersonalizationKey {
134148
- (void)testSinglePersonalizationKey {
135149
[_fakeLogs removeAllObjects];
136150

137-
[_personalization logArmActive:@"key1" config:_configContainer];
151+
[_personalization logArmActiveWithRcParameter:@"key1" config:_configContainer];
138152

139153
OCMVerify(times(2),
140154
[_analyticsMock logEventWithOrigin:kAnalyticsOriginPersonalization
@@ -161,9 +175,9 @@ - (void)testSinglePersonalizationKey {
161175
- (void)testMultiplePersonalizationKeys {
162176
[_fakeLogs removeAllObjects];
163177

164-
[_personalization logArmActive:@"key1" config:_configContainer];
165-
[_personalization logArmActive:@"key2" config:_configContainer];
166-
[_personalization logArmActive:@"key1" config:_configContainer];
178+
[_personalization logArmActiveWithRcParameter:@"key1" config:_configContainer];
179+
[_personalization logArmActiveWithRcParameter:@"key2" config:_configContainer];
180+
[_personalization logArmActiveWithRcParameter:@"key1" config:_configContainer];
167181

168182
OCMVerify(times(4),
169183
[_analyticsMock logEventWithOrigin:kAnalyticsOriginPersonalization

0 commit comments

Comments
 (0)