TracesViewController.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Non-google3 relative import to support building with Xcode.
  15. #import "TracesViewController.h"
  16. #import "../Models/AccessibilityItem.h"
  17. #import "../Models/PerfLogger.h"
  18. #import "../Views/PerfTraceView+Accessibility.h"
  19. #import "../Views/PerfTraceView.h"
  20. #import "TracesViewController+Accessibility.h"
  21. #import "FIRCrashlytics.h"
  22. #import "FirebasePerformance/Sources/Public/FirebasePerformance/FIRPerformance.h"
  23. /** Edge insets used by internal subviews. */
  24. static const CGFloat kEdgeInsetsTop = 10.0f;
  25. static const CGFloat kEdgeInsetsBottom = 10.0f;
  26. static const CGFloat kEdgeInsetsLeft = 20.0f;
  27. static const CGFloat kEdgeInsetsRight = 20.0f;
  28. @interface TracesViewController () <PerfTraceViewDelegate>
  29. /** The scroll view where all the PerfTraceViews are added. */
  30. @property(nonatomic) UIScrollView *contentView;
  31. /** Button to add a new PerfTraceView. */
  32. @property(nonatomic) UIButton *addTraceButton;
  33. /** Button to add a new PerfTraceView. */
  34. @property(nonatomic) UIButton *crashButton;
  35. /** Button to add a new PerfTraceView. */
  36. @property(nonatomic) UIButton *nonFatalButton;
  37. /** A counter to maintain the number of traces created. Used for the unique names for traces. */
  38. @property(nonatomic, assign) NSInteger traceCounter;
  39. /** The most recently created PerfTraceView. */
  40. @property(nonatomic) PerfTraceView *recentTraceView;
  41. /**
  42. * The bottom constraint which manages the content size of the content view (UIScrollView). This is
  43. * usually the constraint of the most recently added PerfTraceView's bottom to be equal to the
  44. * bottom of the content view.
  45. */
  46. @property(nonatomic) NSLayoutConstraint *bottomConstraint;
  47. @end
  48. @implementation TracesViewController
  49. #pragma mark - View life cycle
  50. - (void)loadView {
  51. UIView *perfView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  52. perfView.backgroundColor = [UIColor whiteColor];
  53. self.view = perfView;
  54. [self createViewTree];
  55. [self constrainViews];
  56. }
  57. #pragma mark - Properties
  58. - (UIScrollView *)contentView {
  59. if (!_contentView) {
  60. _contentView = [[UIScrollView alloc] init];
  61. _contentView.translatesAutoresizingMaskIntoConstraints = NO;
  62. _contentView.backgroundColor = [UIColor whiteColor];
  63. _contentView.showsHorizontalScrollIndicator = NO;
  64. _contentView.showsVerticalScrollIndicator = YES;
  65. _contentView.accessibilityLabel = @"ContentView";
  66. }
  67. return _contentView;
  68. }
  69. - (UIButton *)addTraceButton {
  70. if (!_addTraceButton) {
  71. _addTraceButton = [[UIButton alloc] init];
  72. _addTraceButton.translatesAutoresizingMaskIntoConstraints = NO;
  73. [_addTraceButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  74. [_addTraceButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
  75. _addTraceButton.titleLabel.font = [UIFont systemFontOfSize:12.0];
  76. _addTraceButton.contentEdgeInsets =
  77. UIEdgeInsetsMake(kEdgeInsetsTop, kEdgeInsetsLeft, kEdgeInsetsBottom, kEdgeInsetsRight);
  78. _addTraceButton.layer.cornerRadius = 3.0f;
  79. _addTraceButton.layer.borderColor = [[UIColor blackColor] CGColor];
  80. _addTraceButton.layer.borderWidth = 1.0f;
  81. [_addTraceButton setTitle:@"Add trace" forState:UIControlStateNormal];
  82. AccessibilityItem *item = [[self class] addTraceAccessibilityItem];
  83. _addTraceButton.accessibilityIdentifier = item.accessibilityID;
  84. _addTraceButton.accessibilityLabel = item.accessibilityLabel;
  85. [_addTraceButton addTarget:self
  86. action:@selector(createAndPositionNewTraceView:)
  87. forControlEvents:UIControlEventTouchDown];
  88. }
  89. return _addTraceButton;
  90. }
  91. - (UIButton *)crashButton {
  92. if (!_crashButton) {
  93. _crashButton = [[UIButton alloc] init];
  94. _crashButton.translatesAutoresizingMaskIntoConstraints = NO;
  95. [_crashButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  96. [_crashButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
  97. _crashButton.titleLabel.font = [UIFont systemFontOfSize:12.0];
  98. _crashButton.contentEdgeInsets =
  99. UIEdgeInsetsMake(kEdgeInsetsTop, kEdgeInsetsLeft, kEdgeInsetsBottom, kEdgeInsetsRight);
  100. _crashButton.layer.cornerRadius = 3.0f;
  101. _crashButton.layer.borderColor = [[UIColor blackColor] CGColor];
  102. _crashButton.layer.borderWidth = 1.0f;
  103. [_crashButton setTitle:@"Crash - Perflytics!" forState:UIControlStateNormal];
  104. [_crashButton addTarget:self
  105. action:@selector(crashButtonTapped:)
  106. forControlEvents:UIControlEventTouchUpInside];
  107. }
  108. return _crashButton;
  109. }
  110. - (UIButton *)nonFatalButton {
  111. if (!_nonFatalButton) {
  112. _nonFatalButton = [[UIButton alloc] init];
  113. _nonFatalButton.translatesAutoresizingMaskIntoConstraints = NO;
  114. [_nonFatalButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  115. [_nonFatalButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
  116. _nonFatalButton.titleLabel.font = [UIFont systemFontOfSize:12.0];
  117. _nonFatalButton.contentEdgeInsets =
  118. UIEdgeInsetsMake(kEdgeInsetsTop, kEdgeInsetsLeft, kEdgeInsetsBottom, kEdgeInsetsRight);
  119. _nonFatalButton.layer.cornerRadius = 3.0f;
  120. _nonFatalButton.layer.borderColor = [[UIColor blackColor] CGColor];
  121. _nonFatalButton.layer.borderWidth = 1.0f;
  122. [_nonFatalButton setTitle:@"Non-Fatal - Perflytics!" forState:UIControlStateNormal];
  123. [_nonFatalButton addTarget:self
  124. action:@selector(nonFatalButtonTapped:)
  125. forControlEvents:UIControlEventTouchUpInside];
  126. }
  127. return _nonFatalButton;
  128. }
  129. - (IBAction)crashButtonTapped:(id)sender {
  130. NSObject *object = [[NSObject alloc] init];
  131. [object performSelector:@selector(crashTheApp)];
  132. }
  133. - (IBAction)nonFatalButtonTapped:(id)sender {
  134. NSURLSession *session = [NSURLSession
  135. sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  136. NSURL *URL = [NSURL URLWithString:@"https://wifi.google.com"];
  137. NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:URL];
  138. urlRequest.timeoutInterval = 2.0;
  139. NSURLSessionDataTask *dataTask =
  140. [session dataTaskWithRequest:urlRequest
  141. completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  142. NSLog(@"Network request complete.");
  143. if (error) {
  144. [[FIRCrashlytics crashlytics] recordError:error];
  145. } else {
  146. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  147. if (httpResponse.statusCode != 200) {
  148. NSDictionary *userInfo = @{
  149. @"Content-Type" : httpResponse.MIMEType,
  150. @"Referrer" : [httpResponse valueForHTTPHeaderField:@"referrer-policy"]
  151. };
  152. NSError *responseError = [NSError errorWithDomain:NSCocoaErrorDomain
  153. code:httpResponse.statusCode
  154. userInfo:userInfo];
  155. [[FIRCrashlytics crashlytics] recordError:responseError];
  156. }
  157. }
  158. }];
  159. [dataTask resume];
  160. }
  161. #pragma mark - Private Methods
  162. #pragma mark - View hierarchy methods
  163. - (void)constrainViews {
  164. [self addConstraintsString:@"H:|-40-[_addTraceButton]-40-|"
  165. forViewsBinding:NSDictionaryOfVariableBindings(_addTraceButton)];
  166. [self addConstraintsString:@"V:|-60-[_addTraceButton(40)]-10-[_crashButton(40)]-10-[_"
  167. @"nonFatalButton(40)]-10-[_contentView]-|"
  168. forViewsBinding:NSDictionaryOfVariableBindings(_addTraceButton, _crashButton,
  169. _nonFatalButton, _contentView)];
  170. [self addConstraintsString:@"H:|-40-[_crashButton]-40-|"
  171. forViewsBinding:NSDictionaryOfVariableBindings(_crashButton)];
  172. [self addConstraintsString:@"H:|-40-[_nonFatalButton]-40-|"
  173. forViewsBinding:NSDictionaryOfVariableBindings(_nonFatalButton)];
  174. [self addConstraintsString:@"H:|[_contentView]|"
  175. forViewsBinding:NSDictionaryOfVariableBindings(_contentView)];
  176. }
  177. - (void)addConstraintsString:(NSString *)string forViewsBinding:(NSDictionary *)viewsBinding {
  178. NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:string
  179. options:kNilOptions
  180. metrics:nil
  181. views:viewsBinding];
  182. [self.view addConstraints:constraints];
  183. }
  184. - (void)createViewTree {
  185. [self.view addSubview:self.contentView];
  186. [self.view addSubview:self.addTraceButton];
  187. [self.view addSubview:self.crashButton];
  188. [self.view addSubview:self.nonFatalButton];
  189. }
  190. /**
  191. * Creates a new trace and adds a PerfTraceView in the view hierarchy.
  192. *
  193. * @param button Button that initiated the request.
  194. */
  195. - (void)createAndPositionNewTraceView:(UIButton *)button {
  196. PerfTraceView *traceView = [self createTraceView];
  197. if (!traceView) {
  198. NSLog(@"Trace creation disabled.");
  199. return;
  200. }
  201. [self.contentView addSubview:traceView];
  202. // Add constraints to position the new trace view at the bottom of the list of trace views.
  203. if (self.recentTraceView) {
  204. [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.recentTraceView
  205. attribute:NSLayoutAttributeTop
  206. relatedBy:NSLayoutRelationEqual
  207. toItem:traceView
  208. attribute:NSLayoutAttributeBottom
  209. multiplier:1.0f
  210. constant:10.0f]];
  211. [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:traceView
  212. attribute:NSLayoutAttributeLeft
  213. relatedBy:NSLayoutRelationEqual
  214. toItem:self.recentTraceView
  215. attribute:NSLayoutAttributeLeft
  216. multiplier:1.0f
  217. constant:0.0f]];
  218. [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:traceView
  219. attribute:NSLayoutAttributeRight
  220. relatedBy:NSLayoutRelationEqual
  221. toItem:self.recentTraceView
  222. attribute:NSLayoutAttributeRight
  223. multiplier:1.0f
  224. constant:0.0f]];
  225. } else {
  226. [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.contentView
  227. attribute:NSLayoutAttributeBottom
  228. relatedBy:NSLayoutRelationEqual
  229. toItem:traceView
  230. attribute:NSLayoutAttributeBottom
  231. multiplier:1.0
  232. constant:10.0f]];
  233. [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:traceView
  234. attribute:NSLayoutAttributeLeft
  235. relatedBy:NSLayoutRelationEqual
  236. toItem:self.contentView
  237. attribute:NSLayoutAttributeLeft
  238. multiplier:1.0f
  239. constant:10.0f]];
  240. [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:traceView
  241. attribute:NSLayoutAttributeRight
  242. relatedBy:NSLayoutRelationEqual
  243. toItem:self.contentView
  244. attribute:NSLayoutAttributeRight
  245. multiplier:1.0f
  246. constant:-10.0f]];
  247. }
  248. [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:traceView
  249. attribute:NSLayoutAttributeCenterX
  250. relatedBy:NSLayoutRelationEqual
  251. toItem:self.contentView
  252. attribute:NSLayoutAttributeCenterX
  253. multiplier:1.0f
  254. constant:0.0f]];
  255. if (self.bottomConstraint) {
  256. [self.contentView removeConstraint:self.bottomConstraint];
  257. }
  258. self.bottomConstraint = [NSLayoutConstraint constraintWithItem:traceView
  259. attribute:NSLayoutAttributeTop
  260. relatedBy:NSLayoutRelationEqual
  261. toItem:self.contentView
  262. attribute:NSLayoutAttributeTop
  263. multiplier:1.0f
  264. constant:10.0f];
  265. [self.contentView addConstraint:self.bottomConstraint];
  266. self.recentTraceView = traceView;
  267. }
  268. /**
  269. * Creates a new PerfTraceView object with a valid unique name and returns back the object.
  270. *
  271. * @return Instance of PerfTraceView.
  272. */
  273. - (PerfTraceView *)createTraceView {
  274. PerfLog(@"Create trace view");
  275. PerfTraceView *traceView = nil;
  276. NSString *traceName = [NSString stringWithFormat:@"Trace %ld", ++self.traceCounter];
  277. FIRTrace *trace = [[FIRPerformance sharedInstance] traceWithName:traceName];
  278. if (trace) {
  279. [trace start];
  280. traceView = [[PerfTraceView alloc] initWithTrace:trace frame:CGRectZero];
  281. traceView.accessibilityLabel = @"traceView";
  282. traceView.backgroundColor = [UIColor colorWithWhite:0.9f alpha:0.5f];
  283. traceView.translatesAutoresizingMaskIntoConstraints = NO;
  284. traceView.delegate = self;
  285. } else {
  286. --self.traceCounter;
  287. }
  288. return traceView;
  289. }
  290. #pragma mark - PerfTraceViewDelegate methods
  291. - (void)perfTraceViewTraceStopped:(PerfTraceView *)traceView {
  292. PerfLog(@"Stop trace");
  293. [traceView.trace stop];
  294. }
  295. @end