Skip to content

Commit d8e4b0d

Browse files
a7medevHeshamMegid
authored andcommitted
feat(ios): add native-side init API for startup crashes (#1056)
Jira ID: MOB-13246
1 parent f880e94 commit d8e4b0d

File tree

8 files changed

+201
-73
lines changed

8 files changed

+201
-73
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v12.1.0...dev)
44

5+
### Added
6+
7+
- Add an iOS-side init API which allows capturing crashes that happen early in the app lifecycle and before the JavaScript code has started ([#1056](https://github.com/Instabug/Instabug-React-Native/pull/1056)).
8+
59
### Changed
610

711
- Bump Instabug iOS SDK to v12.2.0 ([#1053](https://github.com/Instabug/Instabug-React-Native/pull/1053)). [See release notes](https://github.com/instabug/instabug-ios/releases/tag/12.2.0).

examples/default/ios/InstabugExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
CC3DF8932A1DFC9A003E9914 /* InstabugSurveysTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF88B2A1DFC99003E9914 /* InstabugSurveysTests.m */; };
2121
CC3DF8942A1DFC9A003E9914 /* InstabugAPMTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF88C2A1DFC99003E9914 /* InstabugAPMTests.m */; };
2222
CC3DF8952A1DFC9A003E9914 /* IBGConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF88D2A1DFC9A003E9914 /* IBGConstants.m */; };
23+
CCF1E4092B022CF20024802D /* RNInstabugTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCF1E4082B022CF20024802D /* RNInstabugTests.m */; };
2324
CD36F4707EA1F435D2CC7A15 /* libPods-InstabugExample-InstabugTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF7A6E02D40E0CEEA833CC4 /* libPods-InstabugExample-InstabugTests.a */; };
2425
F7BF47401EF3A435254C97BB /* libPods-InstabugExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BAED0D0441A708AE2390E153 /* libPods-InstabugExample.a */; };
2526
/* End PBXBuildFile section */
@@ -59,6 +60,7 @@
5960
CC3DF88B2A1DFC99003E9914 /* InstabugSurveysTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugSurveysTests.m; sourceTree = "<group>"; };
6061
CC3DF88C2A1DFC99003E9914 /* InstabugAPMTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugAPMTests.m; sourceTree = "<group>"; };
6162
CC3DF88D2A1DFC9A003E9914 /* IBGConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IBGConstants.m; sourceTree = "<group>"; };
63+
CCF1E4082B022CF20024802D /* RNInstabugTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNInstabugTests.m; sourceTree = "<group>"; };
6264
DBCB1B1D023646D84146C91E /* Pods-InstabugExample-InstabugTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugExample-InstabugTests.release.xcconfig"; path = "Target Support Files/Pods-InstabugExample-InstabugTests/Pods-InstabugExample-InstabugTests.release.xcconfig"; sourceTree = "<group>"; };
6365
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
6466
/* End PBXFileReference section */
@@ -97,6 +99,7 @@
9799
CC3DF8872A1DFC99003E9914 /* InstabugSampleTests.m */,
98100
CC3DF88B2A1DFC99003E9914 /* InstabugSurveysTests.m */,
99101
00E356F01AD99517003FC87E /* Supporting Files */,
102+
CCF1E4082B022CF20024802D /* RNInstabugTests.m */,
100103
);
101104
path = InstabugTests;
102105
sourceTree = "<group>";
@@ -447,6 +450,7 @@
447450
CC3DF8932A1DFC9A003E9914 /* InstabugSurveysTests.m in Sources */,
448451
20E556262AC55766007416B1 /* InstabugSessionReplayTests.m in Sources */,
449452
CC3DF88F2A1DFC9A003E9914 /* InstabugBugReportingTests.m in Sources */,
453+
CCF1E4092B022CF20024802D /* RNInstabugTests.m in Sources */,
450454
CC3DF88E2A1DFC9A003E9914 /* InstabugCrashReportingTests.m in Sources */,
451455
CC3DF8942A1DFC9A003E9914 /* InstabugAPMTests.m in Sources */,
452456
CC3DF8922A1DFC9A003E9914 /* InstabugRepliesTests.m in Sources */,

