Compare commits

...

25 Commits

Author SHA1 Message Date
Tanner Bennett 67097982ea Bump version 2021-08-19 00:58:13 -05:00
matrush 1342d3029c Add availability check for iOS 11+ only SFSafariViewController properities 2021-08-09 09:37:23 -07:00
Hossam Ghareeb 62dcef4644 Add ability to delete SQLite rows 2021-07-25 18:15:26 -05:00
Tanner Bennett 7ee296143e Add button to clear SQLite table (@hossamghareeb) 2021-07-25 17:31:29 -05:00
Tanner Bennett c6bac54597 Fix #544; utilize smartQuotesType = .no 2021-07-25 17:03:05 -05:00
Tanner Bennett 5f7bce64ed Create FUNDING.yml 2021-07-25 16:52:59 -05:00
Tanner Bennett d2dde55bb1 Lay groundwork for multiple shortcut sections 2021-05-25 17:47:29 -05:00
Tanner Bennett f1a0a5c5e5 Add shortcuts for collections 2021-05-25 17:46:50 -05:00
Tanner Bennett 0db073459e iOS 13 is now the minimum required SDK
Remove FLEX_AT_LEAST_IOS13_SDK and usages. It was causing several issues being in a header that needed to be private but also included in public headers.
2021-05-25 15:30:46 -05:00
Tanner Bennett 7c7ed9286f Fix code typos 2021-05-25 15:10:45 -05:00
Tanner Bennett aac88dd6c8 Fix presentTool:
presentTool
2021-05-07 14:01:57 -05:00
Tanner Bennett d7376b75cd Add flex_observers shortcut to NSNotificationCenter 2021-05-07 13:58:04 -05:00
Tanner Bennett 5b39b3ed03 Add UIPasteboard shortcuts 2021-05-07 13:58:04 -05:00
Tanner Bennett bbaa85bdbf Add WKWebView/SFVC shortcuts 2021-05-07 12:03:06 -05:00
Tanner Bennett 34e27bc5d9 Stable sort and sort integers numerically 2021-05-01 01:04:12 -05:00
Tanner Bennett 714307273e Show full text for DB browser column headers 2021-05-01 01:04:12 -05:00
TekYin 5242d3c5a1 Fix crash when sharing single file on iPad 2021-04-30 23:28:32 -05:00
ray cf2e94a1d2 Fix crash when viewing keychain item 2021-04-30 23:17:01 -05:00
canius 800acb4cad Fix private header warnings and install path (#514)
Co-authored-by: Canius Chu <canius.chu@farfetch.com>
2021-04-30 22:37:39 -05:00
Saafo 368ce64121 Fix iPad/multi-scene FLEX bar layout bugs (#534)
Prior to this, FLEX could appear in the wrong scene or with the wrong width if the scene is not the full width of the screen, which is almost always the case in multi scene environments.

Thanks @Saafo!
2021-04-25 14:54:23 -05:00
Bang Lee 05f03090a9 Display access group for keychain items (#532) 2021-04-12 01:44:29 -05:00
Tanner Bennett a8803781e8 Add missing files to xcodeproj; oops, thanks @BB9z 2021-04-08 16:41:54 -05:00
Tanner Bennett 170f74b297 Don't codesign FLEX.framework 2021-04-08 16:39:39 -05:00
Tanner Bennett 0d0f2a3073 Add FLEXAlert nullabilities for Swift 2021-04-08 16:39:39 -05:00
Michael Gray d6bddf5199 Quote table names in SQL commands (#529)
Sqlite is often smart enough to not need quotes for identifiers, unless you want to use a keyword as a name. Then you need to quote it

https://www.sqlite.org/lang_keywords.html
2021-04-05 15:02:05 -05:00
51 changed files with 512 additions and 211 deletions
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [NSExceptional]
@@ -9,6 +9,7 @@
#import "FLEXFilteringTableViewController.h"
#import "FLEXTableViewSection.h"
#import "NSArray+FLEX.h"
#import "FLEXMacros.h"
@interface FLEXFilteringTableViewController ()
@@ -187,8 +188,6 @@
[self.filterDelegate.sections[indexPath.section] didPressInfoButtonAction:indexPath.row](self);
}
#if FLEX_AT_LEAST_IOS13_SDK
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
FLEXTableViewSection *section = self.filterDelegate.sections[indexPath.section];
NSString *title = [section menuTitleForRow:indexPath.row];
@@ -207,6 +206,4 @@
return nil;
}
#endif
@end
@@ -50,15 +50,12 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
#pragma mark - Initialization
- (id)init {
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13.0, *)) {
self = [self initWithStyle:UITableViewStyleInsetGrouped];
} else {
self = [self initWithStyle:UITableViewStyleGrouped];
}
#else
self = [self initWithStyle:UITableViewStyleGrouped];
#endif
return self;
}
@@ -106,11 +103,9 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
self.automaticallyShowsSearchBarCancelButton = YES;
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13, *)) {
self.searchController.automaticallyShowsScopeBar = NO;
}
#endif
[self addSearchController:self.searchController];
} else {
@@ -170,21 +165,17 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
}
- (BOOL)automaticallyShowsSearchBarCancelButton {
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13, *)) {
return self.searchController.automaticallyShowsCancelButton;
}
#endif
return _automaticallyShowsSearchBarCancelButton;
}
- (void)setAutomaticallyShowsSearchBarCancelButton:(BOOL)value {
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13, *)) {
self.searchController.automaticallyShowsCancelButton = value;
}
#endif
_automaticallyShowsSearchBarCancelButton = value;
}
-2
View File
@@ -100,7 +100,6 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable void(^)(__kindof UIViewController *host))didPressInfoButtonAction:(NSInteger)row;
#pragma mark - Context Menus
#if FLEX_AT_LEAST_IOS13_SDK
/// By default, this is the title of the row.
/// @return The title of the context menu, if any.
@@ -120,7 +119,6 @@ NS_ASSUME_NONNULL_BEGIN
/// should be a description of what will be copied, and the values should be
/// the strings to copy. Return an empty string as a value to show a disabled action.
- (nullable NSArray<NSString *> *)copyMenuItemsForRow:(NSInteger)row API_AVAILABLE(ios(13.0));
#endif
#pragma mark - Cell Configuration
-4
View File
@@ -64,8 +64,6 @@
return kFLEXDefaultCell;
}
#if FLEX_AT_LEAST_IOS13_SDK
- (NSString *)menuTitleForRow:(NSInteger)row {
NSString *title = [self titleForRow:row];
NSString *subtitle = [self menuSubtitleForRow:row];
@@ -127,8 +125,6 @@
return @[];
}
#endif
- (NSArray<NSString *> *)copyMenuItemsForRow:(NSInteger)row {
return nil;
}
-8
View File
@@ -30,29 +30,21 @@ FLEXTableViewCellReuseIdentifier const kFLEXCodeFontCell = @"kFLEXCodeFontCell";
@implementation FLEXTableView
+ (instancetype)flexDefaultTableView {
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13.0, *)) {
return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
} else {
return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
}
#else
return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
#endif
}
#pragma mark - Initialization
+ (id)groupedTableView {
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13.0, *)) {
return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
} else {
return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
}
#else
return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
#endif
}
+ (id)plainTableView {
@@ -33,6 +33,7 @@
self.inputTextView.delegate = self;
self.inputTextView.inputAccessoryView = [self createToolBar];
if (@available(iOS 11, *)) {
self.inputTextView.smartQuotesType = UITextSmartQuotesTypeNo;
[self.inputTextView.layer setValue:@YES forKey:@"continuousCorners"];
} else {
self.inputTextView.layer.borderWidth = 1.f;
@@ -21,13 +21,22 @@
- (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates;
/// @brief Used to present (or dismiss) a modal view controller ("tool"), typically triggered by pressing a button in the toolbar.
/// @brief Used to present (or dismiss) a modal view controller ("tool"),
/// typically triggered by pressing a button in the toolbar.
///
/// If a tool is already presented, this method simply dismisses it and calls the completion block.
/// If no tool is presented, @code future() @endcode is presented and the completion block is called.
- (void)toggleToolWithViewControllerProvider:(UINavigationController *(^)(void))future
completion:(void (^)(void))completion;
/// @brief Used to present (or dismiss) a modal view controller ("tool"),
/// typically triggered by pressing a button in the toolbar.
///
/// If a tool is already presented, this method dismisses it and presents the given tool.
/// The completion block is called once the tool has been presented.
- (void)presentTool:(UINavigationController *(^)(void))future
completion:(void (^)(void))completion;
// Keyboard shortcut helpers
- (void)toggleSelectTool;
@@ -925,12 +925,26 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
- (void)toggleToolWithViewControllerProvider:(UINavigationController *(^)(void))future
completion:(void (^)(void))completion {
if (self.presentedViewController) {
// We do NOT want to present the future; this is
// a convenience method for toggling the SAME TOOL
[self dismissViewControllerAnimated:YES completion:completion];
} else if (future) {
[self presentViewController:future() animated:YES completion:completion];
}
}
- (void)presentTool:(UINavigationController *(^)(void))future
completion:(void (^)(void))completion {
if (self.presentedViewController) {
// If a tool is already presented, dismiss it first
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:future() animated:YES completion:completion];
}];
} else if (future) {
[self presentViewController:future() animated:YES completion:completion];
}
}
- (FLEXWindow *)window {
return (id)self.view.window;
}
@@ -8,12 +8,21 @@
#import <UIKit/UIKit.h>
@class FLEXDBQueryRowCell;
extern NSString * const kFLEXDBQueryRowCellReuse;
@protocol FLEXDBQueryRowCellLayoutSource <NSObject>
- (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell minXForColumn:(NSUInteger)column;
- (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell widthForColumn:(NSUInteger)column;
@end
@interface FLEXDBQueryRowCell : UITableViewCell
/// An array of NSString, NSNumber, or NSData objects
@property (nonatomic) NSArray *data;
@property (nonatomic, weak) id<FLEXDBQueryRowCellLayoutSource> layoutSource;
@end
@@ -63,11 +63,12 @@ NSString * const kFLEXDBQueryRowCellReuse = @"kFLEXDBQueryRowCellReuse";
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat width = self.contentView.frame.size.width / self.labels.count;
CGFloat height = self.contentView.frame.size.height;
[self.labels flex_forEach:^(UILabel *label, NSUInteger i) {
label.frame = CGRectMake(width * i + 5, 0, (width - 10), height);
CGFloat width = [self.layoutSource dbQueryRowCell:self widthForColumn:i];
CGFloat minX = [self.layoutSource dbQueryRowCell:self minXForColumn:i];
label.frame = CGRectMake(minX + 5, 0, (width - 10), height);
}];
}
@@ -29,6 +29,7 @@
@optional
- (NSArray<NSString *> *)queryRowIDsInTable:(NSString *)tableName;
- (FLEXSQLResult *)executeStatement:(NSString *)SQLStatement;
@end
@@ -29,7 +29,7 @@
- (NSString *)rowTitle:(NSInteger)row;
- (NSArray<NSString *> *)contentForRow:(NSInteger)row;
- (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView widthForContentCellInColumn:(NSInteger)column;
- (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView minWidthForContentCellInColumn:(NSInteger)column;
- (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView heightForContentCellInRow:(NSInteger)row;
- (CGFloat)heightForTopHeaderInTableView:(FLEXMultiColumnTableView *)tableView;
- (CGFloat)widthForLeftHeaderInTableView:(FLEXMultiColumnTableView *)tableView;
@@ -9,10 +9,12 @@
#import "FLEXMultiColumnTableView.h"
#import "FLEXDBQueryRowCell.h"
#import "FLEXTableLeftCell.h"
#import "NSArray+FLEX.h"
#import "FLEXColor.h"
@interface FLEXMultiColumnTableView () <
UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate
UITableViewDataSource, UITableViewDelegate,
UIScrollViewDelegate, FLEXDBQueryRowCellLayoutSource
>
@property (nonatomic) UIScrollView *contentScrollView;
@@ -21,12 +23,12 @@
@property (nonatomic) UITableView *contentTableView;
@property (nonatomic) UIView *leftHeader;
@property (nonatomic) NSArray<UIView *> *headerViews;
/// \c NSNotFound if no column selected
@property (nonatomic) NSInteger sortColumn;
@property (nonatomic) FLEXTableColumnHeaderSortType sortType;
@property (nonatomic) NSArray *rowData;
@property (nonatomic, readonly) NSInteger numberOfColumns;
@property (nonatomic, readonly) NSInteger numberOfRows;
@property (nonatomic, readonly) CGFloat topHeaderHeight;
@@ -71,9 +73,9 @@ static const CGFloat kColumnMargin = 1;
}
CGFloat contentWidth = 0.0;
NSInteger rowsCount = self.numberOfColumns;
for (int i = 0; i < rowsCount; i++) {
contentWidth += [self contentWidthForColumn:i];
NSInteger columnsCount = self.numberOfColumns;
for (int i = 0; i < columnsCount; i++) {
contentWidth += CGRectGetWidth(self.headerViews[i].bounds);
}
CGFloat contentHeight = height - topheaderHeight - topInsets;
@@ -147,26 +149,30 @@ static const CGFloat kColumnMargin = 1;
#pragma mark - Data
- (void)reloadData {
[self loadHeaderData];
[self loadLeftViewData];
[self loadContentData];
[self loadHeaderData];
}
- (void)loadHeaderData {
// Remove existing headers, if any
for (UIView *subview in self.headerScrollView.subviews) {
for (UIView *subview in self.headerViews) {
[subview removeFromSuperview];
}
CGFloat xOffset = 0.0;
for (NSInteger column = 0; column < self.numberOfColumns; column++) {
CGFloat width = [self contentWidthForColumn:column] + self.columnMargin;
FLEXTableColumnHeader *header = [[FLEXTableColumnHeader alloc]
initWithFrame:CGRectMake(xOffset, 0, width, self.topHeaderHeight - 1)
];
__block CGFloat xOffset = 0;
self.headerViews = [NSArray flex_forEachUpTo:self.numberOfColumns map:^id(NSUInteger column) {
FLEXTableColumnHeader *header = [FLEXTableColumnHeader new];
header.titleLabel.text = [self columnTitle:column];
CGSize fittingSize = CGSizeMake(CGFLOAT_MAX, self.topHeaderHeight - 1);
CGFloat width = self.columnMargin + MAX(
[self minContentWidthForColumn:column],
[header sizeThatFits:fittingSize].width
);
header.frame = CGRectMake(xOffset, 0, width, self.topHeaderHeight - 1);
if (column == self.sortColumn) {
header.sortType = self.sortType;
}
@@ -178,21 +184,22 @@ static const CGFloat kColumnMargin = 1;
[header addGestureRecognizer:gesture];
header.userInteractionEnabled = YES;
[self.headerScrollView addSubview:header];
xOffset += width;
}
[self.headerScrollView addSubview:header];
return header;
}];
}
- (void)contentHeaderTap:(UIGestureRecognizer *)gesture {
NSInteger newSortColumn = [self.headerScrollView.subviews indexOfObject:gesture.view];
NSInteger newSortColumn = [self.headerViews indexOfObject:gesture.view];
FLEXTableColumnHeaderSortType newType = FLEXNextTableColumnHeaderSortType(self.sortType);
// Reset old header
FLEXTableColumnHeader *oldHeader = (id)self.headerScrollView.subviews[self.sortColumn];
FLEXTableColumnHeader *oldHeader = (id)self.headerViews[self.sortColumn];
oldHeader.sortType = FLEXTableColumnHeaderSortTypeNone;
// Update new header
FLEXTableColumnHeader *newHeader = (id)self.headerScrollView.subviews[newSortColumn];
FLEXTableColumnHeader *newHeader = (id)self.headerViews[newSortColumn];
newHeader.sortType = newType;
// Update self
@@ -227,13 +234,13 @@ static const CGFloat kColumnMargin = 1;
}
// Right side table view for data
else {
self.rowData = [self.dataSource contentForRow:indexPath.row];
FLEXDBQueryRowCell *cell = [tableView
dequeueReusableCellWithIdentifier:kFLEXDBQueryRowCellReuse forIndexPath:indexPath
];
cell.contentView.backgroundColor = backgroundColor;
cell.data = [self.dataSource contentForRow:indexPath.row];
cell.layoutSource = self;
NSAssert(cell.data.count == self.numberOfColumns, @"Count of data provided was incorrect");
return cell;
}
@@ -280,6 +287,17 @@ static const CGFloat kColumnMargin = 1;
}
#pragma mark FLEXDBQueryRowCellLayoutSource
- (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell minXForColumn:(NSUInteger)column {
return CGRectGetMinX(self.headerViews[column].frame);
}
- (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell widthForColumn:(NSUInteger)column {
return CGRectGetWidth(self.headerViews[column].bounds);
}
#pragma mark DataSource Accessor
- (NSInteger)numberOfRows {
@@ -298,8 +316,8 @@ static const CGFloat kColumnMargin = 1;
return [self.dataSource rowTitle:row];
}
- (CGFloat)contentWidthForColumn:(NSInteger)column {
return [self.dataSource multiColumnTableView:self widthForContentCellInColumn:column];
- (CGFloat)minContentWidthForColumn:(NSInteger)column {
return [self.dataSource multiColumnTableView:self minWidthForContentCellInColumn:column];
}
- (CGFloat)contentHeightForRow:(NSInteger)row {
@@ -30,7 +30,7 @@ static NSString * const QUERY_TABLENAMES = @"SELECT name FROM sqlite_master WHER
- (instancetype)initWithPath:(NSString *)path {
self = [super init];
if (self) {
self.path = path;;
self.path = path;
}
return self;
@@ -114,9 +114,17 @@ static NSString * const QUERY_TABLENAMES = @"SELECT name FROM sqlite_master WHER
}
- (NSArray<NSArray *> *)queryAllDataInTable:(NSString *)tableName {
return [self executeStatement:[@"SELECT * FROM "
stringByAppendingString:tableName
]].rows ?: @[];
NSString *command = [NSString stringWithFormat:@"SELECT * FROM \"%@\"", tableName];
return [self executeStatement:command].rows ?: @[];
}
- (NSArray<NSString *> *)queryRowIDsInTable:(NSString *)tableName {
NSString *command = [NSString stringWithFormat:@"SELECT rowid FROM \"%@\"", tableName];
NSArray<NSArray<NSString *> *> *data = [self executeStatement:command].rows ?: @[];
return [data flex_mapped:^id(NSArray<NSString *> *obj, NSUInteger idx) {
return obj.firstObject;
}];
}
- (FLEXSQLResult *)executeStatement:(NSString *)sql {
@@ -257,7 +265,7 @@ static NSString * const QUERY_TABLENAMES = @"SELECT name FROM sqlite_master WHER
- (FLEXSQLResult *)errorResult:(NSString *)description {
const char *error = sqlite3_errmsg(_db);
NSString *message = error ? @(error) : [NSString
stringWithFormat:@"(%@: empty error", description
stringWithFormat:@"(%@: empty error)", description
];
return [FLEXSQLResult error:message];
@@ -11,6 +11,9 @@
#import "UIFont+FLEX.h"
#import "FLEXUtility.h"
static const CGFloat kMargin = 5;
static const CGFloat kArrowWidth = 20;
@interface FLEXTableColumnHeader ()
@property (nonatomic, readonly) UILabel *arrowLabel;
@property (nonatomic, readonly) UIView *lineView;
@@ -60,9 +63,16 @@
CGSize size = self.frame.size;
self.titleLabel.frame = CGRectMake(5, 0, size.width - 25, size.height);
self.arrowLabel.frame = CGRectMake(size.width - 20, 0, 20, size.height);
self.titleLabel.frame = CGRectMake(kMargin, 0, size.width - kArrowWidth - kMargin, size.height);
self.arrowLabel.frame = CGRectMake(size.width - kArrowWidth, 0, kArrowWidth, size.height);
self.lineView.frame = CGRectMake(size.width - 1, 2, FLEXPointsToPixels(1), size.height - 4);
}
- (CGSize)sizeThatFits:(CGSize)size {
CGFloat margins = kArrowWidth - 2 * kMargin;
size = CGSizeMake(size.width - margins, size.height);
CGFloat width = [_titleLabel sizeThatFits:size].width + margins;
return CGSizeMake(width, size.height);
}
@end
@@ -7,10 +7,20 @@
//
#import <UIKit/UIKit.h>
#import "FLEXDatabaseManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLEXTableContentViewController : UIViewController
/// Display a table with the given columns, rows, and name.
/// @param databaseManager an optional manager to allow modifying the table.
+ (instancetype)columns:(NSArray<NSString *> *)columnNames
rows:(NSArray<NSArray<NSString *> *> *)rowData;
rows:(NSArray<NSArray<NSString *> *> *)rowData
rowIDs:(nullable NSArray<NSString *> *)rowIds
tableName:(NSString *)tableName
database:(nullable id<FLEXDatabaseManager>)databaseManager;
@end
NS_ASSUME_NONNULL_END
@@ -10,12 +10,16 @@
#import "FLEXMultiColumnTableView.h"
#import "FLEXWebViewController.h"
#import "FLEXUtility.h"
#import "UIBarButtonItem+FLEX.h"
@interface FLEXTableContentViewController () <
FLEXMultiColumnTableViewDataSource, FLEXMultiColumnTableViewDelegate
>
@property (nonatomic, readonly) NSArray<NSString *> *columns;
@property (nonatomic, copy) NSArray<NSArray *> *rows;
@property (nonatomic) NSMutableArray<NSArray *> *rows;
@property (nonatomic, readonly) NSString *tableName;
@property (nonatomic, nullable) NSMutableArray<NSString *> *rowIDs;
@property (nonatomic, readonly, nullable) id<FLEXDatabaseManager> databaseManager;
@property (nonatomic) FLEXMultiColumnTableView *multiColumnView;
@end
@@ -23,10 +27,16 @@
@implementation FLEXTableContentViewController
+ (instancetype)columns:(NSArray<NSString *> *)columnNames
rows:(NSArray<NSArray<NSString *> *> *)rowData {
rows:(NSArray<NSArray<NSString *> *> *)rowData
rowIDs:(nullable NSArray<NSString *> *)rowIDs
tableName:(NSString *)tableName
database:(nullable id<FLEXDatabaseManager>)databaseManager {
FLEXTableContentViewController *controller = [self new];
controller->_columns = columnNames;
controller->_rows = rowData;
controller->_columns = columnNames.copy;
controller->_rows = rowData.mutableCopy;
controller->_rowIDs = rowIDs.mutableCopy;
controller->_tableName = tableName.copy;
controller->_databaseManager = databaseManager;
return controller;
}
@@ -38,9 +48,10 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.title = self.tableName;
self.edgesForExtendedLayout = UIRectEdgeNone;
[self.multiColumnView reloadData];
[self setupToolbarItems];
}
- (FLEXMultiColumnTableView *)multiColumnView {
@@ -84,8 +95,8 @@
}
- (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView
widthForContentCellInColumn:(NSInteger)column {
return 120;
minWidthForContentCellInColumn:(NSInteger)column {
return 100;
}
- (CGFloat)heightForTopHeaderInTableView:(FLEXMultiColumnTableView *)tableView {
@@ -118,6 +129,27 @@
make.button(@"Copy").handler(^(NSArray<NSString *> *strings) {
UIPasteboard.generalPasteboard.string = message;
});
// Option to delete row
BOOL hasRowID = self.rows.count && row < self.rows.count;
if (hasRowID) {
make.button(@"Delete").destructiveStyle().handler(^(NSArray<NSString *> *strings) {
NSString *deleteRow = [NSString stringWithFormat:
@"DELETE FROM %@ WHERE rowid = %@",
self.tableName, self.rowIDs[row]
];
[self executeStatementAndShowResult:deleteRow completion:^(BOOL success) {
// Remove deleted row and reload view
if (success) {
[self.rowIDs removeObjectAtIndex:row];
[self.rows removeObjectAtIndex:row];
[self.multiColumnView reloadData];
}
}];
});
}
make.button(@"Dismiss").cancelStyle();
} showFrom:self];
}
@@ -127,7 +159,8 @@
sortType:(FLEXTableColumnHeaderSortType)sortType {
NSArray<NSArray *> *sortContentData = [self.rows
sortedArrayUsingComparator:^NSComparisonResult(NSArray *obj1, NSArray *obj2) {
sortedArrayWithOptions:NSSortStable
usingComparator:^NSComparisonResult(NSArray *obj1, NSArray *obj2) {
id a = obj1[column], b = obj2[column];
if (a == NSNull.null) {
return NSOrderedAscending;
@@ -135,6 +168,11 @@
if (b == NSNull.null) {
return NSOrderedDescending;
}
if ([a respondsToSelector:@selector(compare:options:)] &&
[b respondsToSelector:@selector(compare:options:)]) {
return [a compare:b options:NSNumericSearch];
}
if ([a respondsToSelector:@selector(compare:)] && [b respondsToSelector:@selector(compare:)]) {
return [a compare:b];
@@ -148,12 +186,11 @@
sortContentData = sortContentData.reverseObjectEnumerator.allObjects.copy;
}
self.rows = sortContentData;
self.rows = sortContentData.mutableCopy;
[self.multiColumnView reloadData];
}
#pragma mark -
#pragma mark About Transition
#pragma mark - About Transition
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection
withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator {
@@ -171,4 +208,57 @@
} completion:nil];
}
#pragma mark - Toolbar
- (void)setupToolbarItems {
// We do not support modifying realm databases
if (![self.databaseManager respondsToSelector:@selector(executeStatement:)]) {
return;
}
UIBarButtonItem *trashButton = [FLEXBarButtonItemSystem(Trash, self, @selector(trashPressed))
flex_withTintColor:UIColor.redColor
];
trashButton.enabled = self.databaseManager && self.rows.count;
self.toolbarItems = @[UIBarButtonItem.flex_flexibleSpace, trashButton];
}
- (void)trashPressed {
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Delete All Rows");
make.message(@"All rows in this table will be permanently deleted.\nDo you want to proceed?");
make.button(@"Yes, I'm sure").destructiveStyle().handler(^(NSArray<NSString *> *strings) {
NSString *deleteAll = [NSString stringWithFormat:@"DELETE FROM %@", self.tableName];
[self executeStatementAndShowResult:deleteAll completion:^(BOOL success) {
// Only dismiss on success
if (success) {
[self.navigationController popViewControllerAnimated:YES];
}
}];
});
make.button(@"Cancel").cancelStyle();
} showFrom:self];
}
#pragma mark - Helpers
- (void)executeStatementAndShowResult:(NSString *)statement completion:(void (^_Nullable)(BOOL success))completion {
FLEXSQLResult *result = [self.databaseManager executeStatement:statement];
[FLEXAlert makeAlert:^(FLEXAlert *make) {
if (result.isError) {
make.title(@"Error");
}
make.message(result.message ?: @"<no output>");
make.button(@"Dismiss").cancelStyle().handler(^(NSArray<NSString *> *_) {
if (completion) {
completion(!result.isError);
}
});
} showFrom:self];
}
@end
@@ -14,6 +14,7 @@
#import "FLEXMutableListSection.h"
#import "NSArray+FLEX.h"
#import "FLEXAlert.h"
#import "FLEXMacros.h"
@interface FLEXTableListViewController ()
@property (nonatomic, readonly) id<FLEXDatabaseManager> dbm;
@@ -70,9 +71,10 @@
self.tables.selectionHandler = ^(FLEXTableListViewController *host, NSString *tableName) {
NSArray *rows = [host.dbm queryAllDataInTable:tableName];
NSArray *columns = [host.dbm queryAllColumnsOfTable:tableName];
UIViewController *resultsScreen = [FLEXTableContentViewController columns:columns rows:rows];
resultsScreen.title = tableName;
NSArray *rowIDs = [host.dbm queryRowIDsInTable:tableName];
UIViewController *resultsScreen = [FLEXTableContentViewController
columns:columns rows:rows rowIDs:rowIDs tableName:tableName database:host.dbm
];
[host.navigationController pushViewController:resultsScreen animated:YES];
};
@@ -100,7 +102,7 @@
[FLEXAlert showAlert:@"Message" message:result.message from:self];
} else {
UIViewController *resultsScreen = [FLEXTableContentViewController
columns:result.columns rows:result.rows
columns:result.columns rows:result.rows rowIDs:nil tableName:@"" database:nil
];
[self.navigationController pushViewController:resultsScreen animated:YES];
@@ -356,8 +356,6 @@ typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) {
// Since our actions are outside of that protocol, we need to manually handle the action forwarding from the cells.
}
#if FLEX_AT_LEAST_IOS13_SDK
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView
contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
point:(CGPoint)point __IOS_AVAILABLE(13.0) {
@@ -395,8 +393,6 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
];
}
#endif
- (void)openFileController:(NSString *)fullPath {
UIDocumentInteractionController *controller = [UIDocumentInteractionController new];
controller.URL = [NSURL fileURLWithPath:fullPath];
@@ -474,6 +470,9 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
} else {
// Share sheet for files
UIActivityViewController *shareSheet = [[UIActivityViewController alloc] initWithActivityItems:@[filePath] applicationActivities:nil];
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
shareSheet.popoverPresentationController.sourceView = sender;
}
[self presentViewController:shareSheet animated:true completion:nil];
}
}
@@ -34,6 +34,9 @@ extern NSString *const kFLEXKeychainClassKey;
/// Item description.
extern NSString *const kFLEXKeychainDescriptionKey;
/// Item group.
extern NSString *const kFLEXKeychainGroupKey;
/// Item label.
extern NSString *const kFLEXKeychainLabelKey;
@@ -15,6 +15,7 @@ NSString * const kFLEXKeychainAccountKey = @"acct";
NSString * const kFLEXKeychainCreatedAtKey = @"cdat";
NSString * const kFLEXKeychainClassKey = @"labl";
NSString * const kFLEXKeychainDescriptionKey = @"desc";
NSString * const kFLEXKeychainGroupKey = @"agrp";
NSString * const kFLEXKeychainLabelKey = @"labl";
NSString * const kFLEXKeychainLastModifiedKey = @"mdat";
NSString * const kFLEXKeychainWhereKey = @"svce";
@@ -99,8 +99,9 @@
NSDictionary *item = self.section.filteredList[idx];
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
query.service = item[kFLEXKeychainWhereKey];
query.account = item[kFLEXKeychainAccountKey];
query.service = [item[kFLEXKeychainWhereKey] description];
query.account = [item[kFLEXKeychainAccountKey] description];
query.accessGroup = [item[kFLEXKeychainGroupKey] description];
[query fetch:nil];
return query;
@@ -232,6 +233,7 @@
make.message(@"Service: ").message(query.service);
make.message(@"\nAccount: ").message(query.account);
make.message(@"\nPassword: ").message(query.password);
make.message(@"\nGroup: ").message(query.accessGroup);
make.button(@"Copy Service").handler(^(NSArray<NSString *> *strings) {
[UIPasteboard.generalPasteboard flex_copy:query.service];
@@ -84,7 +84,6 @@
switch (_appearance) {
default:
case UIKeyboardAppearanceDefault:
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13, *)) {
titleColor = UIColor.labelColor;
@@ -97,7 +96,6 @@
}
break;
}
#endif
case UIKeyboardAppearanceLight:
titleColor = UIColor.blackColor;
backgroundColor = lightColor;
@@ -61,6 +61,7 @@
searchBar.keyboardType = UIKeyboardTypeWebSearch;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
if (@available(iOS 11, *)) {
searchBar.smartQuotesType = UITextSmartQuotesTypeNo;
searchBar.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
}
@@ -106,7 +106,6 @@
switch (_appearance) {
case UIKeyboardAppearanceDefault:
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13, *)) {
borderColor = UIColor.systemBackgroundColor;
@@ -119,7 +118,6 @@
}
break;
}
#endif
case UIKeyboardAppearanceLight: {
borderColor = UIColor.clearColor;
backgroundColor = lightColor;
@@ -266,8 +266,6 @@ static BOOL my_os_log_shim_enabled(void *addr) {
}
}
#if FLEX_AT_LEAST_IOS13_SDK
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView
contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
point:(CGPoint)point __IOS_AVAILABLE(13.0) {
@@ -286,6 +284,4 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
];
}
#endif
@end
-4
View File
@@ -8,10 +8,6 @@
#import "FLEXExplorerToolbar.h"
#if !FLEX_AT_LEAST_IOS13_SDK
@class UIWindowScene;
#endif
NS_ASSUME_NONNULL_BEGIN
@interface FLEXManager : NSObject
+9 -8
View File
@@ -49,7 +49,7 @@
NSAssert(NSThread.isMainThread, @"You must use %@ from the main thread only.", NSStringFromClass([self class]));
if (!_explorerWindow) {
_explorerWindow = [[FLEXWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
_explorerWindow = [[FLEXWindow alloc] initWithFrame:FLEXUtility.appKeyWindow.bounds];
_explorerWindow.eventDelegate = self;
_explorerWindow.rootViewController = self.explorerViewController;
}
@@ -69,14 +69,12 @@
- (void)showExplorer {
UIWindow *flex = self.explorerWindow;
flex.hidden = NO;
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13.0, *)) {
// Only look for a new scene if we don't have one
if (!flex.windowScene) {
flex.windowScene = FLEXUtility.activeScene;
flex.windowScene = FLEXUtility.appKeyWindow.windowScene;
}
}
#endif
}
- (void)hideExplorer {
@@ -85,7 +83,11 @@
- (void)toggleExplorer {
if (self.explorerWindow.isHidden) {
[self showExplorer];
if (@available(iOS 13.0, *)) {
[self showExplorerFromScene:FLEXUtility.appKeyWindow.windowScene];
} else {
[self showExplorer];
}
} else {
[self hideExplorer];
}
@@ -100,15 +102,14 @@
}
- (void)presentTool:(UINavigationController * _Nonnull (^)(void))future completion:(void (^)(void))completion {
[self.explorerViewController toggleToolWithViewControllerProvider:future completion:completion];
[self showExplorer];
[self.explorerViewController presentTool:future completion:completion];
}
- (void)showExplorerFromScene:(UIWindowScene *)scene {
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13.0, *)) {
self.explorerWindow.windowScene = scene;
}
#endif
self.explorerWindow.hidden = NO;
}
@@ -390,8 +390,6 @@
}
}
#if FLEX_AT_LEAST_IOS13_SDK
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
NSURLRequest *request = [self transactionAtIndexPath:indexPath].request;
return [UIContextMenuConfiguration
@@ -427,8 +425,6 @@
];
}
#endif
- (FLEXNetworkTransaction *)transactionAtIndexPath:(NSIndexPath *)indexPath {
return self.searchController.isActive ? self.filteredNetworkTransactions[indexPath.row] : self.networkTransactions[indexPath.row];
}
@@ -211,8 +211,6 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
}
}
#if FLEX_AT_LEAST_IOS13_SDK
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
return [UIContextMenuConfiguration
configurationWithIdentifier:nil
@@ -236,8 +234,6 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
];
}
#endif
#pragma mark - View Configuration
+ (NSAttributedString *)attributedTextForRow:(FLEXNetworkDetailRow *)row {
@@ -76,19 +76,33 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
// shortcut section for NSObject.
//
// TODO: rename it to FLEXNSObjectShortcuts or something?
Class sectionClass = nil;
FLEXShortcutsSection *shortcutsSection = [FLEXShortcutsSection forObject:object];
NSArray *sections = @[shortcutsSection];
Class customSectionClass = nil;
Class cls = object_getClass(object);
do {
sectionClass = classesToRegisteredSections[(id<NSCopying>)cls];
} while (!sectionClass && (cls = [cls superclass]));
customSectionClass = classesToRegisteredSections[(id<NSCopying>)cls];
} while (!customSectionClass && (cls = [cls superclass]));
if (!sectionClass) {
sectionClass = [FLEXShortcutsSection class];
if (customSectionClass) {
id customSection = [customSectionClass forObject:object];
BOOL isFLEXShortcutSection = [customSection respondsToSelector:@selector(isNewSection)];
// If the section "replaces" the default shortcuts section,
// only return that section. Otherwise, return both this
// section and the default shortcuts section.
if (isFLEXShortcutSection && ![customSection isNewSection]) {
sections = @[customSection];
} else {
// Custom section will go before shortcuts
sections = @[customSection, shortcutsSection];
}
}
return [FLEXObjectExplorerViewController
exploringObject:object
customSection:[sectionClass forObject:object]
customSections:sections
];
}
@@ -29,6 +29,9 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)exploringObject:(id)objectOrClass;
/// No custom section unless you provide one.
+ (instancetype)exploringObject:(id)objectOrClass customSection:(nullable FLEXTableViewSection *)customSection;
/// No custom sections unless you provide some.
+ (instancetype)exploringObject:(id)objectOrClass
customSections:(nullable NSArray<FLEXTableViewSection *> *)customSections;
/// The object being explored, which may be an instance of a class or a class itself.
@property (nonatomic, readonly) id object;
@@ -30,7 +30,7 @@
#pragma mark - Private properties
@interface FLEXObjectExplorerViewController () <UIGestureRecognizerDelegate>
@property (nonatomic, readonly) FLEXSingleRowSection *descriptionSection;
@property (nonatomic, readonly) FLEXTableViewSection *customSection;
@property (nonatomic, readonly) NSArray<FLEXTableViewSection *> *customSections;
@property (nonatomic) NSIndexSet *customSectionVisibleIndexes;
@property (nonatomic, readonly) NSArray<NSString *> *observedNotifications;
@@ -46,23 +46,27 @@
}
+ (instancetype)exploringObject:(id)target customSection:(FLEXTableViewSection *)section {
return [self exploringObject:target customSections:@[section]];
}
+ (instancetype)exploringObject:(id)target customSections:(NSArray *)customSections {
return [[self alloc]
initWithObject:target
explorer:[FLEXObjectExplorer forObject:target]
customSection:section
customSections:customSections
];
}
- (id)initWithObject:(id)target
explorer:(__kindof FLEXObjectExplorer *)explorer
customSection:(FLEXTableViewSection *)customSection {
customSections:(NSArray<FLEXTableViewSection *> *)customSections {
NSParameterAssert(target);
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
_object = target;
_explorer = explorer;
_customSection = customSection;
_customSections = customSections;
}
return self;
@@ -195,8 +199,10 @@
referencesSection
]];
if (self.customSection) {
[sections insertObject:self.customSection atIndex:0];
if (self.customSections) {
[sections insertObjects:self.customSections atIndexes:[NSIndexSet
indexSetWithIndexesInRange:NSMakeRange(0, self.customSections.count)
]];
}
if (self.descriptionSection) {
[sections insertObject:self.descriptionSection atIndex:0];
@@ -189,8 +189,6 @@
cell.accessoryType = [self accessoryTypeForRow:row];
}
#if FLEX_AT_LEAST_IOS13_SDK
- (NSString *)menuSubtitleForRow:(NSInteger)row {
return [self.metadata[row] contextualSubtitleWithTarget:self.explorer.object];
}
@@ -232,6 +230,4 @@
return [self.metadata[row] copiableMetadataWithTarget:self.explorer.object];
}
#endif
@end
@@ -25,3 +25,7 @@
@interface FLEXShortcutsFactory (Blocks) @end
@interface FLEXShortcutsFactory (Foundation) @end
@interface FLEXShortcutsFactory (WebKit_Safari) @end
@interface FLEXShortcutsFactory (Pasteboard) @end
@@ -10,7 +10,9 @@
#import "FLEXShortcut.h"
#import "FLEXMacros.h"
#import "FLEXRuntimeUtility.h"
#import "NSArray+FLEX.h"
#import "NSObject+FLEX_Reflection.h"
#import "FLEXObjcInternal.h"
#import "Cocoa+FLEXShortcuts.h"
#pragma mark - UIApplication
@@ -282,14 +284,160 @@
]).forClass(NSTimeZone.flex_metaclass);
self.append.classProperties(@[
@"defaultTimeZone", @"systemTimeZone", @"localTimeZone"
@"defaultTimeZone", @"systemTimeZone", @"localTimeZone",
]).forClass(NSTimeZone.class);
// UTF8String is not a real property under the hood
FLEXRuntimeUtilityTryAddNonatomicProperty(2, UTF8String, NSString.class, const char *, PropertyKey(ReadOnly));
self.append.properties(@[@"length"]).methods(@[@"characterAtIndex:"]).forClass(NSString.class);
self.append.properties(@[@"length", @"bytes"]).forClass(NSData.class);
self.append.methods(@[
@"writeToFile:atomically:", @"subdataWithRange:", @"isEqualToData:",
]).properties(@[
@"length", @"bytes",
]).forClass(NSData.class);
self.append.classMethods(@[
@"dataWithJSONObject:options:error:",
@"JSONObjectWithData:options:error:",
@"isValidJSONObject:",
]).forClass(NSJSONSerialization.class);
// NSArray
self.append.classMethods(@[
@"arrayWithObject:", @"arrayWithContentsOfFile:"
]).forClass(NSArray.flex_metaclass);
self.append.methods(@[
@"valueForKeyPath:", @"subarrayWithRange:",
@"arrayByAddingObject:", @"arrayByAddingObjectsFromArray:",
@"filteredArrayUsingPredicate:", @"subarrayWithRange:",
@"containsObject:", @"objectAtIndex:", @"indexOfObject:",
@"makeObjectsPerformSelector:", @"makeObjectsPerformSelector:withObject:",
@"sortedArrayUsingSelector:", @"reverseObjectEnumerator",
@"isEqualToArray:", @"mutableCopy",
]).forClass(NSArray.class);
// NSDictionary
self.append.methods(@[
@"objectForKey:", @"valueForKeyPath:",
@"isEqualToDictionary:", @"mutableCopy",
]).forClass(NSDictionary.class);
// NSSet
self.append.classMethods(@[
@"setWithObject:", @"setWithArray:"
]).forClass(NSSet.flex_metaclass);
self.append.methods(@[
@"allObjects", @"valueForKeyPath:", @"containsObject:",
@"setByAddingObject:", @"setByAddingObjectsFromArray:",
@"filteredSetUsingPredicate:", @"isSubsetOfSet:",
@"makeObjectsPerformSelector:", @"makeObjectsPerformSelector:withObject:",
@"reverseObjectEnumerator", @"isEqualToSet:", @"mutableCopy",
]).forClass(NSSet.class);
// NSMutableArray
self.prepend.methods(@[
@"addObject:", @"insertObject:atIndex:", @"addObjectsFromArray:",
@"removeObject:", @"removeObjectAtIndex:",
@"removeObjectsInArray:", @"removeAllObjects",
@"removeLastObject", @"filterUsingPredicate:",
@"sortUsingSelector:", @"copy",
]).forClass(NSMutableArray.class);
// NSMutableDictionary
self.prepend.methods(@[
@"setObject:forKey:", @"removeObjectForKey:",
@"removeAllObjects", @"removeObjectsForKeys:", @"copy",
]).forClass(NSMutableDictionary.class);
// NSMutableSet
self.prepend.methods(@[
@"addObject:", @"removeObject:", @"filterUsingPredicate:",
@"removeAllObjects", @"addObjectsFromArray:",
@"unionSet:", @"minusSet:", @"intersectSet:", @"copy"
]).forClass(NSMutableSet.class);
self.append.methods(@[@"nextObject", @"allObjects"]).forClass(NSEnumerator.class);
self.append.properties(@[@"flex_observers"]).forClass(NSNotificationCenter.class);
}
@end
#pragma mark - WebKit / Safari
@implementation FLEXShortcutsFactory (WebKit_Safari)
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
Class WKWebView = NSClassFromString(@"WKWebView");
Class SafariVC = NSClassFromString(@"SFSafariViewController");
if (WKWebView) {
self.append.properties(@[
@"configuration", @"scrollView", @"title", @"URL",
@"customUserAgent", @"navigationDelegate"
]).methods(@[@"reload", @"stopLoading"]).forClass(WKWebView);
}
if (SafariVC) {
self.append.properties(@[
@"delegate"
]).forClass(SafariVC);
if (@available(iOS 10.0, *)) {
self.append.properties(@[
@"preferredBarTintColor", @"preferredControlTintColor"
]).forClass(SafariVC);
}
if (@available(iOS 11.0, *)) {
self.append.properties(@[
@"configuration", @"dismissButtonStyle"
]).forClass(SafariVC);
}
}
}
@end
#pragma mark - Pasteboard
@implementation FLEXShortcutsFactory (Pasteboard)
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
self.append.properties(@[
@"name", @"numberOfItems", @"items",
@"string", @"image", @"color", @"URL",
]).forClass(UIPasteboard.class);
}
@end
@interface NSNotificationCenter (Observers)
@property (readonly) NSArray<NSString *> *flex_observers;
@end
@implementation NSNotificationCenter (Observers)
- (id)flex_observers {
NSString *debug = self.debugDescription;
NSArray<NSString *> *observers = [debug componentsSeparatedByString:@"\n"];
NSArray<NSArray<NSString *> *> *splitObservers = [observers flex_mapped:^id(NSString *entry, NSUInteger idx) {
return [entry componentsSeparatedByString:@","];
}];
NSArray *names = [splitObservers flex_mapped:^id(NSArray<NSString *> *entry, NSUInteger idx) {
return entry[0];
}];
NSArray *objects = [splitObservers flex_mapped:^id(NSArray<NSString *> *entry, NSUInteger idx) {
if (entry.count < 2) return NSNull.null;
NSScanner *scanner = [NSScanner scannerWithString:entry[1]];
unsigned long long objectPointerValue;
if ([scanner scanHexLongLong:&objectPointerValue]) {
void *objectPointer = (void *)objectPointerValue;
if (FLEXPointerIsValidObjcObject(objectPointer))
return (__bridge id)(void *)objectPointer;
}
return NSNull.null;
}];
return [NSArray flex_forEachUpTo:names.count map:^id(NSUInteger i) {
return @[names[i], objects[i]];
}];
}
@end
@@ -10,6 +10,8 @@
#import "FLEXObjectInfoSection.h"
@class FLEXProperty, FLEXIvar, FLEXMethod;
NS_ASSUME_NONNULL_BEGIN
/// An abstract base class for custom object "shortcuts" where every
/// row can possibly have some action. The section title is "Shortcuts".
///
@@ -25,11 +27,11 @@
@interface FLEXShortcutsSection : FLEXTableViewSection <FLEXObjectInfoSection>
/// Uses \c kFLEXDefaultCell
+ (instancetype)forObject:(id)objectOrClass rowTitles:(NSArray<NSString *> *)titles;
+ (instancetype)forObject:(id)objectOrClass rowTitles:(nullable NSArray<NSString *> *)titles;
/// Uses \c kFLEXDetailCell for non-empty subtitles, otherwise uses \c kFLEXDefaultCell
+ (instancetype)forObject:(id)objectOrClass
rowTitles:(NSArray<NSString *> *)titles
rowSubtitles:(NSArray<NSString *> *)subtitles;
rowTitles:(nullable NSArray<NSString *> *)titles
rowSubtitles:(nullable NSArray<NSString *> *)subtitles;
/// Uses \c kFLEXDefaultCell for rows that are given a title, otherwise
/// this uses \c kFLEXDetailCell for any other allowed object.
@@ -44,16 +46,15 @@
/// - a \c FLEXIvar
/// - a \c FLEXMethodBase (includes \c FLEXMethod of course)
/// Passing one of the latter 3 will provide a shortcut to that property/ivar/method.
/// @return \c nil if no rows are provided
+ (instancetype)forObject:(id)objectOrClass rows:(NSArray *)rows;
+ (instancetype)forObject:(id)objectOrClass rows:(nullable NSArray *)rows;
/// Same as \c forObject:rows: but the given rows are prepended
/// to the shortcuts already registered for the object's class.
/// \c forObject:rows: does not use the registered shortcuts at all.
+ (instancetype)forObject:(id)objectOrClass additionalRows:(NSArray *)rows;
+ (instancetype)forObject:(id)objectOrClass additionalRows:(nullable NSArray *)rows;
/// Calls into \c forObject:rows: using the registered shortcuts for the object's class.
/// @return \c nil if the object has no shortcuts registered at all
/// @return An empty section if the object has no shortcuts registered at all.
+ (instancetype)forObject:(id)objectOrClass;
/// Subclasses \e may override this to hide the disclosure indicator
@@ -72,10 +73,16 @@
/// Defaults to NO. Has no effect on static subtitles that are passed explicitly.
@property (nonatomic) BOOL cacheSubtitles;
/// Whether this shortcut section overrides the default section or not.
/// Subclasses should not override this method. To provide a second
/// section alongside the default shortcuts section, use \c forObject:rows:
/// @return \c NO if initialized with \c forObject: or \c forObject:additionalRows:
@property (nonatomic, readonly) BOOL isNewSection;
@end
@class FLEXShortcutsFactory;
typedef FLEXShortcutsFactory *(^FLEXShortcutsFactoryNames)(NSArray *names);
typedef FLEXShortcutsFactory *_Nonnull(^FLEXShortcutsFactoryNames)(NSArray *names);
typedef void (^FLEXShortcutsFactoryTarget)(Class targetClass);
/// The block properties below are to be used like SnapKit or Masonry.
@@ -123,3 +130,5 @@ typedef void (^FLEXShortcutsFactoryTarget)(Class targetClass);
@property (nonatomic, readonly) FLEXShortcutsFactoryTarget forClass;
@end
NS_ASSUME_NONNULL_END
@@ -33,6 +33,7 @@
@end
@implementation FLEXShortcutsSection
@synthesize isNewSection = _isNewSection;
#pragma mark Initialization
@@ -47,13 +48,13 @@
}
+ (instancetype)forObject:(id)objectOrClass rows:(NSArray *)rows {
return [[self alloc] initWithObject:objectOrClass rows:rows];
return [[self alloc] initWithObject:objectOrClass rows:rows isNewSection:YES];
}
+ (instancetype)forObject:(id)objectOrClass additionalRows:(NSArray *)toPrepend {
NSArray *rows = [FLEXShortcutsFactory shortcutsForObjectOrClass:objectOrClass];
NSArray *allRows = [toPrepend arrayByAddingObjectsFromArray:rows] ?: rows;
return [self forObject:objectOrClass rows:allRows];
return [[self alloc] initWithObject:objectOrClass rows:allRows isNewSection:NO];
}
+ (instancetype)forObject:(id)objectOrClass {
@@ -72,16 +73,18 @@
_object = object;
_allTitles = titles.copy;
_allSubtitles = subtitles.copy;
_isNewSection = YES;
_numberOfLines = 1;
}
return self;
}
- (id)initWithObject:object rows:(NSArray *)rows {
- (id)initWithObject:object rows:(NSArray *)rows isNewSection:(BOOL)newSection {
self = [super init];
if (self) {
_object = object;
_isNewSection = newSection;
_allShortcuts = [rows flex_mapped:^id(id obj, NSUInteger idx) {
return [FLEXShortcut shortcutFor:obj];
@@ -300,6 +303,7 @@ typedef NSMutableDictionary<Class, NSMutableArray<id<FLEXRuntimeMetadata>> *> Re
}
- (NSArray<id<FLEXRuntimeMetadata>> *)shortcutsForObjectOrClass:(id)objectOrClass {
NSParameterAssert(objectOrClass);
NSMutableArray<id<FLEXRuntimeMetadata>> *shortcuts = [NSMutableArray new];
BOOL isClass = object_isClass(objectOrClass);
@@ -50,8 +50,6 @@
/// Return nil to use the default reuse identifier
- (NSString *)reuseIdentifierWithTarget:(id)object;
#if FLEX_AT_LEAST_IOS13_SDK
/// An array of actions to place in the first section of the context menu.
- (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender API_AVAILABLE(ios(13.0));
/// An array where every 2 elements are a key-value pair. The key is a description
@@ -60,8 +58,6 @@
/// Properties and ivars return the address of an object, if they hold one.
- (NSString *)contextualSubtitleWithTarget:(id)object;
#endif
@end
// Even if a property is readonly, it still may be editable
@@ -129,8 +129,6 @@ FLEXObjectExplorerDefaultsImpl
- (NSString *)reuseIdentifierWithTarget:(id)object { return nil; }
#if FLEX_AT_LEAST_IOS13_SDK
- (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) {
BOOL returnsObject = self.attributes.typeEncoding.flex_typeIsObjectOrClass;
BOOL targetNotNil = [self appropriateTargetForPropertyType:object] != nil;
@@ -207,8 +205,6 @@ FLEXObjectExplorerDefaultsImpl
return nil;
}
#endif
@end
@@ -285,8 +281,6 @@ FLEXObjectExplorerDefaultsImpl
- (NSString *)reuseIdentifierWithTarget:(id)object { return nil; }
#if FLEX_AT_LEAST_IOS13_SDK
- (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) {
Class ivarClass = self.typeEncoding.flex_typeClass;
@@ -337,8 +331,6 @@ FLEXObjectExplorerDefaultsImpl
return nil;
}
#endif
@end
@@ -383,8 +375,6 @@ FLEXObjectExplorerDefaultsImpl
- (NSString *)reuseIdentifierWithTarget:(id)object { return nil; }
#if FLEX_AT_LEAST_IOS13_SDK
- (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) {
return nil;
}
@@ -401,8 +391,6 @@ FLEXObjectExplorerDefaultsImpl
return nil;
}
#endif
@end
@implementation FLEXMethod (UIKitHelpers)
@@ -480,8 +468,6 @@ FLEXObjectExplorerDefaultsImpl
- (NSString *)reuseIdentifierWithTarget:(id)object { return nil; }
#if FLEX_AT_LEAST_IOS13_SDK
- (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) {
return nil;
}
@@ -499,8 +485,6 @@ FLEXObjectExplorerDefaultsImpl
return nil;
}
#endif
@end
@@ -592,8 +576,6 @@ FLEXObjectExplorerDefaultsImpl
return UITableViewCellAccessoryNone;
}
#if FLEX_AT_LEAST_IOS13_SDK
- (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) {
return nil;
}
@@ -606,8 +588,6 @@ FLEXObjectExplorerDefaultsImpl
return nil;
}
#endif
@end
+13 -9
View File
@@ -8,23 +8,25 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class FLEXAlert, FLEXAlertAction;
typedef void (^FLEXAlertReveal)(void);
typedef void (^FLEXAlertBuilder)(FLEXAlert *make);
typedef FLEXAlert *(^FLEXAlertStringProperty)(NSString *);
typedef FLEXAlert *(^FLEXAlertStringArg)(NSString *);
typedef FLEXAlert *(^FLEXAlertTextField)(void(^configurationHandler)(UITextField *textField));
typedef FLEXAlertAction *(^FLEXAlertAddAction)(NSString *title);
typedef FLEXAlertAction *(^FLEXAlertActionStringProperty)(NSString *);
typedef FLEXAlertAction *(^FLEXAlertActionProperty)(void);
typedef FLEXAlertAction *(^FLEXAlertActionBOOLProperty)(BOOL);
typedef FLEXAlertAction *(^FLEXAlertActionHandler)(void(^handler)(NSArray<NSString *> *strings));
typedef FLEXAlert * _Nonnull (^FLEXAlertStringProperty)(NSString * _Nullable);
typedef FLEXAlert * _Nonnull (^FLEXAlertStringArg)(NSString * _Nullable);
typedef FLEXAlert * _Nonnull (^FLEXAlertTextField)(void(^configurationHandler)(UITextField *textField));
typedef FLEXAlertAction * _Nonnull (^FLEXAlertAddAction)(NSString *title);
typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionStringProperty)(NSString * _Nullable);
typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionProperty)(void);
typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionBOOLProperty)(BOOL);
typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionHandler)(void(^handler)(NSArray<NSString *> *strings));
@interface FLEXAlert : NSObject
/// Shows a simple alert with one button which says "Dismiss"
+ (void)showAlert:(NSString *)title message:(NSString *)message from:(UIViewController *)viewController;
+ (void)showAlert:(NSString * _Nullable)title message:(NSString * _Nullable)message from:(UIViewController *)viewController;
/// Construct and display an alert
+ (void)makeAlert:(FLEXAlertBuilder)block showFrom:(UIViewController *)viewController;
@@ -79,3 +81,5 @@ typedef FLEXAlertAction *(^FLEXAlertActionHandler)(void(^handler)(NSArray<NSStri
@property (nonatomic, readonly) UIAlertAction *action;
@end
NS_ASSUME_NONNULL_END
-4
View File
@@ -9,7 +9,6 @@
#import "FLEXColor.h"
#import "FLEXUtility.h"
#if FLEX_AT_LEAST_IOS13_SDK
#define FLEXDynamicColor(dynamic, static) ({ \
UIColor *c; \
if (@available(iOS 13.0, *)) { \
@@ -19,9 +18,6 @@
} \
c; \
});
#else
#define FLEXDynamicColor(dynamic, static) [UIColor static]
#endif
@implementation FLEXColor
-6
View File
@@ -75,12 +75,6 @@ NS_INLINE CGRect FLEXRectSetHeight(CGRect r, CGFloat height) {
r.size.height = height; return r;
}
#ifdef __IPHONE_13_0
#define FLEX_AT_LEAST_IOS13_SDK (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0)
#else
#define FLEX_AT_LEAST_IOS13_SDK NO
#endif
#define FLEXPluralString(count, plural, singular) [NSString \
stringWithFormat:@"%@ %@", @(count), (count == 1 ? singular : plural) \
]
-4
View File
@@ -17,10 +17,6 @@
#import "UIFont+FLEX.h"
#import "FLEXMacros.h"
#if !FLEX_AT_LEAST_IOS13_SDK
@class UIWindowScene;
#endif
@interface FLEXUtility : NSObject
/// The key window of the app, if it is not a \c FLEXWindow.
+1 -7
View File
@@ -63,7 +63,6 @@ BOOL FLEXConstructorsShouldRun() {
return nil;
}
#if FLEX_AT_LEAST_IOS13_SDK
+ (UIWindowScene *)activeScene {
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
// Look for an active UIWindowScene
@@ -75,7 +74,6 @@ BOOL FLEXConstructorsShouldRun() {
return nil;
}
#endif
+ (UIViewController *)topViewControllerInWindow:(UIWindow *)window {
UIViewController *topViewController = window.rootViewController;
@@ -180,8 +178,6 @@ BOOL FLEXConstructorsShouldRun() {
dispatch_once(&onceToken, ^{
UIImage *indentationPatternImage = FLEXResources.hierarchyIndentPattern;
patternColor = [UIColor colorWithPatternImage:indentationPatternImage];
#if FLEX_AT_LEAST_IOS13_SDK
if (@available(iOS 13.0, *)) {
// Create a dark mode version
UIGraphicsBeginImageContextWithOptions(
@@ -200,9 +196,7 @@ BOOL FLEXConstructorsShouldRun() {
? [UIColor colorWithPatternImage:indentationPatternImage]
: [UIColor colorWithPatternImage:darkModePatternImage]);
}];
}
#endif
});
} });
return patternColor;
}
@@ -30,7 +30,7 @@
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = "FLEX"
spec.version = "4.4.1"
spec.version = "4.5.0"
spec.summary = "A set of in-app debugging and exploration tools for iOS"
spec.description = <<-DESC
- Inspect and modify views in the hierarchy.
+32 -12
View File
@@ -144,6 +144,12 @@
C301994A2409B38A00759E8E /* CALayer+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C30199482409B38A00759E8E /* CALayer+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; };
C301994B2409B38A00759E8E /* CALayer+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C30199492409B38A00759E8E /* CALayer+FLEX.m */; };
C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C309B82D223ED64400B228EC /* FLEXLogController.h */; };
C30D2960261FAE9E00D89649 /* FLEXNSStringShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D295C261FAE9E00D89649 /* FLEXNSStringShortcuts.h */; };
C30D2961261FAE9E00D89649 /* FLEXNSDataShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C30D295D261FAE9E00D89649 /* FLEXNSDataShortcuts.m */; };
C30D2962261FAE9E00D89649 /* FLEXNSDataShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D295E261FAE9E00D89649 /* FLEXNSDataShortcuts.h */; };
C30D2963261FAE9E00D89649 /* FLEXNSStringShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C30D295F261FAE9E00D89649 /* FLEXNSStringShortcuts.m */; };
C30D2968261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D2966261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h */; };
C30D2969261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C30D2967261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m */; };
C312A13023ECB5D300E38049 /* FLEXBookmarkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */; };
C312A13123ECB5D300E38049 /* FLEXBookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */; };
C312A13423ECBE5800E38049 /* FLEXBookmarksViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A13223ECBE5800E38049 /* FLEXBookmarksViewController.h */; };
@@ -194,7 +200,7 @@
C3531BA623E88A2100A184AD /* FLEXNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3531BA423E88A2100A184AD /* FLEXNavigationController.m */; };
C3531BAA23E88FAC00A184AD /* FLEXTabList.h in Headers */ = {isa = PBXBuildFile; fileRef = C3531BA823E88FAC00A184AD /* FLEXTabList.h */; };
C3531BAB23E88FAC00A184AD /* FLEXTabList.m in Sources */ = {isa = PBXBuildFile; fileRef = C3531BA923E88FAC00A184AD /* FLEXTabList.m */; };
C362AE8123C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h in Headers */ = {isa = PBXBuildFile; fileRef = C362AE7F23C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h */; settings = {ATTRIBUTES = (Public, ); }; };
C362AE8123C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h in Headers */ = {isa = PBXBuildFile; fileRef = C362AE7F23C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h */; settings = {ATTRIBUTES = (Private, ); }; };
C362AE8223C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m in Sources */ = {isa = PBXBuildFile; fileRef = C362AE8023C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m */; };
C3694DBA23EA1096006625D7 /* FLEXTabsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3694DB823EA1096006625D7 /* FLEXTabsViewController.h */; };
C3694DBB23EA1096006625D7 /* FLEXTabsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3694DB923EA1096006625D7 /* FLEXTabsViewController.m */; };
@@ -248,7 +254,7 @@
C3878DBE23A74A8F0038FDBE /* FLEXNetworkRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B81B5B21410088C3F2 /* FLEXNetworkRecorder.m */; };
C387C87A22DFCD6A00750E58 /* FLEXCarouselCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C387C87822DFCD6A00750E58 /* FLEXCarouselCell.h */; };
C387C87B22DFCD6A00750E58 /* FLEXCarouselCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C387C87922DFCD6A00750E58 /* FLEXCarouselCell.m */; };
C387C88322E0D24A00750E58 /* UIView+FLEX_Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */; settings = {ATTRIBUTES = (Public, ); }; };
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 */; };
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 */; };
@@ -282,7 +288,7 @@
C398626E23AD71C1007E6793 /* FLEXRuntimeClient.m in Sources */ = {isa = PBXBuildFile; fileRef = C398626A23AD71C1007E6793 /* FLEXRuntimeClient.m */; };
C398627223AD7951007E6793 /* UIGestureRecognizer+Blocks.h in Headers */ = {isa = PBXBuildFile; fileRef = C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */; settings = {ATTRIBUTES = (Public, ); }; };
C398627323AD7951007E6793 /* UIGestureRecognizer+Blocks.m in Sources */ = {isa = PBXBuildFile; fileRef = C398627123AD7951007E6793 /* UIGestureRecognizer+Blocks.m */; };
C398627623AD79B7007E6793 /* NSString+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C398627423AD79B6007E6793 /* NSString+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; };
C398627623AD79B7007E6793 /* NSString+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C398627423AD79B6007E6793 /* NSString+FLEX.h */; settings = {ATTRIBUTES = (Private, ); }; };
C398627723AD79B7007E6793 /* NSString+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C398627523AD79B7007E6793 /* NSString+FLEX.m */; };
C398682523AC359600E9E391 /* FLEXShortcutsFactory+Defaults.m in Sources */ = {isa = PBXBuildFile; fileRef = C398682323AC359600E9E391 /* FLEXShortcutsFactory+Defaults.m */; };
C398682623AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h in Headers */ = {isa = PBXBuildFile; fileRef = C398682423AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -343,9 +349,9 @@
C3F646F323A045DB00D4A011 /* FLEXClassShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F646F123A045DB00D4A011 /* FLEXClassShortcuts.m */; };
C3F646F623A04A7500D4A011 /* FLEXShortcut.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F646F423A04A7500D4A011 /* FLEXShortcut.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3F646F723A04A7500D4A011 /* FLEXShortcut.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F646F523A04A7500D4A011 /* FLEXShortcut.m */; };
C3F977832311B38F0032776D /* NSString+ObjcRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F9777D2311B38E0032776D /* NSString+ObjcRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3F977832311B38F0032776D /* NSString+ObjcRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F9777D2311B38E0032776D /* NSString+ObjcRuntime.h */; settings = {ATTRIBUTES = (Private, ); }; };
C3F977842311B38F0032776D /* NSDictionary+ObjcRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F9777E2311B38E0032776D /* NSDictionary+ObjcRuntime.m */; };
C3F977852311B38F0032776D /* NSDictionary+ObjcRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F9777F2311B38F0032776D /* NSDictionary+ObjcRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3F977852311B38F0032776D /* NSDictionary+ObjcRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F9777F2311B38F0032776D /* NSDictionary+ObjcRuntime.h */; settings = {ATTRIBUTES = (Private, ); }; };
C3F977862311B38F0032776D /* NSString+ObjcRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F977802311B38F0032776D /* NSString+ObjcRuntime.m */; };
C3F977872311B38F0032776D /* NSObject+FLEX_Reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F977812311B38F0032776D /* NSObject+FLEX_Reflection.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3F977882311B38F0032776D /* NSObject+FLEX_Reflection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F977822311B38F0032776D /* NSObject+FLEX_Reflection.m */; };
@@ -506,6 +512,12 @@
C30199482409B38A00759E8E /* CALayer+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CALayer+FLEX.h"; sourceTree = "<group>"; };
C30199492409B38A00759E8E /* CALayer+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CALayer+FLEX.m"; sourceTree = "<group>"; };
C309B82D223ED64400B228EC /* FLEXLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXLogController.h; sourceTree = "<group>"; };
C30D295C261FAE9E00D89649 /* FLEXNSStringShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNSStringShortcuts.h; sourceTree = "<group>"; };
C30D295D261FAE9E00D89649 /* FLEXNSDataShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNSDataShortcuts.m; sourceTree = "<group>"; };
C30D295E261FAE9E00D89649 /* FLEXNSDataShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNSDataShortcuts.h; sourceTree = "<group>"; };
C30D295F261FAE9E00D89649 /* FLEXNSStringShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNSStringShortcuts.m; sourceTree = "<group>"; };
C30D2966261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Cocoa+FLEXShortcuts.h"; sourceTree = "<group>"; };
C30D2967261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Cocoa+FLEXShortcuts.m"; sourceTree = "<group>"; };
C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBookmarkManager.h; sourceTree = "<group>"; };
C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBookmarkManager.m; sourceTree = "<group>"; };
C312A13223ECBE5800E38049 /* FLEXBookmarksViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBookmarksViewController.h; sourceTree = "<group>"; };
@@ -1094,6 +1106,8 @@
C31E4E53259D4A4100712288 /* Private */ = {
isa = PBXGroup;
children = (
C30D2966261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h */,
C30D2967261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m */,
C362AE7F23C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h */,
C362AE8023C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m */,
C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */,
@@ -1392,6 +1406,10 @@
C32F3A17247C6B3E0063542D /* FLEXUIAppShortcuts.m */,
C31D93E223E38CBE005517BF /* FLEXBlockShortcuts.h */,
C31D93E323E38CBE005517BF /* FLEXBlockShortcuts.m */,
C30D295E261FAE9E00D89649 /* FLEXNSDataShortcuts.h */,
C30D295D261FAE9E00D89649 /* FLEXNSDataShortcuts.m */,
C30D295C261FAE9E00D89649 /* FLEXNSStringShortcuts.h */,
C30D295F261FAE9E00D89649 /* FLEXNSStringShortcuts.m */,
);
path = Shortcuts;
sourceTree = "<group>";
@@ -1519,6 +1537,7 @@
C32F3A18247C6B3E0063542D /* FLEXUIAppShortcuts.h in Headers */,
779B1ED61C0C4D7C001F5E49 /* FLEXTableContentViewController.h in Headers */,
C3DFCDB82418336D00BB7084 /* NSUserDefaults+FLEX.h in Headers */,
C30D2968261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h in Headers */,
3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */,
C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */,
C34EE30821CB23CC00BD3A7C /* FLEXOSLogController.h in Headers */,
@@ -1527,6 +1546,7 @@
3A4C94E71B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h in Headers */,
224D49AA1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.h in Headers */,
C386D6A9241995A800699085 /* FLEXTypeEncodingParser.h in Headers */,
C30D2962261FAE9E00D89649 /* FLEXNSDataShortcuts.h in Headers */,
3A4C95031B5B21410088C3F2 /* FLEXArgumentInputView.h in Headers */,
C398627623AD79B7007E6793 /* NSString+FLEX.h in Headers */,
94A5151D1C4CA1F10063292F /* FLEXExplorerViewController.h in Headers */,
@@ -1593,6 +1613,7 @@
C3F977872311B38F0032776D /* NSObject+FLEX_Reflection.h in Headers */,
C3DB9F642107FC9600B46809 /* FLEXObjectRef.h in Headers */,
3A4C95401B5B21410088C3F2 /* FLEXNetworkTransactionCell.h in Headers */,
C30D2960261FAE9E00D89649 /* FLEXNSStringShortcuts.h in Headers */,
C398626523AD70F5007E6793 /* FLEXKBToolbarButton.h in Headers */,
3A4C95241B5B21410088C3F2 /* FLEXFileBrowserController.h in Headers */,
C31D93E423E38CBE005517BF /* FLEXBlockShortcuts.h in Headers */,
@@ -1662,7 +1683,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = FLEX;
LastUpgradeCheck = 1200;
LastUpgradeCheck = 1240;
ORGANIZATIONNAME = Flipboard;
TargetAttributes = {
1C27A8B51F0E5A0300F0D02D = {
@@ -1733,6 +1754,7 @@
C3878DBE23A74A8F0038FDBE /* FLEXNetworkRecorder.m in Sources */,
C3878DBC23A749F70038FDBE /* FLEXFieldEditorViewController.m in Sources */,
942DCD871BAE0CA300DB5DC2 /* FLEXKeyboardShortcutManager.m in Sources */,
C30D2961261FAE9E00D89649 /* FLEXNSDataShortcuts.m in Sources */,
C3F977862311B38F0032776D /* NSString+ObjcRuntime.m in Sources */,
C3A9424A23C78878006871A3 /* FLEXHierarchyViewController.m in Sources */,
C31C4A6A23342A2200C35F12 /* FLEXMetadataSection.m in Sources */,
@@ -1835,6 +1857,7 @@
C32A195F231732E800EB02AC /* FLEXCollectionContentSection.m in Sources */,
C36FBFCD230F3B98008D95D5 /* FLEXMethod.m in Sources */,
779B1ED31C0C4D7C001F5E49 /* FLEXTableColumnHeader.m in Sources */,
C30D2963261FAE9E00D89649 /* FLEXNSStringShortcuts.m in Sources */,
C37A0C94218BAC9600848CA7 /* FLEXObjcInternal.mm in Sources */,
C31D93E523E38CBE005517BF /* FLEXBlockShortcuts.m in Sources */,
C312A13123ECB5D300E38049 /* FLEXBookmarkManager.m in Sources */,
@@ -1864,6 +1887,7 @@
3A4C953F1B5B21410088C3F2 /* FLEXNetworkTransactionDetailController.m in Sources */,
224D49AB1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m in Sources */,
C312A13C23ECE79000E38049 /* FLEXWindowManagerController.m in Sources */,
C30D2969261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m in Sources */,
C36FBFDA230F3B98008D95D5 /* FLEXPropertyAttributes.m in Sources */,
779B1ED91C0C4D7C001F5E49 /* FLEXTableLeftCell.m in Sources */,
3A4C94E61B5B21410088C3F2 /* FLEXUtility.m in Sources */,
@@ -2061,7 +2085,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_STRICT_PROTOTYPES = NO;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -2069,13 +2093,11 @@
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = Classes/Info.plist;
INSTALL_PATH = "@rpath";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)";
@@ -2098,7 +2120,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_STRICT_PROTOTYPES = NO;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -2106,13 +2128,11 @@
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = Classes/Info.plist;
INSTALL_PATH = "@rpath";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)";
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"