Skip to content

Commit

Permalink
fix: Read initWithDict for Hybrid SDKs (#2579)
Browse files Browse the repository at this point in the history
Add SentryOptions:initWithDict only for Hybrid SDKs again.

Fixes GH-2547
  • Loading branch information
philipphofmann authored Jan 9, 2023
1 parent c882c53 commit e6fd1fb
Show file tree
Hide file tree
Showing 10 changed files with 1,057 additions and 30 deletions.
10 changes: 7 additions & 3 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@
7B2A70DB27D607CF008B0D15 /* SentryThreadWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B2A70DA27D607CF008B0D15 /* SentryThreadWrapper.h */; };
7B2A70DD27D6083D008B0D15 /* SentryThreadWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B2A70DC27D6083D008B0D15 /* SentryThreadWrapper.m */; };
7B2A70DF27D60904008B0D15 /* SentryTestThreadWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2A70DE27D60904008B0D15 /* SentryTestThreadWrapper.swift */; };
7B2BB0032966F55900A1E102 /* SentryOptions+HybridSDKs.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B2BB0012966F55900A1E102 /* SentryOptions+HybridSDKs.h */; settings = {ATTRIBUTES = (Private, ); }; };
7B30B67C26527886006B2752 /* SentryDisplayLinkWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B30B67B26527886006B2752 /* SentryDisplayLinkWrapper.h */; };
7B30B67E26527894006B2752 /* SentryDisplayLinkWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B30B67D26527894006B2752 /* SentryDisplayLinkWrapper.m */; };
7B30B68026527C3C006B2752 /* SentryFramesTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B30B67F26527C3C006B2752 /* SentryFramesTrackerTests.swift */; };
Expand Down Expand Up @@ -1103,6 +1104,7 @@
7B2A70DA27D607CF008B0D15 /* SentryThreadWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryThreadWrapper.h; path = include/SentryThreadWrapper.h; sourceTree = "<group>"; };
7B2A70DC27D6083D008B0D15 /* SentryThreadWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryThreadWrapper.m; sourceTree = "<group>"; };
7B2A70DE27D60904008B0D15 /* SentryTestThreadWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTestThreadWrapper.swift; sourceTree = "<group>"; };
7B2BB0012966F55900A1E102 /* SentryOptions+HybridSDKs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryOptions+HybridSDKs.h"; path = "include/HybridPublic/SentryOptions+HybridSDKs.h"; sourceTree = "<group>"; };
7B30B67B26527886006B2752 /* SentryDisplayLinkWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDisplayLinkWrapper.h; path = include/SentryDisplayLinkWrapper.h; sourceTree = "<group>"; };
7B30B67D26527894006B2752 /* SentryDisplayLinkWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SentryDisplayLinkWrapper.m; path = include/SentryDisplayLinkWrapper.m; sourceTree = "<group>"; };
7B30B67F26527C3C006B2752 /* SentryFramesTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryFramesTrackerTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1586,13 +1588,13 @@
D8BD2E67293619F600D96C6A /* PrivatesHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PrivatesHeader.h; path = include/HybridPublic/PrivatesHeader.h; sourceTree = "<group>"; };
D8C67E9928000E23007E326E /* SentryUIApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryUIApplication.h; path = include/SentryUIApplication.h; sourceTree = "<group>"; };
D8C67E9A28000E23007E326E /* SentryScreenshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryScreenshot.h; path = include/SentryScreenshot.h; sourceTree = "<group>"; };
D8CB742A294B1DD000A5F964 /* SentryUIApplicationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUIApplicationTests.swift; sourceTree = "<group>"; };
D8CB742C294B294B00A5F964 /* MockUIScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockUIScene.h; sourceTree = "<group>"; };
D8CB742D294B294B00A5F964 /* MockUIScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockUIScene.m; sourceTree = "<group>"; };
D8CB74142947246600A5F964 /* SentryEnvelopeAttachmentHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryEnvelopeAttachmentHeader.h; path = include/SentryEnvelopeAttachmentHeader.h; sourceTree = "<group>"; };
D8CB7416294724CC00A5F964 /* SentryEnvelopeAttachmentHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryEnvelopeAttachmentHeader.m; sourceTree = "<group>"; };
D8CB74182947285A00A5F964 /* SentryEnvelopeItemHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryEnvelopeItemHeader.h; path = Public/SentryEnvelopeItemHeader.h; sourceTree = "<group>"; };
D8CB741A2947286500A5F964 /* SentryEnvelopeItemHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryEnvelopeItemHeader.m; sourceTree = "<group>"; };
D8CB742A294B1DD000A5F964 /* SentryUIApplicationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUIApplicationTests.swift; sourceTree = "<group>"; };
D8CB742C294B294B00A5F964 /* MockUIScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockUIScene.h; sourceTree = "<group>"; };
D8CB742D294B294B00A5F964 /* MockUIScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockUIScene.m; sourceTree = "<group>"; };
D8CE69BB277E39C700C6EC5C /* SentryFileIOTrackingIntegrationObjCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryFileIOTrackingIntegrationObjCTests.m; sourceTree = "<group>"; };
D8F6A2452885512100320515 /* SentryPredicateDescriptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryPredicateDescriptor.m; sourceTree = "<group>"; };
D8F6A24A2885515B00320515 /* SentryPredicateDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryPredicateDescriptor.h; path = include/SentryPredicateDescriptor.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2100,6 +2102,7 @@
63EED6BC2237923600E02400 /* SentryOptions.h */,
63EED6BD2237923600E02400 /* SentryOptions.m */,
7BDEAA002632A4580001EA25 /* SentryOptions+Private.h */,
7B2BB0012966F55900A1E102 /* SentryOptions+HybridSDKs.h */,
7D9B079F23D1E89800C5FC8E /* SentryMeta.h */,
7D082B8023C628780029866B /* SentryMeta.m */,
);
Expand Down Expand Up @@ -3271,6 +3274,7 @@
7B85DC1E24EFAFCD007D01D2 /* SentryClient+Private.h in Headers */,
A8AFFCCF2906C03700967CD7 /* SentryRequest.h in Headers */,
7BD4BD4327EB29BA0071F4FF /* SentryClientReport.h in Headers */,
7B2BB0032966F55900A1E102 /* SentryOptions+HybridSDKs.h in Headers */,
7BF9EF762722B34700B5BBEF /* SentrySubClassFinder.h in Headers */,
7BC852332458802C005A70F0 /* SentryDataCategoryMapper.h in Headers */,
7BDB03B7251364F800BAE198 /* SentryDispatchQueueWrapper.h in Headers */,
Expand Down
3 changes: 0 additions & 3 deletions Sources/Sentry/Public/SentryOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Options)
@interface SentryOptions : NSObject

