Skip to content

Commit

Permalink
Fix null settings entry crash on tvOS (#1052)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsneed authored Jan 26, 2023
1 parent 89df4dd commit 78e3a09
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 4 deletions.
6 changes: 6 additions & 0 deletions Segment.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
460367F52981A8E30081CCA3 /* tvOSSettingsBad.json in Resources */ = {isa = PBXBuildFile; fileRef = 460367F42981A8E30081CCA3 /* tvOSSettingsBad.json */; };
460367F62981AAA50081CCA3 /* tvOSSettingsBad.json in Resources */ = {isa = PBXBuildFile; fileRef = 460367F42981A8E30081CCA3 /* tvOSSettingsBad.json */; };
5AF0E8AE77F57B356DACCFE7 /* AutoScreenReportingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E8457CCFC077382BF449 /* AutoScreenReportingTest.swift */; };
630FC8EA2107F2A500A759C5 /* SEGScreenReporting.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AF0EDFEDC0EE79A0A0EB713 /* SEGScreenReporting.h */; settings = {ATTRIBUTES = (Public, ); }; };
6E265C791FB1178C0030E08E /* IntegrationsManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E265C781FB1178C0030E08E /* IntegrationsManagerTest.swift */; };
Expand Down Expand Up @@ -98,6 +100,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
460367F42981A8E30081CCA3 /* tvOSSettingsBad.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = tvOSSettingsBad.json; sourceTree = "<group>"; };
5AF0E8457CCFC077382BF449 /* AutoScreenReportingTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoScreenReportingTest.swift; sourceTree = "<group>"; };
5AF0EDFEDC0EE79A0A0EB713 /* SEGScreenReporting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SEGScreenReporting.h; sourceTree = "<group>"; };
63E090D722DD49C300DEC7EC /* UIViewController+SegScreenTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+SegScreenTest.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -359,6 +362,7 @@
EADEB8E41DECD335005322DA /* TestUtils.swift */,
A3BECDCE24BCE1E3009E2BD3 /* ObjcUtils.h */,
A3BECDCF24BCE1E3009E2BD3 /* ObjcUtils.m */,
460367F42981A8E30081CCA3 /* tvOSSettingsBad.json */,
);
path = Utils;
sourceTree = "<group>";
Expand Down Expand Up @@ -501,13 +505,15 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
460367F62981AAA50081CCA3 /* tvOSSettingsBad.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
EADEB8681DECD0EF005322DA /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
460367F52981A8E30081CCA3 /* tvOSSettingsBad.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
8 changes: 4 additions & 4 deletions Segment/Internal/SEGUserDefaultsStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ - (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key
{
if (!self.crypto) {
key = [self namespacedKey:key];
[self setObject:dictionary forKey:key];
[self setObject:[dictionary plistCompatible] forKey:key];
return;
}
[self setPlist:dictionary forKey:key];
[self setPlist:[dictionary plistCompatible] forKey:key];
}

- (NSArray *)arrayForKey:(NSString *)key
Expand All @@ -113,10 +113,10 @@ - (void)setArray:(NSArray *)array forKey:(NSString *)key
{
if (!self.crypto) {
key = [self namespacedKey:key];
[self setObject:array forKey:key];
[self setObject:[array plistCompatible] forKey:key];
return;
}
[self setPlist:array forKey:key];
[self setPlist:[array plistCompatible] forKey:key];
}

- (NSString *)stringForKey:(NSString *)key
Expand Down
9 changes: 9 additions & 0 deletions Segment/Internal/SEGUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,14 @@ NSString *SEGEventNameForScreenTitle(NSString *title);
@interface NSArray(SerializableDeepCopy) <SEGSerializableDeepCopy>
@end

@interface NSDictionary(PListJSON)
- (NSDictionary *)plistCompatible;
@end

@interface NSArray(PListJSON)
- (NSDictionary *)plistCompatible;
@end



NS_ASSUME_NONNULL_END
48 changes: 48 additions & 0 deletions Segment/Internal/SEGUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -653,3 +653,51 @@ - (NSMutableArray *)serializableMutableDeepCopy {
}

@end


@implementation NSDictionary(PListJSON)

- (NSDictionary *)plistCompatible {
const NSMutableDictionary *replaced = [NSMutableDictionary new];
const id null = [NSNull null];

for(NSString *key in self) {
const id object = [self objectForKey:key];
if(object == null) {
continue;
} else if ([object isKindOfClass:[NSDictionary class]]) {
[replaced setObject:[object plistCompatible] forKey:key];
} else if ([object isKindOfClass:[NSArray class]]) {
[replaced setObject:[object plistCompatible] forKey:key];
} else {
[replaced setObject:object forKey:key];
}
}
return [NSDictionary dictionaryWithDictionary:(NSDictionary*)replaced];
}

@end

@implementation NSArray(PListJSON)

- (NSArray *)plistCompatible {
const NSMutableArray *replaced = [NSMutableArray new];
const id null = [NSNull null];

for (int i=0; i<[self count]; i++) {
const id object = [self objectAtIndex:i];

if ([object isKindOfClass:[NSDictionary class]]) {
[replaced setObject:[object plistCompatible] atIndexedSubscript:i];
} else if ([object isKindOfClass:[NSArray class]]) {
[replaced setObject:[object plistCompatible] atIndexedSubscript:i];
} else if (object == null) {
continue;
} else {
[replaced setObject:object atIndexedSubscript:i];
}
}
return [NSArray arrayWithArray:(NSArray*)replaced];
}

@end
26 changes: 26 additions & 0 deletions SegmentTests/AnalyticsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,30 @@ class AnalyticsTests: XCTestCase {
XCTAssertNotNil(data)
}
}

#if os(tvOS)
func testTVOSSettingsBad() {
// this test is expected to crash if it fails.
var initialized = false
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: SEGAnalyticsIntegrationDidStart), object: nil, queue: nil) { (notification) in
let key = notification.object as? String
if (key == "Segment.io") {
initialized = true
}
}

var config = AnalyticsConfiguration(writeKey: "1234")
let url = Bundle(for: AnalyticsTests.self).url(forResource: "tvOSSettingsBad.json", withExtension: nil)
let data = try! Data(contentsOf: url!)
let settings = try! JSONSerialization.jsonObject(with: data) as! NSDictionary

let analytics = Analytics(configuration: config)

analytics.test_integrationsManager()?.test_setCachedSettings(settings: settings as NSDictionary)

while (!initialized) { // wait for integrations to get setup
RunLoop.main.run(until: Date.distantPast)
}
}
#endif
}
49 changes: 49 additions & 0 deletions SegmentTests/Utils/tvOSSettingsBad.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"integrations": {
"Segment.io": {
"apiKey": "1234",
"unbundledIntegrations": [],
"addBundledMetadata": true
}
},
"edgeFunction": {},
"analyticsNextEnabled": false,
"middlewareSettings": {
"routingRules": [
{
"transformers": [
[
{
"type": "drop"
}
],
[
{
"type": "allow_properties",
"config": {
"allow": {
"traits": null,
"context": null,
"_metadata": null,
"integrations": null,
"properties.content": [
"asset_id",
"full_episode",
"load_type",
"program",
"title",
"video_tms_id"
]
}
}
}
]
]
}
]
},
"enabledMiddleware": {},
"metrics": {
"sampleRate": 0.1
},
}

0 comments on commit 78e3a09

Please sign in to comment.