examples/default/ios/InstabugTests/InstabugSampleTests.m

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#import "InstabugReactBridge.h"
1313
#import <Instabug/IBGTypes.h>
1414
#import "IBGConstants.h"
15+
#import "RNInstabug.h"
1516

1617
@protocol InstabugCPTestProtocol <NSObject>
1718
/**
@@ -36,6 +37,7 @@ - (void)setEnabled:(BOOL)isEnabled;
3637

3738
@interface InstabugSampleTests : XCTestCase
3839
@property (nonatomic, retain) InstabugReactBridge *instabugBridge;
40+
@property (nonatomic, retain) id mRNInstabug;
3941
@end
4042

4143
@implementation InstabugSampleTests
@@ -44,6 +46,7 @@ @implementation InstabugSampleTests
4446
- (void)setUp {
4547
// Put setup code here. This method is called before the invocation of each test method in the class.
4648
self.instabugBridge = [[InstabugReactBridge alloc] init];
49+
self.mRNInstabug = OCMClassMock([RNInstabug class]);
4750
}
4851

4952
/*
@@ -62,23 +65,14 @@ - (void)testSetEnabled {
6265
}
6366

6467
- (void)testInit {
65-
id<InstabugCPTestProtocol> mock = OCMClassMock([Instabug class]);
6668
IBGInvocationEvent floatingButtonInvocationEvent = IBGInvocationEventFloatingButton;
6769
NSString *appToken = @"app_token";
6870
NSArray *invocationEvents = [NSArray arrayWithObjects:[NSNumber numberWithInteger:floatingButtonInvocationEvent], nil];
6971
IBGSDKDebugLogsLevel sdkDebugLogsLevel = IBGSDKDebugLogsLevelDebug;
7072

71-
XCTestExpectation *expectation = [self expectationWithDescription:@"Testing [Instabug init]"];
72-
73-
OCMStub([mock startWithToken:appToken invocationEvents:floatingButtonInvocationEvent]);
7473
[self.instabugBridge init:appToken invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel];
7574

76-
[[NSRunLoop mainRunLoop] performBlock:^{
77-
OCMVerify([mock startWithToken:appToken invocationEvents:floatingButtonInvocationEvent]);
78-
[expectation fulfill];
79-
}];
80-
81-
[self waitForExpectationsWithTimeout:EXPECTATION_TIMEOUT handler:nil];
75+
OCMVerify([self.mRNInstabug initWithToken:appToken invocationEvents:floatingButtonInvocationEvent debugLogsLevel:sdkDebugLogsLevel]);
8276
}
8377

8478
- (void)testSetUserData {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#import <XCTest/XCTest.h>
2+
#import "OCMock/OCMock.h"
3+
#import "Instabug/Instabug.h"
4+
#import "Instabug/IBGSurvey.h"
5+
#import <Instabug/IBGTypes.h>
6+
#import "RNInstabug.h"
7+
#import "RNInstabug/Instabug+CP.h"
8+
#import "RNInstabug/IBGNetworkLogger+CP.h"
9+
#import "IBGConstants.h"
10+
11+
@interface RNInstabugTests : XCTestCase
12+
13+
@property (nonatomic, strong) id mInstabug;
14+
@property (nonatomic, strong) id mIBGNetworkLogger;
15+
16+
@end
17+
18+
// Expose the `reset` API on RNInstabug to allow multiple calls to `initWithToken`.
19+
@interface RNInstabug (Test)
20+
+ (void)reset;
21+
@end
22+
23+
@implementation RNInstabugTests
24+
25+
- (void)setUp {
26+
self.mInstabug = OCMClassMock([Instabug class]);
27+
self.mIBGNetworkLogger = OCMClassMock([IBGNetworkLogger class]);
28+
29+
[RNInstabug reset];
30+
}
31+
32+
- (void)testInitWithoutLogsLevel {
33+
NSString *token = @"app-token";
34+
IBGInvocationEvent invocationEvents = IBGInvocationEventFloatingButton | IBGInvocationEventShake;
35+
36+
[RNInstabug initWithToken:token invocationEvents:invocationEvents];
37+
38+
OCMVerify([self.mInstabug startWithToken:token invocationEvents:invocationEvents]);
39+
OCMVerify([self.mInstabug setCurrentPlatform:IBGPlatformReactNative]);
40+
OCMVerify([self.mIBGNetworkLogger disableAutomaticCapturingOfNetworkLogs]);
41+
OCMVerify([self.mIBGNetworkLogger setEnabled:YES]);
42+
}
43+
44+
- (void)testInitStartsOnce {
45+
NSString *token = @"app-token";
46+
IBGInvocationEvent invocationEvents = IBGInvocationEventFloatingButton | IBGInvocationEventShake;
47+
48+
// Call init twice to check that only 1 call to native methods happens.
49+
[RNInstabug initWithToken:token invocationEvents:invocationEvents];
50+
[RNInstabug initWithToken:token invocationEvents:invocationEvents];
51+
52+
OCMVerify(times(1), [self.mInstabug startWithToken:token invocationEvents:invocationEvents]);
53+
OCMVerify(times(1), [self.mInstabug setCurrentPlatform:IBGPlatformReactNative]);
54+
OCMVerify(times(1), [self.mIBGNetworkLogger disableAutomaticCapturingOfNetworkLogs]);
55+
OCMVerify(times(1), [self.mIBGNetworkLogger setEnabled:YES]);
56+
}
57+
58+
- (void)testInitWithLogsLevel {
59+
NSString *token = @"app-token";
60+
IBGInvocationEvent invocationEvents = IBGInvocationEventFloatingButton | IBGInvocationEventShake;
61+
IBGSDKDebugLogsLevel debugLogsLevel = IBGSDKDebugLogsLevelDebug;
62+
63+
[RNInstabug initWithToken:token invocationEvents:invocationEvents debugLogsLevel:debugLogsLevel];
64+
65+
OCMVerify([self.mInstabug startWithToken:token invocationEvents:invocationEvents]);
66+
OCMVerify([self.mInstabug setCurrentPlatform:IBGPlatformReactNative]);
67+
OCMVerify([self.mIBGNetworkLogger disableAutomaticCapturingOfNetworkLogs]);
68+
OCMVerify([self.mIBGNetworkLogger setEnabled:YES]);
69+
}
70+
71+
@end

ios/RNInstabug/InstabugReactBridge.m

Lines changed: 3 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@
1212
#import <Instabug/IBGLog.h>
1313
#import <Instabug/IBGAPM.h>
1414
#import <asl.h>
15-
#import <React/RCTLog.h>
1615
#import <os/log.h>
17-
#import <Instabug/IBGTypes.h>
1816
#import <React/RCTUIManager.h>
19-
#import "Util/IBGNetworkLogger+CP.h"
17+
#import "RNInstabug.h"
2018

2119
@interface Instabug (PrivateWillSendAPI)
2220
+ (void)setWillSendReportHandler_private:(void(^)(IBGReport *report, void(^reportCompletionHandler)(IBGReport *)))willSendReportHandler_private;
@@ -40,31 +38,13 @@ - (dispatch_queue_t)methodQueue {
4038
}
4139

4240
RCT_EXPORT_METHOD(init:(NSString *)token invocationEvents:(NSArray*)invocationEventsArray debugLogsLevel:(IBGSDKDebugLogsLevel)sdkDebugLogsLevel) {
43-
SEL setPrivateApiSEL = NSSelectorFromString(@"setCurrentPlatform:");
44-
if ([[Instabug class] respondsToSelector:setPrivateApiSEL]) {
45-
NSInteger *platform = IBGPlatformReactNative;
46-
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[Instabug class] methodSignatureForSelector:setPrivateApiSEL]];
47-
[inv setSelector:setPrivateApiSEL];
48-
[inv setTarget:[Instabug class]];
49-
[inv setArgument:&(platform) atIndex:2];
50-
[inv invoke];
51-
}
5241
IBGInvocationEvent invocationEvents = 0;
53-
NSLog(@"invocation events: %ld",(long)invocationEvents);
42+
5443
for (NSNumber *boxedValue in invocationEventsArray) {
5544
invocationEvents |= [boxedValue intValue];
5645
}
57-
[IBGNetworkLogger disableAutomaticCapturingOfNetworkLogs];
58-
[Instabug startWithToken:token invocationEvents:invocationEvents];
59-
[Instabug setSdkDebugLogsLevel:sdkDebugLogsLevel];
6046

61-
RCTAddLogFunction(InstabugReactLogFunction);
62-
RCTSetLogThreshold(RCTLogLevelInfo);
63-
64-
IBGNetworkLogger.enabled = YES;
65-
66-
// Temporarily disabling APM hot launches
67-
IBGAPM.hotAppLaunchEnabled = NO;
47+
[RNInstabug initWithToken:token invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel];
6848
}
6949

7050
RCT_EXPORT_METHOD(setReproStepsConfig:(IBGUserStepsMode)bugMode :(IBGUserStepsMode)crashMode:(IBGUserStepsMode)sessionReplayMode) {
@@ -415,44 +395,4 @@ + (BOOL)iOSVersionIsLessThan:(NSString *)iOSVersion {
415395
return [iOSVersion compare:[UIDevice currentDevice].systemVersion options:NSNumericSearch] == NSOrderedDescending;
416396
};
417397

418-
// Note: This function is used to bridge IBGNSLog with RCTLogFunction.
419-
// This log function should not be used externally and is only an implementation detail.
420-
void RNIBGLog(IBGLogLevel logLevel, NSString *format, ...) {
421-
va_list arg_list;
422-
va_start(arg_list, format);
423-
IBGNSLogWithLevel(format, arg_list, logLevel);
424-
va_end(arg_list);
425-
}
426-
427-
RCTLogFunction InstabugReactLogFunction = ^(
428-
RCTLogLevel level,
429-
__unused RCTLogSource source,
430-
NSString *fileName,
431-
NSNumber *lineNumber,
432-
NSString *message
433-
)
434-
{
435-
NSString *formatString = @"Instabug - REACT LOG: %@";
436-
NSString *log = RCTFormatLog([NSDate date], level, fileName, lineNumber, message);
437-
438-
switch(level) {
439-
case RCTLogLevelTrace:
440-
RNIBGLog(IBGLogLevelVerbose, formatString, log);
441-
break;
442-
case RCTLogLevelInfo:
443-
RNIBGLog(IBGLogLevelInfo, formatString, log);
444-
break;
445-
case RCTLogLevelWarning:
446-
RNIBGLog(IBGLogLevelWarning, formatString, log);
447-
break;
448-
case RCTLogLevelError:
449-
RNIBGLog(IBGLogLevelError, formatString, log);
450-
break;
451-
case RCTLogLevelFatal:
452-
RNIBGLog(IBGLogLevelError, formatString, log);
453-
break;
454-
}
455-
};
456-
457-
458398
@end

ios/RNInstabug/RNInstabug.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef RNInstabug_h
2+
#define RNInstabug_h
3+
4+
#import <Instabug/Instabug.h>
5+
6+
@interface RNInstabug : NSObject
7+
8+
+ (void)initWithToken:(NSString *)token invocationEvents:(IBGInvocationEvent)invocationEvents debugLogsLevel:(IBGSDKDebugLogsLevel)debugLogsLevel;
9+
+ (void)initWithToken:(NSString *)token invocationEvents:(IBGInvocationEvent)invocationEvents;
10+
11+
@end
12+
13+
#endif /* RNInstabug_h */