- (_Nullable instancetype)initWithDsn:(NSString *)dsn
didFailWithError:(NSError *_Nullable *_Nullable)error;

/**
* The DSN tells the SDK where to send the events to. If this value is not provided, the SDK will
* not send any events.
Expand Down
236 changes: 230 additions & 6 deletions Sources/Sentry/SentryOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ - (instancetype)init
return self;
}

- (_Nullable instancetype)initWithDsn:(NSString *)dsn
didFailWithError:(NSError *_Nullable *_Nullable)error
- (_Nullable instancetype)initWithDict:(NSDictionary<NSString *, id> *)options
didFailWithError:(NSError *_Nullable *_Nullable)error
{
if (self = [self init]) {
self.parsedDsn = [[SentryDsn alloc] initWithString:dsn didFailWithError:error];
self.dsn = dsn;

if (error != nil && *error != nil) {
if (![self validateOptions:options didFailWithError:error]) {
[SentryLog
logWithMessage:[NSString stringWithFormat:@"Failed to initialize: %@", *error]
andLevel:kSentryLevelError];
return nil;
}
}
Expand Down Expand Up @@ -215,6 +215,230 @@ - (void)setDsn:(NSString *)dsn
}
}

/**
* Populates all `SentryOptions` values from `options` dict using fallbacks/defaults if needed.
*/
- (BOOL)validateOptions:(NSDictionary<NSString *, id> *)options
didFailWithError:(NSError *_Nullable *_Nullable)error
{
NSPredicate *isNSString = [NSPredicate predicateWithBlock:^BOOL(
id object, NSDictionary *bindings) { return [object isKindOfClass:[NSString class]]; }];

[self setBool:options[@"debug"] block:^(BOOL value) { self->_debug = value; }];

if ([options[@"diagnosticLevel"] isKindOfClass:[NSString class]]) {
for (SentryLevel level = 0; level <= kSentryLevelFatal; level++) {
if ([nameForSentryLevel(level) isEqualToString:options[@"diagnosticLevel"]]) {
self.diagnosticLevel = level;
break;
}
}
}

NSString *dsn = @"";
if (nil != options[@"dsn"] && [options[@"dsn"] isKindOfClass:[NSString class]]) {
dsn = options[@"dsn"];
}

self.parsedDsn = [[SentryDsn alloc] initWithString:dsn didFailWithError:error];

if ([options[@"release"] isKindOfClass:[NSString class]]) {
self.releaseName = options[@"release"];
}

if ([options[@"environment"] isKindOfClass:[NSString class]]) {
self.environment = options[@"environment"];
}

if ([options[@"dist"] isKindOfClass:[NSString class]]) {
self.dist = options[@"dist"];
}

[self setBool:options[@"enabled"] block:^(BOOL value) { self->_enabled = value; }];

if ([options[@"shutdownTimeInterval"] isKindOfClass:[NSNumber class]]) {
self.shutdownTimeInterval = [options[@"shutdownTimeInterval"] doubleValue];
}

[self setBool:options[@"enableCrashHandler"]
block:^(BOOL value) { self->_enableCrashHandler = value; }];

if ([options[@"maxBreadcrumbs"] isKindOfClass:[NSNumber class]]) {
self.maxBreadcrumbs = [options[@"maxBreadcrumbs"] unsignedIntValue];
}

[self setBool:options[@"enableNetworkBreadcrumbs"]
block:^(BOOL value) { self->_enableNetworkBreadcrumbs = value; }];

if ([options[@"maxCacheItems"] isKindOfClass:[NSNumber class]]) {
self.maxCacheItems = [options[@"maxCacheItems"] unsignedIntValue];
}

if ([self isBlock:options[@"beforeSend"]]) {
self.beforeSend = options[@"beforeSend"];
}

if ([self isBlock:options[@"beforeBreadcrumb"]]) {
self.beforeBreadcrumb = options[@"beforeBreadcrumb"];
}

if ([self isBlock:options[@"onCrashedLastRun"]]) {
self.onCrashedLastRun = options[@"onCrashedLastRun"];
}

if ([options[@"integrations"] isKindOfClass:[NSArray class]]) {
self.integrations = [options[@"integrations"] filteredArrayUsingPredicate:isNSString];
}

if ([options[@"sampleRate"] isKindOfClass:[NSNumber class]]) {
self.sampleRate = options[@"sampleRate"];
}

[self setBool:options[@"enableAutoSessionTracking"]
block:^(BOOL value) { self->_enableAutoSessionTracking = value; }];

[self setBool:options[@"enableWatchdogTerminationTracking"]
block:^(BOOL value) { self->_enableWatchdogTerminationTracking = value; }];

if ([options[@"sessionTrackingIntervalMillis"] isKindOfClass:[NSNumber class]]) {
self.sessionTrackingIntervalMillis =
[options[@"sessionTrackingIntervalMillis"] unsignedIntValue];
}

[self setBool:options[@"attachStacktrace"]
block:^(BOOL value) { self->_attachStacktrace = value; }];

[self setBool:options[@"stitchAsyncCode"]
block:^(BOOL value) { self->_stitchAsyncCode = value; }];

if ([options[@"maxAttachmentSize"] isKindOfClass:[NSNumber class]]) {
self.maxAttachmentSize = [options[@"maxAttachmentSize"] unsignedIntValue];
}

[self setBool:options[@"sendDefaultPii"]
block:^(BOOL value) { self->_sendDefaultPii = value; }];

[self setBool:options[@"enableAutoPerformanceTracing"]
block:^(BOOL value) { self->_enableAutoPerformanceTracing = value; }];

[self setBool:options[@"enableCaptureFailedRequests"]
block:^(BOOL value) { self->_enableCaptureFailedRequests = value; }];

#if SENTRY_HAS_UIKIT
[self setBool:options[@"enableUIViewControllerTracing"]
block:^(BOOL value) { self->_enableUIViewControllerTracing = value; }];

[self setBool:options[@"attachScreenshot"]
block:^(BOOL value) { self->_attachScreenshot = value; }];

[self setBool:options[@"attachViewHierarchy"]
block:^(BOOL value) { self->_attachViewHierarchy = value; }];

[self setBool:options[@"enableUserInteractionTracing"]
block:^(BOOL value) { self->_enableUserInteractionTracing = value; }];

if ([options[@"idleTimeout"] isKindOfClass:[NSNumber class]]) {
self.idleTimeout = [options[@"idleTimeout"] doubleValue];
}

[self setBool:options[@"enablePreWarmedAppStartTracing"]
block:^(BOOL value) { self->_enablePreWarmedAppStartTracing = value; }];
#endif

[self setBool:options[@"enableAppHangTracking"]
block:^(BOOL value) { self->_enableAppHangTracking = value; }];

if ([options[@"appHangTimeoutInterval"] isKindOfClass:[NSNumber class]]) {
self.appHangTimeoutInterval = [options[@"appHangTimeoutInterval"] doubleValue];
}

[self setBool:options[@"enableNetworkTracking"]
block:^(BOOL value) { self->_enableNetworkTracking = value; }];

[self setBool:options[@"enableFileIOTracing"]
block:^(BOOL value) { self->_enableFileIOTracing = value; }];

if ([options[@"tracesSampleRate"] isKindOfClass:[NSNumber class]]) {
self.tracesSampleRate = options[@"tracesSampleRate"];
}

if ([self isBlock:options[@"tracesSampler"]]) {
self.tracesSampler = options[@"tracesSampler"];
}

if ([options[@"inAppIncludes"] isKindOfClass:[NSArray class]]) {
NSArray<NSString *> *inAppIncludes =
[options[@"inAppIncludes"] filteredArrayUsingPredicate:isNSString];
_inAppIncludes = [_inAppIncludes arrayByAddingObjectsFromArray:inAppIncludes];
}

if ([options[@"inAppExcludes"] isKindOfClass:[NSArray class]]) {
_inAppExcludes = [options[@"inAppExcludes"] filteredArrayUsingPredicate:isNSString];
}

if ([options[@"urlSessionDelegate"] conformsToProtocol:@protocol(NSURLSessionDelegate)]) {
self.urlSessionDelegate = options[@"urlSessionDelegate"];
}

[self setBool:options[@"enableSwizzling"]
block:^(BOOL value) { self->_enableSwizzling = value; }];

[self setBool:options[@"enableCoreDataTracing"]
block:^(BOOL value) { self->_enableCoreDataTracing = value; }];

#if SENTRY_TARGET_PROFILING_SUPPORTED
if ([options[@"profilesSampleRate"] isKindOfClass:[NSNumber class]]) {
self.profilesSampleRate = options[@"profilesSampleRate"];
}

if ([self isBlock:options[@"profilesSampler"]]) {
self.profilesSampler = options[@"profilesSampler"];
}

[self setBool:options[@"enableProfiling"]
block:^(BOOL value) { self->_enableProfiling = value; }];
#endif

[self setBool:options[@"sendClientReports"]
block:^(BOOL value) { self->_sendClientReports = value; }];

[self setBool:options[@"enableAutoBreadcrumbTracking"]
block:^(BOOL value) { self->_enableAutoBreadcrumbTracking = value; }];

if ([options[@"tracePropagationTargets"] isKindOfClass:[NSArray class]]) {
self.tracePropagationTargets = options[@"tracePropagationTargets"];
}

if ([options[@"failedRequestStatusCodes"] isKindOfClass:[NSArray class]]) {
self.failedRequestStatusCodes = options[@"failedRequestStatusCodes"];
}

if ([options[@"failedRequestTargets"] isKindOfClass:[NSArray class]]) {
self.failedRequestTargets = options[@"failedRequestTargets"];
}

#if SENTRY_HAS_METRIC_KIT
if (@available(iOS 14.0, macOS 12.0, macCatalyst 14.0, *)) {
[self setBool:options[@"enableMetricKit"]
block:^(BOOL value) { self->_enableMetricKit = value; }];
}
#endif

if (nil != error && nil != *error) {
return NO;
} else {
return YES;
}
}

