Skip to content

Commit 9b48451

Browse files
christibbspaulb777
andauthored
Remove Protobuf from the ABT SDK (#5890)
* Remove Protobuf dependency, add struct-ish class that mimics ABTExperimentPayload * Remove a lot of the compiler errors, but not all of them. We need to figure out how RC serializes the NSData objects passed to FIRExperimentController in updateExperimentsWithServiceOrigin: * Add ABTExperimentPayload class that replaces generated proto, refactor classes that depend on it * Add unit testing class for ABTExperimentPayload * Updates to ABTExperimentPayload API, tests for ABTConditionalUserPropertyTest, add tests for ABTExperimentPayload * Update podspec to pull in testing resources * Add utility class for parsing test files. Use it in ABTExperimentPayloadTests * Fix FIRExperimentController tests * Better documentation * Run scripts/styles.sh * Fix whitespace * Remove protobuf imports * Bump pod spec version for ABT SDK, depend on new ABT SDK in RC. Fix up RemoteConfigExperiment to use new ABTExperimentPayload object * scripts/style.sh * Update In-app messaging to use new ABTExperimentPayload class, which involves extending the ABTExperimentPayload initializer to accept different values for overflowPolicy and experimentStartTime * Delete test payload * Revert scripts/check_imports.swift * Better initialization tags for ABTExperimentLite * Update CHANGELOGs * Remove private headers in podspec, use integerValue to parse integers * Refactor ABTExperimentPayload header into Private headers. Fix integer types for millisecond metadata * scripts/styles.sh * Fix integer parsing * Fix copyrights * Fix CHANGELOG typo: * Add private_header_files to public_header_files * Try just using NSInteger for experimentStartTimeMillis * Bump FIAM dependency on ABTesting * Use long long for milli parameters (32-bit apps) * Use longlong in test timestamps * Don't cast to nsnumber in tests * Use in64_t * Fix Test Overflow * Bump major version on ABT SDK * Bump ABTesting version in Firebase.podspec Co-authored-by: Paul Beusterien <[email protected]>
1 parent a9a0a10 commit 9b48451

33 files changed

+901
-895
lines changed

Firebase.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
6161

6262
s.subspec 'ABTesting' do |ss|
6363
ss.dependency 'Firebase/CoreOnly'
64-
ss.dependency 'FirebaseABTesting', '~> 3.3.0'
64+
ss.dependency 'FirebaseABTesting', '~> 4.0.0'
6565
end
6666

6767
s.subspec 'AdMob' do |ss|

FirebaseABTesting.podspec

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'FirebaseABTesting'
3-
s.version = '3.3.0'
3+
s.version = '4.0.0'
44
s.summary = 'Firebase ABTesting'
55

66
s.description = <<-DESC
@@ -35,20 +35,19 @@ Firebase Cloud Messaging and Firebase Remote Config in your app.
3535
'FirebaseCore/Sources/Private/*.h',
3636
]
3737
s.requires_arc = base_dir + '*.m'
38-
s.public_header_files = base_dir + 'Public/*.h', base_dir + 'Protos/developers/mobile/abt/proto/*.h'
39-
s.private_header_files = base_dir + 'Protos/developers/mobile/abt/proto/*.h'
38+
s.public_header_files = base_dir + 'Public/*.h', base_dir + 'Private/*.h'
39+
s.private_header_files = base_dir + 'Private/*.h'
4040
s.pod_target_xcconfig = {
4141
'GCC_C_LANGUAGE_STANDARD' => 'c99',
4242
'GCC_PREPROCESSOR_DEFINITIONS' =>
43-
'GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 ' +
4443
'FIRABTesting_VERSION=' + String(s.version),
4544
'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"'
4645
}
4746
s.dependency 'FirebaseCore', '~> 6.8'
48-
s.dependency 'Protobuf', '~> 3.9', '>= 3.9.2'
4947

