Skip to content

Commit cccecbf

Browse files
committed
Fix Objective-C app
1 parent 0338982 commit cccecbf

File tree

3 files changed

+217
-17
lines changed

3 files changed

+217
-17
lines changed

bazel/framework_imports_extractor.bzl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ def _framework_imports_extractor(ctx):
2929
arch = arch[4:]
3030

3131
outputs.extend([
32-
ctx.actions.declare_file("Capture.framework/Modules/Capture.swiftmodule/{}.swiftdoc".format(arch)),
33-
ctx.actions.declare_file("Capture.framework/Modules/Capture.swiftmodule/{}.swiftinterface".format(arch)),
32+
ctx.actions.declare_file("Capture.framework/Modules/Capture.swiftmodule/{}.swiftmodule".format(arch)),
3433
])
3534

3635
if len(ctx.attr.framework[0].files.to_list()) != 1:

examples/objective-c/CAPViewController.m

Lines changed: 198 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,26 @@
33

44
#import "CAPViewController.h"
55

6-
NSString * const kCaptureAPIKey= @"<YOUR API KEY GOES HERE>";
7-
NSString * const kCaptureURLString = @"https://api.bitdrift.io";
8-
NSString * const kDeviceId = @"ios-objc-helloworld";
6+
static NSString * const kDefaultCaptureAPIKey = @"<YOUR API KEY GOES HERE>";
7+
static NSString * const kDefaultCaptureURLString = @"https://api.bitdrift.io";
8+
static NSString * const kSavedCaptureAPIKeyKey = @"capture_api_key";
9+
static NSString * const kSavedCaptureURLKey = @"capture_api_url";
10+
11+
@interface CAPIssueLogger : NSObject <IssueReportCallback>
12+
@end
13+
14+
@implementation CAPIssueLogger
15+
16+
- (void)onBeforeReportSendWithReport:(CAPIssueReport *)report {
17+
NSMutableDictionary<NSString *, NSString *> *fields = [NSMutableDictionary dictionaryWithDictionary:report.fields ?: @{}];
18+
fields[@"reportType"] = report.reportType ?: @"";
19+
fields[@"reason"] = report.reason ?: @"";
20+
fields[@"details"] = report.details ?: @"";
21+
fields[@"sessionID"] = report.sessionID ?: @"";
22+
[CAPLogger logInfo:@"Bitdrift IssueCallback" fields:fields];
23+
}
24+
25+
@end
926