- (void)setBool:(id)value block:(void (^)(BOOL))block
{
// Entries in the dictionary can be NSNull. Especially, on React-Native, this can happen.
if (value != nil && ![value isEqual:[NSNull null]]) {
block([value boolValue]);
}
}

- (void)addInAppInclude:(NSString *)inAppInclude
{
_inAppIncludes = [self.inAppIncludes arrayByAddingObject:inAppInclude];
Expand Down
13 changes: 13 additions & 0 deletions Sources/Sentry/include/HybridPublic/SentryOptions+HybridSDKs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#import "SentryOptions.h"

NS_ASSUME_NONNULL_BEGIN

@interface
SentryOptions (HybridSDKs)

- (_Nullable instancetype)initWithDict:(NSDictionary<NSString *, id> *)options
didFailWithError:(NSError *_Nullable *_Nullable)error;

@end

NS_ASSUME_NONNULL_END
17 changes: 9 additions & 8 deletions Tests/SentryTests/Networking/SentryDsnTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#import "SentryError.h"
#import "SentryMeta.h"
#import "SentryNSURLRequest.h"
#import "SentryOptions+HybridSDKs.h"
#import <Sentry/Sentry.h>
#import <XCTest/XCTest.h>

Expand All @@ -14,8 +15,8 @@ @implementation SentryDsnTests
- (void)testMissingUsernamePassword
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"https://sentry.io"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"https://sentry.io" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}
Expand Down Expand Up @@ -62,26 +63,26 @@ - (void)testDsnHeaderUsername
- (void)testMissingScheme
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"sentry.io"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"https://sentry.io" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}