ios/RNInstabug/RNInstabug.m

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#import <Instabug/Instabug.h>
2+
#import <React/RCTLog.h>
3+
#import "RNInstabug.h"
4+
#import "Util/IBGNetworkLogger+CP.h"
5+
#import "Util/Instabug+CP.h"
6+
7+
@implementation RNInstabug
8+
9+
static BOOL didInit = NO;
10+
11+
/// Resets `didInit` allowing re-initialization, it should not be added to the header file and is there for testing purposes.
12+
+ (void)reset {
13+
didInit = NO;
14+
}
15+
16+
+ (void)initWithToken:(NSString *)token invocationEvents:(IBGInvocationEvent)invocationEvents {
17+
// Initialization is performed only once to avoid unexpected behavior.
18+
if (didInit) {
19+
NSLog(@"IBG-RN: Skipped iOS SDK re-initialization");
20+
return;
21+
}
22+
23+
didInit = YES;
24+
25+
[Instabug setCurrentPlatform:IBGPlatformReactNative];
26+
27+
// Disable automatic network logging in the iOS SDK to avoid duplicate network logs coming
28+
// from both the iOS and React Native SDKs
29+
[IBGNetworkLogger disableAutomaticCapturingOfNetworkLogs];
30+
31+
[Instabug startWithToken:token invocationEvents:invocationEvents];
32+
33+
// Setup automatic capturing of JavaScript console logs
34+
RCTAddLogFunction(InstabugReactLogFunction);
35+
RCTSetLogThreshold(RCTLogLevelInfo);
36+
37+
// Even though automatic network logging is disabled in the iOS SDK, the network logger itself
38+
// is still needed since network logs captured by the React Native SDK need to be logged through it
39+
IBGNetworkLogger.enabled = YES;
40+
41+
// Temporarily disabling APM hot launches
42+
IBGAPM.hotAppLaunchEnabled = NO;
43+
}
44+
45+
+ (void)initWithToken:(NSString *)token
46+
invocationEvents:(IBGInvocationEvent)invocationEvents
47+
debugLogsLevel:(IBGSDKDebugLogsLevel)debugLogsLevel {
48+
[Instabug setSdkDebugLogsLevel:debugLogsLevel];
49+
[self initWithToken:token invocationEvents:invocationEvents];
50+
}
51+
52+
// Note: This function is used to bridge IBGNSLog with RCTLogFunction.
53+
// This log function should not be used externally and is only an implementation detail.
54+
void RNIBGLog(IBGLogLevel logLevel, NSString *format, ...) {
55+
va_list arg_list;
56+
va_start(arg_list, format);
57+
IBGNSLogWithLevel(format, arg_list, logLevel);
58+
va_end(arg_list);
59+
}
60+
61+
RCTLogFunction InstabugReactLogFunction = ^(RCTLogLevel level,
62+
__unused RCTLogSource source,
63+
NSString *fileName,
64+
NSNumber *lineNumber,
65+
NSString *message)
66+
{
67+
NSString *formatString = @"Instabug - REACT LOG: %@";
68+
NSString *log = RCTFormatLog([NSDate date], level, fileName, lineNumber, message);
69+
70+
switch(level) {
71+
case RCTLogLevelTrace:
72+
RNIBGLog(IBGLogLevelVerbose, formatString, log);
73+
break;
74+
case RCTLogLevelInfo:
75+
RNIBGLog(IBGLogLevelInfo, formatString, log);
76+
break;
77+
case RCTLogLevelWarning:
78+
RNIBGLog(IBGLogLevelWarning, formatString, log);
79+
break;
80+
case RCTLogLevelError:
81+
RNIBGLog(IBGLogLevelError, formatString, log);
82+
break;
83+
case RCTLogLevelFatal:
84+
RNIBGLog(IBGLogLevelError, formatString, log);
85+
break;
86+
}
87+
};
88+
89+
@end
90+

ios/RNInstabug/Util/Instabug+CP.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#import <Instabug/Instabug.h>
2+
#import <Instabug/IBGTypes.h>
3+
4+
NS_ASSUME_NONNULL_BEGIN
5+
6+
@interface Instabug (CP)
7+
8+
+ (void)setCurrentPlatform:(IBGPlatform)platform;
9+
10+
@end
11+
12+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)