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
1027NSString *logLevelToString (LogLevel level) {
1128 switch (level) {
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
0 commit comments