5048
s.test_spec 'unit' do |unit_tests|
51-
unit_tests.source_files = 'FirebaseABTesting/Tests/Unit/*.[mh]'
49+
unit_tests.source_files = 'FirebaseABTesting/Tests/Unit/**/*.[mh]'
50+
unit_tests.resources = 'FirebaseABTesting/Tests/Unit/Resources/*.txt'
5251
unit_tests.requires_app_host = true
5352
unit_tests.dependency 'OCMock'
5453
end

FirebaseABTesting/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# v4.0.0
2+
- [changed] Removed Protobuf dependency (#5890).
3+
14
# v3.2.0
25
- [added] Added completion handler for FIRExperimentController's updateExperimentsWithServiceOrigin method.
36
- [deprecated] Deprecated `FIRExperimentController.updateExperiments(serviceOrigin:events:policy:lastStartTime:payloads:)`.

FirebaseABTesting/Sources/ABTConditionalUserPropertyController.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#import <FirebaseABTesting/ABTExperimentPayload.h>
1516
#import <Foundation/Foundation.h>
1617

17-
#import "FirebaseABTesting/Sources/Protos/developers/mobile/abt/proto/ExperimentPayload.pbobjc.h"
18-
1918
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
2019

2120
NS_ASSUME_NONNULL_BEGIN
@@ -69,7 +68,7 @@ NS_ASSUME_NONNULL_BEGIN
6968
- (void)setExperimentWithOrigin:(NSString *)origin
7069
payload:(ABTExperimentPayload *)payload
7170
events:(FIRLifecycleEvents *)events
72-
policy:(ABTExperimentPayload_ExperimentOverflowPolicy)policy;
71+
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy;
7372

7473
/**
7574
* Unavailable. Use sharedInstanceWithAnalytics: instead.

FirebaseABTesting/Sources/ABTConditionalUserPropertyController.m

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ - (void)clearExperiment:(NSString *)experimentID
7373
- (void)setExperimentWithOrigin:(NSString *)origin
7474
payload:(ABTExperimentPayload *)payload
7575
events:(FIRLifecycleEvents *)events
76-
policy:(ABTExperimentPayload_ExperimentOverflowPolicy)policy {
76+
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy {
7777
NSInteger maxNumOfExperiments = [self maxNumberOfExperimentsOfOrigin:origin];
7878
if (maxNumOfExperiments < 0) {
7979
return;
@@ -88,10 +88,10 @@ - (void)setExperimentWithOrigin:(NSString *)origin
8888
}
8989

9090
if (maxNumOfExperiments <= experiments.count) {
91-
ABTExperimentPayload_ExperimentOverflowPolicy overflowPolicy =
91+
ABTExperimentPayloadExperimentOverflowPolicy overflowPolicy =
9292
[self overflowPolicyWithPayload:payload originalPolicy:policy];
9393
id experimentToClear = experiments.firstObject;
94-
if (overflowPolicy == ABTExperimentPayload_ExperimentOverflowPolicy_DiscardOldest &&
94+
if (overflowPolicy == ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest &&
9595
experimentToClear) {
9696
NSString *expID = [self experimentIDOfExperiment:experimentToClear];
9797
NSString *varID = [self variantIDOfExperiment:experimentToClear];
@@ -265,17 +265,17 @@ - (BOOL)isExperiment:(id)experiment theSameAsPayload:(ABTExperimentPayload *)pay
265265
[variantID isEqualToString:payload.variantId];
266266
}
267267

268-
- (ABTExperimentPayload_ExperimentOverflowPolicy)
268+
- (ABTExperimentPayloadExperimentOverflowPolicy)
269269
overflowPolicyWithPayload:(ABTExperimentPayload *)payload
270-
originalPolicy:(ABTExperimentPayload_ExperimentOverflowPolicy)originalPolicy {
271-
if (payload.overflowPolicy != ABTExperimentPayload_ExperimentOverflowPolicy_PolicyUnspecified) {
270+
originalPolicy:(ABTExperimentPayloadExperimentOverflowPolicy)originalPolicy {
271+
if ([payload overflowPolicyIsValid]) {
272272
return payload.overflowPolicy;
273273
}
274-
if (originalPolicy != ABTExperimentPayload_ExperimentOverflowPolicy_PolicyUnspecified &&
275-
ABTExperimentPayload_ExperimentOverflowPolicy_IsValidValue(originalPolicy)) {
274+
if (originalPolicy == ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest ||
275+
originalPolicy == ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest) {
276276
return originalPolicy;
277277
}
278-
return ABTExperimentPayload_ExperimentOverflowPolicy_DiscardOldest;
278+
return ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
279279
}
280280

281281
@end
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright 2020 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 "ABTExperimentPayload.h"
16+
17+
static NSString *const kExperimentPayloadKeyExperimentID = @"experimentId";
18+
static NSString *const kExperimentPayloadKeyVariantID = @"variantId";
19+
20+
// Start time can either be a date string or integer (milliseconds since 1970).
21+
static NSString *const kExperimentPayloadKeyExperimentStartTime = @"experimentStartTime";
22+
static NSString *const kExperimentPayloadKeyExperimentStartTimeMillis =
23+
@"experimentStartTimeMillis";
24+
static NSString *const kExperimentPayloadKeyTriggerEvent = @"triggerEvent";
25+
static NSString *const kExperimentPayloadKeyTriggerTimeoutMillis = @"triggerTimeoutMillis";
26+
static NSString *const kExperimentPayloadKeyTimeToLiveMillis = @"timeToLiveMillis";
27+
static NSString *const kExperimentPayloadKeySetEventToLog = @"setEventToLog";
28+
static NSString *const kExperimentPayloadKeyActivateEventToLog = @"activateEventToLog";
29+
static NSString *const kExperimentPayloadKeyClearEventToLog = @"clearEventToLog";
30+
static NSString *const kExperimentPayloadKeyTimeoutEventToLog = @"timeoutEventToLog";
31+
static NSString *const kExperimentPayloadKeyTTLExpiryEventToLog = @"ttlExpiryEventToLog";
32+
33+
static NSString *const kExperimentPayloadKeyOverflowPolicy = @"overflowPolicy";
34+
static NSString *const kExperimentPayloadValueDiscardOldestOverflowPolicy = @"DISCARD_OLDEST";
35+
static NSString *const kExperimentPayloadValueIgnoreNewestOverflowPolicy = @"IGNORE_NEWEST";
36+
37+
static NSString *const kExperimentPayloadKeyOngoingExperiments = @"ongoingExperiments";
38+
39+
@implementation ABTExperimentLite
40+
41+
- (instancetype)initWithExperimentId:(NSString *)experimentId {
42+
if (self = [super init]) {
43+
_experimentId = experimentId;
44+
}
45+
return self;
46+
}
47+
48+
@end
49+
50+
@implementation ABTExperimentPayload
51+
52+
+ (NSDateFormatter *)experimentStartTimeFormatter {
53+
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
54+
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
55+
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
56+
// Locale needs to be hardcoded. See
57+
// https://developer.apple.com/library/ios/#qa/qa1480/_index.html for more details.
58+
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
59+
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
60+
return dateFormatter;
61+
}
62+
63+
+ (instancetype)parseFromData:(NSData *)data {
64+
NSError *error;
65+
NSDictionary *experimentDictionary =
66+
[NSJSONSerialization JSONObjectWithData:data
67+
options:NSJSONReadingAllowFragments
68+
error:&error];
69+
if (error != nil) {
70+
return nil;
71+
} else {
72+
return [[ABTExperimentPayload alloc] initWithDictionary:experimentDictionary];
73+
}
74+
}
75+
76+
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dictionary {
77+
if (self = [super init]) {
78+
_experimentId = dictionary[kExperimentPayloadKeyExperimentID];
79+
_variantId = dictionary[kExperimentPayloadKeyVariantID];
80+
_triggerEvent = dictionary[kExperimentPayloadKeyTriggerEvent];
81+
_setEventToLog = dictionary[kExperimentPayloadKeySetEventToLog];
82+
_activateEventToLog = dictionary[kExperimentPayloadKeyActivateEventToLog];
83+
_clearEventToLog = dictionary[kExperimentPayloadKeyClearEventToLog];
84+
_timeoutEventToLog = dictionary[kExperimentPayloadKeyTimeoutEventToLog];
85+
_ttlExpiryEventToLog = dictionary[kExperimentPayloadKeyTTLExpiryEventToLog];
86+
87+
// Experiment start time can either be in the form of a date string or milliseconds since 1970.
88+
if (dictionary[kExperimentPayloadKeyExperimentStartTime]) {
89+
// Convert from date string.
90+
NSDate *experimentStartTime = [[[self class] experimentStartTimeFormatter]
91+
dateFromString:dictionary[kExperimentPayloadKeyExperimentStartTime]];
92+
_experimentStartTimeMillis =
93+
[@([experimentStartTime timeIntervalSince1970] * 1000) longLongValue];
94+
} else if (dictionary[kExperimentPayloadKeyExperimentStartTimeMillis]) {
95+
// Simply store milliseconds.
96+
_experimentStartTimeMillis =
97+
[dictionary[kExperimentPayloadKeyExperimentStartTimeMillis] longLongValue];
98+
;
99+
}
100+
101+
_triggerTimeoutMillis = [dictionary[kExperimentPayloadKeyTriggerTimeoutMillis] longLongValue];
102+
_timeToLiveMillis = [dictionary[kExperimentPayloadKeyTimeToLiveMillis] longLongValue];
103+
104+
// Overflow policy can be an integer, or string e.g. "DISCARD_OLDEST" or "IGNORE_NEWEST".
105+
if ([dictionary[kExperimentPayloadKeyOverflowPolicy] isKindOfClass:[NSString class]]) {
106+
// If it's a string, pick against the preset string values.
107+
NSString *policy = dictionary[kExperimentPayloadKeyOverflowPolicy];
108+
if ([policy isEqualToString:kExperimentPayloadValueDiscardOldestOverflowPolicy]) {
109+
_overflowPolicy = ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
110+
} else if ([policy isEqualToString:kExperimentPayloadValueIgnoreNewestOverflowPolicy]) {
111+
_overflowPolicy = ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest;
112+
} else {
113+
_overflowPolicy = ABTExperimentPayloadExperimentOverflowPolicyUnrecognizedValue;
114+
}
115+
} else {
116+
_overflowPolicy = [dictionary[kExperimentPayloadKeyOverflowPolicy] intValue];
117+
}
118+
119+
NSMutableArray<ABTExperimentLite *> *ongoingExperiments = [[NSMutableArray alloc] init];
120+
121+
NSArray<NSDictionary<NSString *, NSString *> *> *ongoingExperimentsArray =
122+
dictionary[kExperimentPayloadKeyOngoingExperiments];
123+
124+
for (NSDictionary<NSString *, NSString *> *experimentDictionary in ongoingExperimentsArray) {
125+
NSString *experimentId = experimentDictionary[kExperimentPayloadKeyExperimentID];
126+
if (experimentId) {
127+
ABTExperimentLite *liteExperiment =
128+
[[ABTExperimentLite alloc] initWithExperimentId:experimentId];
129+
[ongoingExperiments addObject:liteExperiment];
130+
}
131+
}
132+
133+
_ongoingExperiments = [ongoingExperiments copy];
134+
}
135+
return self;
136+
}
137+
138+
- (void)clearTriggerEvent {
139+
_triggerEvent = nil;
140+
}
141+
142+
- (BOOL)overflowPolicyIsValid {
143+
return self.overflowPolicy == ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest ||
144+
self.overflowPolicy == ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
145+
}
146+
147+
- (void)setOverflowPolicy:(ABTExperimentPayloadExperimentOverflowPolicy)overflowPolicy {
148+
_overflowPolicy = overflowPolicy;
149+
}
150+
151+
@end

FirebaseABTesting/Sources/FIRExperimentController.m

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#import <FirebaseABTesting/FIRExperimentController.h>
1616

17+
#import <FirebaseABTesting/ABTExperimentPayload.h>
1718
#import <FirebaseABTesting/FIRLifecycleEvents.h>
1819
#import "FirebaseABTesting/Sources/ABTConditionalUserPropertyController.h"
1920
#import "FirebaseABTesting/Sources/ABTConstants.h"
@@ -34,19 +35,19 @@
3435
#define STR_EXPAND(x) #x
3536
3637
/// Default experiment overflow policy.
37-
const ABTExperimentPayload_ExperimentOverflowPolicy FIRDefaultExperimentOverflowPolicy =
38-
ABTExperimentPayload_ExperimentOverflowPolicy_DiscardOldest;
38+
const ABTExperimentPayloadExperimentOverflowPolicy FIRDefaultExperimentOverflowPolicy =
39+
ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
3940
4041
/// Deserialize the experiment payloads.
4142
ABTExperimentPayload *ABTDeserializeExperimentPayload(NSData *payload) {
43+
// Verify that we have a JSON object.
4244
NSError *error;
43-
ABTExperimentPayload *experimentPayload = [ABTExperimentPayload parseFromData:payload
44-
error:&error];
45-
if (error) {
45+
id JSONObject = [NSJSONSerialization JSONObjectWithData:payload options:kNilOptions error:&error];
46+
if (JSONObject == nil) {
4647
FIRLogError(kFIRLoggerABTesting, @"I-ABT000001", @"Failed to parse experiment payload: %@",
4748
error.debugDescription);
4849
}
49-
return experimentPayload;
50+
return [ABTExperimentPayload parseFromData:payload];
5051
}
5152
5253
/// Returns a list of experiments to be set given the payloads and current list of experiments from
@@ -173,7 +174,7 @@ + (FIRExperimentController *)sharedInstance {
173174
174175
- (void)updateExperimentsWithServiceOrigin:(NSString *)origin
175176
events:(FIRLifecycleEvents *)events
176-
policy:(ABTExperimentPayload_ExperimentOverflowPolicy)policy
177+
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy
177178
lastStartTime:(NSTimeInterval)lastStartTime
178179
payloads:(NSArray<NSData *> *)payloads
179180
completionHandler:
@@ -192,7 +193,7 @@ - (void)updateExperimentsWithServiceOrigin:(NSString *)origin
192193
193194
- (void)updateExperimentsWithServiceOrigin:(NSString *)origin
194195
events:(FIRLifecycleEvents *)events
195-
policy:(ABTExperimentPayload_ExperimentOverflowPolicy)policy
196+
policy:(ABTExperimentPayloadExperimentOverflowPolicy)policy
196197
lastStartTime:(NSTimeInterval)lastStartTime
197198
payloads:(NSArray<NSData *> *)payloads {
198199
[self updateExperimentsWithServiceOrigin:origin
@@ -207,7 +208,7 @@ - (void)updateExperimentsWithServiceOrigin:(NSString *)origin
207208
updateExperimentConditionalUserPropertiesWithServiceOrigin:(NSString *)origin
208209
events:(FIRLifecycleEvents *)events
209210
policy:
210-
(ABTExperimentPayload_ExperimentOverflowPolicy)
211+
(ABTExperimentPayloadExperimentOverflowPolicy)
211212
policy
212213
lastStartTime:(NSTimeInterval)lastStartTime
213214
payloads:(NSArray<NSData *> *)payloads
@@ -325,7 +326,7 @@ - (void)activateExperiment:(ABTExperimentPayload *)experimentPayload
325326
FIRLifecycleEvents *lifecycleEvents = [[FIRLifecycleEvents alloc] init];
326327
327328
// Ensure that trigger event is nil, which will immediately set the experiment to active.
328-
experimentPayload.triggerEvent = nil;
329+
[experimentPayload clearTriggerEvent];
329330
330331
[controller setExperimentWithOrigin:origin
331332
payload:experimentPayload

0 commit comments

Comments
 (0)