mirror of
				https://github.com/SoPat712/YTLitePlus.git
				synced 2025-10-31 04:44:14 -04:00 
			
		
		
		
	added files via upload
This commit is contained in:
		
							
								
								
									
										535
									
								
								Tweaks/FLEX/Network/FLEXHTTPTransactionDetailController.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										535
									
								
								Tweaks/FLEX/Network/FLEXHTTPTransactionDetailController.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,535 @@ | ||||
| // | ||||
| //  FLEXNetworkTransactionDetailController.m | ||||
| //  Flipboard | ||||
| // | ||||
| //  Created by Ryan Olson on 2/10/15. | ||||
| //  Copyright (c) 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "FLEXColor.h" | ||||
| #import "FLEXHTTPTransactionDetailController.h" | ||||
| #import "FLEXNetworkCurlLogger.h" | ||||
| #import "FLEXNetworkRecorder.h" | ||||
| #import "FLEXNetworkTransaction.h" | ||||
| #import "FLEXWebViewController.h" | ||||
| #import "FLEXImagePreviewViewController.h" | ||||
| #import "FLEXMultilineTableViewCell.h" | ||||
| #import "FLEXUtility.h" | ||||
| #import "FLEXManager+Private.h" | ||||
| #import "FLEXTableView.h" | ||||
| #import "UIBarButtonItem+FLEX.h" | ||||
|  | ||||
| typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void); | ||||
|  | ||||
| @interface FLEXNetworkDetailRow : NSObject | ||||
| @property (nonatomic, copy) NSString *title; | ||||
| @property (nonatomic, copy) NSString *detailText; | ||||
| @property (nonatomic, copy) FLEXNetworkDetailRowSelectionFuture selectionFuture; | ||||
| @end | ||||
|  | ||||
| @implementation FLEXNetworkDetailRow | ||||
| @end | ||||
|  | ||||
| @interface FLEXNetworkDetailSection : NSObject | ||||
| @property (nonatomic, copy) NSString *title; | ||||
| @property (nonatomic, copy) NSArray<FLEXNetworkDetailRow *> *rows; | ||||
| @end | ||||
|  | ||||
| @implementation FLEXNetworkDetailSection | ||||
| @end | ||||
|  | ||||
| @interface FLEXHTTPTransactionDetailController () | ||||
|  | ||||
| @property (nonatomic, readonly) FLEXHTTPTransaction *transaction; | ||||
| @property (nonatomic, copy) NSArray<FLEXNetworkDetailSection *> *sections; | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation FLEXHTTPTransactionDetailController | ||||
|  | ||||
| + (instancetype)withTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     FLEXHTTPTransactionDetailController *controller = [self new]; | ||||
|     controller.transaction = transaction; | ||||
|     return controller; | ||||
| } | ||||
|  | ||||
| - (instancetype)initWithStyle:(UITableViewStyle)style { | ||||
|     // Force grouped style | ||||
|     return [super initWithStyle:UITableViewStyleGrouped]; | ||||
| } | ||||
|  | ||||
| - (void)viewDidLoad { | ||||
|     [super viewDidLoad]; | ||||
|  | ||||
|     [NSNotificationCenter.defaultCenter addObserver:self | ||||
|         selector:@selector(handleTransactionUpdatedNotification:) | ||||
|         name:kFLEXNetworkRecorderTransactionUpdatedNotification | ||||
|         object:nil | ||||
|     ]; | ||||
|     self.toolbarItems = @[ | ||||
|         UIBarButtonItem.flex_flexibleSpace, | ||||
|         [UIBarButtonItem | ||||
|             flex_itemWithTitle:@"Copy curl" | ||||
|             target:self | ||||
|             action:@selector(copyButtonPressed:) | ||||
|         ] | ||||
|     ]; | ||||
|      | ||||
|     [self.tableView registerClass:[FLEXMultilineTableViewCell class] forCellReuseIdentifier:kFLEXMultilineCell]; | ||||
| } | ||||
|  | ||||
| - (void)setTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     if (![_transaction isEqual:transaction]) { | ||||
|         _transaction = transaction; | ||||
|         self.title = [transaction.request.URL lastPathComponent]; | ||||
|         [self rebuildTableSections]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (void)setSections:(NSArray<FLEXNetworkDetailSection *> *)sections { | ||||
|     if (![_sections isEqual:sections]) { | ||||
|         _sections = [sections copy]; | ||||
|         [self.tableView reloadData]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (void)rebuildTableSections { | ||||
|     NSMutableArray<FLEXNetworkDetailSection *> *sections = [NSMutableArray new]; | ||||
|  | ||||
|     FLEXNetworkDetailSection *generalSection = [[self class] generalSectionForTransaction:self.transaction]; | ||||
|     if (generalSection.rows.count > 0) { | ||||
|         [sections addObject:generalSection]; | ||||
|     } | ||||
|     FLEXNetworkDetailSection *requestHeadersSection = [[self class] requestHeadersSectionForTransaction:self.transaction]; | ||||
|     if (requestHeadersSection.rows.count > 0) { | ||||
|         [sections addObject:requestHeadersSection]; | ||||
|     } | ||||
|     FLEXNetworkDetailSection *queryParametersSection = [[self class] queryParametersSectionForTransaction:self.transaction]; | ||||
|     if (queryParametersSection.rows.count > 0) { | ||||
|         [sections addObject:queryParametersSection]; | ||||
|     } | ||||
|     FLEXNetworkDetailSection *postBodySection = [[self class] postBodySectionForTransaction:self.transaction]; | ||||
|     if (postBodySection.rows.count > 0) { | ||||
|         [sections addObject:postBodySection]; | ||||
|     } | ||||
|     FLEXNetworkDetailSection *responseHeadersSection = [[self class] responseHeadersSectionForTransaction:self.transaction]; | ||||
|     if (responseHeadersSection.rows.count > 0) { | ||||
|         [sections addObject:responseHeadersSection]; | ||||
|     } | ||||
|  | ||||
|     self.sections = sections; | ||||
| } | ||||
|  | ||||
| - (void)handleTransactionUpdatedNotification:(NSNotification *)notification { | ||||
|     FLEXNetworkTransaction *transaction = [[notification userInfo] objectForKey:kFLEXNetworkRecorderUserInfoTransactionKey]; | ||||
|     if (transaction == self.transaction) { | ||||
|         [self rebuildTableSections]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (void)copyButtonPressed:(id)sender { | ||||
|     [UIPasteboard.generalPasteboard setString:[FLEXNetworkCurlLogger curlCommandString:_transaction.request]]; | ||||
| } | ||||
|  | ||||
| #pragma mark - Table view data source | ||||
|  | ||||
| - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { | ||||
|     return self.sections.count; | ||||
| } | ||||
|  | ||||
| - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { | ||||
|     FLEXNetworkDetailSection *sectionModel = self.sections[section]; | ||||
|     return sectionModel.rows.count; | ||||
| } | ||||
|  | ||||
| - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { | ||||
|     FLEXNetworkDetailSection *sectionModel = self.sections[section]; | ||||
|     return sectionModel.title; | ||||
| } | ||||
|  | ||||
| - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { | ||||
|     FLEXMultilineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXMultilineCell forIndexPath:indexPath]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *rowModel = [self rowModelAtIndexPath:indexPath]; | ||||
|  | ||||
|     cell.textLabel.attributedText = [[self class] attributedTextForRow:rowModel]; | ||||
|     cell.accessoryType = rowModel.selectionFuture ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; | ||||
|     cell.selectionStyle = rowModel.selectionFuture ? UITableViewCellSelectionStyleDefault : UITableViewCellSelectionStyleNone; | ||||
|  | ||||
|     return cell; | ||||
| } | ||||
|  | ||||
| - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | ||||
|     FLEXNetworkDetailRow *rowModel = [self rowModelAtIndexPath:indexPath]; | ||||
|  | ||||
|     UIViewController *viewController = nil; | ||||
|     if (rowModel.selectionFuture) { | ||||
|         viewController = rowModel.selectionFuture(); | ||||
|     } | ||||
|  | ||||
|     if ([viewController isKindOfClass:UIAlertController.class]) { | ||||
|         [self presentViewController:viewController animated:YES completion:nil]; | ||||
|     } else if (viewController) { | ||||
|         [self.navigationController pushViewController:viewController animated:YES]; | ||||
|     } | ||||
|  | ||||
|     [tableView deselectRowAtIndexPath:indexPath animated:YES]; | ||||
| } | ||||
|  | ||||
| - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { | ||||
|     FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath]; | ||||
|     NSAttributedString *attributedText = [[self class] attributedTextForRow:row]; | ||||
|     BOOL showsAccessory = row.selectionFuture != nil; | ||||
|     return [FLEXMultilineTableViewCell | ||||
|         preferredHeightWithAttributedText:attributedText | ||||
|         maxWidth:tableView.bounds.size.width | ||||
|         style:tableView.style | ||||
|         showsAccessory:showsAccessory | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView { | ||||
|     return [NSArray flex_forEachUpTo:self.sections.count map:^id(NSUInteger i) { | ||||
|         return @"⦁"; | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| - (FLEXNetworkDetailRow *)rowModelAtIndexPath:(NSIndexPath *)indexPath { | ||||
|     FLEXNetworkDetailSection *sectionModel = self.sections[indexPath.section]; | ||||
|     return sectionModel.rows[indexPath.row]; | ||||
| } | ||||
|  | ||||
| #pragma mark - Cell Copying | ||||
|  | ||||
| - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { | ||||
|     return action == @selector(copy:); | ||||
| } | ||||
|  | ||||
| - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { | ||||
|     if (action == @selector(copy:)) { | ||||
|         FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath]; | ||||
|         UIPasteboard.generalPasteboard.string = row.detailText; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) { | ||||
|     return [UIContextMenuConfiguration | ||||
|         configurationWithIdentifier:nil | ||||
|         previewProvider:nil | ||||
|         actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) { | ||||
|             UIAction *copy = [UIAction | ||||
|                 actionWithTitle:@"Copy" | ||||
|                 image:nil | ||||
|                 identifier:nil | ||||
|                 handler:^(__kindof UIAction *action) { | ||||
|                     FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath]; | ||||
|                     UIPasteboard.generalPasteboard.string = row.detailText; | ||||
|                 } | ||||
|             ]; | ||||
|             return [UIMenu | ||||
|                 menuWithTitle:@"" image:nil identifier:nil | ||||
|                 options:UIMenuOptionsDisplayInline | ||||
|                 children:@[copy] | ||||
|             ]; | ||||
|         } | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| #pragma mark - View Configuration | ||||
|  | ||||
| + (NSAttributedString *)attributedTextForRow:(FLEXNetworkDetailRow *)row { | ||||
|     NSDictionary<NSString *, id> *titleAttributes = @{ NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Medium" size:12.0], | ||||
|                                                        NSForegroundColorAttributeName : [UIColor colorWithWhite:0.5 alpha:1.0] }; | ||||
|     NSDictionary<NSString *, id> *detailAttributes = @{ NSFontAttributeName : UIFont.flex_defaultTableCellFont, | ||||
|                                                         NSForegroundColorAttributeName : FLEXColor.primaryTextColor }; | ||||
|  | ||||
|     NSString *title = [NSString stringWithFormat:@"%@: ", row.title]; | ||||
|     NSString *detailText = row.detailText ?: @""; | ||||
|     NSMutableAttributedString *attributedText = [NSMutableAttributedString new]; | ||||
|     [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:title attributes:titleAttributes]]; | ||||
|     [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:detailText attributes:detailAttributes]]; | ||||
|  | ||||
|     return attributedText; | ||||
| } | ||||
|  | ||||
| #pragma mark - Table Data Generation | ||||
|  | ||||
| + (FLEXNetworkDetailSection *)generalSectionForTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     NSMutableArray<FLEXNetworkDetailRow *> *rows = [NSMutableArray new]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *requestURLRow = [FLEXNetworkDetailRow new]; | ||||
|     requestURLRow.title = @"Request URL"; | ||||
|     NSURL *url = transaction.request.URL; | ||||
|     requestURLRow.detailText = url.absoluteString; | ||||
|     requestURLRow.selectionFuture = ^{ | ||||
|         UIViewController *urlWebViewController = [[FLEXWebViewController alloc] initWithURL:url]; | ||||
|         urlWebViewController.title = url.absoluteString; | ||||
|         return urlWebViewController; | ||||
|     }; | ||||
|     [rows addObject:requestURLRow]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *requestMethodRow = [FLEXNetworkDetailRow new]; | ||||
|     requestMethodRow.title = @"Request Method"; | ||||
|     requestMethodRow.detailText = transaction.request.HTTPMethod; | ||||
|     [rows addObject:requestMethodRow]; | ||||
|  | ||||
|     if (transaction.cachedRequestBody.length > 0) { | ||||
|         FLEXNetworkDetailRow *postBodySizeRow = [FLEXNetworkDetailRow new]; | ||||
|         postBodySizeRow.title = @"Request Body Size"; | ||||
|         postBodySizeRow.detailText = [NSByteCountFormatter stringFromByteCount:transaction.cachedRequestBody.length countStyle:NSByteCountFormatterCountStyleBinary]; | ||||
|         [rows addObject:postBodySizeRow]; | ||||
|  | ||||
|         FLEXNetworkDetailRow *postBodyRow = [FLEXNetworkDetailRow new]; | ||||
|         postBodyRow.title = @"Request Body"; | ||||
|         postBodyRow.detailText = @"tap to view"; | ||||
|         postBodyRow.selectionFuture = ^UIViewController * () { | ||||
|             // Show the body if we can | ||||
|             NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"]; | ||||
|             UIViewController *detailViewController = [self detailViewControllerForMIMEType:contentType data:[self postBodyDataForTransaction:transaction]]; | ||||
|             if (detailViewController) { | ||||
|                 detailViewController.title = @"Request Body"; | ||||
|                 return detailViewController; | ||||
|             } | ||||
|  | ||||
|             // We can't show the body, alert user | ||||
|             return [FLEXAlert makeAlert:^(FLEXAlert *make) { | ||||
|                 make.title(@"Can't View HTTP Body Data"); | ||||
|                 make.message(@"FLEX does not have a viewer for request body data with MIME type: "); | ||||
|                 make.message(contentType); | ||||
|                 make.button(@"Dismiss").cancelStyle(); | ||||
|             }]; | ||||
|         }; | ||||
|  | ||||
|         [rows addObject:postBodyRow]; | ||||
|     } | ||||
|  | ||||
|     NSString *statusCodeString = [FLEXUtility statusCodeStringFromURLResponse:transaction.response]; | ||||
|     if (statusCodeString.length > 0) { | ||||
|         FLEXNetworkDetailRow *statusCodeRow = [FLEXNetworkDetailRow new]; | ||||
|         statusCodeRow.title = @"Status Code"; | ||||
|         statusCodeRow.detailText = statusCodeString; | ||||
|         [rows addObject:statusCodeRow]; | ||||
|     } | ||||
|  | ||||
|     if (transaction.error) { | ||||
|         FLEXNetworkDetailRow *errorRow = [FLEXNetworkDetailRow new]; | ||||
|         errorRow.title = @"Error"; | ||||
|         errorRow.detailText = transaction.error.localizedDescription; | ||||
|         [rows addObject:errorRow]; | ||||
|     } | ||||
|  | ||||
|     FLEXNetworkDetailRow *responseBodyRow = [FLEXNetworkDetailRow new]; | ||||
|     responseBodyRow.title = @"Response Body"; | ||||
|     NSData *responseData = [FLEXNetworkRecorder.defaultRecorder cachedResponseBodyForTransaction:transaction]; | ||||
|     if (responseData.length > 0) { | ||||
|         responseBodyRow.detailText = @"tap to view"; | ||||
|  | ||||
|         // Avoid a long lived strong reference to the response data in case we need to purge it from the cache. | ||||
|         weakify(responseData) | ||||
|         responseBodyRow.selectionFuture = ^UIViewController *() { strongify(responseData) | ||||
|  | ||||
|             // Show the response if we can | ||||
|             NSString *contentType = transaction.response.MIMEType; | ||||
|             if (responseData) { | ||||
|                 UIViewController *bodyDetails = [self detailViewControllerForMIMEType:contentType data:responseData]; | ||||
|                 if (bodyDetails) { | ||||
|                     bodyDetails.title = @"Response"; | ||||
|                     return bodyDetails; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // We can't show the response, alert user | ||||
|             return [FLEXAlert makeAlert:^(FLEXAlert *make) { | ||||
|                 make.title(@"Unable to View Response"); | ||||
|                 if (responseData) { | ||||
|                     make.message(@"No viewer content type: ").message(contentType); | ||||
|                 } else { | ||||
|                     make.message(@"The response has been purged from the cache"); | ||||
|                 } | ||||
|                 make.button(@"OK").cancelStyle(); | ||||
|             }]; | ||||
|         }; | ||||
|     } else { | ||||
|         BOOL emptyResponse = transaction.receivedDataLength == 0; | ||||
|         responseBodyRow.detailText = emptyResponse ? @"empty" : @"not in cache"; | ||||
|     } | ||||
|  | ||||
|     [rows addObject:responseBodyRow]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *responseSizeRow = [FLEXNetworkDetailRow new]; | ||||
|     responseSizeRow.title = @"Response Size"; | ||||
|     responseSizeRow.detailText = [NSByteCountFormatter stringFromByteCount:transaction.receivedDataLength countStyle:NSByteCountFormatterCountStyleBinary]; | ||||
|     [rows addObject:responseSizeRow]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *mimeTypeRow = [FLEXNetworkDetailRow new]; | ||||
|     mimeTypeRow.title = @"MIME Type"; | ||||
|     mimeTypeRow.detailText = transaction.response.MIMEType; | ||||
|     [rows addObject:mimeTypeRow]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *mechanismRow = [FLEXNetworkDetailRow new]; | ||||
|     mechanismRow.title = @"Mechanism"; | ||||
|     mechanismRow.detailText = transaction.requestMechanism; | ||||
|     [rows addObject:mechanismRow]; | ||||
|  | ||||
|     NSDateFormatter *startTimeFormatter = [NSDateFormatter new]; | ||||
|     startTimeFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; | ||||
|  | ||||
|     FLEXNetworkDetailRow *localStartTimeRow = [FLEXNetworkDetailRow new]; | ||||
|     localStartTimeRow.title = [NSString stringWithFormat:@"Start Time (%@)", [NSTimeZone.localTimeZone abbreviationForDate:transaction.startTime]]; | ||||
|     localStartTimeRow.detailText = [startTimeFormatter stringFromDate:transaction.startTime]; | ||||
|     [rows addObject:localStartTimeRow]; | ||||
|  | ||||
|     startTimeFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *utcStartTimeRow = [FLEXNetworkDetailRow new]; | ||||
|     utcStartTimeRow.title = @"Start Time (UTC)"; | ||||
|     utcStartTimeRow.detailText = [startTimeFormatter stringFromDate:transaction.startTime]; | ||||
|     [rows addObject:utcStartTimeRow]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *unixStartTime = [FLEXNetworkDetailRow new]; | ||||
|     unixStartTime.title = @"Unix Start Time"; | ||||
|     unixStartTime.detailText = [NSString stringWithFormat:@"%f", [transaction.startTime timeIntervalSince1970]]; | ||||
|     [rows addObject:unixStartTime]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *durationRow = [FLEXNetworkDetailRow new]; | ||||
|     durationRow.title = @"Total Duration"; | ||||
|     durationRow.detailText = [FLEXUtility stringFromRequestDuration:transaction.duration]; | ||||
|     [rows addObject:durationRow]; | ||||
|  | ||||
|     FLEXNetworkDetailRow *latencyRow = [FLEXNetworkDetailRow new]; | ||||
|     latencyRow.title = @"Latency"; | ||||
|     latencyRow.detailText = [FLEXUtility stringFromRequestDuration:transaction.latency]; | ||||
|     [rows addObject:latencyRow]; | ||||
|  | ||||
|     FLEXNetworkDetailSection *generalSection = [FLEXNetworkDetailSection new]; | ||||
|     generalSection.title = @"General"; | ||||
|     generalSection.rows = rows; | ||||
|  | ||||
|     return generalSection; | ||||
| } | ||||
|  | ||||
| + (FLEXNetworkDetailSection *)requestHeadersSectionForTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     FLEXNetworkDetailSection *requestHeadersSection = [FLEXNetworkDetailSection new]; | ||||
|     requestHeadersSection.title = @"Request Headers"; | ||||
|     requestHeadersSection.rows = [self networkDetailRowsFromDictionary:transaction.request.allHTTPHeaderFields]; | ||||
|  | ||||
|     return requestHeadersSection; | ||||
| } | ||||
|  | ||||
| + (FLEXNetworkDetailSection *)postBodySectionForTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     FLEXNetworkDetailSection *postBodySection = [FLEXNetworkDetailSection new]; | ||||
|     postBodySection.title = @"Request Body Parameters"; | ||||
|     if (transaction.cachedRequestBody.length > 0) { | ||||
|         NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"]; | ||||
|         if ([contentType hasPrefix:@"application/x-www-form-urlencoded"]) { | ||||
|             NSData *body = [self postBodyDataForTransaction:transaction]; | ||||
|             NSString *bodyString = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]; | ||||
|             postBodySection.rows = [self networkDetailRowsFromQueryItems:[FLEXUtility itemsFromQueryString:bodyString]]; | ||||
|         } | ||||
|     } | ||||
|     return postBodySection; | ||||
| } | ||||
|  | ||||
| + (FLEXNetworkDetailSection *)queryParametersSectionForTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     NSArray<NSURLQueryItem *> *queries = [FLEXUtility itemsFromQueryString:transaction.request.URL.query]; | ||||
|     FLEXNetworkDetailSection *querySection = [FLEXNetworkDetailSection new]; | ||||
|     querySection.title = @"Query Parameters"; | ||||
|     querySection.rows = [self networkDetailRowsFromQueryItems:queries]; | ||||
|  | ||||
|     return querySection; | ||||
| } | ||||
|  | ||||
| + (FLEXNetworkDetailSection *)responseHeadersSectionForTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     FLEXNetworkDetailSection *responseHeadersSection = [FLEXNetworkDetailSection new]; | ||||
|     responseHeadersSection.title = @"Response Headers"; | ||||
|     if ([transaction.response isKindOfClass:[NSHTTPURLResponse class]]) { | ||||
|         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)transaction.response; | ||||
|         responseHeadersSection.rows = [self networkDetailRowsFromDictionary:httpResponse.allHeaderFields]; | ||||
|     } | ||||
|     return responseHeadersSection; | ||||
| } | ||||
|  | ||||
| + (NSArray<FLEXNetworkDetailRow *> *)networkDetailRowsFromDictionary:(NSDictionary<NSString *, id> *)dictionary { | ||||
|     NSMutableArray<FLEXNetworkDetailRow *> *rows = [NSMutableArray new]; | ||||
|     NSArray<NSString *> *sortedKeys = [dictionary.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; | ||||
|      | ||||
|     for (NSString *key in sortedKeys) { | ||||
|         id value = dictionary[key]; | ||||
|         FLEXNetworkDetailRow *row = [FLEXNetworkDetailRow new]; | ||||
|         row.title = key; | ||||
|         row.detailText = [value description]; | ||||
|         [rows addObject:row]; | ||||
|     } | ||||
|  | ||||
|     return rows.copy; | ||||
| } | ||||
|  | ||||
| + (NSArray<FLEXNetworkDetailRow *> *)networkDetailRowsFromQueryItems:(NSArray<NSURLQueryItem *> *)items { | ||||
|     // Sort the items by name | ||||
|     items = [items sortedArrayUsingComparator:^NSComparisonResult(NSURLQueryItem *item1, NSURLQueryItem *item2) { | ||||
|         return [item1.name caseInsensitiveCompare:item2.name]; | ||||
|     }]; | ||||
|  | ||||
|     NSMutableArray<FLEXNetworkDetailRow *> *rows = [NSMutableArray new]; | ||||
|     for (NSURLQueryItem *item in items) { | ||||
|         FLEXNetworkDetailRow *row = [FLEXNetworkDetailRow new]; | ||||
|         row.title = item.name; | ||||
|         row.detailText = item.value; | ||||
|         [rows addObject:row]; | ||||
|     } | ||||
|  | ||||
|     return [rows copy]; | ||||
| } | ||||
|  | ||||
| + (UIViewController *)detailViewControllerForMIMEType:(NSString *)mimeType data:(NSData *)data { | ||||
|     FLEXCustomContentViewerFuture makeCustomViewer = FLEXManager.sharedManager.customContentTypeViewers[mimeType.lowercaseString]; | ||||
|  | ||||
|     if (makeCustomViewer) { | ||||
|         UIViewController *viewer = makeCustomViewer(data); | ||||
|  | ||||
|         if (viewer) { | ||||
|             return viewer; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // FIXME (RKO): Don't rely on UTF8 string encoding | ||||
|     UIViewController *detailViewController = nil; | ||||
|     if ([FLEXUtility isValidJSONData:data]) { | ||||
|         NSString *prettyJSON = [FLEXUtility prettyJSONStringFromData:data]; | ||||
|         if (prettyJSON.length > 0) { | ||||
|             detailViewController = [[FLEXWebViewController alloc] initWithText:prettyJSON]; | ||||
|         } | ||||
|     } else if ([mimeType hasPrefix:@"image/"]) { | ||||
|         UIImage *image = [UIImage imageWithData:data]; | ||||
|         detailViewController = [FLEXImagePreviewViewController forImage:image]; | ||||
|     } else if ([mimeType isEqual:@"application/x-plist"]) { | ||||
|         id propertyList = [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:NULL]; | ||||
|         detailViewController = [[FLEXWebViewController alloc] initWithText:[propertyList description]]; | ||||
|     } | ||||
|  | ||||
|     // Fall back to trying to show the response as text | ||||
|     if (!detailViewController) { | ||||
|         NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | ||||
|         if (text.length > 0) { | ||||
|             detailViewController = [[FLEXWebViewController alloc] initWithText:text]; | ||||
|         } | ||||
|     } | ||||
|     return detailViewController; | ||||
| } | ||||
|  | ||||
| + (NSData *)postBodyDataForTransaction:(FLEXHTTPTransaction *)transaction { | ||||
|     NSData *bodyData = transaction.cachedRequestBody; | ||||
|     if (bodyData.length > 0) { | ||||
|         NSString *contentEncoding = [transaction.request valueForHTTPHeaderField:@"Content-Encoding"]; | ||||
|         if ([contentEncoding rangeOfString:@"deflate" options:NSCaseInsensitiveSearch].length > 0 || [contentEncoding rangeOfString:@"gzip" options:NSCaseInsensitiveSearch].length > 0) { | ||||
|             bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData]; | ||||
|         } | ||||
|     } | ||||
|     return bodyData; | ||||
| } | ||||
|  | ||||
| @end | ||||
		Reference in New Issue
	
	Block a user
	 Balackburn
					Balackburn