Compare commits

..

13 Commits

Author SHA1 Message Date
Tanner Bennett 0c0e936143 Fix bug in new FLEXMirror usage 2022-04-27 12:34:56 -05:00
Tanner Bennett 5ab4f8d2a6 Fix annoying divider color in editor screens 2022-04-27 11:53:57 -05:00
Tanner Bennett ade5f81bc6 Update SPM example deps 2022-04-27 11:53:57 -05:00
Tanner Bennett 1cd3a90809 Enable displaying ivar names for custom struct types
FLEXMetadataExtras.h
2022-04-27 11:53:57 -05:00
Tanner Bennett 29f3c3d3cd Make CommitDetails a struct to demo Reflex 2022-04-26 19:48:49 -05:00
Tanner Bennett a4d49c0ca9 Podspec: gnu++11 2022-04-26 19:48:49 -05:00
Tanner Bennett f4bcfe708c Delete modulemap 2022-04-26 19:46:20 -05:00
Tanner Bennett 1523bf0e4d Use Reflex instead of FLEX in SPM project 2022-04-24 19:06:14 -05:00
Tanner Bennett ee18360f89 Initialize FLEXObjectExplorer.reflexAvailable 2022-04-24 19:06:14 -05:00
Tanner Bennett 19df6d0f0a Use Reflex and FLEXMirror 2022-04-24 19:06:14 -05:00
Tanner Bennett e6e38dfba5 Add FLEXSwiftInternal to check if object from Swift 2022-04-24 19:06:14 -05:00
Tanner Bennett d2a7ba388e Modernize FLEXMirror
Differentiate between class and instance props/methods
2022-04-24 19:05:45 -05:00
Tanner Bennett 33873531d2 Add (unused) libflex modulemap file
For my own use case, I dynamically link FLEX, so I need to build Reflex using libflex.tbd as well as this modulemap file.
2022-04-24 19:05:45 -05:00
85 changed files with 227 additions and 1370 deletions
-1
View File
@@ -21,4 +21,3 @@ DerivedData
Podfile.lock
IDEWorkspaceChecks.plist
*.xcworkspace
.build
@@ -16,13 +16,4 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface UINavigationController (FLEXObjectExploring)
/// Push an object explorer view controller onto the navigation stack
- (void)pushExplorerForObject:(id)object;
/// Push an object explorer view controller onto the navigation stack
- (void)pushExplorerForObject:(id)object animated:(BOOL)animated;
@end
NS_ASSUME_NONNULL_END
@@ -8,7 +8,6 @@
#import "FLEXNavigationController.h"
#import "FLEXExplorerViewController.h"
#import "FLEXObjectExplorerFactory.h"
#import "FLEXTabList.h"
@interface UINavigationController (Private) <UIGestureRecognizerDelegate>
@@ -29,8 +28,7 @@
@implementation FLEXNavigationController
+ (instancetype)withRootViewController:(UIViewController *)rootVC {
FLEXNavigationController *nav = [[self alloc] initWithRootViewController:rootVC];
return nav;
return [[self alloc] initWithRootViewController:rootVC];
}
- (void)viewDidLoad {
@@ -67,17 +65,6 @@
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (@available(iOS 15.0, *)) {
UISheetPresentationController *presenter = self.sheetPresentationController;
presenter.detents = @[
UISheetPresentationControllerDetent.mediumDetent,
UISheetPresentationControllerDetent.largeDetent,
];
presenter.prefersScrollingExpandsWhenScrolledToEdge = NO;
presenter.selectedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge;
presenter.largestUndimmedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge;
}
if (self.beingPresented && !self.didSetupPendingDismissButtons) {
for (UIViewController *vc in self.viewControllers) {
[self addNavigationBarItemsToViewController:vc.navigationItem];
@@ -106,13 +93,6 @@
[self addNavigationBarItemsToViewController:viewController.navigationItem];
}
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
// Workaround for UIActivityViewController trying to dismiss us for some reason
if (![self.viewControllers.lastObject.presentedViewController isKindOfClass:UIActivityViewController.self]) {
[super dismissViewControllerAnimated:flag completion:completion];
}
}
- (void)dismissAnimated {
// Tabs are only closed if the done button is pressed; this
// allows you to leave a tab open by dragging down to dismiss
@@ -214,18 +194,3 @@
}
@end
@implementation UINavigationController (FLEXObjectExploring)
- (void)pushExplorerForObject:(id)object {
[self pushExplorerForObject:object animated:YES];
}
- (void)pushExplorerForObject:(id)object animated:(BOOL)animated {
UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:object];
if (explorer) {
[self pushViewController:explorer animated:animated];
}
}
@end
@@ -96,11 +96,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
self.searchController.searchBar.placeholder = @"Filter";
self.searchController.searchResultsUpdater = (id)self;
self.searchController.delegate = (id)self;
if (@available(iOS 9.1, *)) {
self.searchController.obscuresBackgroundDuringPresentation = NO;
} else {
self.searchController.dimsBackgroundDuringPresentation = NO;
}
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;
/// Not necessary in iOS 13; remove this when iOS 13 is the minimum deployment target
self.searchController.searchBar.delegate = self;
@@ -214,8 +210,6 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tableView.estimatedRowHeight = 10;
_shareToolbarItem = FLEXBarButtonItemSystem(Action, self, @selector(shareButtonPressed:));
_bookmarksToolbarItem = [UIBarButtonItem
flex_itemWithImage:FLEXResources.bookmarksIcon target:self action:@selector(showBookmarks)
+4 -8
View File
@@ -8,8 +8,6 @@
#import "FLEXTableViewSection.h"
NS_ASSUME_NONNULL_BEGIN
/// A section providing a specific single row.
///
/// You may optionally provide a view controller to push when the row
@@ -18,15 +16,13 @@ NS_ASSUME_NONNULL_BEGIN
@interface FLEXSingleRowSection : FLEXTableViewSection
/// @param reuseIdentifier if nil, kFLEXDefaultCell is used.
+ (instancetype)title:(nullable NSString *)sectionTitle
reuse:(nullable NSString *)reuseIdentifier
+ (instancetype)title:(NSString *)sectionTitle
reuse:(NSString *)reuseIdentifier
cell:(void(^)(__kindof UITableViewCell *cell))cellConfiguration;
@property (nullable, nonatomic) UIViewController *pushOnSelection;
@property (nullable, nonatomic) void (^selectionAction)(UIViewController *host);
@property (nonatomic) UIViewController *pushOnSelection;
@property (nonatomic) void (^selectionAction)(UIViewController *host);
/// Called to determine whether the single row should display itself or not.
@property (nonatomic) BOOL (^filterMatcher)(NSString *filterText);
@end
NS_ASSUME_NONNULL_END
-2
View File
@@ -30,8 +30,6 @@
- (id)initWithTitle:(NSString *)sectionTitle
reuse:(NSString *)reuseIdentifier
cell:(void (^)(__kindof UITableViewCell *))cellConfiguration {
NSParameterAssert(cellConfiguration);
self = [super init];
if (self) {
_title = sectionTitle;
+1 -1
View File
@@ -79,7 +79,7 @@
return @"";
}
- (NSArray<UIMenuElement *> *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13.0)) {
- (NSArray<UIMenuElement *> *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13)) {
NSArray<NSString *> *copyItems = [self copyMenuItemsForRow:row];
NSAssert(copyItems.count % 2 == 0, @"copyMenuItemsForRow: should return an even list");
@@ -46,13 +46,7 @@
UIPickerView *fontsPicker = [UIPickerView new];
fontsPicker.dataSource = self;
fontsPicker.delegate = self;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Deprecated in iOS 13; from then on, selection is always shown
fontsPicker.showsSelectionIndicator = YES;
#pragma clang diagnostic pop
return fontsPicker;
}
@@ -37,7 +37,7 @@
_commitHandler = onCommit;
[NSNotificationCenter.defaultCenter
addObserver:self selector:@selector(keyboardDidShow:)
name:UIKeyboardWillShowNotification object:nil
name:UIKeyboardDidShowNotification object:nil
];
[NSNotificationCenter.defaultCenter
addObserver:self selector:@selector(keyboardWillHide:)
@@ -129,14 +129,6 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
if (@available(iOS 10.0, *)) {
_selectionFBG = [UISelectionFeedbackGenerator new];
}
// Observe keyboard to move self out of the way
[NSNotificationCenter.defaultCenter
addObserver:self
selector:@selector(keyboardShown:)
name:UIKeyboardWillShowNotification
object:nil
];
}
- (void)viewWillAppear:(BOOL)animated {
@@ -170,10 +162,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
UIInterfaceOrientationMask supportedOrientations = FLEXUtility.infoPlistSupportedInterfaceOrientationsMask;
// We check its class by name because using isKindOfClass will fail for the same class defined
// twice in the runtime; and the goal here is to avoid calling -supportedInterfaceOrientations
// recursively when I'm inspecting FLEX with itself from a tweak dylib
if (viewControllerToAsk && ![NSStringFromClass([viewControllerToAsk class]) hasPrefix:@"FLEX"]) {
if (viewControllerToAsk && ![viewControllerToAsk isKindOfClass:[self class]]) {
supportedOrientations = [viewControllerToAsk supportedInterfaceOrientations];
}
@@ -386,21 +375,6 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
return [self.view convertRect:frameInWindow fromView:nil];
}
- (void)keyboardShown:(NSNotification *)notif {
CGRect keyboardFrame = [notif.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect toolbarFrame = self.explorerToolbar.frame;
if (CGRectGetMinY(keyboardFrame) < CGRectGetMaxY(toolbarFrame)) {
toolbarFrame.origin.y = keyboardFrame.origin.y - toolbarFrame.size.height;
// Subtract a little more, to ignore accessory input views
toolbarFrame.origin.y -= 50;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.5
options:UIViewAnimationOptionCurveEaseOut animations:^{
[self updateToolbarPositionWithUnconstrainedFrame:toolbarFrame];
} completion:nil];
}
}
#pragma mark - Toolbar Buttons
@@ -429,12 +403,8 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
}
- (UIWindow *)statusWindow {
if (!@available(iOS 16, *)) {
NSString *statusBarString = [NSString stringWithFormat:@"%@arWindow", @"_statusB"];
return [UIApplication.sharedApplication valueForKey:statusBarString];
}
return nil;
NSString *statusBarString = [NSString stringWithFormat:@"%@arWindow", @"_statusB"];
return [UIApplication.sharedApplication valueForKey:statusBarString];
}
- (void)recentButtonTapped:(FLEXExplorerToolbarItem *)sender {
@@ -466,11 +436,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
toolbar.moveItem.selected = self.currentMode == FLEXExplorerModeMove;
// Recent only enabled when we have a last active tab
if (!self.presentedViewController) {
toolbar.recentItem.enabled = FLEXTabList.sharedList.activeTab != nil;
} else {
toolbar.recentItem.enabled = NO;
}
toolbar.recentItem.enabled = FLEXTabList.sharedList.activeTab != nil;
}
@@ -852,34 +818,31 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
#pragma mark - Touch Handling
- (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates {
BOOL shouldReceiveTouch = NO;
CGPoint pointInLocalCoordinates = [self.view convertPoint:pointInWindowCoordinates fromView:nil];
// If we have a modal presented, is it in the modal?
if (self.presentedViewController) {
UIView *presentedView = self.presentedViewController.view;
CGPoint pipvc = [presentedView convertPoint:pointInLocalCoordinates fromView:self.view];
UIView *hit = [presentedView hitTest:pipvc withEvent:nil];
if (hit != nil) {
return YES;
}
}
// Always if we're in selection mode
if (self.currentMode == FLEXExplorerModeSelect) {
return YES;
}
// Always in move mode too
if (self.currentMode == FLEXExplorerModeMove) {
return YES;
}
// Always if it's on the toolbar
if (CGRectContainsPoint(self.explorerToolbar.frame, pointInLocalCoordinates)) {
return YES;
shouldReceiveTouch = YES;
}
return NO;
// Always if we're in selection mode
if (!shouldReceiveTouch && self.currentMode == FLEXExplorerModeSelect) {
shouldReceiveTouch = YES;
}
// Always in move mode too
if (!shouldReceiveTouch && self.currentMode == FLEXExplorerModeMove) {
shouldReceiveTouch = YES;
}
// Always if we have a modal presented
if (!shouldReceiveTouch && self.presentedViewController) {
shouldReceiveTouch = YES;
}
return shouldReceiveTouch;
}
@@ -925,14 +888,8 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
// up in case we start replacing them again in the future
self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
[self updateButtonStates];
// Show the view controller
[super presentViewController:toPresent animated:animated completion:^{
[self updateButtonStates];
if (completion) completion();
}];
[super presentViewController:toPresent animated:animated completion:completion];
}
- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion {
@@ -953,11 +910,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
[self updateButtonStates];
[super dismissViewControllerAnimated:animated completion:^{
[self updateButtonStates];
if (completion) completion();
}];
[super dismissViewControllerAnimated:animated completion:completion];
}
- (BOOL)wantsWindowToBecomeKey {
+7 -2
View File
@@ -18,13 +18,18 @@
// 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.
self.windowLevel = UIWindowLevelAlert - 1;
// UIWindowLevelStatusBar + 100 seems to hit that balance.
self.windowLevel = UIWindowLevelStatusBar + 100.0;
}
return self;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return [self.eventDelegate shouldHandleTouchAtPoint:point];
BOOL pointInside = NO;
if ([self.eventDelegate shouldHandleTouchAtPoint:point]) {
pointInside = [super pointInside:point withEvent:event];
}
return pointInside;
}
- (BOOL)shouldAffectStatusBarAppearance {
@@ -84,16 +84,6 @@
if (idx != NSNotFound) {
[self closeTabAtIndex:idx];
}
// Not sure how this is possible, but it happens sometimes
if (self.activeTab == tab) {
[self chooseNewActiveTab];
}
// It is possible for an object explorer to form a retain cycle
// with its own navigation controller; clearing the view controllers
// manually when closing a tab breaks the cycle
tab.viewControllers = @[];
}
- (void)closeTabAtIndex:(NSInteger)idx {
-1
View File
@@ -18,4 +18,3 @@
#import "NSArray+FLEX.h"
#import "NSUserDefaults+FLEX.h"
#import "NSTimer+FLEX.h"
#import "NSDateFormatter+FLEX.h"
@@ -93,37 +93,16 @@
}
- (void)queryButtonPressed {
[self showQueryInput:nil];
}
- (void)showQueryInput:(NSString *)prefillQuery {
FLEXSQLiteDatabaseManager *database = self.dbm;
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Execute an SQL query");
make.configuredTextField(^(UITextField *textField) {
textField.text = prefillQuery;
});
make.textField(nil);
make.button(@"Run").handler(^(NSArray<NSString *> *strings) {
NSString *query = strings[0];
FLEXSQLResult *result = [database executeStatement:query];
FLEXSQLResult *result = [database executeStatement:strings[0]];
if (result.message) {
// Allow users to edit their last query if it had an error
if ([result.message containsString:@"error"]) {
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Error").message(result.message);
make.button(@"Edit Query").preferred().handler(^(NSArray<NSString *> *_) {
// Show query editor again with our last input
[self showQueryInput:query];
});
make.button(@"Cancel").cancelStyle();
} showFrom:self];
} else {
[FLEXAlert showAlert:@"Message" message:result.message from:self];
}
[FLEXAlert showAlert:@"Message" message:result.message from:self];
} else {
UIViewController *resultsScreen = [FLEXTableContentViewController
columns:result.columns rows:result.rows
@@ -1,14 +0,0 @@
//
// FLEXAPNSViewController.h
// FLEX
//
// Created by Tanner Bennett on 6/28/22.
// Copyright © 2022 FLEX Team. All rights reserved.
//
#import "FLEXGlobalsEntry.h"
#import "FLEXFilteringTableViewController.h"
@interface FLEXAPNSViewController : FLEXFilteringTableViewController <FLEXGlobalsEntry>
@end
@@ -1,372 +0,0 @@
//
// FLEXAPNSViewController.m
// FLEX
//
// Created by Tanner Bennett on 6/28/22.
// Copyright © 2022 FLEX Team. All rights reserved.
//
#import "FLEXAPNSViewController.h"
#import "FLEXObjectExplorerFactory.h"
#import "FLEXMutableListSection.h"
#import "FLEXSingleRowSection.h"
#import "NSUserDefaults+FLEX.h"
#import "UIBarButtonItem+FLEX.h"
#import "NSDateFormatter+FLEX.h"
#import "FLEXResources.h"
#import "FLEXUtility.h"
#import "FLEXRuntimeUtility.h"
#import "flex_fishhook.h"
#import <dlfcn.h>
#import <UserNotifications/UserNotifications.h>
#define orig(method, ...) if (orig_##method) { orig_##method(__VA_ARGS__); }
#define method_lookup(__selector, __cls, __return, ...) \
([__cls instancesRespondToSelector:__selector] ? \
(__return(*)(__VA_ARGS__))class_getMethodImplementation(__cls, __selector) : nil)
@interface FLEXAPNSViewController ()
@property (nonatomic, readonly, class) Class appDelegateClass;
@property (nonatomic, class) NSData *deviceToken;
@property (nonatomic, class) NSError *registrationError;
@property (nonatomic, readonly, class) NSString *deviceTokenString;
@property (nonatomic, readonly, class) NSMutableArray<NSDictionary *> *remoteNotifications;
@property (nonatomic, readonly, class) NSMutableArray<UNNotification *> *userNotifications API_AVAILABLE(ios(10.0));
@property (nonatomic) FLEXSingleRowSection *deviceToken;
@property (nonatomic) FLEXMutableListSection<NSDictionary *> *remoteNotifications;
@property (nonatomic) FLEXMutableListSection<UNNotification *> *userNotifications API_AVAILABLE(ios(10.0));
@end
@implementation FLEXAPNSViewController
#pragma mark Swizzles
/// Hook User Notifications related methods on the app delegate
/// and UNUserNotificationCenter delegate classes
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
if (!NSUserDefaults.standardUserDefaults.flex_enableAPNSCapture) {
return;
}
//──────────────────────//
// App Delegate //
//──────────────────────//
// Hook UIApplication to intercept app delegate
Class uiapp = UIApplication.self;
auto orig_uiapp_setDelegate = (void(*)(id, SEL, id))class_getMethodImplementation(
uiapp, @selector(setDelegate:)
);
IMP uiapp_setDelegate = imp_implementationWithBlock(^(id _, id delegate) {
[self hookAppDelegateClass:[delegate class]];
orig_uiapp_setDelegate(_, @selector(setDelegate:), delegate);
});
class_replaceMethod(
uiapp,
@selector(setDelegate:),
uiapp_setDelegate,
"v@:@"
);
//───────────────────────────────────────────//
// UNUserNotificationCenter Delegate //
//───────────────────────────────────────────//
if (@available(iOS 10.0, *)) {
Class unusernc = UNUserNotificationCenter.self;
auto orig_unusernc_setDelegate = (void(*)(id, SEL, id))class_getMethodImplementation(
unusernc, @selector(setDelegate:)
);
IMP unusernc_setDelegate = imp_implementationWithBlock(^(id _, id delegate) {
[self hookUNUserNotificationCenterDelegateClass:[delegate class]];
orig_unusernc_setDelegate(_, @selector(setDelegate:), delegate);
});
class_replaceMethod(
unusernc,
@selector(setDelegate:),
unusernc_setDelegate,
"v@:@"
);
}
}
+ (void)hookAppDelegateClass:(Class)appDelegate {
// Abort if we already hooked something
if (_appDelegateClass) {
return;
}
_appDelegateClass = appDelegate;
// Better documentation for what's happening is in hookUNUserNotificationCenterDelegateClass: below
auto types_didRegisterForRemoteNotificationsWithDeviceToken = "v@:@@";
auto types_didFailToRegisterForRemoteNotificationsWithError = "v@:@@";
auto types_didReceiveRemoteNotification = "v@:@@@?";
auto sel_didRegisterForRemoteNotifications = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:);
auto sel_didFailToRegisterForRemoteNotifs = @selector(application:didFailToRegisterForRemoteNotificationsWithError:);
auto sel_didReceiveRemoteNotification = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
auto orig_didRegisterForRemoteNotificationsWithDeviceToken = method_lookup(
sel_didRegisterForRemoteNotifications, appDelegate, void, id, SEL, id, id);
auto orig_didFailToRegisterForRemoteNotificationsWithError = method_lookup(
sel_didFailToRegisterForRemoteNotifs, appDelegate, void, id, SEL, id, id);
auto orig_didReceiveRemoteNotification = method_lookup(
sel_didReceiveRemoteNotification, appDelegate, void, id, SEL, id, id, id);
IMP didRegisterForRemoteNotificationsWithDeviceToken = imp_implementationWithBlock(^(id _, id app, NSData *token) {
self.deviceToken = token;
orig(didRegisterForRemoteNotificationsWithDeviceToken, _, nil, app, token);
});
IMP didFailToRegisterForRemoteNotificationsWithError = imp_implementationWithBlock(^(id _, id app, NSError *error) {
self.registrationError = error;
orig(didFailToRegisterForRemoteNotificationsWithError, _, nil, app, error);
});
IMP didReceiveRemoteNotification = imp_implementationWithBlock(^(id _, id app, NSDictionary *payload, id handler) {
// TODO: notify when new notifications are added
[self.remoteNotifications addObject:payload];
orig(didReceiveRemoteNotification, _, nil, app, payload, handler);
});
class_replaceMethod(
appDelegate,
sel_didRegisterForRemoteNotifications,
didRegisterForRemoteNotificationsWithDeviceToken,
types_didRegisterForRemoteNotificationsWithDeviceToken
);
class_replaceMethod(
appDelegate,
sel_didFailToRegisterForRemoteNotifs,
didFailToRegisterForRemoteNotificationsWithError,
types_didFailToRegisterForRemoteNotificationsWithError
);
class_replaceMethod(
appDelegate,
sel_didReceiveRemoteNotification,
didReceiveRemoteNotification,
types_didReceiveRemoteNotification
);
}
+ (void)hookUNUserNotificationCenterDelegateClass:(Class)delegate API_AVAILABLE(ios(10.0)) {
// Selector
auto sel_didReceiveNotification =
@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:);
// Original implementation (or nil if unimplemented)
auto orig_didReceiveNotification = method_lookup(
sel_didReceiveNotification, delegate, void, id, SEL, id, id, id);
// Our hook (ignores self and other unneeded parameters)
IMP didReceiveNotification = imp_implementationWithBlock(^(id _, id __, UNNotification *notification, id ___) {
[self.userNotifications addObject:notification];
// This macro is a no-op if there is no original implementation
orig(didReceiveNotification, _, nil, __, notification, ___);
});
// Set the hook
class_replaceMethod(
delegate,
sel_didReceiveNotification,
didReceiveNotification,
"v@:@@@?"
);
}
#pragma mark Class Properties
static Class _appDelegateClass = nil;
+ (Class)appDelegateClass {
return _appDelegateClass;
}
static NSData *_apnsDeviceToken = nil;
+ (NSData *)deviceToken {
return _apnsDeviceToken;
}
+ (void)setDeviceToken:(NSData *)deviceToken {
_apnsDeviceToken = deviceToken;
}
+ (NSString *)deviceTokenString {
static NSString *_deviceTokenString = nil;
if (!_deviceTokenString && self.deviceToken) {
NSData *token = self.deviceToken;
NSUInteger capacity = token.length * 2;
NSMutableString *tokenString = [NSMutableString stringWithCapacity:capacity];
const UInt8 *tokenData = token.bytes;
for (NSUInteger idx = 0; idx < token.length; ++idx) {
[tokenString appendFormat:@"%02X", (int)tokenData[idx]];
}
_deviceTokenString = tokenString;
}
return _deviceTokenString;
}
static NSError *_apnsRegistrationError = nil;
+ (NSError *)registrationError {
return _apnsRegistrationError;
}
+ (void)setRegistrationError:(NSError *)error {
_apnsRegistrationError = error;
}
+ (NSMutableArray<NSDictionary *> *)userNotifications {
static NSMutableArray *_userNotifications = nil;
if (!_userNotifications) {
_userNotifications = [NSMutableArray new];
}
return _userNotifications;
}
+ (NSMutableArray<NSDictionary *> *)remoteNotifications {
static NSMutableArray *_remoteNotifications = nil;
if (!_remoteNotifications) {
_remoteNotifications = [NSMutableArray new];
}
return _remoteNotifications;
}
#pragma mark Instance stuff
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Push Notifications";
self.refreshControl = [UIRefreshControl new];
[self.refreshControl addTarget:self action:@selector(reloadData) forControlEvents:UIControlEventValueChanged];
[self addToolbarItems:@[
[UIBarButtonItem
flex_itemWithImage:FLEXResources.gearIcon
target:self
action:@selector(settingsButtonTapped)
],
]];
}
- (NSArray<FLEXTableViewSection *> *)makeSections {
self.deviceToken = [FLEXSingleRowSection title:@"APNS Device Token" reuse:nil cell:^(UITableViewCell *cell) {
NSString *tokenString = FLEXAPNSViewController.deviceTokenString;
if (tokenString) {
cell.textLabel.text = tokenString;
cell.textLabel.numberOfLines = 0;
}
else if (!NSUserDefaults.standardUserDefaults.flex_enableAPNSCapture) {
cell.textLabel.text = @"APNS capture disabled";
}
else {
cell.textLabel.text = @"Not yet registered";
}
}];
self.deviceToken.selectionAction = ^(UIViewController *host) {
UIPasteboard.generalPasteboard.string = FLEXAPNSViewController.deviceTokenString;
[FLEXAlert showQuickAlert:@"Copied to Clipboard" from:host];
};
// Remote Notifications //
self.remoteNotifications = [FLEXMutableListSection list:FLEXAPNSViewController.remoteNotifications
cellConfiguration:^(UITableViewCell *cell, NSDictionary *notif, NSInteger row) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// TODO: date received
cell.detailTextLabel.text = [FLEXRuntimeUtility summaryForObject:notif];
}
filterMatcher:^BOOL(NSString *filterText, NSDictionary *notif) {
return [notif.description localizedCaseInsensitiveContainsString:filterText];
}
];
self.remoteNotifications.customTitle = @"Remote Notifications";
self.remoteNotifications.selectionHandler = ^(UIViewController *host, NSDictionary *notif) {
[host.navigationController pushViewController:[
FLEXObjectExplorerFactory explorerViewControllerForObject:notif
] animated:YES];
};
// User Notifications //
if (@available(iOS 10.0, *)) {
self.userNotifications = [FLEXMutableListSection list:FLEXAPNSViewController.userNotifications
cellConfiguration:^(UITableViewCell *cell, UNNotification *notif, NSInteger row) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Subtitle is 'subtitle \n date'
NSString *dateString = [NSDateFormatter flex_stringFrom:notif.date format:FLEXDateFormatPreciseClock];
NSString *subtitle = notif.request.content.subtitle;
subtitle = subtitle ? [NSString stringWithFormat:@"%@\n%@", subtitle, dateString] : dateString;
cell.textLabel.text = notif.request.content.title;
cell.detailTextLabel.text = subtitle;
}
filterMatcher:^BOOL(NSString *filterText, NSDictionary *notif) {
return [notif.description localizedCaseInsensitiveContainsString:filterText];
}
];
self.userNotifications.customTitle = @"Push Notifications";
self.userNotifications.selectionHandler = ^(UIViewController *host, UNNotification *notif) {
[host.navigationController pushViewController:[
FLEXObjectExplorerFactory explorerViewControllerForObject:notif.request
] animated:YES];
};
return @[self.deviceToken, self.remoteNotifications, self.userNotifications];
}
else {
return @[self.deviceToken, self.remoteNotifications];
}
}
- (void)reloadData {
[self.refreshControl endRefreshing];
self.remoteNotifications.customTitle = [NSString stringWithFormat:
@"%@ notifications", @(self.remoteNotifications.filteredList.count)
];
[super reloadData];
}
- (void)settingsButtonTapped {
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
BOOL enabled = defaults.flex_enableAPNSCapture;
NSString *apnsToggle = enabled ? @"Disable Capture" : @"Enable Capture";
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Settings")
.message(@"Enable or disable the capture of push notifications.\n\n")
.message(@"This will hook UIApplicationMain on launch until it is disabled, ")
.message(@"and swizzle some app delegate methods. Restart the app for changes to take effect.");
make.button(apnsToggle).destructiveStyle().handler(^(NSArray<NSString *> *strings) {
[defaults flex_toggleBoolForKey:kFLEXDefaultsAPNSCaptureEnabledKey];
});
make.button(@"Dismiss").cancelStyle();
} showFrom:self];
}
#pragma mark - FLEXGlobalsEntry
+ (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row {
return @"📌 Push Notifications";
}
+ (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row {
return [self new];
}
@end
@@ -26,14 +26,6 @@
self.title = @"Cookies";
}
- (NSString *)headerTitle {
return self.cookies.title;
}
- (void)setHeaderTitle:(NSString *)headerTitle {
self.cookies.customTitle = headerTitle;
}
- (NSArray<FLEXTableViewSection *> *)makeSections {
NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc]
initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)
@@ -158,7 +158,7 @@ typedef NS_ENUM(NSUInteger, FLEXObjectReferenceSection) {
}
+ (instancetype)subclassesOfClassWithName:(NSString *)className {
NSArray<FLEXObjectRef *> *references = [FLEXRuntimeUtility subclassesOfClassWithName:className];
NSArray<FLEXObjectRef *> *references = [FLEXHeapEnumerator subclassesOfClassWithName:className];
FLEXObjectListViewController *controller = [[self alloc] initWithReferences:references];
controller.title = [NSString stringWithFormat:@"Subclasses of %@ (%@)",
className, @(references.count)
@@ -109,10 +109,4 @@
_retainer = nil;
}
- (NSString *)debugDescription {
return [NSString stringWithFormat:@"<%@: %@>",
[self class], self.reference
];
}
@end
@@ -1,20 +0,0 @@
//
// FLEXActivityViewController.h
// FLEX
//
// Created by Tanner Bennett on 5/26/22.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/// Wraps UIActivityViewController so that it can't dismiss other view controllers
@interface FLEXActivityViewController : UIActivityViewController
/// @param source A \c UIVIew, \c UIBarButtonItem, or \c NSValue representing a source rect.
+ (id)sharing:(NSArray *)items source:(nullable id)source;
@end
NS_ASSUME_NONNULL_END
@@ -1,42 +0,0 @@
//
// FLEXActivityViewController.m
// FLEX
//
// Created by Tanner Bennett on 5/26/22.
//
#import "FLEXActivityViewController.h"
#import "FLEXMacros.h"
@interface FLEXActivityViewController ()
@end
@implementation FLEXActivityViewController
+ (id)sharing:(NSArray *)items source:(id)sender {
UIViewController *shareSheet = [[UIActivityViewController alloc]
initWithActivityItems:items applicationActivities:nil
];
if (sender && UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
UIPopoverPresentationController *popover = shareSheet.popoverPresentationController;
// Source view
if ([sender isKindOfClass:UIView.self]) {
popover.sourceView = sender;
}
// Source bar item
if ([sender isKindOfClass:UIBarButtonItem.self]) {
popover.barButtonItem = sender;
}
// Source rect
if ([sender isKindOfClass:NSValue.self]) {
CGRect rect = [sender CGRectValue];
popover.sourceRect = rect;
}
}
return shareSheet;
}
@end
@@ -8,6 +8,7 @@
#import "FLEXTableViewController.h"
#import "FLEXGlobalsEntry.h"
#import "FLEXFileBrowserSearchOperation.h"
@interface FLEXFileBrowserController : FLEXTableViewController <FLEXGlobalsEntry>
@@ -9,13 +9,11 @@
#import "FLEXFileBrowserController.h"
#import "FLEXUtility.h"
#import "FLEXWebViewController.h"
#import "FLEXActivityViewController.h"
#import "FLEXImagePreviewViewController.h"
#import "FLEXTableListViewController.h"
#import "FLEXObjectExplorerFactory.h"
#import "FLEXObjectExplorerViewController.h"
#import <mach-o/loader.h>
#import "FLEXFileBrowserSearchOperation.h"
@interface FLEXFileBrowserTableViewCell : UITableViewCell
@end
@@ -266,19 +264,14 @@ typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) {
if ([pathExtension isEqualToString:@"json"]) {
prettyString = [FLEXUtility prettyJSONStringFromData:fileData];
} else {
// Try to decode an archived object, regardless of file extension
NSKeyedUnarchiver *unarchiver = ({
NSKeyedUnarchiver *obj = nil;
if (@available(iOS 12.0, *)) {
obj = [[NSKeyedUnarchiver alloc] initForReadingFromData:fileData error:nil];
} else {
obj = [[NSKeyedUnarchiver alloc] initForReadingWithData:fileData];
}
obj.requiresSecureCoding = NO;
obj;
});
id object = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
// Regardless of file extension...
id object = nil;
@try {
// Try to decode an archived object regardless of file extension
object = [NSKeyedUnarchiver unarchiveObjectWithData:fileData];
} @catch (NSException *e) { }
// Try to decode other things instead
object = object ?: [NSPropertyListSerialization
propertyListWithData:fileData
@@ -476,7 +469,10 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
[self openFileController:pathString];
} else {
// Share sheet for files
UIViewController *shareSheet = [FLEXActivityViewController sharing:@[filePath] source:sender];
UIActivityViewController *shareSheet = [[UIActivityViewController alloc] initWithActivityItems:@[filePath] applicationActivities:nil];
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
shareSheet.popoverPresentationController.sourceView = sender;
}
[self presentViewController:shareSheet animated:true completion:nil];
}
}
@@ -19,7 +19,6 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
FLEXGlobalsRowCookies,
FLEXGlobalsRowBrowseRuntime,
FLEXGlobalsRowAppKeychainItems,
FLEXGlobalsRowPushNotifications,
FLEXGlobalsRowAppDelegate,
FLEXGlobalsRowRootViewController,
FLEXGlobalsRowUserDefaults,
@@ -11,7 +11,6 @@
#import "FLEXRuntimeUtility.h"
#import "FLEXObjcRuntimeViewController.h"
#import "FLEXKeychainViewController.h"
#import "FLEXAPNSViewController.h"
#import "FLEXObjectExplorerViewController.h"
#import "FLEXObjectExplorerFactory.h"
#import "FLEXLiveObjectsController.h"
@@ -58,8 +57,6 @@
switch (row) {
case FLEXGlobalsRowAppKeychainItems:
return [FLEXKeychainViewController flex_concreteGlobalsEntry:row];
case FLEXGlobalsRowPushNotifications:
return [FLEXAPNSViewController flex_concreteGlobalsEntry:row];
case FLEXGlobalsRowAddressInspector:
return [FLEXAddressExplorerCoordinator flex_concreteGlobalsEntry:row];
case FLEXGlobalsRowBrowseRuntime:
@@ -97,14 +94,13 @@
case FLEXGlobalsRowMainThread:
case FLEXGlobalsRowOperationQueue:
return [FLEXObjectExplorerFactory flex_concreteGlobalsEntry:row];
case FLEXGlobalsRowCount: break;
default:
@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:@"Missing globals case in switch" userInfo:nil
];
}
@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:@"Missing globals case in switch" userInfo:nil
];
}
+ (NSArray<FLEXGlobalsSection *> *)defaultGlobalSections {
@@ -126,7 +122,6 @@
[self globalsEntryForRow:FLEXGlobalsRowMainBundle],
[self globalsEntryForRow:FLEXGlobalsRowUserDefaults],
[self globalsEntryForRow:FLEXGlobalsRowAppKeychainItems],
[self globalsEntryForRow:FLEXGlobalsRowPushNotifications],
[self globalsEntryForRow:FLEXGlobalsRowApplication],
[self globalsEntryForRow:FLEXGlobalsRowAppDelegate],
[self globalsEntryForRow:FLEXGlobalsRowKeyWindow],
@@ -28,26 +28,25 @@
if (status == errSecSuccess) {//item already exists, update it!
query = [[NSMutableDictionary alloc]init];
query[(__bridge id)kSecValueData] = self.passwordData;
#if __IPHONE_4_0 && TARGET_OS_IPHONE
#if __IPHONE_4_0 && TARGET_OS_IPHONE
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
if (accessibilityType) {
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
}
#endif
#endif
status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
}
else if (status == errSecItemNotFound){//item not found, create it!
}else if (status == errSecItemNotFound){//item not found, create it!
query = [self query];
if (self.label) {
query[(__bridge id)kSecAttrLabel] = self.label;
}
query[(__bridge id)kSecValueData] = self.passwordData;
#if __IPHONE_4_0 && TARGET_OS_IPHONE
#if __IPHONE_4_0 && TARGET_OS_IPHONE
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
if (accessibilityType) {
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
}
#endif
#endif
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
}
@@ -70,9 +69,9 @@
}
NSMutableDictionary *query = [self query];
#if TARGET_OS_IPHONE
#if TARGET_OS_IPHONE
status = SecItemDelete((__bridge CFDictionaryRef)query);
#else
#else
// On Mac OS, SecItemDelete will not delete a key created in a different
// app, nor in a different version of the same app.
//
@@ -89,7 +88,7 @@
status = SecKeychainItemDelete((SecKeychainItemRef)result);
CFRelease(result);
}
#endif
#endif
if (status != errSecSuccess && error != NULL) {
*error = [self errorWithCode:status];
@@ -103,12 +102,12 @@
NSMutableDictionary *query = [self query];
query[(__bridge id)kSecReturnAttributes] = @YES;
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
#if __IPHONE_4_0 && TARGET_OS_IPHONE
#if __IPHONE_4_0 && TARGET_OS_IPHONE
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
if (accessibilityType) {
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
}
#endif
#endif
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
@@ -150,6 +149,19 @@
#pragma mark - Accessors
- (void)setPasswordObject:(id<NSCoding>)object {
self.passwordData = [NSKeyedArchiver archivedDataWithRootObject:object];
}
- (id<NSCoding>)passwordObject {
if (self.passwordData.length) {
return [NSKeyedUnarchiver unarchiveObjectWithData:self.passwordData];
}
return nil;
}
- (void)setPassword:(NSString *)password {
self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
@@ -169,11 +181,11 @@
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
+ (BOOL)isSynchronizationAvailable {
#if TARGET_OS_IPHONE
#if TARGET_OS_IPHONE
return YES;
#else
#else
return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4;
#endif
#endif
}
#endif
@@ -192,15 +204,15 @@
dictionary[(__bridge id)kSecAttrAccount] = self.account;
}
#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
#if !TARGET_IPHONE_SIMULATOR
#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
#if !TARGET_IPHONE_SIMULATOR
if (self.accessGroup) {
dictionary[(__bridge id)kSecAttrAccessGroup] = self.accessGroup;
}
#endif
#endif
#endif
#endif
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
if ([[self class] isSynchronizationAvailable]) {
id value;
@@ -221,7 +233,7 @@
dictionary[(__bridge id)(kSecAttrSynchronizable)] = value;
}
#endif
#endif
return dictionary;
}
@@ -239,7 +251,7 @@
case errSecSuccess: return nil;
case FLEXKeychainErrorBadArguments: message = NSLocalizedStringFromTableInBundle(@"FLEXKeychainErrorBadArguments", @"FLEXKeychain", resourcesBundle, nil); break;
#if TARGET_OS_IPHONE
#if TARGET_OS_IPHONE
case errSecUnimplemented: {
message = NSLocalizedStringFromTableInBundle(@"errSecUnimplemented", @"FLEXKeychain", resourcesBundle, nil);
break;
@@ -279,10 +291,10 @@
default: {
message = NSLocalizedStringFromTableInBundle(@"errSecDefault", @"FLEXKeychain", resourcesBundle, nil);
}
#else
#else
default:
message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL);
#endif
#endif
}
NSDictionary *userInfo = message ? @{ NSLocalizedDescriptionKey : message } : nil;
@@ -122,12 +122,7 @@
path = [NSString stringWithFormat:format, path, path];
}
if (!dlopen(path.UTF8String, RTLD_NOW)) {
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Error").message(@(dlerror()));
make.button(@"Dismiss").cancelStyle();
}];
}
dlopen(path.UTF8String, RTLD_NOW);
});
} showFrom:self];
}
@@ -26,7 +26,7 @@
*
* See <os/object.h> for details.
*/
#if !TARGET_OS_MACCATALYST && !__has_include(<xpc/xpc.h>)
#if !TARGET_OS_MACCATALYST
#if OS_OBJECT_USE_OBJC
OS_OBJECT_DECL(xpc_object);
#else
@@ -9,7 +9,6 @@
#import "FLEXSystemLogCell.h"
#import "FLEXSystemLogMessage.h"
#import "UIFont+FLEX.h"
#import "NSDateFormatter+FLEX.h"
NSString *const kFLEXSystemLogCellIdentifier = @"FLEXSystemLogCellIdentifier";
@@ -107,7 +106,14 @@ static const UIEdgeInsets kFLEXLogMessageCellInsets = {10.0, 10.0, 10.0, 10.0};
}
+ (NSString *)logTimeStringFromDate:(NSDate *)date {
return [NSDateFormatter flex_stringFrom:date format:FLEXDateFormatVerbose];
static NSDateFormatter *formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
});
return [formatter stringFromDate:date];
}
@end
@@ -1 +0,0 @@
../../Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h
-1
View File
@@ -1 +0,0 @@
../../Classes/Utility/Categories/NSDateFormatter+FLEX.h
-10
View File
@@ -23,22 +23,12 @@ NS_ASSUME_NONNULL_BEGIN
/// Programmatically dismiss anything presented by FLEX, leaving only the toolbar visible.
- (void)dismissAnyPresentedTools:(void (^_Nullable)(void))completion;
/// Programmatically present something on top of the FLEX toolbar.
/// This method will automatically dismiss any currently presented tool,
/// so you do not need to call \c dismissAnyPresentedTools: yourself.
- (void)presentTool:(UINavigationController *(^)(void))viewControllerFuture
completion:(void (^_Nullable)(void))completion;
/// Programmatically presents a new navigation controller with the given view controller.
/// The completion block is passed this new navigation controller.
- (void)presentEmbeddedTool:(UIViewController *)viewController
completion:(void (^_Nullable)(UINavigationController *))completion;
/// Programmatically presents a new navigation controller exploring the given object.
/// The completion block is passed this new navigation controller.
- (void)presentObjectExplorer:(id)object completion:(void (^_Nullable)(UINavigationController *))completion;
/// Use this to present the explorer in a specific scene when the one
/// it chooses by default is not the one you wish to display it in.
- (void)showExplorerFromScene:(UIWindowScene *)scene API_AVAILABLE(ios(13.0));
+1 -16
View File
@@ -10,8 +10,7 @@
#import "FLEXUtility.h"
#import "FLEXExplorerViewController.h"
#import "FLEXWindow.h"
#import "FLEXNavigationController.h"
#import "FLEXObjectExplorerFactory.h"
#import "FLEXObjectExplorerViewController.h"
#import "FLEXFileBrowserController.h"
@interface FLEXManager () <FLEXWindowEventDelegate, FLEXExplorerViewControllerDelegate>
@@ -107,20 +106,6 @@
[self.explorerViewController presentTool:future completion:completion];
}
- (void)presentEmbeddedTool:(UIViewController *)tool completion:(void (^)(UINavigationController *))completion {
FLEXNavigationController *nav = [FLEXNavigationController withRootViewController:tool];
[self presentTool:^UINavigationController *{
return nav;
} completion:^{
if (completion) completion(nav);
}];
}
- (void)presentObjectExplorer:(id)object completion:(void (^)(UINavigationController *))completion {
UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:object];
[self presentEmbeddedTool:explorer completion:completion];
}
- (void)showExplorerFromScene:(UIWindowScene *)scene {
if (@available(iOS 13.0, *)) {
self.explorerWindow.windowScene = scene;
@@ -15,6 +15,9 @@ typedef std::string (*ReturnsString)(void *);
@implementation FLEXFirebaseSetDataInfo
+ (instancetype)data:(NSDictionary *)data merge:(NSNumber *)merge mergeFields:(NSArray *)mergeFields {
NSParameterAssert(data);
NSParameterAssert(merge || mergeFields);
FLEXFirebaseSetDataInfo *info = [self new];
info->_documentData = data;
info->_merge = merge;
@@ -18,7 +18,6 @@
#import "FLEXManager+Private.h"
#import "FLEXTableView.h"
#import "UIBarButtonItem+FLEX.h"
#import "NSDateFormatter+FLEX.h"
typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
@@ -290,8 +289,7 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
postBodyRow.selectionFuture = ^UIViewController * () {
// Show the body if we can
NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"];
NSData *body = [self postBodyDataForTransaction:transaction];
UIViewController *detailViewController = [self detailViewControllerForMIMEType:contentType data:body];
UIViewController *detailViewController = [self detailViewControllerForMIMEType:contentType data:[self postBodyDataForTransaction:transaction]];
if (detailViewController) {
detailViewController.title = @"Request Body";
return detailViewController;
@@ -299,13 +297,8 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
// We can't show the body, alert user
return [FLEXAlert makeAlert:^(FLEXAlert *make) {
if (!body) {
make.title(@"Empty HTTP Body");
} else {
make.title(@"Cannot View HTTP Body Data");
make.message(@"FLEX does not have a viewer for request body data with MIME type: ");
}
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();
}];
@@ -382,14 +375,19 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
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 = [NSDateFormatter flex_stringFrom:transaction.startTime format:FLEXDateFormatVerbose];
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 = [NSDateFormatter flex_stringFrom:transaction.startTime format:FLEXDateFormatVerbose];
utcStartTimeRow.detailText = [startTimeFormatter stringFromDate:transaction.startTime];
[rows addObject:utcStartTimeRow];
FLEXNetworkDetailRow *unixStartTime = [FLEXNetworkDetailRow new];
@@ -488,10 +486,6 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
}
+ (UIViewController *)detailViewControllerForMIMEType:(NSString *)mimeType data:(NSData *)data {
if (!data) {
return nil; // An alert will be presented in place of this screen
}
FLEXCustomContentViewerFuture makeCustomViewer = FLEXManager.sharedManager.customContentTypeViewers[mimeType.lowercaseString];
if (makeCustomViewer) {
@@ -529,8 +523,11 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
+ (NSData *)postBodyDataForTransaction:(FLEXHTTPTransaction *)transaction {
NSData *bodyData = transaction.cachedRequestBody;
if (bodyData.length > 0 && [FLEXUtility hasCompressedContentEncoding:transaction.request]) {
bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData];
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;
}
+2 -17
View File
@@ -6,7 +6,6 @@
//
#import "FLEXNetworkCurlLogger.h"
#import "FLEXUtility.h"
@implementation FLEXNetworkCurlLogger
@@ -29,22 +28,8 @@
}
if (request.HTTPBody) {
NSData *bodyData = request.HTTPBody;
if ([FLEXUtility hasCompressedContentEncoding:request]) {
bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData];
}
NSString *body = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding];
if (body != nil) {
[curlCommandString appendFormat:@"-d \'%@\'", body];
} else {
// Fallback to using base64 encoding
[curlCommandString appendString:@"--data-binary @-"];
NSString *base64 = [request.HTTPBody base64EncodedStringWithOptions:0];
NSString *prefix = [NSString stringWithFormat:@"echo -n '%@' | base64 -D | ", base64];
[curlCommandString insertString:prefix atIndex:0];
}
NSString *body = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
[curlCommandString appendFormat:@"-d \'%@\'", body];
}
return curlCommandString;
@@ -332,13 +332,13 @@ typedef NS_ENUM(NSInteger, FLEXNetworkObserverMode) {
make.title(@"Network Monitor Disabled");
make.message(@"You must enable network monitoring to proceed.");
make.button(@"Turn On").preferred().handler(^(NSArray<NSString *> *strings) {
make.button(@"Turn On").handler(^(NSArray<NSString *> *strings) {
FLEXNetworkObserver.enabled = YES;
[host.navigationController pushViewController:[
self globalsEntryViewController:row
] animated:YES];
});
make.button(@"Dismiss").cancelStyle();
}).cancelStyle();
make.button(@"Dismiss");
} showFrom:host];
}
};
+3 -10
View File
@@ -14,13 +14,6 @@
#import "NSUserDefaults+FLEX.h"
#import "OSCache.h"
#define Synchronized(queue, obj) ({ \
__block id __synchronized_retval = nil; \
dispatch_sync(queue, ^{ __synchronized_retval = obj; }); \
__synchronized_retval; \
})
NSString *const kFLEXNetworkRecorderNewTransactionNotification = @"kFLEXNetworkRecorderNewTransactionNotification";
NSString *const kFLEXNetworkRecorderTransactionUpdatedNotification = @"kFLEXNetworkRecorderTransactionUpdatedNotification";
NSString *const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction";
@@ -91,15 +84,15 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
}
- (NSArray<FLEXHTTPTransaction *> *)HTTPTransactions {
return Synchronized(self.queue, self.orderedHTTPTransactions.copy);
return self.orderedHTTPTransactions.copy;
}
- (NSArray<FLEXWebsocketTransaction *> *)websocketTransactions {
return Synchronized(self.queue, self.orderedWSTransactions.copy);
return self.orderedWSTransactions.copy;
}
- (NSArray<FLEXFirebaseTransaction *> *)firebaseTransactions {
return Synchronized(self.queue, self.orderedFirebaseTransactions.copy);
return self.orderedFirebaseTransactions.copy;
}
- (NSData *)cachedResponseBodyForTransaction:(FLEXHTTPTransaction *)transaction {
@@ -107,7 +107,7 @@
#pragma mark - Table View Data Source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
return self.hostDenylist.count ? 2 : 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+1 -1
View File
@@ -90,7 +90,7 @@ typedef NS_ENUM(NSUInteger, FLEXWebsocketMessageDirection) {
@property (nonatomic) NSTimeInterval latency;
@property (nonatomic) NSTimeInterval duration;
/// Populated lazily, nullable. Handles both normal HTTPBody data and HTTPBodyStreams.
/// Populated lazily. Handles both normal HTTPBody data and HTTPBodyStreams.
@property (nonatomic, readonly) NSData *cachedRequestBody;
@end
+8 -2
View File
@@ -9,7 +9,6 @@
#import "FLEXNetworkTransaction.h"
#import "FLEXResources.h"
#import "FLEXUtility.h"
#import "NSDateFormatter+FLEX.h"
@implementation FLEXNetworkTransaction
@@ -46,7 +45,14 @@
}
- (NSString *)timestampStringFromRequestDate:(NSDate *)date {
return [NSDateFormatter flex_stringFrom:date format:FLEXDateFormatPreciseClock];
static NSDateFormatter *dateFormatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateFormatter = [NSDateFormatter new];
dateFormatter.dateFormat = @"HH:mm:ss";
});
return [dateFormatter stringFromDate:date];
}
- (void)setState:(FLEXNetworkTransactionState)transactionState {
@@ -23,7 +23,6 @@
#import "FLEXMethod.h"
#import "Firestore.h"
#import <AVFoundation/AVFoundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <dispatch/queue.h>
@@ -238,9 +237,7 @@ static void _logos_method$_ungrouped$FIRDocumentReference$getDocumentWithComplet
FIRDocumentSnapshotBlock orig = completion;
completion = ^(FIRDocumentSnapshot *document, NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDocumentDidFetch:document error:error transactionID:requestID];
if (orig != nil) {
orig(document, error);
}
orig(document, error);
};
// Forward invocation
@@ -259,9 +256,7 @@ static void _logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$(
FIRQuerySnapshotBlock orig = completion;
completion = ^(FIRQuerySnapshot *query, NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRQueryDidFetch:query error:error transactionID:requestID];
if (orig != nil) {
orig(query, error);
}
orig(query, error);
};
// Forward invocation
@@ -290,9 +285,7 @@ static void _logos_method$_ungrouped$FIRDocumentReference$setData$merge$completi
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID];
if (orig != nil) {
orig(error);
}
orig(error);
};
// Forward invocation
@@ -320,9 +313,7 @@ static void _logos_method$_ungrouped$FIRDocumentReference$setData$mergeFields$co
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID];
if (orig != nil) {
orig(error);
}
orig(error);
};
// Forward invocation
@@ -342,9 +333,7 @@ static void _logos_method$_ungrouped$FIRDocumentReference$updateData$completion$
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidUpdateData:error transactionID:requestID];
if (orig != nil) {
orig(error);
}
orig(error);
};
// Forward invocation
@@ -364,9 +353,7 @@ static void _logos_method$_ungrouped$FIRDocumentReference$deleteDocumentWithComp
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidDeleteDocument:error transactionID:requestID];
if (orig != nil) {
orig(error);
}
orig(error);
};
// Forward invocation
@@ -384,9 +371,7 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
void (^orig)(NSError *) = completion;
completion = ^(NSError *error) {
[FLEXNetworkRecorder.defaultRecorder recordFIRDidAddDocument:error transactionID:requestID];
if (orig != nil) {
orig(error);
}
orig(error);
};
// Forward invocation
@@ -533,17 +518,8 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
[self injectIntoNSURLConnectionAsynchronousClassMethod];
[self injectIntoNSURLConnectionSynchronousClassMethod];
Class URLSession = [NSURLSession class];
[self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:URLSession];
[self injectIntoNSURLSessionAsyncUploadTaskMethods:URLSession];
// At some point, NSURLSession.sharedSession became an __NSURLSessionLocal,
// which is not the class returned by [NSURLSession class], of course
Class URLSessionLocal = NSClassFromString(@"__NSURLSessionLocal");
if (URLSessionLocal && (URLSession != URLSessionLocal)) {
[self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:URLSessionLocal];
[self injectIntoNSURLSessionAsyncUploadTaskMethods:URLSessionLocal];
}
[self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods];
[self injectIntoNSURLSessionAsyncUploadTaskMethods];
if (@available(iOS 13.0, *)) {
Class websocketTask = NSClassFromString(@"__NSURLSessionWebSocketTask");
@@ -667,22 +643,15 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
Method originalResume = class_getInstanceMethod(class, selector);
IMP implementation = imp_implementationWithBlock(^(NSURLSessionTask *slf) {
if (@available(iOS 11.0, *)) {
// AVAggregateAssetDownloadTask deeply does not like to be looked at. Accessing -currentRequest or
// -originalRequest will crash. Do not try to observe these. https://github.com/FLEXTool/FLEX/issues/276
if (![slf isKindOfClass:[AVAggregateAssetDownloadTask class]]) {
// iOS's internal HTTP parser finalization code is mysteriously not thread safe,
// invoking it asynchronously has a chance to cause a `double free` crash.
// This line below will ask for HTTPBody synchronously, make the HTTPParser
// parse the request, and cache them in advance. After that the HTTPParser
// will be finalized. Make sure other threads inspecting the request
// won't trigger a race to finalize the parser.
[slf.currentRequest HTTPBody];
[FLEXNetworkObserver.sharedObserver URLSessionTaskWillResume:slf];
}
}
// iOS's internal HTTP parser finalization code is mysteriously not thread safe,
// invoking it asynchronously has a chance to cause a `double free` crash.
// This line below will ask for HTTPBody synchronously, make the HTTPParser
// parse the request, and cache them in advance. After that the HTTPParser
// will be finalized. Make sure other threads inspecting the request
// won't trigger a race to finalize the parser.
[slf.currentRequest HTTPBody];
[FLEXNetworkObserver.sharedObserver URLSessionTaskWillResume:slf];
((void(*)(id, SEL))objc_msgSend)(
slf, swizzledSelector
);
@@ -834,11 +803,11 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
});
}
+ (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:(Class)sessionClass {
+ (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = sessionClass;
Class class = [NSURLSession class];
// The method signatures here are close enough that
// we can use the same logic to inject into all of them.
const SEL selectors[] = {
@@ -902,11 +871,11 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
});
}
+ (void)injectIntoNSURLSessionAsyncUploadTaskMethods:(Class)sessionClass {
+ (void)injectIntoNSURLSessionAsyncUploadTaskMethods {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = sessionClass;
Class class = [NSURLSession class];
// The method signatures here are close enough that we can use the same logic to inject into both of them.
// Note that they have 3 arguments, so we can't easily combine with the data and download method above.
typedef NSURLSessionUploadTask *(^UploadTaskMethod)(
@@ -1622,19 +1591,15 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
[FLEXNetworkObserver.sharedObserver
websocketTask:slf sendMessagage:message
];
id completionHook = ^(NSError *error) {
completion = ^(NSError *error) {
[FLEXNetworkObserver.sharedObserver
websocketTaskMessageSendCompletion:message
error:error
];
if (completion) {
completion(error);
}
};
((void(*)(id, SEL, id, id))objc_msgSend)(
slf, swizzledSelector, message, completionHook
slf, swizzledSelector, message, completion
);
};
@@ -1903,12 +1868,6 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
// Fix for "Response body not in cache" issue reported by developers
// See this github comment for detailed explanation on why this happens
// https://github.com/FLEXTool/FLEX/issues/568#issuecomment-1141015572
if (requestState.dataAccumulator == nil) {
requestState.dataAccumulator = [NSMutableData new];
}
[requestState.dataAccumulator appendData:data];
[FLEXNetworkRecorder.defaultRecorder
@@ -1987,14 +1946,6 @@ didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data
}
- (void)URLSessionTaskWillResume:(NSURLSessionTask *)task {
if (@available(iOS 11.0, *)) {
// AVAggregateAssetDownloadTask deeply does not like to be looked at. Accessing -currentRequest or
// -originalRequest will crash. Do not try to observe these. https://github.com/FLEXTool/FLEX/issues/276
if ([task isKindOfClass:[AVAggregateAssetDownloadTask class]]) {
return;
}
}
// Since resume can be called multiple times on the same task, only treat the first resume as
// the equivalent to connection:willSendRequest:...
[self performBlock:^{
@@ -353,10 +353,6 @@
if ([names containsObject:name]) {
return NO;
} else {
if (!name) {
return NO;
}
[names addObject:name];
// Skip methods and properties which are just overrides,
@@ -10,7 +10,6 @@
#import "FLEXGlobalsViewController.h"
#import "FLEXClassShortcuts.h"
#import "FLEXViewShortcuts.h"
#import "FLEXWindowShortcuts.h"
#import "FLEXViewControllerShortcuts.h"
#import "FLEXUIAppShortcuts.h"
#import "FLEXImageShortcuts.h"
@@ -49,7 +48,6 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
ClassKey(UIViewController) : [FLEXViewControllerShortcuts class],
ClassKey(UIApplication) : [FLEXUIAppShortcuts class],
ClassKey(UIView) : [FLEXViewShortcuts class],
ClassKey(UIWindow) : [FLEXWindowShortcuts class],
ClassKey(UIImage) : [FLEXImageShortcuts class],
ClassKey(CALayer) : [FLEXLayerShortcuts class],
ClassKey(UIColor) : [FLEXColorPreviewSection class],
@@ -182,7 +180,7 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
return [self explorerViewControllerForObject:UIDevice.currentDevice];
case FLEXGlobalsRowPasteboard:
return [self explorerViewControllerForObject:UIPasteboard.generalPasteboard];
case FLEXGlobalsRowURLSession:
case FLEXGlobalsRowURLSession:
return [self explorerViewControllerForObject:NSURLSession.sharedSession];
case FLEXGlobalsRowURLCache:
return [self explorerViewControllerForObject:NSURLCache.sharedURLCache];
@@ -217,22 +215,8 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
return nil;
}
case FLEXGlobalsRowNetworkHistory:
case FLEXGlobalsRowSystemLog:
case FLEXGlobalsRowLiveObjects:
case FLEXGlobalsRowAddressInspector:
case FLEXGlobalsRowCookies:
case FLEXGlobalsRowBrowseRuntime:
case FLEXGlobalsRowAppKeychainItems:
case FLEXGlobalsRowPushNotifications:
case FLEXGlobalsRowBrowseBundle:
case FLEXGlobalsRowBrowseContainer:
case FLEXGlobalsRowCount:
return nil;
default: return nil;
}
return nil;
}
+ (FLEXGlobalsEntryRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row {
@@ -55,3 +55,4 @@ typedef void (^FLEXMutableListCellForElement)(__kindof UITableViewCell *cell, id
- (void)mutate:(void(^)(NSMutableArray *list))block;
@end
@@ -1,13 +0,0 @@
//
// FLEXWindowShortcuts.h
// FLEX
//
// Created by AnthoPak on 26/09/2022.
//
#import "FLEXShortcutsSection.h"
/// Adds "Animations Speed" shortcut for all windows
@interface FLEXWindowShortcuts : FLEXShortcutsSection
@end
@@ -1,48 +0,0 @@
//
// FLEXWindowShortcuts.m
// FLEX
//
// Created by AnthoPak on 26/09/2022.
//
#import "FLEXWindowShortcuts.h"
#import "FLEXShortcut.h"
#import "FLEXAlert.h"
#import "FLEXObjectExplorerViewController.h"
@implementation FLEXWindowShortcuts
#pragma mark - Overrides
+ (instancetype)forObject:(UIView *)view {
return [self forObject:view additionalRows:@[
[FLEXActionShortcut title:@"Animation Speed" subtitle:^NSString *(UIWindow *window) {
return [NSString stringWithFormat:@"Current speed: %.2f", window.layer.speed];
} selectionHandler:^(UIViewController *host, UIWindow *window) {
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Change Animation Speed");
make.message([NSString stringWithFormat:@"Current speed: %.2f", window.layer.speed]);
make.configuredTextField(^(UITextField * _Nonnull textField) {
textField.placeholder = @"Default: 1.0";
textField.keyboardType = UIKeyboardTypeDecimalPad;
});
make.button(@"OK").handler(^(NSArray<NSString *> *strings) {
NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
CGFloat speedValue = [formatter numberFromString:strings.firstObject].floatValue;
window.layer.speed = speedValue;
// Refresh the host view controller to update the shortcut subtitle, reflecting current speed
// TODO: this shouldn't be necessary
[(FLEXObjectExplorerViewController *)host reloadData];
});
make.button(@"Cancel").cancelStyle();
} showFrom:host];
} accessoryType:^UITableViewCellAccessoryType(id _Nonnull object) {
return UITableViewCellAccessoryDisclosureIndicator;
}]
]];
}
@end
+1 -1
View File
@@ -33,7 +33,7 @@
+ (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:(FLEXExplorerToolbarItem *)backupItem {
NSParameterAssert(title); NSParameterAssert(image);
FLEXExplorerToolbarItem *toolbarItem = [self buttonWithType:UIButtonTypeCustom];
FLEXExplorerToolbarItem *toolbarItem = [self buttonWithType:UIButtonTypeSystem];
toolbarItem.sibling = backupItem;
toolbarItem.title = title;
toolbarItem.image = image;
@@ -1,24 +0,0 @@
//
// NSDateFormatter+FLEX.h
// libflex:FLEX
//
// Created by Tanner Bennett on 7/24/22.
// Copyright © 2022 Flipboard. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, FLEXDateFormat) {
// hour:minute [AM|PM]
FLEXDateFormatClock,
// hour:minute:second [AM|PM]
FLEXDateFormatPreciseClock,
// year-month-day hour:minute:second.millisecond
FLEXDateFormatVerbose,
};
@interface NSDateFormatter (FLEX)
+ (NSString *)flex_stringFrom:(NSDate *)date format:(FLEXDateFormat)format;
@end
@@ -1,34 +0,0 @@
//
// NSDateFormatter+FLEX.m
// libflex:FLEX
//
// Created by Tanner Bennett on 7/24/22.
// Copyright © 2022 Flipboard. All rights reserved.
//
#import "NSDateFormatter+FLEX.h"
@implementation NSDateFormatter (FLEX)
+ (NSString *)flex_stringFrom:(NSDate *)date format:(FLEXDateFormat)format {
static NSDateFormatter *formatter = nil;
if (!formatter) {
formatter = [NSDateFormatter new];
}
switch (format) {
case FLEXDateFormatClock:
formatter.dateFormat = @"h:mm a";
break;
case FLEXDateFormatPreciseClock:
formatter.dateFormat = @"h:mm:ss a";
break;
case FLEXDateFormatVerbose:
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
break;
}
return [formatter stringFromDate:date];
}
@end
@@ -19,7 +19,6 @@ extern NSString * const kFLEXDefaultsHideVariablePreviewsKey;
extern NSString * const kFLEXDefaultsNetworkObserverEnabledKey;
extern NSString * const kFLEXDefaultsNetworkHostDenylistKey;
extern NSString * const kFLEXDefaultsDisableOSLogForceASLKey;
extern NSString * const kFLEXDefaultsAPNSCaptureEnabledKey;
extern NSString * const kFLEXDefaultsRegisterJSONExplorerKey;
/// All BOOL preferences are NO by default
@@ -43,8 +42,6 @@ extern NSString * const kFLEXDefaultsRegisterJSONExplorerKey;
@property (nonatomic) BOOL flex_disableOSLog;
@property (nonatomic) BOOL flex_cacheOSLogMessages;
@property (nonatomic) BOOL flex_enableAPNSCapture;
@property (nonatomic) BOOL flex_explorerHidesPropertyIvars;
@property (nonatomic) BOOL flex_explorerHidesPropertyMethods;
@property (nonatomic) BOOL flex_explorerHidesPrivateMethods;
@@ -19,7 +19,6 @@ NSString * const kFLEXDefaultsNetworkObserverEnabledKey = @"com.flex.FLEXNetwork
NSString * const kFLEXDefaultsNetworkObserverLastModeKey = @"com.flex.FLEXNetworkObserver.lastMode";
NSString * const kFLEXDefaultsNetworkHostDenylistKey = @"com.flipboard.FLEX.network_host_denylist";
NSString * const kFLEXDefaultsDisableOSLogForceASLKey = @"com.flipboard.FLEX.try_disable_os_log";
NSString * const kFLEXDefaultsAPNSCaptureEnabledKey = @"com.flipboard.FLEX.capture_apns";
NSString * const kFLEXDefaultsRegisterJSONExplorerKey = @"com.flipboard.FLEX.view_json_as_object";
#define FLEXDefaultsPathForFile(name) ({ \
@@ -124,16 +123,6 @@ NSString * const kFLEXDefaultsRegisterJSONExplorerKey = @"com.flipboard.FLEX.vie
];
}
#pragma mark Push Notifications
- (BOOL)flex_enableAPNSCapture {
return [self boolForKey:kFLEXDefaultsAPNSCaptureEnabledKey];
}
- (void)setFlex_enableAPNSCapture:(BOOL)enable {
[self setBool:enable forKey:kFLEXDefaultsAPNSCaptureEnabledKey];
}
#pragma mark Object Explorer
- (BOOL)flex_explorerHidesPropertyIvars {
+1 -7
View File
@@ -28,9 +28,6 @@ typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionHandler)(void(^handler)(NSAr
/// Shows a simple alert with one button which says "Dismiss"
+ (void)showAlert:(NSString * _Nullable)title message:(NSString * _Nullable)message from:(UIViewController *)viewController;
/// Shows a simple alert with no buttons and only a title, for half a second
+ (void)showQuickAlert:(NSString *)title from:(UIViewController *)viewController;
/// Construct and display an alert
+ (void)makeAlert:(FLEXAlertBuilder)block showFrom:(UIViewController *)viewController;
/// Construct and display an action sheet-style alert
@@ -71,11 +68,8 @@ typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionHandler)(void(^handler)(NSAr
@property (nonatomic, readonly) FLEXAlertActionStringProperty title;
/// Make the action destructive. It appears with red text.
@property (nonatomic, readonly) FLEXAlertActionProperty destructiveStyle;
/// Make the action cancel-style. It sometimes appears with a bolder font.
/// Make the action cancel-style. It appears with a bolder font.
@property (nonatomic, readonly) FLEXAlertActionProperty cancelStyle;
/// Make the action the preferred action. It appears with a bolder font.
/// The first action that is set as preferred will be used as the preferred action.
@property (nonatomic, readonly) FLEXAlertActionProperty preferred;
/// Enable or disable the action. Enabled by default.
@property (nonatomic, readonly) FLEXAlertActionBOOLProperty enabled;
/// Give the button an action. The action takes an array of text field strings.
+1 -33
View File
@@ -22,7 +22,6 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
@property (nonatomic) NSString *_title;
@property (nonatomic) UIAlertActionStyle _style;
@property (nonatomic) BOOL _disable;
@property (nonatomic) BOOL _isPreferred;
@property (nonatomic) void(^_handler)(UIAlertAction *action);
@property (nonatomic) UIAlertAction *_action;
@end
@@ -35,18 +34,6 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
} showFrom:viewController];
}
+ (void)showQuickAlert:(NSString *)title from:(UIViewController *)viewController {
UIAlertController *alert = [self makeAlert:^(FLEXAlert *make) {
make.title(title);
}];
[viewController presentViewController:alert animated:YES completion:^{
flex_dispatch_after(0.5, dispatch_get_main_queue(), ^{
[alert dismissViewControllerAnimated:YES completion:nil];
});
}];
}
#pragma mark Initialization
- (instancetype)initWithController:(UIAlertController *)controller {
@@ -73,18 +60,7 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
[alert._controller addAction:builder.action];
}
UIAlertController *controller = alert._controller;
// Set preferred action on alert controller
for (FLEXAlertAction *builder in alert._actions) {
UIAlertAction *action = builder.action;
if (builder._isPreferred) {
controller.preferredAction = action;
break;
}
}
return controller;
return alert._controller;
}
+ (void)make:(FLEXAlertBuilder)block
@@ -215,14 +191,6 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
};
}
- (FLEXAlertActionProperty)preferred {
return ^FLEXAlertAction *() {
FLEXAlertActionMutationAssertion();
self._isPreferred = YES;
return self;
};
}
- (FLEXAlertActionBOOLProperty)enabled {
return ^FLEXAlertAction *(BOOL enabled) {
FLEXAlertActionMutationAssertion();
+2 -31
View File
@@ -9,46 +9,17 @@
#import <Foundation/Foundation.h>
@class FLEXObjectRef;
NS_ASSUME_NONNULL_BEGIN
typedef void (^flex_object_enumeration_block_t)(__unsafe_unretained id object, __unsafe_unretained Class actualClass);
/// Counts and identifies all class instances on the heap.
@interface FLEXHeapSnapshot : NSObject
/// The names of every class instance discovered on the heap.
@property (nonatomic, readonly) NSArray<NSString *> *classNames;
/// A mapping of instance counts to class names.
@property (nonatomic, readonly) NSDictionary<NSString *, NSNumber *> *instanceCountsForClassNames;
/// A mapping of class instance size to class name.
///
/// To roughly calculate the memory usage of an entire class, multiply this number by the instance count.
@property (nonatomic, readonly) NSDictionary<NSString *, NSNumber *> *instanceSizesForClassNames;
@end
@interface FLEXHeapEnumerator : NSObject
/// Use carefully; this method puts a global lock on the heap in between callbacks.
///
/// Inspired by:
/// [heap_find.cpp](https://llvm.org/svn/llvm-project/lldb/tags/RELEASE_34/final/examples/darwin/heap_find/heap/heap_find.cpp)
/// and [samdmarshall](https://gist.github.com/samdmarshall/17f4e66b5e2e579fd396)
+ (void)enumerateLiveObjectsUsingBlock:(flex_object_enumeration_block_t)callback
NS_SWIFT_UNAVAILABLE("Use one of the other methods instead.");
+ (void)enumerateLiveObjectsUsingBlock:(flex_object_enumeration_block_t)block;
/// Returned references are not validated beyond containing a valid isa.
/// To validate them yourself, pass each reference's object to \c FLEXPointerIsValidObjcObject
+ (NSArray<FLEXObjectRef *> *)instancesOfClassWithName:(NSString *)className retained:(BOOL)retain;
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className;
/// Returned references have been validated via \c FLEXPointerIsValidObjcObject
/// @param object the object to find references to
/// @param retain whether to retain the objects referencing \c object
+ (NSArray<FLEXObjectRef *> *)objectsWithReferencesToObject:(id)object retained:(BOOL)retain;
/// Capture all live objects on the heap and do with this information what you will.
+ (FLEXHeapSnapshot *)generateHeapSnapshot;
@end
NS_ASSUME_NONNULL_END
+6 -56
View File
@@ -22,18 +22,6 @@ typedef struct {
Class isa;
} flex_maybe_object_t;
@implementation FLEXHeapSnapshot
+ (instancetype)snapshotWithCounts:(NSDictionary<NSString *, NSNumber *> *)counts
sizes:(NSDictionary<NSString *, NSNumber *> *)sizes {
FLEXHeapSnapshot *snapshot = [FLEXHeapSnapshot new];
snapshot->_classNames = counts.allKeys;
snapshot->_instanceCountsForClassNames = counts;
snapshot->_instanceSizesForClassNames = sizes;
return snapshot;
}
@end
@implementation FLEXHeapEnumerator
static void range_callback(task_t task, void *context, unsigned type, vm_range_t *ranges, unsigned rangeCount) {
@@ -150,6 +138,12 @@ static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_add
return references;
}
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className {
NSArray<Class> *classes = FLEXGetAllSubclasses(NSClassFromString(className), NO);
NSArray<FLEXObjectRef *> *references = [FLEXObjectRef referencingClasses:classes];
return references;
}
+ (NSArray<FLEXObjectRef *> *)objectsWithReferencesToObject:(id)object retained:(BOOL)retain {
NSMutableArray<FLEXObjectRef *> *instances = [NSMutableArray new];
[FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id tryObject, __unsafe_unretained Class actualClass) {
@@ -191,48 +185,4 @@ static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_add
return instances;
}
+ (FLEXHeapSnapshot *)generateHeapSnapshot {
// Set up a CFMutableDictionary with class pointer keys and NSUInteger values.
// We abuse CFMutableDictionary a little to have primitive keys through judicious casting, but it gets the job done.
// The dictionary is intialized with a 0 count for each class so that it doesn't have to expand during enumeration.
// While it might be a little cleaner to populate an NSMutableDictionary with class name string keys to NSNumber
// counts, we choose the CF/primitives approach because it lets us enumerate the objects in the heap without
// allocating any memory during enumeration. The alternative of creating one NSString/NSNumber per object
// on the heap ends up polluting the count of live objects quite a bit.
unsigned int classCount = 0;
Class *classes = objc_copyClassList(&classCount);
CFMutableDictionaryRef mutableCountsForClasses = CFDictionaryCreateMutable(NULL, classCount, NULL, NULL);
for (unsigned int i = 0; i < classCount; i++) {
CFDictionarySetValue(mutableCountsForClasses, (__bridge const void *)classes[i], (const void *)0);
}
// Enumerate all objects on the heap to build the counts of instances for each class
[FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, __unsafe_unretained Class cls) {
NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(
mutableCountsForClasses, (__bridge const void *)cls
);
instanceCount++;
CFDictionarySetValue(
mutableCountsForClasses, (__bridge const void *)cls, (const void *)instanceCount
);
}];
// Convert our CF primitive dictionary into a nicer mapping of class name strings to instance counts
NSMutableDictionary<NSString *, NSNumber *> *countsForClassNames = [NSMutableDictionary new];
NSMutableDictionary<NSString *, NSNumber *> *sizesForClassNames = [NSMutableDictionary new];
for (unsigned int i = 0; i < classCount; i++) {
Class class = classes[i];
NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)(class));
NSString *className = @(class_getName(class));
if (instanceCount > 0) {
countsForClassNames[className] = @(instanceCount);
sizesForClassNames[className] = @(class_getInstanceSize(class));
}
}
free(classes);
return [FLEXHeapSnapshot snapshotWithCounts:countsForClassNames sizes:sizesForClassNames];
}
@end
-5
View File
@@ -9,11 +9,6 @@
#ifndef FLEXMacros_h
#define FLEXMacros_h
#ifndef __cplusplus
#ifndef auto
#define auto __auto_type
#endif
#endif
#define flex_keywordify class NSObject;
#define ctor flex_keywordify __attribute__((constructor)) void __flex_ctor_##__LINE__()
-1
View File
@@ -51,7 +51,6 @@
+ (NSString *)prettyJSONStringFromData:(NSData *)data;
+ (BOOL)isValidJSONData:(NSData *)data;
+ (NSData *)inflatedDataFromCompressedData:(NSData *)compressedData;
+ (BOOL)hasCompressedContentEncoding:(NSURLRequest *)request;
// Swizzling utilities
+3 -9
View File
@@ -14,7 +14,7 @@
#import <objc/runtime.h>
#import <zlib.h>
BOOL FLEXConstructorsShouldRun(void) {
BOOL FLEXConstructorsShouldRun() {
#if FLEX_DISABLE_CTORS
return NO;
#else
@@ -100,9 +100,8 @@ BOOL FLEXConstructorsShouldRun(void) {
description = [description stringByAppendingFormat:@" %@", [self stringForCGRect:view.frame]];
}
if (view.accessibilityLabel.length > 0 || view.accessibilityIdentifier.length > 0) {
description = [description stringByAppendingFormat:@" · %@",
view.accessibilityLabel.length > 0 ? view.accessibilityLabel : view.accessibilityIdentifier];
if (view.accessibilityLabel.length > 0) {
description = [description stringByAppendingFormat:@" · %@", view.accessibilityLabel];
}
return description;
@@ -413,11 +412,6 @@ BOOL FLEXConstructorsShouldRun(void) {
return inflatedData;
}
+ (BOOL)hasCompressedContentEncoding:(NSURLRequest *)request {
NSString *contentEncoding = [request valueForHTTPHeaderField:@"Content-Encoding"];
return ([contentEncoding rangeOfString:@"deflate" options:NSCaseInsensitiveSearch].length > 0 || [contentEncoding rangeOfString:@"gzip" options:NSCaseInsensitiveSearch].length > 0);
}
+ (NSArray<UIWindow *> *)allWindows {
BOOL includeInternalWindows = YES;
BOOL onlyVisibleWindows = NO;
@@ -29,7 +29,7 @@
self.textView.backgroundColor = UIColor.blackColor;
self.textView.textColor = UIColor.whiteColor;
self.textView.font = [UIFont boldSystemFontOfSize:14.0];
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
self.title = @"Simulator Shortcuts";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed:)];
+4 -12
View File
@@ -7,7 +7,6 @@
//
#import "FLEXRuntimeConstants.h"
@class FLEXObjectRef;
#define PropertyKey(suffix) kFLEXPropertyAttributeKey##suffix : @""
#define PropertyKeyGetter(getter) kFLEXPropertyAttributeKeyCustomGetter : NSStringFromSelector(@selector(getter))
@@ -46,9 +45,7 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
@interface FLEXRuntimeUtility : NSObject
#pragma mark - General Helpers
/// Calls into \c FLEXPointerIsValidObjcObject()
// General Helpers
+ (BOOL)pointerIsValidObjcObject:(const void *)pointer;
/// Unwraps raw pointers to objects stored in NSValue, and re-boxes C strings into NSStrings.
+ (id)potentiallyUnwrapBoxedPointer:(id)returnedObjectOrNil type:(const FLEXTypeEncoding *)returnType;
@@ -62,8 +59,6 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
/// @return The class hierarchy for the given object or class,
/// from the current class to the root-most class.
+ (NSArray<Class> *)classHierarchyOfObject:(id)objectOrClass;
/// @return Every subclass of the given class name.
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className;
/// Used to describe an object in brief within an explorer row
+ (NSString *)summaryForObject:(id)value;
@@ -74,19 +69,16 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
+ (BOOL)safeObject:(id)object isKindOfClass:(Class)cls;
+ (BOOL)safeObject:(id)object respondsToSelector:(SEL)sel;
#pragma mark - Property Helpers
// Property Helpers
+ (BOOL)tryAddPropertyWithName:(const char *)name
attributes:(NSDictionary<NSString *, NSString *> *)attributePairs
toClass:(__unsafe_unretained Class)theClass;
+ (NSArray<NSString *> *)allPropertyAttributeKeys;
#pragma mark - Method Helpers
// Method Helpers
+ (NSArray *)prettyArgumentComponentsForMethod:(Method)method;
#pragma mark - Method Calling/Field Editing
// Method Calling/Field Editing
+ (id)performSelector:(SEL)selector onObject:(id)object;
+ (id)performSelector:(SEL)selector
onObject:(id)object
@@ -9,8 +9,6 @@
#import <UIKit/UIKit.h>
#import "FLEXRuntimeUtility.h"
#import "FLEXObjcInternal.h"
#import "FLEXObjectRef.h"
#import "NSObject+FLEX_Reflection.h"
#import "FLEXTypeEncodingParser.h"
#import "FLEXMethod.h"
@@ -94,12 +92,6 @@ NSString * const FLEXRuntimeUtilityErrorDomain = @"FLEXRuntimeUtilityErrorDomain
return superClasses;
}
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className {
NSArray<Class> *classes = FLEXGetAllSubclasses(NSClassFromString(className), NO);
NSArray<FLEXObjectRef *> *references = [FLEXObjectRef referencingClasses:classes];
return references;
}
+ (NSString *)safeClassNameForObject:(id)object {
// Don't assume that we have an NSObject subclass
if ([self safeObject:object respondsToSelector:@selector(class)]) {
@@ -168,7 +160,6 @@ NSString * const FLEXRuntimeUtilityErrorDomain = @"FLEXRuntimeUtilityErrorDomain
// Single line display - replace newlines and tabs with spaces.
description = [[self safeDescriptionForObject:value] stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
description = [description stringByReplacingOccurrencesOfString:@"\t" withString:@" "];
description = [description stringByReplacingOccurrencesOfString:@" " withString:@" "];
} @catch (NSException *e) {
description = [@"Thrown: " stringByAppendingString:e.reason ?: @"(nil exception reason)"];
}
@@ -59,7 +59,6 @@ NS_INLINE BOOL flex_isTaggedPointer(const void *ptr) {
#define FLEXPointerIsTaggedPointer(obj) flex_isTaggedPointer((__bridge void *)obj)
/// Whether the given pointer is a valid, readable address.
BOOL FLEXPointerIsReadable(const void * ptr);
/// @brief Assumes memory is valid and readable.
@@ -19,7 +19,7 @@ CFSetRef FLEXKnownUnsafeIvars = nil;
(class_getInstanceVariable([cls class], name) ?: (void *)kCFNull)
__attribute__((constructor))
static void FLEXRuntimeSafteyInit(void) {
static void FLEXRuntimeSafteyInit() {
FLEXKnownUnsafeClasses = CFSetCreate(
kCFAllocatorDefault,
(const void **)(uintptr_t)FLEXKnownUnsafeClassList(),
@@ -39,7 +39,7 @@ static void FLEXRuntimeSafteyInit(void) {
);
}
const Class * FLEXKnownUnsafeClassList(void) {
const Class * FLEXKnownUnsafeClassList() {
if (!_UnsafeClasses) {
const Class ignored[] = {
FLEXClassPointerOrCFNull(@"__ARCLite__"),
@@ -74,7 +74,7 @@ const Class * FLEXKnownUnsafeClassList(void) {
return _UnsafeClasses;
}
NSSet * FLEXKnownUnsafeClassNames(void) {
NSSet * FLEXKnownUnsafeClassNames() {
static NSSet *set = nil;
if (!set) {
NSArray *ignored = @[
@@ -85,8 +85,8 @@ extern "C" BOOL FLEXIsSwiftObjectOrClass(id objOrClass) {
class_data_bits_t rodata = ((__bridge objc_class_ *)(cls))->bits;
if (@available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *)) {
return (rodata & FAST_IS_SWIFT_STABLE) != 0;
return rodata & FAST_IS_SWIFT_STABLE;
} else {
return (rodata & FAST_IS_SWIFT_LEGACY) != 0;
return rodata & FAST_IS_SWIFT_LEGACY;
}
}
@@ -23,7 +23,7 @@
NSString *_flex_description;
}
/// Constructs and returns a \c FLEXSimpleMethod instance with the given name, type encoding, and implementation.
/// Constructs and returns an \c FLEXSimpleMethod instance with the given name, type encoding, and implementation.
+ (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation;
/// The selector of the method.
@@ -52,15 +52,9 @@
}
- (NSString *)debugDescription {
if (@available(iOS 10.0, *)) {
return [NSString stringWithFormat:@"<%@ name=%@, %lu required properties, %lu optional properties %lu required methods, %lu optional methods, %lu protocols>",
NSStringFromClass(self.class), self.name, (unsigned long)self.requiredProperties.count, (unsigned long)self.optionalProperties.count,
(unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count];
} else {
return [NSString stringWithFormat:@"<%@ name=%@, %lu properties, %lu required methods, %lu optional methods, %lu protocols>",
return [NSString stringWithFormat:@"<%@ name=%@, %lu properties, %lu required methods, %lu optional methods, %lu protocols>",
NSStringFromClass(self.class), self.name, (unsigned long)self.properties.count,
(unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count];
}
}
- (void)examine {
@@ -7,7 +7,6 @@
//
#import "FLEXImagePreviewViewController.h"
#import "FLEXActivityViewController.h"
#import "FLEXUtility.h"
#import "FLEXColor.h"
#import "FLEXResources.h"
@@ -51,10 +50,6 @@
return self;
}
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
[super dismissViewControllerAnimated:flag completion:completion];
}
#pragma mark Lifecycle
@@ -133,7 +128,7 @@
}
});
UIViewController *activityVC = [FLEXActivityViewController sharing:@[self.image] source:sender];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[self.image] applicationActivities:@[]];
if (!canSaveToCameraRoll && !didShowWarning) {
didShowWarning = YES;
@@ -88,19 +88,8 @@
@implementation FHSView (Snapshotting)
+ (UIImage *)drawView:(UIView *)view {
if (CGRectIsEmpty(view.bounds)) {
return [UIImage new];
}
CGSize size = view.bounds.size;
CGFloat minUnit = 1.f / UIScreen.mainScreen.scale;
// Every drawn view must not have 0 width or height
CGSize minsize = CGSizeMake(MAX(size.width, minUnit), MAX(size.height, minUnit));
CGRect minBounds = CGRectMake(0, 0, minsize.width, minsize.height);
UIGraphicsBeginImageContextWithOptions(minsize, NO, 0);
[view drawViewHierarchyInRect:minBounds afterScreenUpdates:YES];
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
@@ -24,9 +24,7 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
@interface FLEXHierarchyTableViewController ()
@property (nonatomic) NSArray<UIView *> *allViews;
/// Map of view's address to its depth
/// @((uintptr_t)(__bridge void *)view) -> depth
@property (nonatomic) NSMapTable<NSNumber *, NSNumber *> *depthsForViews;
@property (nonatomic) NSMapTable<UIView *, NSNumber *> *depthsForViews;
@property (nonatomic) NSArray<UIView *> *viewsAtTap;
@property (nonatomic) NSArray<UIView *> *displayedViews;
@property (nonatomic, readonly) BOOL showScopeBar;
@@ -48,7 +46,7 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
- (instancetype)initWithViews:(NSArray<UIView *> *)allViews
viewsAtTap:(NSArray<UIView *> *)viewsAtTap
selectedView:(UIView *)selectedView
depths:(NSMapTable<NSNumber *, NSNumber *> *)depthsForViews {
depths:(NSMapTable<UIView *, NSNumber *> *)depthsForViews {
NSParameterAssert(allViews);
NSParameterAssert(depthsForViews.count == allViews.count);
@@ -137,7 +135,7 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
tryView = tryView.superview;
depth++;
}
depths[@((uintptr_t)(__bridge void *)view)] = @(depth);
depths[(id)view] = @(depth);
}
return depths;
@@ -225,7 +223,7 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
cell.textLabel.text = [FLEXUtility descriptionForView:view includingFrame:NO];
cell.detailTextLabel.text = [FLEXUtility detailDescriptionForView:view];
cell.randomColorTag = [FLEXUtility consistentRandomColorForObject:view];
cell.viewDepth = self.depthsForViews[@((uintptr_t)(__bridge void *)view)].integerValue;
cell.viewDepth = self.depthsForViews[view].integerValue;
cell.indicatedViewColor = view.backgroundColor;
if (view.isHidden || view.alpha < 0.01) {
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
A9D70EC640E633FE829EF695 /* libPods-FLEXample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBD699DDBAC5A16D8CFD39AC /* libPods-FLEXample.a */; };
C376F92B28DBE503004E88BA /* dogs.realm in Resources */ = {isa = PBXBuildFile; fileRef = C386D706241AA66100699085 /* dogs.realm */; };
C386D6D02419975A00699085 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6CF2419975A00699085 /* AppDelegate.swift */; };
C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6D12419975A00699085 /* SceneDelegate.swift */; };
C386D6D62419975B00699085 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D52419975B00699085 /* Assets.xcassets */; };
@@ -239,7 +238,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C376F92B28DBE503004E88BA /* dogs.realm in Resources */,
C386D6DC2419975B00699085 /* LaunchScreen.storyboard in Resources */,
C386D6FF241AA4B700699085 /* image.png in Resources */,
C386D704241AA61600699085 /* music_library.db in Resources */,
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -50,13 +50,6 @@
ReferencedContainer = "container:FLEXample-Cocoapods.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "_MSSafeMode"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
C35F0EF4292CC5CA00B728BC /* Reflex in Frameworks */ = {isa = PBXBuildFile; productRef = C35F0EF3292CC5CA00B728BC /* Reflex */; };
C386D6D02419975A00699085 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6CF2419975A00699085 /* AppDelegate.swift */; };
C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6D12419975A00699085 /* SceneDelegate.swift */; };
C386D6D62419975B00699085 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D52419975B00699085 /* Assets.xcassets */; };
@@ -22,6 +21,7 @@
C386D705241AA61600699085 /* music_library_schema.jpg in Resources */ = {isa = PBXBuildFile; fileRef = C386D703241AA61600699085 /* music_library_schema.jpg */; };
C386D70B241AA67800699085 /* Dog.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D709241AA67800699085 /* Dog.m */; };
C386D70C241AA67800699085 /* Owner.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D70A241AA67800699085 /* Owner.m */; };
C38D96EC2818F306008709D0 /* Reflex in Frameworks */ = {isa = PBXBuildFile; productRef = C38D96EB2818F306008709D0 /* Reflex */; };
C3A67856241AB8AD005A4681 /* MiscNetworkRequests.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */; };
C3A67858241ADDF7005A4681 /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A67857241ADDF7005A4681 /* Commit.swift */; };
C3B3760025B8CDA300AD43AB /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = C3B375FF25B8CDA300AD43AB /* Person.m */; };
@@ -62,7 +62,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C35F0EF4292CC5CA00B728BC /* Reflex in Frameworks */,
C38D96EC2818F306008709D0 /* Reflex in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -183,7 +183,7 @@
);
name = FLEXample;
packageProductDependencies = (
C35F0EF3292CC5CA00B728BC /* Reflex */,
C38D96EB2818F306008709D0 /* Reflex */,
);
productName = FLEXample;
productReference = C386D6CC2419975A00699085 /* FLEXample.app */;
@@ -215,7 +215,7 @@
);
mainGroup = C386D6C32419975A00699085;
packageReferences = (
C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */,
C38D96EA2818F306008709D0 /* XCRemoteSwiftPackageReference "Reflex" */,
);
productRefGroup = C386D6CD2419975A00699085 /* Products */;
projectDirPath = "";
@@ -462,9 +462,9 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */ = {
C38D96EA2818F306008709D0 /* XCRemoteSwiftPackageReference "Reflex" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/FLEXTool/Reflex";
repositoryURL = "file:///Users/tanner/Repos/Reflex";
requirement = {
branch = master;
kind = branch;
@@ -473,9 +473,9 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
C35F0EF3292CC5CA00B728BC /* Reflex */ = {
C38D96EB2818F306008709D0 /* Reflex */ = {
isa = XCSwiftPackageProductDependency;
package = C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */;
package = C38D96EA2818F306008709D0 /* XCRemoteSwiftPackageReference "Reflex" */;
productName = Reflex;
};
/* End XCSwiftPackageProductDependency section */
@@ -50,13 +50,6 @@
ReferencedContainer = "container:FLEXample-SPM.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "_MSSafeMode"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -16,24 +16,6 @@
@property (nonatomic, readonly) NSMutableDictionary<NSString *, UIImage *> *avatars;
@end
@interface UIAlertController (Private)
@property (nonatomic) UIViewController *contentViewController;
@end
@interface ContentViewController : UIViewController
+ (instancetype)customView:(UIView *)view;
@end
@implementation ContentViewController
+ (instancetype)customView:(UIView *)view {
ContentViewController *vc = [self new];
vc.view = view;
return vc;
}
@end
@implementation CommitListViewController
- (id)init {
@@ -41,16 +23,6 @@
return [self initWithStyle:UITableViewStylePlain];
}
- (void)showCustomView {
UIAlertController *alert = [FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Custom View").button(@"Dismiss").cancelStyle();
}];
// I like to use this to easily display and interact with custom views I'm working on
alert.contentViewController = [ContentViewController customView:[UIView new]];
[self presentViewController:alert animated:YES completion:nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
@@ -63,10 +35,6 @@
];
self.navigationItem.rightBarButtonItem.accessibilityIdentifier = @"toggle-explorer";
self.navigationItem.leftBarButtonItem = [UIBarButtonItem
flex_itemWithTitle:@"Test" target:self action:@selector(showCustomView)
];
// Load and process commits
NSString *commitsURL = @"https://api.github.com/repos/Flipboard/FLEX/commits";
[self startDataTask:commitsURL completion:^(NSData *data, NSInteger statusCode, NSError *error) {
+1 -3
View File
@@ -153,7 +153,7 @@
}
- (void)sendExampleWebsocketTraffic:(NSURLSession *)sessionWithDelegate {
NSString *APIKey = @"VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV";
NSString *APIKey = @"oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm";
NSString *wsurl = [NSString stringWithFormat:@"wss://demo.piesocket.com/v3/channel_1?api_key=%@&notify_self", APIKey];
NSURLSessionWebSocketTask *task = [sessionWithDelegate webSocketTaskWithURL:[NSURL URLWithString:wsurl]];
[task resume];
@@ -167,8 +167,6 @@
[task sendMessage:message completionHandler:^(NSError *error) {
if (error) {
NSLog(@"Error sending WS message: %@", error.localizedDescription);
} else {
NSLog(@"WS message sent.");
}
}];
}
@@ -27,8 +27,6 @@
</dict>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>NSPhotoLibraryAddUsageDescription</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Save images</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
+4 -4
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = "FLEX"
spec.version = "5.22.10"
spec.version = "4.6.1"
spec.summary = "A set of in-app debugging and exploration tools for iOS"
spec.description = <<-DESC
- Inspect and modify views in the hierarchy.
@@ -28,6 +28,7 @@ Pod::Spec.new do |spec|
spec.license = { :type => "BSD", :file => "LICENSE" }
spec.author = { "Tanner Bennett" => "tannerbennett@me.com" }
spec.social_media_url = "https://twitter.com/NSExceptional"
spec.platform = :ios, "9.0"
spec.source = { :git => "https://github.com/FLEXTool/FLEX.git", :tag => "#{spec.version}" }
spec.source_files = "Classes/**/*.{h,c,m,mm}"
@@ -36,7 +37,7 @@ Pod::Spec.new do |spec|
spec.libraries = [ "z", "sqlite3" ]
spec.requires_arc = true
spec.library = 'stdc++'
spec.pod_target_xcconfig = {
spec.xcconfig = {
'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++11',
}
spec.compiler_flags = "-Wno-unsupported-availability-guard", "-Wno-deprecated-declarations"
@@ -53,7 +54,6 @@ Pod::Spec.new do |spec|
"Classes/Utility/FLEXResources.h",
"Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h",
"Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h",
"Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h",
"Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h"
"Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h"
]
end
+3 -35
View File
@@ -178,8 +178,6 @@
C33C825F2316DC8600DD2451 /* FLEXObjectExplorer.m in Sources */ = {isa = PBXBuildFile; fileRef = C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */; };
C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C33E46AD223B02CD004BD0E6 /* FLEXASLLogController.h */; };
C33E46B0223B02CD004BD0E6 /* FLEXASLLogController.m in Sources */ = {isa = PBXBuildFile; fileRef = C33E46AE223B02CD004BD0E6 /* FLEXASLLogController.m */; };
C34063B528400A26007B46D3 /* FLEXActivityViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C34063B328400A26007B46D3 /* FLEXActivityViewController.h */; };
C34063B628400A26007B46D3 /* FLEXActivityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C34063B428400A26007B46D3 /* FLEXActivityViewController.m */; };
C3474C4023DA496400466532 /* FLEXKeyValueTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3474C3E23DA496400466532 /* FLEXKeyValueTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3474C4123DA496400466532 /* FLEXKeyValueTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3474C3F23DA496400466532 /* FLEXKeyValueTableViewCell.m */; };
C3490E1F233BDD73002AE200 /* FLEXSingleRowSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3490E1D233BDD73002AE200 /* FLEXSingleRowSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -248,7 +246,7 @@
C383C3C523B6BB81007A321B /* FLEXCodeFontCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C383C3C323B6BB81007A321B /* FLEXCodeFontCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
C383C3C623B6BB81007A321B /* FLEXCodeFontCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C383C3C423B6BB81007A321B /* FLEXCodeFontCell.m */; };
C3854DF023F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C3854DEF23F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m */; };
C38568FA272B3BFC00B1E37F /* FLEXSwiftInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */; settings = {ATTRIBUTES = (Public, ); }; };
C38568FA272B3BFC00B1E37F /* FLEXSwiftInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */; };
C38568FB272B3BFC00B1E37F /* FLEXSwiftInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = C38568F9272B3BFC00B1E37F /* FLEXSwiftInternal.mm */; };
C386D6A9241995A800699085 /* FLEXTypeEncodingParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C3854DF223F36C9E00FCD1E2 /* FLEXTypeEncodingParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
C386D6E924199C1B00699085 /* FLEX-Core.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6E824199C1B00699085 /* FLEX-Core.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -267,7 +265,7 @@
C387C88322E0D24A00750E58 /* UIView+FLEX_Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */; settings = {ATTRIBUTES = (Private, ); }; };
C387C88422E0D24A00750E58 /* UIView+FLEX_Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */; };
C38D970228190C93008709D0 /* FLEXMetadataExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = C38D970028190C93008709D0 /* FLEXMetadataExtras.m */; };
C38D970328190C93008709D0 /* FLEXMetadataExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = C38D970128190C93008709D0 /* FLEXMetadataExtras.h */; settings = {ATTRIBUTES = (Public, ); }; };
C38D970328190C93008709D0 /* FLEXMetadataExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = C38D970128190C93008709D0 /* FLEXMetadataExtras.h */; };
C38DF0EA22CFE4370077B4AD /* FLEXTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38DF0E822CFE4370077B4AD /* FLEXTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
C38DF0EB22CFE4370077B4AD /* FLEXTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38DF0E922CFE4370077B4AD /* FLEXTableViewController.m */; };
C38EF26223A2FCD20047A7EC /* FLEXViewControllerShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF26023A2FCD20047A7EC /* FLEXViewControllerShortcuts.m */; };
@@ -309,8 +307,6 @@
C39EADC923F37B89005618BE /* FLEXTypeEncodingParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C3854DF123F36C9E00FCD1E2 /* FLEXTypeEncodingParser.m */; };
C39ED92822D63F3200B5773A /* FLEXAddressExplorerCoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = C39ED92622D63F3200B5773A /* FLEXAddressExplorerCoordinator.h */; };
C39ED92922D63F3200B5773A /* FLEXAddressExplorerCoordinator.m in Sources */ = {isa = PBXBuildFile; fileRef = C39ED92722D63F3200B5773A /* FLEXAddressExplorerCoordinator.m */; };
C3A04B64288E007100F2C16D /* NSDateFormatter+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3A04B65288E007100F2C16D /* NSDateFormatter+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */; };
C3A9422C23C3DA39006871A3 /* FHSView.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9422823C3DA39006871A3 /* FHSView.h */; };
C3A9422D23C3DA39006871A3 /* FHSView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9422923C3DA39006871A3 /* FHSView.m */; };
C3A9423423C3F98E006871A3 /* FHSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9423223C3F98E006871A3 /* FHSViewController.h */; };
@@ -345,12 +341,8 @@
C3E5DA02231700EE00E655DB /* FLEXObjectInfoSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3EB6F8D242E9C83006EA386 /* FLEXRuntimeExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EB6F8B242E9C83006EA386 /* FLEXRuntimeExporter.m */; };
C3EB6F8E242E9C83006EA386 /* FLEXRuntimeExporter.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EB6F8C242E9C83006EA386 /* FLEXRuntimeExporter.h */; };
C3EBE111287028D600702098 /* FLEXAPNSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */; };
C3EBE112287028D600702098 /* FLEXAPNSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EBE110287028D600702098 /* FLEXAPNSViewController.m */; };
C3EE76BF22DFC63600EC0AA0 /* FLEXScopeCarousel.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EE76BD22DFC63600EC0AA0 /* FLEXScopeCarousel.h */; };
C3EE76C022DFC63600EC0AA0 /* FLEXScopeCarousel.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EE76BE22DFC63600EC0AA0 /* FLEXScopeCarousel.m */; };
C3EF290528EE8BC500B337B6 /* FLEXWindowShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */; };
C3EF290628EE8BC500B337B6 /* FLEXWindowShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */; };
C3F31D3D2267D883003C991A /* FLEXSubtitleTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D342267D883003C991A /* FLEXSubtitleTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3F31D3E2267D883003C991A /* FLEXTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D352267D883003C991A /* FLEXTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3F31D3F2267D883003C991A /* FLEXMultilineTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D362267D883003C991A /* FLEXMultilineTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -566,8 +558,6 @@
C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FLEXObjectExplorer.m; path = Classes/ObjectExplorers/FLEXObjectExplorer.m; sourceTree = SOURCE_ROOT; };
C33E46AD223B02CD004BD0E6 /* FLEXASLLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXASLLogController.h; sourceTree = "<group>"; };
C33E46AE223B02CD004BD0E6 /* FLEXASLLogController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXASLLogController.m; sourceTree = "<group>"; };
C34063B328400A26007B46D3 /* FLEXActivityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXActivityViewController.h; sourceTree = "<group>"; };
C34063B428400A26007B46D3 /* FLEXActivityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXActivityViewController.m; sourceTree = "<group>"; };
C3474C3E23DA496400466532 /* FLEXKeyValueTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXKeyValueTableViewCell.h; sourceTree = "<group>"; };
C3474C3F23DA496400466532 /* FLEXKeyValueTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyValueTableViewCell.m; sourceTree = "<group>"; };
C3490E1D233BDD73002AE200 /* FLEXSingleRowSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSingleRowSection.h; sourceTree = "<group>"; };
@@ -696,8 +686,6 @@
C398682423AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FLEXShortcutsFactory+Defaults.h"; sourceTree = "<group>"; };
C39ED92622D63F3200B5773A /* FLEXAddressExplorerCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXAddressExplorerCoordinator.h; sourceTree = "<group>"; };
C39ED92722D63F3200B5773A /* FLEXAddressExplorerCoordinator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXAddressExplorerCoordinator.m; sourceTree = "<group>"; };
C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+FLEX.h"; sourceTree = "<group>"; };
C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+FLEX.m"; sourceTree = "<group>"; };
C3A9422823C3DA39006871A3 /* FHSView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FHSView.h; sourceTree = "<group>"; };
C3A9422923C3DA39006871A3 /* FHSView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FHSView.m; sourceTree = "<group>"; };
C3A9423223C3F98E006871A3 /* FHSViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FHSViewController.h; sourceTree = "<group>"; };
@@ -731,12 +719,8 @@
C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectInfoSection.h; sourceTree = "<group>"; };
C3EB6F8B242E9C83006EA386 /* FLEXRuntimeExporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeExporter.m; sourceTree = "<group>"; };
C3EB6F8C242E9C83006EA386 /* FLEXRuntimeExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeExporter.h; sourceTree = "<group>"; };
C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXAPNSViewController.h; sourceTree = "<group>"; };
C3EBE110287028D600702098 /* FLEXAPNSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXAPNSViewController.m; sourceTree = "<group>"; };
C3EE76BD22DFC63600EC0AA0 /* FLEXScopeCarousel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXScopeCarousel.h; sourceTree = "<group>"; };
C3EE76BE22DFC63600EC0AA0 /* FLEXScopeCarousel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXScopeCarousel.m; sourceTree = "<group>"; };
C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXWindowShortcuts.h; sourceTree = "<group>"; };
C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXWindowShortcuts.m; sourceTree = "<group>"; };
C3F31D342267D883003C991A /* FLEXSubtitleTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSubtitleTableViewCell.h; sourceTree = "<group>"; };
C3F31D352267D883003C991A /* FLEXTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableViewCell.h; sourceTree = "<group>"; };
C3F31D362267D883003C991A /* FLEXMultilineTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMultilineTableViewCell.h; sourceTree = "<group>"; };
@@ -974,8 +958,6 @@
C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */,
3A4C94A91B5B21410088C3F2 /* FLEXLiveObjectsController.h */,
3A4C94AA1B5B21410088C3F2 /* FLEXLiveObjectsController.m */,
C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */,
C3EBE110287028D600702098 /* FLEXAPNSViewController.m */,
679F64841BD53B7B00A8C94C /* FLEXCookiesViewController.h */,
679F64851BD53B7B00A8C94C /* FLEXCookiesViewController.m */,
3A4C94AB1B5B21410088C3F2 /* FLEXWebViewController.h */,
@@ -1180,8 +1162,6 @@
3A4C94A01B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m */,
3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserController.h */,
3A4C94A21B5B21410088C3F2 /* FLEXFileBrowserController.m */,
C34063B328400A26007B46D3 /* FLEXActivityViewController.h */,
C34063B428400A26007B46D3 /* FLEXActivityViewController.m */,
);
path = FileBrowser;
sourceTree = "<group>";
@@ -1318,8 +1298,6 @@
C31E4E53259D4A4100712288 /* Private */,
C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */,
C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */,
C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */,
C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */,
C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */,
C398627123AD7951007E6793 /* UIGestureRecognizer+Blocks.m */,
C3F646BF239EAA8F00D4A011 /* UIPasteboard+FLEX.h */,
@@ -1451,8 +1429,6 @@
C3F527C02318670F009CBA07 /* FLEXImageShortcuts.m */,
C3F527C3231891F6009CBA07 /* FLEXViewShortcuts.h */,
C3F527C4231891F6009CBA07 /* FLEXViewShortcuts.m */,
C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */,
C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */,
C3F646F023A045DB00D4A011 /* FLEXClassShortcuts.h */,
C3F646F123A045DB00D4A011 /* FLEXClassShortcuts.m */,
C34D4EAE23A2ABD900C1F903 /* FLEXLayerShortcuts.h */,
@@ -1613,7 +1589,6 @@
C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */,
C34EE30821CB23CC00BD3A7C /* FLEXOSLogController.h in Headers */,
C38568FA272B3BFC00B1E37F /* FLEXSwiftInternal.h in Headers */,
C3EBE111287028D600702098 /* FLEXAPNSViewController.h in Headers */,
3A4C94FF1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.h in Headers */,
C398625423AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.h in Headers */,
3A4C94E71B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h in Headers */,
@@ -1631,7 +1606,6 @@
C3F31D432267D883003C991A /* FLEXTableView.h in Headers */,
3A4C95071B5B21410088C3F2 /* FLEXDefaultEditorViewController.h in Headers */,
C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */,
C34063B528400A26007B46D3 /* FLEXActivityViewController.h in Headers */,
C3694DBA23EA1096006625D7 /* FLEXTabsViewController.h in Headers */,
C31C4A6923342A2200C35F12 /* FLEXMetadataSection.h in Headers */,
C3F977832311B38F0032776D /* NSString+ObjcRuntime.h in Headers */,
@@ -1680,7 +1654,6 @@
C397E318240EC98F0091E4EC /* FLEXSQLResult.h in Headers */,
7349FD6A22B93CDF00051810 /* FLEXColor.h in Headers */,
C36FBFD3230F3B98008D95D5 /* FLEXMethod.h in Headers */,
C3EF290528EE8BC500B337B6 /* FLEXWindowShortcuts.h in Headers */,
C36FBFD8230F3B98008D95D5 /* FLEXPropertyAttributes.h in Headers */,
3A4C94FB1B5B21410088C3F2 /* FLEXArgumentInputStringView.h in Headers */,
C38EF26323A2FCD20047A7EC /* FLEXViewControllerShortcuts.h in Headers */,
@@ -1705,7 +1678,6 @@
94A515181C4CA1D70063292F /* FLEXManager+Private.h in Headers */,
C383C3B923B6A62A007A321B /* FLEXRuntimeSafety.h in Headers */,
C39ED92822D63F3200B5773A /* FLEXAddressExplorerCoordinator.h in Headers */,
C3A04B64288E007100F2C16D /* NSDateFormatter+FLEX.h in Headers */,
C313854923F5F7D50046E63C /* flex_fishhook.h in Headers */,
C3A9424023C5443A006871A3 /* FHSSnapshotNodes.h in Headers */,
C32A195E231732E800EB02AC /* FLEXCollectionContentSection.h in Headers */,
@@ -1761,7 +1733,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = FLEX;
LastUpgradeCheck = 1400;
LastUpgradeCheck = 1240;
ORGANIZATIONNAME = Flipboard;
TargetAttributes = {
1C27A8B51F0E5A0300F0D02D = {
@@ -1901,7 +1873,6 @@
3A4C94E41B5B21410088C3F2 /* FLEXRuntimeUtility.m in Sources */,
3A4C94D41B5B21410088C3F2 /* FLEXObjectExplorerFactory.m in Sources */,
94A515171C4CA1D70063292F /* FLEXManager.m in Sources */,
C34063B628400A26007B46D3 /* FLEXActivityViewController.m in Sources */,
C398626623AD70F5007E6793 /* FLEXKBToolbarButton.m in Sources */,
3A4C952F1B5B21410088C3F2 /* FLEXWebViewController.m in Sources */,
3A4C95041B5B21410088C3F2 /* FLEXArgumentInputView.m in Sources */,
@@ -1922,15 +1893,12 @@
3A4C95311B5B21410088C3F2 /* FLEXSystemLogMessage.m in Sources */,
C3F31D422267D883003C991A /* FLEXTableViewCell.m in Sources */,
3A4C94F41B5B21410088C3F2 /* FLEXArgumentInputFontView.m in Sources */,
C3EBE112287028D600702098 /* FLEXAPNSViewController.m in Sources */,
C3694DBB23EA1096006625D7 /* FLEXTabsViewController.m in Sources */,
C3A9424623C641C1006871A3 /* SceneKit+Snapshot.m in Sources */,
C3A04B65288E007100F2C16D /* NSDateFormatter+FLEX.m in Sources */,
3A4C953B1B5B21410088C3F2 /* FLEXNetworkSettingsController.m in Sources */,
3A4C94FC1B5B21410088C3F2 /* FLEXArgumentInputStringView.m in Sources */,
3A4C94F81B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.m in Sources */,
3A4C95351B5B21410088C3F2 /* FLEXSystemLogViewController.m in Sources */,
C3EF290628EE8BC500B337B6 /* FLEXWindowShortcuts.m in Sources */,
C3DBFD0C26CE2FAF00E0466A /* OSCache.m in Sources */,
C383C3C623B6BB81007A321B /* FLEXCodeFontCell.m in Sources */,
3A4C95271B5B21410088C3F2 /* FLEXGlobalsViewController.m in Sources */,
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
+1 -9
View File
@@ -7,17 +7,9 @@ enum FLEXBuildOptions {
static let silenceWarnings = false
}
#if swift(>=5.9)
let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v12)]
#elseif swift(>=5.7)
let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v11)]
#else
let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v10)]
#endif
let package = Package(
name: "FLEX",
platforms: platforms,
platforms: [.iOS(.v10)],
products: [
.library(name: "FLEX", targets: ["FLEX"])
],
+1 -1
View File
@@ -221,7 +221,7 @@ pod 'FLEX', :configurations => ['Debug']
### Swift Package Manager
In Xcode, navigate to `Build Settings > Build Options > Excluded Source File Names`. For your `Release` configuration, set it to `FLEX*` like this to exclude all files with the `FLEX` prefix:
In Xcode, navigate to `Build Settings > Build Options > Excluded Source File Names`. For your `Release` configuration, set it to `FLEX.o` like this to exclude all files with the `FLEX` prefix:
<img width=75% height=75% src=https://user-images.githubusercontent.com/1234765/98673373-8545c080-2357-11eb-9587-0743998e23ba.png>
-36
View File
@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>output_path</key>
<string>build</string>
<key>framework</key>
<string>FLEX</string>
<key>targets</key>
<array>
<dict>
<key>sdk</key>
<string>iOS</string>
<key>project</key>
<string>FLEX.xcodeproj</string>
<key>scheme</key>
<string>FLEX</string>
</dict>
<dict>
<key>sdk</key>
<string>iOSSimulator</string>
<key>project</key>
<string>FLEX.xcodeproj</string>
<key>scheme</key>
<string>FLEX</string>
</dict>
<!--
Remove this comment and add more targets for Simulators and the Devices.
-->
</array>
<key>finalActions</key>
<array>
<string>openDirectory</string>
</array>
</dict>
</plist>
-5
View File
@@ -33,10 +33,6 @@ generate_headers() {
# "Classes/ObjectExplorers/**/*.h", "Classes/Editing/**/*.h",
# "Classes/Utility/FLEXMacros.h", "Classes/Utility/Categories/*.h",
# "Classes/Utility/FLEXAlert.h", "Classes/Utility/FLEXResources.h"
# "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h",
# "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h",
# "Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h",
# "Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h"
rm -rf Classes/Headers
mkdir -p Classes/Headers
@@ -62,7 +58,6 @@ makeheader "Classes/Utility/FLEXResources.h"
makeheader "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h"
makeheader "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h"
makeheader "Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h"
makeheader "Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h"
# Print all folders in Classes for use in Package.swift
for folder in `find "Classes" -type d`; do