Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert event.app from NSDictionary to a structured class #520

Merged
merged 2 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Bugsnag Notifiers on other platforms.

## Enhancements

* Convert `event.app` from `NSDictionary` to a structured class
[#520](https://github.com/bugsnag/bugsnag-cocoa/pull/520)

* Make `BugsnagClient` a public interface
[#517](https://github.com/bugsnag/bugsnag-cocoa/pull/517)

Expand Down
4 changes: 4 additions & 0 deletions OSX/Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@
E79E6BDA1F4E3850002B35F9 /* BSG_RFC3339DateTool.m in Sources */ = {isa = PBXBuildFile; fileRef = E79E6B6E1F4E3850002B35F9 /* BSG_RFC3339DateTool.m */; };
E79E6BDB1F4E3850002B35F9 /* BSG_KSCrashReportFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = E79E6B711F4E3850002B35F9 /* BSG_KSCrashReportFilter.h */; };
E79E6BDC1F4E3850002B35F9 /* BSG_KSCrashReportFilterCompletion.h in Headers */ = {isa = PBXBuildFile; fileRef = E79E6B721F4E3850002B35F9 /* BSG_KSCrashReportFilterCompletion.h */; };
E7A9E56924361B6300D99F8A /* BugsnagAppTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E790C4A22434CB6A006FFB26 /* BugsnagAppTest.m */; };
E7AB4B9E2423E184004F015A /* BugsnagOnBreadcrumbTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E7AB4B9D2423E184004F015A /* BugsnagOnBreadcrumbTest.m */; };
E7CE78BB1FD94E77001D07E0 /* KSCrashReportConverter_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7CE78991FD94E60001D07E0 /* KSCrashReportConverter_Tests.m */; };
E7CE78BC1FD94E77001D07E0 /* KSCrashReportStore_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7CE78951FD94E5F001D07E0 /* KSCrashReportStore_Tests.m */; };
Expand Down Expand Up @@ -291,6 +292,7 @@
E790C46D24349CE2006FFB26 /* BugsnagAppWithState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagAppWithState.m; path = ../Source/BugsnagAppWithState.m; sourceTree = "<group>"; };
E790C46E24349CE2006FFB26 /* BugsnagThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagThread.h; path = ../Source/BugsnagThread.h; sourceTree = "<group>"; };
E790C46F24349CE2006FFB26 /* BugsnagThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagThread.m; path = ../Source/BugsnagThread.m; sourceTree = "<group>"; };
E790C4A22434CB6A006FFB26 /* BugsnagAppTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagAppTest.m; sourceTree = "<group>"; };
E791482B1FD82B0C003EFEBF /* BugsnagSessionTrackerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTrackerTest.m; sourceTree = "<group>"; };
E791482C1FD82B0C003EFEBF /* BugsnagSessionTrackingPayloadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTrackingPayloadTest.m; sourceTree = "<group>"; };
E791482D1FD82B0C003EFEBF /* BugsnagSessionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTest.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -555,6 +557,7 @@
8A2C8FAF1C6BC1F700846019 /* Tests */ = {
isa = PBXGroup;
children = (
E790C4A22434CB6A006FFB26 /* BugsnagAppTest.m */,
E790C41F2432314A006FFB26 /* BugsnagClientMirrorTest.m */,
E7AB4B9D2423E184004F015A /* BugsnagOnBreadcrumbTest.m */,
00F9393B23FD2D9B008C7073 /* BugsnagTestsDummyClass.h */,
Expand Down Expand Up @@ -1039,6 +1042,7 @@
buildActionMask = 2147483647;
files = (
E7CE78CC1FD94E77001D07E0 /* KSSystemInfo_Tests.m in Sources */,
E7A9E56924361B6300D99F8A /* BugsnagAppTest.m in Sources */,
4B775FD422CBE02F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */,
E7CE78C91FD94E77001D07E0 /* KSSignalInfo_Tests.m in Sources */,
E7CE78C61FD94E77001D07E0 /* KSMach_Tests.m in Sources */,
Expand Down
39 changes: 35 additions & 4 deletions Source/BugsnagApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,45 @@

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/**
* Stateless information set by the notifier about your app can be found on this class. These values
* can be accessed and amended if necessary.
*/
@interface BugsnagApp : NSObject

@end
/**
* The bundle version used by the application
*/
@property(nonatomic) NSString *_Nullable bundleVersion;

/**
* The revision ID from the manifest (React Native apps only)
*/
@property(nonatomic) NSString *_Nullable codeBundleId;

/**
* Unique identifier for the debug symbols file corresponding to the application
*/
@property(nonatomic) NSString *_Nullable dsymUuid;

NS_ASSUME_NONNULL_END
/**
* The app identifier used by the application
*/
@property(nonatomic) NSString *_Nullable id;

/**
* The release stage set in Configuration
*/
@property(nonatomic) NSString *_Nullable releaseStage;

/**
* The application type set in Configuration
*/
@property(nonatomic) NSString *_Nullable type;

/**
* The version of the application set in Configuration
*/
@property(nonatomic) NSString *_Nullable version;

@end
41 changes: 41 additions & 0 deletions Source/BugsnagApp.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,48 @@
//

#import "BugsnagApp.h"
#import "BugsnagKeys.h"
#import "BugsnagConfiguration.h"
#import "BugsnagCollections.h"

@implementation BugsnagApp

+ (BugsnagApp *)appWithDictionary:(NSDictionary *)event
config:(BugsnagConfiguration *)config
{
BugsnagApp *app = [BugsnagApp new];
[self populateFields:app dictionary:event config:config];
return app;
}

+ (void)populateFields:(BugsnagApp *)app
dictionary:(NSDictionary *)event
config:(BugsnagConfiguration *)config
{
NSDictionary *system = event[BSGKeySystem];
app.id = system[@"CFBundleIdentifier"];
app.bundleVersion = system[@"CFBundleVersion"];
app.dsymUuid = system[@"app_uuid"];
app.version = [event valueForKeyPath:@"user.config.appVersion"] ?: system[@"CFBundleShortVersionString"];
app.releaseStage = [event valueForKeyPath:@"user.config.releaseStage"] ?: config.releaseStage;
app.codeBundleId = config.codeBundleId;
app.type = config.appType;
}

- (NSDictionary *)toDict
{
NSMutableDictionary *dict = [NSMutableDictionary new];
BSGDictInsertIfNotNil(dict, self.bundleVersion, @"bundleVersion");
BSGDictInsertIfNotNil(dict, self.codeBundleId, @"codeBundleId");
BSGDictInsertIfNotNil(dict, self.id, @"id");
BSGDictInsertIfNotNil(dict, self.releaseStage, @"releaseStage");
BSGDictInsertIfNotNil(dict, self.type, @"type");
BSGDictInsertIfNotNil(dict, self.version, @"version");

if (self.dsymUuid != nil) {
BSGDictInsertIfNotNil(dict, @[self.dsymUuid], @"dsymUUIDs");
}
return dict;
}

@end
19 changes: 15 additions & 4 deletions Source/BugsnagAppWithState.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,25 @@

#import "BugsnagApp.h"

NS_ASSUME_NONNULL_BEGIN

/**
* Stateful information set by the notifier about your app can be found on this class. These values
* can be accessed and amended if necessary.
*/
@interface BugsnagAppWithState : BugsnagApp

@end
/**
* The number of milliseconds the application was running before the event occurred
*/
@property(nonatomic) NSUInteger duration;

/**
* The number of milliseconds the application was running in the foreground before the
* event occurred
*/
@property(nonatomic) NSUInteger durationInForeground;

NS_ASSUME_NONNULL_END
/**
* Whether the application was in the foreground when the event occurred
*/
@property(nonatomic) BOOL inForeground;
@end
51 changes: 51 additions & 0 deletions Source/BugsnagAppWithState.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,58 @@
//

#import "BugsnagAppWithState.h"
#import "BugsnagKeys.h"
#import "BugsnagConfiguration.h"
#import "BugsnagCollections.h"

@interface BugsnagApp ()
+ (void)populateFields:(BugsnagApp *)app
dictionary:(NSDictionary *)event
config:(BugsnagConfiguration *)config;

- (NSDictionary *)toDict;
@end

@implementation BugsnagAppWithState

+ (BugsnagAppWithState *)appWithOomData:(NSDictionary *)event
{
BugsnagAppWithState *app = [BugsnagAppWithState new];
app.id = event[@"id"];
app.releaseStage = event[@"releaseStage"];
app.version = event[@"version"];
app.bundleVersion = event[@"bundleVersion"];
app.codeBundleId = event[@"codeBundleId"];
app.inForeground = [event[@"inForeground"] boolValue];
app.type = event[@"type"];
return app;
}

+ (BugsnagAppWithState *)appWithDictionary:(NSDictionary *)event
config:(BugsnagConfiguration *)config
{
BugsnagAppWithState *app = [BugsnagAppWithState new];
NSDictionary *system = event[BSGKeySystem];
NSDictionary *stats = system[@"application_stats"];

// convert from seconds to milliseconds
NSUInteger activeTimeSinceLaunch = (NSUInteger) ([stats[@"active_time_since_launch"] longValue] * 1000);
NSUInteger backgroundTimeSinceLaunch = (NSUInteger) ([stats[@"background_time_since_launch"] longValue] * 1000);

app.durationInForeground = activeTimeSinceLaunch;
app.duration = activeTimeSinceLaunch + backgroundTimeSinceLaunch;
app.inForeground = [stats[@"application_in_foreground"] boolValue];
[BugsnagApp populateFields:app dictionary:event config:config];
return app;
}

- (NSDictionary *)toDict
{
NSMutableDictionary *dict = (NSMutableDictionary *) [super toDict];
BSGDictInsertIfNotNil(dict, @(self.duration), @"duration");
BSGDictInsertIfNotNil(dict, @(self.durationInForeground), @"durationInForeground");
BSGDictInsertIfNotNil(dict, @(self.inForeground), @"inForeground");
return dict;
}

@end
8 changes: 8 additions & 0 deletions Source/BugsnagConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ - (instancetype _Nonnull)initWithApiKey:(NSString *_Nonnull)apiKey
#else
_releaseStage = BSGKeyProduction;
#endif

#if TARGET_OS_TV
_appType = @"tvOS";
#elif TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
_appType = @"iOS";
#elif TARGET_OS_MAC
_appType = @"macOS";
#endif

return self;
}
Expand Down
14 changes: 4 additions & 10 deletions Source/BugsnagEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@class BugsnagHandledState;
@class BugsnagSession;
@class BugsnagBreadcrumb;
@class BugsnagAppWithState;
@class BugsnagMetadata;

typedef NS_ENUM(NSUInteger, BSGSeverity) {
Expand Down Expand Up @@ -90,10 +91,6 @@ initWithErrorName:(NSString *_Nonnull)name
* The severity of the error generating the report
*/
@property(readwrite) BSGSeverity severity;
/**
* The release stage of the application
*/
@property(readwrite, copy, nullable) NSString *releaseStage;
/**
* The class of the error generating the report
*/
Expand All @@ -120,19 +117,16 @@ initWithErrorName:(NSString *_Nonnull)name
/**
* Device information such as OS name and version
*/
@property(readwrite, copy, nullable) NSDictionary *device;
@property(readonly, nonnull) NSDictionary *device;
/**
* Device state such as memory allocation at crash time
*/
@property(readwrite, copy, nullable) NSDictionary *deviceState;

/**
* App information such as the name, version, and bundle ID
*/
@property(readwrite, copy, nullable) NSDictionary *app;
/**
* Device state such as oreground status and run duration
*/
@property(readwrite, copy, nullable) NSDictionary *appState;
@property(readonly, nonnull) BugsnagAppWithState *app;

/**
* Whether the event was a crash (i.e. unhandled) or handled error in which the system
Expand Down
Loading