Compare commits

...

46 Commits

Author SHA1 Message Date
Ryan Olson a42af79040 Protect against a nil NSURLConnection or NSURLSession object being passed to the swizzled delegate methods.
See https://github.com/Flipboard/FLEX/issues/61 for motivation and background.
2015-05-23 14:59:57 -07:00
Ryan Olson 792634527a Merge pull request #68 from modnovolyk/fix-nsurlsessiontask-callback-bug
Fix Network Debugging compatibility with Alamofire
2015-05-19 18:00:02 -07:00
Ryan Olson 5627219c56 Merge pull request #67 from modnovolyk/fix-build-as-framework-issue
Fix build errors while building FLEX as framework for usage in Swift project
2015-05-19 17:53:45 -07:00
Max Odnovolyk 4e81d4b476 Merge branch 'fix-build-as-framework-issue' into fix-nsurlsessiontask-callback-bug
* fix-build-as-framework-issue:
  Make all headers except FLEXManager.h private
  Mute deprecated warning with less pre-processor noise
  Revert changes from fix-build-as-framework-issue branch
2015-05-18 09:17:50 +03:00
Max Odnovolyk 001d58cd89 Make all headers except FLEXManager.h private 2015-05-18 08:37:55 +03:00
Max Odnovolyk 03c96d8fdb Mute deprecated warning with less pre-processor noise 2015-05-18 08:02:39 +03:00
Max Odnovolyk b5423192fb Revert changes from fix-build-as-framework-issue branch 2015-05-18 07:41:31 +03:00
Max Odnovolyk 1efb40a07e Fix NSURLSession task creation with empty completion handler bug 2015-05-17 02:35:17 +03:00
Max Odnovolyk 6c6023dc84 Suppress deprecated-declarations warnings while building as framework via Cocoapods 2015-05-16 03:39:42 +03:00
Max Odnovolyk a6dc4b010c Update FLEX.podspec 2015-05-16 02:53:14 +03:00
Max Odnovolyk dd87da4134 Podspec private_header_files pattern update 2015-05-16 02:08:01 +03:00
Max Odnovolyk 627ff6cbe2 Exclude '*Private*.{h,m}' files from frameworks public headers 2015-05-16 01:58:27 +03:00
Max Odnovolyk 9cc8435cae Hide public asl.h import to prevent 'Include of non-modular header inside framework module' error when building FLEX as framework. 2015-05-16 01:30:43 +03:00
Ryan Olson 3d977450ca Allow the FLEXWindow to become key when it wants to accept input and affect the status bar.
The previous logic was preventing FLEXWindow from ever becoming key.
2015-05-14 10:08:54 -07:00
Ryan Olson f7c482ceed Only allow the FLEXWindow to become key when it has a modal presented.
See https://github.com/Flipboard/FLEX/issues/64 for a more detailed explanation of the motivation for this change.
2015-05-13 21:12:36 -07:00
Ryan Olson c38b90ee60 Change approach to status bar and rotation handling.
Rather than trying to mimic system behavior with status bars and rotation, we can do better by trying to get out of the way entirely. This resolves the UIAlertView/UIAlertController related infinite recursion crashes that started in 8.3. Unfortunately, this approach requires using private API.
2015-04-22 18:38:05 -07:00
Ryan Olson 70491431fa Update CocoaPods example usage 2015-04-16 09:52:32 -07:00
Ryan Olson 6bc055911e Remove redundant contact info from README
Replaced by shield
2015-04-16 09:46:11 -07:00
Ryan Olson 9b1e13b963 Add additional README shields (pod, license, platform, contact) 2015-04-16 09:44:06 -07:00
Ryan Olson 74a73893d4 Add travis shield to README 2015-04-16 09:26:01 -07:00
Ryan Olson 4a3ab17851 Specify simulator in travis.yml 2015-04-16 09:19:12 -07:00
Ryan Olson 5b8efe71a7 Update .travis.yml with Xcode project and scheme 2015-04-15 17:23:15 -07:00
Ryan Olson b7f2d9bcbe Add shared scheme for the example UICatalog xcodeproj
For Travis CI
2015-04-15 17:15:56 -07:00
Ryan Olson f4efc6dbbf Add .travis.yml 2015-04-15 16:04:42 -07:00
Ryan Olson efab760253 Fix setShouldEnableOnLauch: in FLEXNetworkObserver
Doh!
2015-04-01 10:38:28 -07:00
Ryan Olson 48826e2160 Fix readable type encoding for “@?” typically seen with block objects 2015-03-25 23:16:31 -07:00
Fabien Sanglard 0b4e231814 CamelCase directory names that previously had spaces 2015-03-25 09:38:54 -07:00
Ryan Olson 6f2d811338 Improve network history table view performance when lots of network activity is occurring.
This was showing up hot in the profile. The documentation for -[UITableView reloadRowsAtIndexPaths:withRowAnimation:] actually suggests what we’re doing here now for cases where you don’t need to make updates obvious to the user.
2015-03-25 09:22:24 -07:00
Ryan Olson 9c9ce5e2e1 More accurate request timing info if the network recorder queue gets backed up. 2015-03-25 09:22:24 -07:00
Ryan Olson 08b4559b26 Merge pull request #55 from mustafa/fix-imports
fix some imports so we don't depend on pch file
2015-03-24 19:48:41 -07:00
Mustafa Furniturewala 913ad5e2c6 fix some imports so we don't depend on pch file
this is useful if this is being added to a dynamic framework
2015-03-24 11:21:16 -07:00
Ryan Olson 7649dc616c Merge pull request #51 from larrytin/larrytin-patch-1
FLEX requires iOS 7 or higher
2015-03-08 11:53:55 -07:00
田传武 efabb29a52 FLEX requires iOS 7 or higher 2015-03-08 19:40:26 +08:00
Ryan Olson e3612e31d7 Merge pull request #50 from louis-cai/master
update README.md
2015-03-05 22:23:33 -08:00
cailu 2575d2eaee update CocoaPosd version 2015-03-06 11:40:55 +08:00
Ryan Olson f041002e73 Bump version to 2.0.2 2015-03-05 11:07:02 -08:00
Ryan Olson 0da49c1eb6 Avoid trying to thumbnail nil image responses.
These lead to image IO errors in the console log.
2015-03-05 10:59:32 -08:00
Ryan Olson a3a84b0cd7 Merge pull request #48 from DaidoujiChen/feature/json_detect
enhance json detect
2015-03-04 09:35:00 -08:00
DaidoujiChen 33be034e2b enhance json detect
rollback method prettyJSONStringFromData
2015-03-04 15:05:21 +08:00
Ryan Olson f590263d9f Add copy button to request detail view controller.
Copies the text contents of all the rows (i.e. general, request headers, response headers, query parameters, etc.)
2015-03-03 22:42:50 -08:00
Ryan Olson a1c378a9d5 Support copying individual network detail cells via long press 2015-03-03 22:30:50 -08:00
Ryan Olson 617db9c48a Bump version to 2.0.1 2015-02-25 09:47:52 -08:00
Ryan Olson b05f78c388 Merge pull request #47 from judev/patch-1
Fix fileURLOrData passthrough in network observer
2015-02-25 07:55:02 -08:00
Jude Venn d9ecb2359b Fix fileURLOrData passthrough in network observer
Completion handler may be expecting fileURL so should always pass through the response that the session task gave us.
2015-02-25 15:26:59 +00:00
Ryan Olson 5f9a61c755 Show copy menu from long press on network history cells. 2015-02-24 16:38:32 -08:00
Ryan Olson 3dd178c029 Update podspec for v2.0.0 2015-02-24 10:02:26 -08:00
106 changed files with 339 additions and 145 deletions
+4
View File
@@ -0,0 +1,4 @@
language: objective-c
xcode_project: Example/UICatalog.xcodeproj
xcode_scheme: UICatalog
xcode_sdk: iphonesimulator
-36
View File
@@ -1,36 +0,0 @@
//
// FLEXWindow.m
// Flipboard
//
// Created by Ryan Olson on 4/13/14.
// Copyright (c) 2014 Flipboard. All rights reserved.
//
#import "FLEXWindow.h"
@implementation FLEXWindow
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
// Some apps have windows at UIWindowLevelStatusBar + n.
// If we make the window level too high, we block out UIAlertViews.
// There's a balance between staying above the app's windows and staying below alerts.
// UIWindowLevelStatusBar + 100 seems to hit that balance.
self.windowLevel = UIWindowLevelStatusBar + 100.0;
}
return self;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL pointInside = NO;
if ([self.eventDelegate shouldHandleTouchAtPoint:point]) {
pointInside = [super pointInside:point withEvent:event];
}
return pointInside;
}
@end
@@ -15,6 +15,7 @@
@property (nonatomic, weak) id <FLEXExplorerViewControllerDelegate> delegate;
- (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates;
- (BOOL)wantsWindowToBecomeKey;
@end
@@ -120,76 +120,26 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
}
#pragma mark - Status Bar Wrangling for iOS 7
// Try to get the preferred status bar properties from the app's root view controller (not us).
// In general, our window shouldn't be the key window when this view controller is asked about the status bar.
// However, we guard against infinite recursion and provide a reasonable default for status bar behavior in case our window is the keyWindow.
- (UIViewController *)viewControllerForStatusBarAndOrientationProperties
{
UIViewController *viewControllerToAsk = [[[UIApplication sharedApplication] keyWindow] rootViewController];
// On iPhone, modal view controllers get asked
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
while (viewControllerToAsk.presentedViewController) {
viewControllerToAsk = viewControllerToAsk.presentedViewController;
}
}
return viewControllerToAsk;
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
UIStatusBarStyle preferredStyle = UIStatusBarStyleDefault;
if (viewControllerToAsk && viewControllerToAsk != self) {
// We might need to foward to a child
UIViewController *childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarStyle];
while (childViewControllerToAsk && childViewControllerToAsk != viewControllerToAsk) {
viewControllerToAsk = childViewControllerToAsk;
childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarStyle];
}
preferredStyle = [viewControllerToAsk preferredStatusBarStyle];
}
return preferredStyle;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
UIStatusBarAnimation preferredAnimation = UIStatusBarAnimationFade;
if (viewControllerToAsk && viewControllerToAsk != self) {
preferredAnimation = [viewControllerToAsk preferredStatusBarUpdateAnimation];
}
return preferredAnimation;
}
- (BOOL)prefersStatusBarHidden
{
UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
BOOL prefersHidden = NO;
if (viewControllerToAsk && viewControllerToAsk != self) {
// Again, we might need to forward to a child
UIViewController *childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarHidden];
while (childViewControllerToAsk && childViewControllerToAsk != viewControllerToAsk) {
viewControllerToAsk = childViewControllerToAsk;
childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarHidden];
}
prefersHidden = [viewControllerToAsk prefersStatusBarHidden];
}
return prefersHidden;
}
#pragma mark - Rotation
- (UIViewController *)viewControllerForRotationAndOrientation
{
UIWindow *window = self.previousKeyWindow ?: [[UIApplication sharedApplication] keyWindow];
UIViewController *viewController = window.rootViewController;
NSString *viewControllerSelectorString = [@[@"_vie", @"wContro", @"llerFor", @"Supported", @"Interface", @"Orientations"] componentsJoinedByString:@""];
SEL viewControllerSelector = NSSelectorFromString(viewControllerSelectorString);
if ([viewController respondsToSelector:viewControllerSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
viewController = [viewController performSelector:viewControllerSelector];
#pragma clang diagnostic pop
}
return viewController;
}
- (NSUInteger)supportedInterfaceOrientations
{
UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
NSUInteger supportedOrientations = [FLEXUtility infoPlistSupportedInterfaceOrientationsMask];
if (viewControllerToAsk && viewControllerToAsk != self) {
supportedOrientations = [viewControllerToAsk supportedInterfaceOrientations];
@@ -206,7 +156,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
- (BOOL)shouldAutorotate
{
UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
BOOL shouldAutorotate = YES;
if (viewControllerToAsk && viewControllerToAsk != self) {
shouldAutorotate = [viewControllerToAsk shouldAutorotate];
@@ -860,9 +810,10 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
- (void)resignKeyAndDismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
{
[self.previousKeyWindow makeKeyWindow];
UIWindow *previousKeyWindow = self.previousKeyWindow;
self.previousKeyWindow = nil;
[previousKeyWindow makeKeyWindow];
[[previousKeyWindow rootViewController] setNeedsStatusBarAppearanceUpdate];
// Restore the status bar window's normal window level.
// We want it above FLEX while a modal is presented for scroll to top, but below FLEX otherwise for exploration.
@@ -874,4 +825,9 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
[self dismissViewControllerAnimated:animated completion:completion];
}
- (BOOL)wantsWindowToBecomeKey
{
return self.previousKeyWindow != nil;
}
@end
@@ -7,6 +7,7 @@
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface FLEXManager : NSObject
@@ -111,6 +111,12 @@
return [self.explorerViewController shouldReceiveTouchAtWindowPoint:pointInWindow];
}
- (BOOL)canBecomeKeyWindow
{
// Only when the explorer view controller wants it because it needs to accept key input & affect the status bar.
return [self.explorerViewController wantsWindowToBecomeKey];
}
#pragma mark - FLEXExplorerViewControllerDelegate
@@ -19,5 +19,6 @@
@protocol FLEXWindowEventDelegate <NSObject>
- (BOOL)shouldHandleTouchAtPoint:(CGPoint)pointInWindow;
- (BOOL)canBecomeKeyWindow;
@end
+67
View File
@@ -0,0 +1,67 @@
//
// FLEXWindow.m
// Flipboard
//
// Created by Ryan Olson on 4/13/14.
// Copyright (c) 2014 Flipboard. All rights reserved.
//
#import "FLEXWindow.h"
#import <objc/runtime.h>
@implementation FLEXWindow
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
// Some apps have windows at UIWindowLevelStatusBar + n.
// If we make the window level too high, we block out UIAlertViews.
// There's a balance between staying above the app's windows and staying below alerts.
// UIWindowLevelStatusBar + 100 seems to hit that balance.
self.windowLevel = UIWindowLevelStatusBar + 100.0;
}
return self;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL pointInside = NO;
if ([self.eventDelegate shouldHandleTouchAtPoint:point]) {
pointInside = [super pointInside:point withEvent:event];
}
return pointInside;
}
- (BOOL)shouldAffectStatusBarAppearance
{
return [self isKeyWindow];
}
- (BOOL)canBecomeKeyWindow
{
return [self.eventDelegate canBecomeKeyWindow];
}
+ (void)initialize
{
// This adds a method (superclass override) at runtime which gives us the status bar behavior we want.
// The FLEX window is intended to be an overlay that generally doesn't affect the app underneath.
// Most of the time, we want the app's main window(s) to be in control of status bar behavior.
// Done at runtime with an obfuscated selector because it is private API. But you shoudn't ship this to the App Store anyways...
NSString *canAffectSelectorString = [@[@"_can", @"Affect", @"Status", @"Bar", @"Appearance"] componentsJoinedByString:@""];
SEL canAffectSelector = NSSelectorFromString(canAffectSelectorString);
Method shouldAffectMethod = class_getInstanceMethod(self, @selector(shouldAffectStatusBarAppearance));
IMP canAffectImplementation = method_getImplementation(shouldAffectMethod);
class_addMethod(self, canAffectSelector, canAffectImplementation, method_getTypeEncoding(shouldAffectMethod));
// One more...
NSString *canBecomeKeySelectorString = [NSString stringWithFormat:@"_%@", NSStringFromSelector(@selector(canBecomeKeyWindow))];
SEL canBecomeKeySelector = NSSelectorFromString(canBecomeKeySelectorString);
Method canBecomeKeyMethod = class_getInstanceMethod(self, @selector(canBecomeKeyWindow));
IMP canBecomeKeyImplementation = method_getImplementation(canBecomeKeyMethod);
class_addMethod(self, canBecomeKeySelector, canBecomeKeyImplementation, method_getTypeEncoding(canBecomeKeyMethod));
}
@end
@@ -7,6 +7,7 @@
//
#import "FLEXFileBrowserFileOperationController.h"
#import <UIKit/UIKit.h>
@interface FLEXFileBrowserFileDeleteOperationController () <UIAlertViewDelegate>
@@ -316,7 +316,7 @@
NSString *subpath = [self.childPaths objectAtIndex:indexPath.row];
fullPath = [self.path stringByAppendingPathComponent:subpath];
} else {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForCell:sender];
indexPath = [self.searchController.searchResultsTableView indexPathForCell:sender];
fullPath = [self.searchPaths objectAtIndex:indexPath.row];
}
@@ -334,7 +334,7 @@
NSString *subpath = [self.childPaths objectAtIndex:indexPath.row];
fullPath = [self.path stringByAppendingPathComponent:subpath];
} else {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForCell:sender];
indexPath = [self.searchController.searchResultsTableView indexPathForCell:sender];
fullPath = [self.searchPaths objectAtIndex:indexPath.row];
}
@@ -345,9 +345,9 @@
- (void)reloadDisplayedPaths
{
if (self.searchDisplayController.isActive) {
if (self.searchController.isActive) {
[self reloadSearchPaths];
[self.searchDisplayController.searchResultsTableView reloadData];
[self.searchController.searchResultsTableView reloadData];
} else {
[self reloadChildPaths];
[self.tableView reloadData];
@@ -62,7 +62,10 @@
UISearchBar *searchBar = [[UISearchBar alloc] init];
[searchBar sizeToFit];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
#pragma clang diagnostic pop
self.searchController.delegate = self;
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
@@ -229,10 +232,10 @@
for (UITableView *tableView in tableViews) {
for (FLEXNetworkTransactionTableViewCell *cell in [tableView visibleCells]) {
if ([cell.transaction isEqual:transaction]) {
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
if (indexPath) {
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
// Using -[UITableView reloadRowsAtIndexPaths:withRowAnimation:] is overkill here and kicks off a lot of
// work that can make the table view somewhat unresponseive when lots of updates are streaming in.
// We just need to tell the cell that it needs to re-layout.
[cell setNeedsLayout];
break;
}
}
@@ -309,6 +312,27 @@
[self.navigationController pushViewController:detailViewController animated:YES];
}
#pragma mark - Menu Actions
- (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:)) {
FLEXNetworkTransaction *transaction = [self transactionAtIndexPath:indexPath inTableView:tableView];
NSString *requestURLString = transaction.request.URL.absoluteString ?: @"";
[[UIPasteboard generalPasteboard] setString:requestURLString];
}
}
- (FLEXNetworkTransaction *)transactionAtIndexPath:(NSIndexPath *)indexPath inTableView:(UITableView *)tableView
{
FLEXNetworkTransaction *transaction = nil;
@@ -337,9 +361,9 @@
return [[transaction.request.URL absoluteString] rangeOfString:searchString options:NSCaseInsensitiveSearch].length > 0;
}]];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.searchDisplayController.searchBar.text isEqual:searchString]) {
if ([self.searchController.searchBar.text isEqual:searchString]) {
self.filteredNetworkTransactions = filteredNetworkTransactions;
[self.searchDisplayController.searchResultsTableView reloadData];
[self.searchController.searchResultsTableView reloadData];
}
});
});
+10 -4
View File
@@ -103,6 +103,8 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
- (void)recordRequestWillBeSentWithRequestID:(NSString *)requestID request:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
{
NSDate *startDate = [NSDate date];
if (redirectResponse) {
[self recordResponseReceivedWithRequestID:requestID response:redirectResponse];
[self recordLoadingFinishedWithRequestID:requestID responseBody:nil];
@@ -112,7 +114,7 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
FLEXNetworkTransaction *transaction = [[FLEXNetworkTransaction alloc] init];
transaction.requestID = requestID;
transaction.request = request;
transaction.startTime = [NSDate date];
transaction.startTime = startDate;
[self.orderedTransactions insertObject:transaction atIndex:0];
[self.networkTransactionsForRequestIdentifiers setObject:transaction forKey:requestID];
@@ -124,6 +126,8 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
- (void)recordResponseReceivedWithRequestID:(NSString *)requestID response:(NSURLResponse *)response
{
NSDate *responseDate = [NSDate date];
dispatch_async(self.queue, ^{
FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestID];
if (!transaction) {
@@ -131,7 +135,7 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
}
transaction.response = response;
transaction.transactionState = FLEXNetworkTransactionStateReceivingData;
transaction.latency = -[transaction.startTime timeIntervalSinceNow];
transaction.latency = -[transaction.startTime timeIntervalSinceDate:responseDate];
[self postUpdateNotificationForTransaction:transaction];
});
@@ -152,13 +156,15 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
- (void)recordLoadingFinishedWithRequestID:(NSString *)requestID responseBody:(NSData *)responseBody
{
NSDate *finishedDate = [NSDate date];
dispatch_async(self.queue, ^{
FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestID];
if (!transaction) {
return;
}
transaction.transactionState = FLEXNetworkTransactionStateFinished;
transaction.duration = -[transaction.startTime timeIntervalSinceNow];
transaction.duration = -[transaction.startTime timeIntervalSinceDate:finishedDate];
BOOL shouldCache = [responseBody length] > 0;
if (!self.shouldCacheMediaResponses) {
@@ -173,7 +179,7 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
}
NSString *mimeType = transaction.response.MIMEType;
if ([mimeType hasPrefix:@"image/"]) {
if ([mimeType hasPrefix:@"image/"] && [responseBody length] > 0) {
// Thumbnail image previews on a separate background queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSInteger maxPixelDimension = [[UIScreen mainScreen] scale] * 32.0;
+1
View File
@@ -7,6 +7,7 @@
//
#import <Foundation/Foundation.h>
#import "UIKit/UIKit.h"
typedef NS_ENUM(NSInteger, FLEXNetworkTransactionState) {
FLEXNetworkTransactionStateUnstarted,
@@ -53,6 +53,7 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTransactionUpdatedNotification:) name:kFLEXNetworkRecorderTransactionUpdatedNotification object:nil];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Copy" style:UIBarButtonItemStylePlain target:self action:@selector(copyButtonPressed:)];
}
return self;
}
@@ -117,6 +118,30 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
}
}
- (void)copyButtonPressed:(id)sender
{
NSMutableString *requestDetailString = [NSMutableString string];
for (FLEXNetworkDetailSection *section in self.sections) {
if ([section.rows count] > 0) {
if ([section.title length] > 0) {
[requestDetailString appendString:section.title];
[requestDetailString appendString:@"\n\n"];
}
for (FLEXNetworkDetailRow *row in section.rows) {
NSString *rowDescription = [[[self class] attributedTextForRow:row] string];
if ([rowDescription length] > 0) {
[requestDetailString appendString:rowDescription];
[requestDetailString appendString:@"\n"];
}
}
[requestDetailString appendString:@"\n\n"];
}
}
[[UIPasteboard generalPasteboard] setString:requestDetailString];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
@@ -179,6 +204,26 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
return [sectionModel.rows objectAtIndex: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] setString:row.detailText];
}
}
#pragma mark - View Configuration
+ (NSAttributedString *)attributedTextForRow:(FLEXNetworkDetailRow *)row
@@ -400,7 +445,7 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
{
// FIXME (RKO): Don't rely on UTF8 string encoding
UIViewController *detailViewController = nil;
if ([mimeType isEqual:@"application/json"] || [mimeType isEqual:@"application/javascript"] || [mimeType isEqual:@"text/javascript"]) {
if ([FLEXUtility isValidJSONData:data]) {
NSString *prettyJSON = [FLEXUtility prettyJSONStringFromData:data];
if ([prettyJSON length] > 0) {
detailViewController = [[FLEXWebViewController alloc] initWithText:prettyJSON];
@@ -12,6 +12,8 @@
// which Square, Inc. licenses this file to you.
//
#import <Foundation/Foundation.h>
extern NSString *const kFLEXNetworkObserverEnabledStateChangedNotification;
/// This class swizzles NSURLConnection and NSURLSession delegate methods to observe events in the URL loading system.
@@ -102,7 +102,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
+ (void)setShouldEnableOnLaunch:(BOOL)shouldEnableOnLaunch
{
[[NSUserDefaults standardUserDefaults] setObject:@YES forKey:kFLEXNetworkObserverEnableOnLaunchDefaultsKey];
[[NSUserDefaults standardUserDefaults] setBool:shouldEnableOnLaunch forKey:kFLEXNetworkObserverEnableOnLaunchDefaultsKey];
}
+ (BOOL)shouldEnableOnLaunch
@@ -149,6 +149,14 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
/// The superclass implementation (and implementations in classes above that) will be executed without inteference if called from the original implementation.
+ (void)sniffWithoutDuplicationForObject:(NSObject *)object selector:(SEL)selector sniffingBlock:(void (^)(void))sniffingBlock originalImplementationBlock:(void (^)(void))originalImplementationBlock
{
// If we don't have an object to detect nested calls on, just run the original implmentation and bail.
// This case can happen if someone besides the URL loading system calls the delegate methods directly.
// See https://github.com/Flipboard/FLEX/issues/61 for an example.
if (!object) {
originalImplementationBlock();
return;
}
const void *key = selector;
// Don't run the sniffing block if we're inside a nested call
@@ -479,7 +487,10 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
NSURLSessionTask *(^asyncDataOrDownloadSwizzleBlock)(Class, id, NSURLSessionAsyncCompletion) = ^NSURLSessionTask *(Class slf, id argument, NSURLSessionAsyncCompletion completion) {
NSURLSessionTask *task = nil;
if ([FLEXNetworkObserver isEnabled]) {
// If completion block was not provided sender expect to receive delegated methods or does not
// interested in callback at all. In this case we should just call original method implementation
// with nil completion block.
if ([FLEXNetworkObserver isEnabled] && completion) {
NSString *requestID = [self nextRequestID];
NSString *mechanism = [self mechansimFromClassMethod:selector onClass:class];
NSURLSessionAsyncCompletion completionWrapper = [self asyncCompletionWrapperForRequestID:requestID mechanism:mechanism completion:completion];
@@ -565,7 +576,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
// Call through to the original completion handler
if (completion) {
completion(data, response, error);
completion(fileURLOrData, response, error);
}
};
return completionWrapper;
+1 -1
View File
@@ -583,7 +583,7 @@ const unsigned int kFLEXNumberOfImplicitArgs = 2;
if (encodingCString[0] == '@') {
NSString *class = [encodingString substringFromIndex:1];
class = [class stringByReplacingOccurrencesOfString:@"\"" withString:@""];
if ([class length] == 0) {
if ([class length] == 0 || [class isEqual:@"?"]) {
class = @"id";
} else {
class = [class stringByAppendingString:@" *"];
+1
View File
@@ -35,6 +35,7 @@
+ (NSString *)statusCodeStringFromURLResponse:(NSURLResponse *)response;
+ (NSDictionary *)dictionaryFromQuery:(NSString *)query;
+ (NSString *)prettyJSONStringFromData:(NSData *)data;
+ (BOOL)isValidJSONData:(NSData *)data;
+ (NSData *)inflatedDataFromCompressedData:(NSData *)compressedData;
@end
+6 -1
View File
@@ -278,10 +278,15 @@
} else {
prettyString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
return prettyString;
}
+ (BOOL)isValidJSONData:(NSData *)data
{
return [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL] ? YES : NO;
}
// Thanks to the following links for help with this method
// http://www.cocoanetics.com/2012/02/decompressing-files-into-memory/
// https://github.com/nicklockwood/GZIP

Some files were not shown because too many files have changed in this diff Show More