- (void)testMissingHost
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"http:///1"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"http:///1" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}

- (void)testUnsupportedProtocol
{
NSError *error = nil;
SentryOptions *options = [[SentryOptions alloc] initWithDsn:@"ftp://sentry.io/1"
didFailWithError:&error];
SentryOptions *options = [[SentryOptions alloc] initWithDict:@{ @"dsn" : @"ftp://sentry.io/1" }
didFailWithError:&error];
XCTAssertEqual(kSentryErrorInvalidDsnError, error.code);
XCTAssertNil(options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SentryTransportInitializerTests: XCTestCase {
}

func testDefault() throws {
let options = try Options(dsn: SentryTransportInitializerTests.dsnAsString)
let options = try Options(dict: ["dsn": SentryTransportInitializerTests.dsnAsString])

let result = TransportInitializer.initTransport(options, sentryFileManager: fileManager)

Expand Down
4 changes: 3 additions & 1 deletion Tests/SentryTests/SentryClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ class SentryClientTest: XCTestCase {
func getSut(configureOptions: (Options) -> Void = { _ in }) -> SentryClient {
var client: SentryClient!
do {
let options = try Options(dsn: SentryClientTest.dsn)
let options = try Options(dict: [
"dsn": SentryClientTest.dsn
])
configureOptions(options)

client = SentryClient(
Expand Down
Loading

0 comments on commit e6fd1fb

Please sign in to comment.