1027
NSString *logLevelToString(LogLevel level) {
1128
switch (level) {
@@ -24,16 +41,29 @@
2441
return @"Unknown";
2542
}
2643

27-
@interface CAPViewController ()
44+
@interface CAPViewController () <UITextFieldDelegate>
2845

2946
@property (nonatomic, strong) NSArray<NSNumber *> *logLevels;
3047
@property (nonatomic, assign) LogLevel selectedLogLevel;
3148
@property (nonatomic, weak) UIButton *pickLogLevelButton;
49+
@property (nonatomic, weak) UITextField *apiKeyTextField;
50+
@property (nonatomic, weak) UITextField *apiURLTextField;
51+
@property (nonatomic, strong) CAPIssueLogger *issueLogger;
3252

3353
@end
3454

3555
@implementation CAPViewController
3656

57+
- (NSString *)savedAPIKey {
58+
NSString *apiKey = [[NSUserDefaults standardUserDefaults] stringForKey:kSavedCaptureAPIKeyKey];
59+
return apiKey.length > 0 ? apiKey : kDefaultCaptureAPIKey;
60+
}
61+
62+
- (NSString *)savedAPIURLString {
63+
NSString *apiURLString = [[NSUserDefaults standardUserDefaults] stringForKey:kSavedCaptureURLKey];
64+
return apiURLString.length > 0 ? apiURLString : kDefaultCaptureURLString;
65+
}
66+
3767
- (instancetype)init {
3868
self = [super init];
3969

@@ -53,16 +83,48 @@ - (instancetype)init {
5383
}
5484

5585
- (void)loadView {
86+
UIView *rootView = [UIView new];
87+
rootView.backgroundColor = [UIColor whiteColor];
88+
89+
UIView *configurationView = [self createConfigurationView];
5690
UIView *sendLogView = [self createSendLogView];
91+
UIView *copySessionURLView = [self createCopySessionURLView];
92+
UIView *crashView = [self createCrashView];
5793
UIView *copySessionIDView = [self createCopySessionIDView];
5894

59-
UIStackView *view = [[UIStackView alloc]
60-
initWithArrangedSubviews:@[[UIView new], sendLogView, copySessionIDView]];
61-
view.axis = UILayoutConstraintAxisVertical;
62-
view.distribution = UIStackViewDistributionEqualCentering;
63-
view.backgroundColor = [UIColor whiteColor];
64-
65-
self.view = view;
95+
UIStackView *contentStack = [[UIStackView alloc]
96+
initWithArrangedSubviews:@[configurationView, sendLogView, crashView, copySessionURLView, copySessionIDView]];
97+
contentStack.axis = UILayoutConstraintAxisVertical;
98+
contentStack.spacing = 16;
99+
contentStack.translatesAutoresizingMaskIntoConstraints = NO;
100+
101+
UIScrollView *scrollView = [UIScrollView new];
102+
scrollView.alwaysBounceVertical = YES;
103+
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
104+
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
105+
106+
[scrollView addSubview:contentStack];
107+
[rootView addSubview:scrollView];
108+
109+
UILayoutGuide *safeArea = rootView.safeAreaLayoutGuide;
110+
[NSLayoutConstraint activateConstraints:@[
111+
[scrollView.topAnchor constraintEqualToAnchor:safeArea.topAnchor],
112+
[scrollView.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor],
113+
[scrollView.trailingAnchor constraintEqualToAnchor:safeArea.trailingAnchor],
114+
[scrollView.bottomAnchor constraintEqualToAnchor:safeArea.bottomAnchor],
115+
116+
[contentStack.topAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.topAnchor constant:16],
117+
[contentStack.leadingAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.leadingAnchor constant:16],
118+
[contentStack.trailingAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.trailingAnchor constant:-16],
119+
[contentStack.bottomAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.bottomAnchor constant:-16],
120+
[contentStack.widthAnchor constraintEqualToAnchor:scrollView.frameLayoutGuide.widthAnchor constant:-32],
121+
]];
122+
123+
UITapGestureRecognizer *dismissKeyboardGesture = [[UITapGestureRecognizer alloc] initWithTarget:rootView action:@selector(endEditing:)];
124+
dismissKeyboardGesture.cancelsTouchesInView = NO;
125+
[rootView addGestureRecognizer:dismissKeyboardGesture];
126+
127+
self.view = rootView;
66128
}
67129

68130
- (void)viewWillAppear:(BOOL)animated {
@@ -79,29 +141,89 @@ - (void)setSelectedLogLevel:(LogLevel)selectedLogLevel {
79141
// MARK: - Setup
80142

81143
- (void)setUpLogger {
144+
NSURL *apiURL = [NSURL URLWithString:[self savedAPIURLString]];
145+
self.issueLogger = [CAPIssueLogger new];
146+
CAPIssueCallbackConfiguration *issueCallbackConfiguration = [[CAPIssueCallbackConfiguration alloc]
147+
initWithCallbackQueue:dispatch_get_main_queue()
148+
issueReportCallback:self.issueLogger];
82149
CAPConfiguration *config = [[CAPConfiguration alloc] initWithEnableFatalIssueReporting:true
83150
enableURLSessionIntegration:true
84151
sleepMode:false
85-
apiURL:[NSURL URLWithString:kCaptureURLString]
86-
rootFileURL:nil];
152+
apiURL:apiURL
153+
rootFileURL:nil
154+
issueCallbackConfiguration:issueCallbackConfiguration];
87155
[CAPLogger
88-
startWithAPIKey:kCaptureAPIKey
156+
startWithAPIKey:[self savedAPIKey]
89157
sessionStrategy:[CAPSessionStrategy fixed]
90-
configuration: config
158+
configuration: config
91159
];
92160

93161
[CAPLogger logInfo:@"An objective-c example app is launching" fields:nil];
162+
163+
NSDictionary *previousRunInfo = [CAPLogger previousRunInfo];
164+
if (previousRunInfo != nil) {
165+
NSMutableDictionary<NSString *, NSString *> *fields = [NSMutableDictionary new];
166+
NSNumber *hasFatallyTerminated = previousRunInfo[@"hasFatallyTerminated"];
167+
fields[@"hasFatallyTerminated"] = hasFatallyTerminated.boolValue ? @"true" : @"false";
168+
[CAPLogger logInfo:@"Bitdrift PreviousRunInfo" fields:fields];
169+
}
94170
}
95171

96172
- (void)refresh {
97173
[self.pickLogLevelButton
98174
setTitle:logLevelToString(self.selectedLogLevel)
99175
forState:UIControlStateNormal
100176
];
177+
178+
self.apiKeyTextField.text = [self savedAPIKey];
179+
self.apiURLTextField.text = [self savedAPIURLString];
101180
}
102181

103182
// MARK: - Views
104183

184+
- (UIView *)createConfigurationView {
185+
UILabel *apiKeyLabel = [UILabel new];
186+
apiKeyLabel.text = @"API Key";
187+
188+
UITextField *apiKeyTextField = [UITextField new];
189+
apiKeyTextField.borderStyle = UITextBorderStyleRoundedRect;
190+
apiKeyTextField.placeholder = kDefaultCaptureAPIKey;
191+
apiKeyTextField.autocorrectionType = UITextAutocorrectionTypeNo;
192+
apiKeyTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
193+
apiKeyTextField.returnKeyType = UIReturnKeyDone;
194+
apiKeyTextField.delegate = self;
195+
apiKeyTextField.text = [self savedAPIKey];
196+
197+
UILabel *apiURLLabel = [UILabel new];
198+
apiURLLabel.text = @"API URL";
199+
200+
UITextField *apiURLTextField = [UITextField new];
201+
apiURLTextField.borderStyle = UITextBorderStyleRoundedRect;
202+
apiURLTextField.placeholder = kDefaultCaptureURLString;
203+
apiURLTextField.autocorrectionType = UITextAutocorrectionTypeNo;
204+
apiURLTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
205+
apiURLTextField.keyboardType = UIKeyboardTypeURL;
206+
apiURLTextField.returnKeyType = UIReturnKeyDone;
207+
apiURLTextField.delegate = self;
208+
apiURLTextField.text = [self savedAPIURLString];
209+
210+
UIButton *saveButton = [UIButton buttonWithType:UIButtonTypeSystem];
211+
saveButton.backgroundColor = UIColor.systemBlueColor;
212+
saveButton.tintColor = UIColor.whiteColor;
213+
[saveButton setTitle:@"Save Config" forState:UIControlStateNormal];
214+
[saveButton addTarget:self action:@selector(saveConfiguration) forControlEvents:UIControlEventTouchUpInside];
215+
216+
UIStackView *configurationView = [[UIStackView alloc]
217+
initWithArrangedSubviews:@[apiKeyLabel, apiKeyTextField, apiURLLabel, apiURLTextField, saveButton]];
218+
configurationView.axis = UILayoutConstraintAxisVertical;
219+
configurationView.spacing = 8;
220+
221+
self.apiKeyTextField = apiKeyTextField;
222+
self.apiURLTextField = apiURLTextField;
223+
224+
return configurationView;
225+
}
226+
105227
- (UIView *)createSendLogView {
106228
UIButton *sendLogButton = [UIButton buttonWithType:UIButtonTypeSystem];
107229
sendLogButton.backgroundColor = UIColor.lightGrayColor;
@@ -133,6 +255,32 @@ - (UIView *)createSendLogView {
133255
return sendLogRow;
134256
}
135257

258+
- (UIView *)createCrashView {
259+
UIButton *crashButton = [UIButton buttonWithType:UIButtonTypeSystem];
260+
crashButton.backgroundColor = UIColor.systemRedColor;
261+
crashButton.tintColor = UIColor.whiteColor;
262+
[crashButton setTitle:@"Trigger Controlled Crash"
263+
forState:UIControlStateNormal];
264+
[crashButton addTarget:self
265+
action:@selector(triggerControlledCrash)
266+
forControlEvents:UIControlEventTouchUpInside];
267+
268+
return crashButton;
269+
}
270+
271+
- (UIView *)createCopySessionURLView {
272+
UIButton *copySessionURLButton = [UIButton buttonWithType:UIButtonTypeSystem];
273+
copySessionURLButton.backgroundColor = UIColor.systemGreenColor;
274+
copySessionURLButton.tintColor = UIColor.whiteColor;
275+
[copySessionURLButton setTitle:@"Copy Session URL"
276+
forState:UIControlStateNormal];
277+
[copySessionURLButton addTarget:self
278+
action:@selector(copySessionURL)
279+
forControlEvents:UIControlEventTouchUpInside];
280+
281+
return copySessionURLButton;
282+
}
283+
136284
- (UIView *)createCopySessionIDView {
137285
UIStackView *copySessionIDRow = [UIStackView new];
138286
copySessionIDRow.axis = UILayoutConstraintAxisVertical;
@@ -199,8 +347,43 @@ - (void)pickLogLevel {
199347
[self presentViewController:alert animated:YES completion:nil];
200348
}
201349

350+
- (void)saveConfiguration {
351+
[self.view endEditing:YES];
352+
353+
NSString *apiKey = self.apiKeyTextField.text.length > 0 ? self.apiKeyTextField.text : kDefaultCaptureAPIKey;
354+
NSString *apiURLString = self.apiURLTextField.text.length > 0 ? self.apiURLTextField.text : kDefaultCaptureURLString;
355+
356+
[[NSUserDefaults standardUserDefaults] setObject:apiKey forKey:kSavedCaptureAPIKeyKey];
357+
[[NSUserDefaults standardUserDefaults] setObject:apiURLString forKey:kSavedCaptureURLKey];
358+
[[NSUserDefaults standardUserDefaults] synchronize];
359+
360+
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Saved"
361+
message:@"Restart the app to apply the new settings."
362+
preferredStyle:UIAlertControllerStyleAlert];
363+
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
364+
[self presentViewController:alert animated:YES completion:nil];
365+
}
366+
367+
- (void)triggerControlledCrash {
368+
[CAPLogger logInfo:@"About to trigger controlled crash" fields:nil];
369+
NSArray<NSNumber *> *items = @[@1, @2, @3];
370+
NSLog(@"About to access out-of-bounds item: %@", items[3]);
371+
}
372+
373+
- (void)copySessionURL {
374+
NSString *sessionURL = [[CAPLogger class] sessionURL];
375+
if (sessionURL.length > 0) {
376+
UIPasteboard.generalPasteboard.string = sessionURL;
377+
}
378+
}
379+
202380
- (void)copySessionIDRow {
203381
UIPasteboard.generalPasteboard.string = [[CAPLogger class] sessionID];
204382
}
205383

384+
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
385+
[textField resignFirstResponder];
386+
return YES;
387+
}
388+
206389
@end

platform/swift/source/LoggerObjc.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Foundation
1313
public final class CAPConfiguration: NSObject {
1414
let underlyingConfig: Configuration
1515
public let enableURLSessionIntegration: Bool
16+
public let issueCallbackConfiguration: IssueCallbackConfiguration?
1617

1718
/// Initializes a new instance of the Capture configuration.
1819
///
@@ -22,6 +23,7 @@ public final class CAPConfiguration: NSObject {
2223
public init(enableFatalIssueReporting: Bool, enableURLSessionIntegration: Bool) {
2324
self.underlyingConfig = Configuration(enableFatalIssueReporting: enableFatalIssueReporting)
2425
self.enableURLSessionIntegration = enableURLSessionIntegration
26+
self.issueCallbackConfiguration = nil
2527
}
2628

2729
/// Initializes a new instance of the Capture configuration.
@@ -41,6 +43,7 @@ public final class CAPConfiguration: NSObject {
4143
self.underlyingConfig = Configuration(sleepMode: sleepMode, enableFatalIssueReporting: enableFatalIssueReporting,
4244
apiURL: apiURL ?? URL(string: "https://api.bitdrift.io")!, rootFileURL: rootFileURL)
4345
self.enableURLSessionIntegration = enableURLSessionIntegration
46+
self.issueCallbackConfiguration = nil
4447
}
4548

4649
/// Initializes a new instance of the Capture configuration.
@@ -52,6 +55,21 @@ public final class CAPConfiguration: NSObject {
5255
public init(enableFatalIssueReporting: Bool, enableURLSessionIntegration: Bool, sleepMode: SleepMode) {
5356
self.underlyingConfig = Configuration(sleepMode: sleepMode, enableFatalIssueReporting: enableFatalIssueReporting)
5457
self.enableURLSessionIntegration = enableURLSessionIntegration
58+
self.issueCallbackConfiguration = nil
59+
}
60+
61+
@objc
62+
public init(enableFatalIssueReporting: Bool, enableURLSessionIntegration: Bool,
63+
sleepMode: SleepMode, apiURL: URL?, rootFileURL: URL?,
64+
issueCallbackConfiguration: IssueCallbackConfiguration?)
65+
{
66+
self.underlyingConfig = Configuration(sleepMode: sleepMode,
67+
enableFatalIssueReporting: enableFatalIssueReporting,
68+
apiURL: apiURL ?? URL(string: "https://api.bitdrift.io")!,
69+
rootFileURL: rootFileURL,
70+
issueCallbackConfiguration: issueCallbackConfiguration)
71+
self.enableURLSessionIntegration = enableURLSessionIntegration
72+
self.issueCallbackConfiguration = issueCallbackConfiguration
5573
}
5674
}
5775

0 commit comments

Comments
 (0)