Skip to content

Commit 2cf8800

Browse files
Merge pull request #520 from bugsnag/v6-structured-app
Convert event.app from NSDictionary to a structured class
2 parents 6f7fcd9 + d8039a0 commit 2cf8800

23 files changed

+442
-256
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Bugsnag Notifiers on other platforms.
88

99
## Enhancements
1010

11+
* Convert `event.app` from `NSDictionary` to a structured class
12+
[#520](https://github.com/bugsnag/bugsnag-cocoa/pull/520)
13+
1114
* Make `BugsnagClient` a public interface
1215
[#517](https://github.com/bugsnag/bugsnag-cocoa/pull/517)
1316

OSX/Bugsnag.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
E79E6BDA1F4E3850002B35F9 /* BSG_RFC3339DateTool.m in Sources */ = {isa = PBXBuildFile; fileRef = E79E6B6E1F4E3850002B35F9 /* BSG_RFC3339DateTool.m */; };
180180
E79E6BDB1F4E3850002B35F9 /* BSG_KSCrashReportFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = E79E6B711F4E3850002B35F9 /* BSG_KSCrashReportFilter.h */; };
181181
E79E6BDC1F4E3850002B35F9 /* BSG_KSCrashReportFilterCompletion.h in Headers */ = {isa = PBXBuildFile; fileRef = E79E6B721F4E3850002B35F9 /* BSG_KSCrashReportFilterCompletion.h */; };
182+
E7A9E56924361B6300D99F8A /* BugsnagAppTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E790C4A22434CB6A006FFB26 /* BugsnagAppTest.m */; };
182183
E7AB4B9E2423E184004F015A /* BugsnagOnBreadcrumbTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E7AB4B9D2423E184004F015A /* BugsnagOnBreadcrumbTest.m */; };
183184
E7CE78BB1FD94E77001D07E0 /* KSCrashReportConverter_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7CE78991FD94E60001D07E0 /* KSCrashReportConverter_Tests.m */; };
184185
E7CE78BC1FD94E77001D07E0 /* KSCrashReportStore_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7CE78951FD94E5F001D07E0 /* KSCrashReportStore_Tests.m */; };
@@ -291,6 +292,7 @@
291292
E790C46D24349CE2006FFB26 /* BugsnagAppWithState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagAppWithState.m; path = ../Source/BugsnagAppWithState.m; sourceTree = "<group>"; };
292293
E790C46E24349CE2006FFB26 /* BugsnagThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagThread.h; path = ../Source/BugsnagThread.h; sourceTree = "<group>"; };
293294
E790C46F24349CE2006FFB26 /* BugsnagThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagThread.m; path = ../Source/BugsnagThread.m; sourceTree = "<group>"; };
295+
E790C4A22434CB6A006FFB26 /* BugsnagAppTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagAppTest.m; sourceTree = "<group>"; };
294296
E791482B1FD82B0C003EFEBF /* BugsnagSessionTrackerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTrackerTest.m; sourceTree = "<group>"; };
295297
E791482C1FD82B0C003EFEBF /* BugsnagSessionTrackingPayloadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTrackingPayloadTest.m; sourceTree = "<group>"; };
296298
E791482D1FD82B0C003EFEBF /* BugsnagSessionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTest.m; sourceTree = "<group>"; };
@@ -555,6 +557,7 @@
555557
8A2C8FAF1C6BC1F700846019 /* Tests */ = {
556558
isa = PBXGroup;
557559
children = (
560+
E790C4A22434CB6A006FFB26 /* BugsnagAppTest.m */,
558561
E790C41F2432314A006FFB26 /* BugsnagClientMirrorTest.m */,
559562
E7AB4B9D2423E184004F015A /* BugsnagOnBreadcrumbTest.m */,
560563
00F9393B23FD2D9B008C7073 /* BugsnagTestsDummyClass.h */,
@@ -1039,6 +1042,7 @@
10391042
buildActionMask = 2147483647;
10401043
files = (
10411044
E7CE78CC1FD94E77001D07E0 /* KSSystemInfo_Tests.m in Sources */,
1045+
E7A9E56924361B6300D99F8A /* BugsnagAppTest.m in Sources */,
10421046
4B775FD422CBE02F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */,
10431047
E7CE78C91FD94E77001D07E0 /* KSSignalInfo_Tests.m in Sources */,
10441048
E7CE78C61FD94E77001D07E0 /* KSMach_Tests.m in Sources */,

Source/BugsnagApp.h

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,45 @@
88

99
#import <Foundation/Foundation.h>
1010

11-
NS_ASSUME_NONNULL_BEGIN
12-
1311
/**
1412
* Stateless information set by the notifier about your app can be found on this class. These values
1513
* can be accessed and amended if necessary.
1614
*/
1715
@interface BugsnagApp : NSObject
1816

19-
@end
17+
/**
18+
* The bundle version used by the application
19+
*/
20+
@property(nonatomic) NSString *_Nullable bundleVersion;
21+
22+
/**
23+
* The revision ID from the manifest (React Native apps only)
24+
*/
25+
@property(nonatomic) NSString *_Nullable codeBundleId;
26+
27+
/**
28+
* Unique identifier for the debug symbols file corresponding to the application
29+
*/
30+
@property(nonatomic) NSString *_Nullable dsymUuid;
2031

21-
NS_ASSUME_NONNULL_END
32+
/**
33+
* The app identifier used by the application
34+
*/
35+
@property(nonatomic) NSString *_Nullable id;
36+
37+
/**
38+
* The release stage set in Configuration
39+
*/
40+
@property(nonatomic) NSString *_Nullable releaseStage;
41+
42+
/**
43+
* The application type set in Configuration
44+
*/
45+
@property(nonatomic) NSString *_Nullable type;
46+
47+
/**
48+
* The version of the application set in Configuration
49+
*/
50+
@property(nonatomic) NSString *_Nullable version;
51+
52+
@end

Source/BugsnagApp.m

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,48 @@
77
//
88

99
#import "BugsnagApp.h"
10+
#import "BugsnagKeys.h"
11+
#import "BugsnagConfiguration.h"
12+
#import "BugsnagCollections.h"
1013

1114
@implementation BugsnagApp
1215

16+
+ (BugsnagApp *)appWithDictionary:(NSDictionary *)event
17+
config:(BugsnagConfiguration *)config
18+
{
19+
BugsnagApp *app = [BugsnagApp new];
20+
[self populateFields:app dictionary:event config:config];
21+
return app;
22+
}
23+
24+
+ (void)populateFields:(BugsnagApp *)app
25+
dictionary:(NSDictionary *)event
26+
config:(BugsnagConfiguration *)config
27+
{
28+
NSDictionary *system = event[BSGKeySystem];
29+
app.id = system[@"CFBundleIdentifier"];
30+
app.bundleVersion = system[@"CFBundleVersion"];
31+
app.dsymUuid = system[@"app_uuid"];
32+
app.version = [event valueForKeyPath:@"user.config.appVersion"] ?: system[@"CFBundleShortVersionString"];
33+
app.releaseStage = [event valueForKeyPath:@"user.config.releaseStage"] ?: config.releaseStage;
34+
app.codeBundleId = config.codeBundleId;
35+
app.type = config.appType;
36+
}
37+
38+
- (NSDictionary *)toDict
39+
{
40+
NSMutableDictionary *dict = [NSMutableDictionary new];
41+
BSGDictInsertIfNotNil(dict, self.bundleVersion, @"bundleVersion");
42+
BSGDictInsertIfNotNil(dict, self.codeBundleId, @"codeBundleId");
43+
BSGDictInsertIfNotNil(dict, self.id, @"id");
44+
BSGDictInsertIfNotNil(dict, self.releaseStage, @"releaseStage");
45+
BSGDictInsertIfNotNil(dict, self.type, @"type");
46+
BSGDictInsertIfNotNil(dict, self.version, @"version");
47+
48+
if (self.dsymUuid != nil) {
49+
BSGDictInsertIfNotNil(dict, @[self.dsymUuid], @"dsymUUIDs");
50+
}
51+
return dict;
52+
}
53+
1354
@end

Source/BugsnagAppWithState.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,25 @@
1010

1111
#import "BugsnagApp.h"
1212

13-
NS_ASSUME_NONNULL_BEGIN
14-
1513
/**
1614
* Stateful information set by the notifier about your app can be found on this class. These values
1715
* can be accessed and amended if necessary.
1816
*/
1917
@interface BugsnagAppWithState : BugsnagApp
2018

21-
@end
19+
/**
20+
* The number of milliseconds the application was running before the event occurred
21+
*/
22+
@property(nonatomic) NSUInteger duration;
23+
24+
/**
25+
* The number of milliseconds the application was running in the foreground before the
26+
* event occurred
27+
*/
28+
@property(nonatomic) NSUInteger durationInForeground;
2229

23-
NS_ASSUME_NONNULL_END
30+
/**
31+
* Whether the application was in the foreground when the event occurred
32+
*/
33+
@property(nonatomic) BOOL inForeground;
34+
@end

Source/BugsnagAppWithState.m

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,58 @@
77
//
88

99
#import "BugsnagAppWithState.h"
10+
#import "BugsnagKeys.h"
11+
#import "BugsnagConfiguration.h"
12+
#import "BugsnagCollections.h"
13+
14+
@interface BugsnagApp ()
15+
+ (void)populateFields:(BugsnagApp *)app
16+
dictionary:(NSDictionary *)event
17+
config:(BugsnagConfiguration *)config;
18+
19+
- (NSDictionary *)toDict;
20+
@end
1021

1122
@implementation BugsnagAppWithState
1223

24+
+ (BugsnagAppWithState *)appWithOomData:(NSDictionary *)event
25+
{
26+
BugsnagAppWithState *app = [BugsnagAppWithState new];
27+
app.id = event[@"id"];
28+
app.releaseStage = event[@"releaseStage"];
29+
app.version = event[@"version"];
30+
app.bundleVersion = event[@"bundleVersion"];
31+
app.codeBundleId = event[@"codeBundleId"];
32+
app.inForeground = [event[@"inForeground"] boolValue];
33+
app.type = event[@"type"];
34+
return app;
35+
}
36+
37+
+ (BugsnagAppWithState *)appWithDictionary:(NSDictionary *)event
38+
config:(BugsnagConfiguration *)config
39+
{
40+
BugsnagAppWithState *app = [BugsnagAppWithState new];
41+
NSDictionary *system = event[BSGKeySystem];
42+
NSDictionary *stats = system[@"application_stats"];
43+
44+
// convert from seconds to milliseconds
45+
NSUInteger activeTimeSinceLaunch = (NSUInteger) ([stats[@"active_time_since_launch"] longValue] * 1000);
46+
NSUInteger backgroundTimeSinceLaunch = (NSUInteger) ([stats[@"background_time_since_launch"] longValue] * 1000);
47+
48+
app.durationInForeground = activeTimeSinceLaunch;
49+
app.duration = activeTimeSinceLaunch + backgroundTimeSinceLaunch;
50+
app.inForeground = [stats[@"application_in_foreground"] boolValue];
51+
[BugsnagApp populateFields:app dictionary:event config:config];
52+
return app;
53+
}
54+
55+
- (NSDictionary *)toDict
56+
{
57+
NSMutableDictionary *dict = (NSMutableDictionary *) [super toDict];
58+
BSGDictInsertIfNotNil(dict, @(self.duration), @"duration");
59+
BSGDictInsertIfNotNil(dict, @(self.durationInForeground), @"durationInForeground");
60+
BSGDictInsertIfNotNil(dict, @(self.inForeground), @"inForeground");
61+
return dict;
62+
}
63+
1364
@end

Source/BugsnagConfiguration.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ - (instancetype _Nonnull)initWithApiKey:(NSString *_Nonnull)apiKey
221221
#else
222222
_releaseStage = BSGKeyProduction;
223223
#endif
224+
225+
#if TARGET_OS_TV
226+
_appType = @"tvOS";
227+
#elif TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
228+
_appType = @"iOS";
229+
#elif TARGET_OS_MAC
230+
_appType = @"macOS";
231+
#endif
224232

225233
return self;
226234
}

Source/BugsnagEvent.h

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
@class BugsnagHandledState;
1414
@class BugsnagSession;
1515
@class BugsnagBreadcrumb;
16+
@class BugsnagAppWithState;
1617
@class BugsnagMetadata;
1718

1819
typedef NS_ENUM(NSUInteger, BSGSeverity) {
@@ -90,10 +91,6 @@ initWithErrorName:(NSString *_Nonnull)name
9091
* The severity of the error generating the report
9192
*/
9293
@property(readwrite) BSGSeverity severity;
93-
/**
94-
* The release stage of the application
95-
*/
96-
@property(readwrite, copy, nullable) NSString *releaseStage;
9794
/**
9895
* The class of the error generating the report
9996
*/
@@ -120,19 +117,16 @@ initWithErrorName:(NSString *_Nonnull)name
120117
/**
121118
* Device information such as OS name and version
122119
*/
123-
@property(readwrite, copy, nullable) NSDictionary *device;
120+
@property(readonly, nonnull) NSDictionary *device;
124121
/**
125122
* Device state such as memory allocation at crash time
126123
*/
127124
@property(readwrite, copy, nullable) NSDictionary *deviceState;
125+
128126
/**
129127
* App information such as the name, version, and bundle ID
130128
*/
131-
@property(readwrite, copy, nullable) NSDictionary *app;
132-
/**
133-
* Device state such as oreground status and run duration
134-
*/
135-
@property(readwrite, copy, nullable) NSDictionary *appState;
129+
@property(readonly, nonnull) BugsnagAppWithState *app;
136130

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

0 commit comments

Comments
 (0)