Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c0a3b43eb | |||
| b6914ff990 | |||
| f601018373 | |||
| a171eedc40 | |||
| 8acbc804b4 | |||
| 30a9f5628d | |||
| 7362870570 | |||
| 26333cbb25 | |||
| 15377eabbc | |||
| b211a845d2 |
@@ -7,6 +7,8 @@
|
||||
//
|
||||
|
||||
#import "FLEXNavigationController.h"
|
||||
#import "FLEXExplorerViewController.h"
|
||||
#import "FLEXTabList.h"
|
||||
|
||||
@interface UINavigationController (Private) <UIGestureRecognizerDelegate>
|
||||
- (void)_gestureRecognizedInteractiveHide:(UIGestureRecognizer *)sender;
|
||||
@@ -23,7 +25,24 @@
|
||||
@implementation FLEXNavigationController
|
||||
|
||||
+ (instancetype)withRootViewController:(UIViewController *)rootVC {
|
||||
return [[self alloc] initWithRootViewController:rootVC];
|
||||
FLEXNavigationController *instance = [[self alloc] initWithRootViewController:rootVC];
|
||||
|
||||
// Give root view controllers a Done button
|
||||
UIBarButtonItem *done = [[UIBarButtonItem alloc]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
|
||||
target:instance
|
||||
action:@selector(dismissAnimated)
|
||||
];
|
||||
|
||||
// Prepend the button if other buttons exist already
|
||||
NSArray *existingItems = rootVC.navigationItem.rightBarButtonItems;
|
||||
if (existingItems.count) {
|
||||
rootVC.navigationItem.rightBarButtonItems = [@[done] arrayByAddingObjectsFromArray:existingItems];
|
||||
} else {
|
||||
rootVC.navigationItem.rightBarButtonItem = done;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@@ -32,6 +51,26 @@
|
||||
self.waitingToAddTab = YES;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
if (self.waitingToAddTab) {
|
||||
// Only add new tab if we're presented properly
|
||||
if ([self.presentingViewController isKindOfClass:[FLEXExplorerViewController class]]) {
|
||||
// New navigation controllers always add themselves as new tabs,
|
||||
// tabs are closed by FLEXExplorerViewController
|
||||
[FLEXTabList.sharedList addTab:self];
|
||||
self.waitingToAddTab = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismissAnimated {
|
||||
// TODO tabs not closed on swipe down gesture
|
||||
[FLEXTabList.sharedList closeTab:self];
|
||||
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)_gestureRecognizedInteractiveHide:(UIPanGestureRecognizer *)sender {
|
||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
||||
BOOL show = self.topViewController.toolbarItems.count;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "FLEXTableView.h"
|
||||
@class FLEXScopeCarousel, FLEXWindow;
|
||||
|
||||
typedef CGFloat FLEXDebounceInterval;
|
||||
@@ -106,4 +107,14 @@ extern CGFloat const kFLEXDebounceForExpensiveIO;
|
||||
/// in the background before updating the UI back on the main queue.
|
||||
- (void)onBackgroundQueue:(NSArray *(^)(void))backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock;
|
||||
|
||||
/// Whether or not to display the "share" icon in the middle of the toolbar. NO by default.
|
||||
@property (nonatomic) BOOL showsShareToolbarItem;
|
||||
/// Called when the share button is pressed.
|
||||
/// Default implementation does nothign. Subclasses may override.
|
||||
- (void)shareButtonPressed;
|
||||
|
||||
/// Subclasses may call this to opt-out of all toolbar related behavior.
|
||||
/// This is necessary if you want to disable the gesture which reveals the toolbar.
|
||||
- (void)disableToolbar;
|
||||
|
||||
@end
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
//
|
||||
|
||||
#import "FLEXTableViewController.h"
|
||||
#import "FLEXExplorerViewController.h"
|
||||
#import "FLEXBookmarksViewController.h"
|
||||
#import "FLEXTabsViewController.h"
|
||||
#import "FLEXScopeCarousel.h"
|
||||
#import "FLEXTableView.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface Block : NSObject
|
||||
@@ -33,7 +37,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
@synthesize tableHeaderViewContainer = _tableHeaderViewContainer;
|
||||
@synthesize automaticallyShowsSearchBarCancelButton = _automaticallyShowsSearchBarCancelButton;
|
||||
|
||||
#pragma mark - Public
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (id)init {
|
||||
#if FLEX_AT_LEAST_IOS13_SDK
|
||||
@@ -60,6 +64,9 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (FLEXWindow *)window {
|
||||
return (id)self.view.window;
|
||||
}
|
||||
@@ -178,6 +185,20 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setsShowsShareToolbarItem:(BOOL)showsShareToolbarItem {
|
||||
_showsShareToolbarItem = showsShareToolbarItem;
|
||||
if (self.isViewLoaded) {
|
||||
[self setupToolbarItems];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)disableToolbar {
|
||||
self.navigationController.toolbarHidden = YES;
|
||||
self.navigationController.hidesBarsOnSwipe = NO;
|
||||
self.toolbarItems = nil;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - View Controller Lifecycle
|
||||
|
||||
- (void)loadView {
|
||||
@@ -190,6 +211,10 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
[super viewDidLoad];
|
||||
|
||||
self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
|
||||
|
||||
// Toolbar
|
||||
self.navigationController.toolbarHidden = NO;
|
||||
self.navigationController.hidesBarsOnSwipe = YES;
|
||||
|
||||
// On iOS 13, the root view controller shows it's search bar no matter what.
|
||||
// Turning this off avoids some weird flash the navigation bar does when we
|
||||
@@ -203,11 +228,6 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews {
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
@@ -217,6 +237,8 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
self.navigationItem.hidesSearchBarWhenScrolling = NO;
|
||||
}
|
||||
}
|
||||
|
||||
[self setupToolbarItems];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@@ -248,8 +270,36 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
self.didInitiallyRevealSearchBar = NO;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)setupToolbarItems {
|
||||
UIBarButtonItem *emptySpaceOrShare = UIBarButtonItem.flex_fixedSpace;
|
||||
if (self.showsShareToolbarItem) {
|
||||
emptySpaceOrShare = FLEXBarButtonItemSystem(Action, self, @selector(shareButtonPressed));
|
||||
}
|
||||
|
||||
self.toolbarItems = @[
|
||||
UIBarButtonItem.flex_fixedSpace,
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
UIBarButtonItem.flex_fixedSpace,
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
UIBarButtonItem.flex_fixedSpace,
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
emptySpaceOrShare,
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
FLEXBarButtonItemSystem(Bookmarks, self, @selector(showBookmarks)),
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
FLEXBarButtonItemSystem(Organize, self, @selector(showTabSwitcher)),
|
||||
];
|
||||
|
||||
// Disable tabs entirely when not presented by FLEXExplorerViewController
|
||||
UIViewController *presenter = self.navigationController.presentingViewController;
|
||||
if (![presenter isKindOfClass:[FLEXExplorerViewController class]]) {
|
||||
self.toolbarItems.lastObject.enabled = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)debounce:(void(^)(void))block {
|
||||
[self.debounceTimer invalidate];
|
||||
|
||||
@@ -366,6 +416,25 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
return _tableHeaderViewContainer;
|
||||
}
|
||||
|
||||
- (void)showBookmarks {
|
||||
UINavigationController *nav = [[UINavigationController alloc]
|
||||
initWithRootViewController:[FLEXBookmarksViewController new]
|
||||
];
|
||||
[self presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)showTabSwitcher {
|
||||
UINavigationController *nav = [[UINavigationController alloc]
|
||||
initWithRootViewController:[FLEXTabsViewController new]
|
||||
];
|
||||
[self presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)shareButtonPressed {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Search Bar
|
||||
|
||||
#pragma mark UISearchResultsUpdating
|
||||
@@ -391,6 +460,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark UISearchControllerDelegate
|
||||
|
||||
- (void)willPresentSearchController:(UISearchController *)searchController {
|
||||
@@ -407,6 +477,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark UISearchBarDelegate
|
||||
|
||||
/// Not necessary in iOS 13; remove this when iOS 13 is the deployment target
|
||||
@@ -414,6 +485,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
[self updateSearchResultsForSearchController:self.searchController];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Table view
|
||||
|
||||
/// Not having a title in the first section looks weird with a rounded-corner table view style
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#import "FLEXPropertyAttributes.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXColor.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
|
||||
@interface FLEXFieldEditorViewController () <FLEXArgumentInputViewDelegate>
|
||||
|
||||
@@ -61,7 +62,9 @@
|
||||
target:self
|
||||
action:@selector(getterButtonPressed:)
|
||||
];
|
||||
self.navigationItem.rightBarButtonItems = @[self.setterButton, self.getterButton];
|
||||
self.toolbarItems = @[
|
||||
UIBarButtonItem.flex_flexibleSpace, self.getterButton, self.setterButton
|
||||
];
|
||||
|
||||
// Configure input view
|
||||
self.fieldEditorView.fieldDescription = self.fieldDescription;
|
||||
@@ -72,7 +75,12 @@
|
||||
|
||||
// Don't show a "set" button for switches; we mutate when the switch is flipped
|
||||
if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
self.setterButton.enabled = NO;
|
||||
self.setterButton.title = @"Flip the switch to call the setter";
|
||||
// Put getter button before setter button
|
||||
self.toolbarItems = @[
|
||||
UIBarButtonItem.flex_flexibleSpace, self.setterButton, self.getterButton
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
// For subclass use only.
|
||||
@property (nonatomic, readonly) id target;
|
||||
@property (nonatomic, readonly) FLEXFieldEditorView *fieldEditorView;
|
||||
/// Subclasses can change the button title via the \c title property
|
||||
/// Subclasses can change the button title via the button's \c title property
|
||||
@property (nonatomic, readonly) UIBarButtonItem *setterButton;
|
||||
|
||||
- (void)actionButtonPressed:(id)sender;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "FLEXArgumentInputView.h"
|
||||
#import "FLEXArgumentInputViewFactory.h"
|
||||
#import "FLEXObjectExplorerViewController.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
|
||||
@interface FLEXVariableEditorViewController () <UIScrollViewDelegate>
|
||||
@property (nonatomic) UIScrollView *scrollView;
|
||||
@@ -98,7 +99,9 @@
|
||||
target:self
|
||||
action:@selector(actionButtonPressed:)
|
||||
];
|
||||
self.navigationItem.rightBarButtonItem = self.setterButton;
|
||||
|
||||
self.navigationController.toolbarHidden = NO;
|
||||
self.toolbarItems = @[UIBarButtonItem.flex_flexibleSpace, self.setterButton];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// FLEXBookmarkManager.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/6/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FLEXBookmarkManager : NSObject
|
||||
|
||||
@property (nonatomic, readonly, class) NSMutableArray *bookmarks;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// FLEXBookmarkManager.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/6/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXBookmarkManager.h"
|
||||
|
||||
static NSMutableArray *kFLEXBookmarkManagerBookmarks = nil;
|
||||
|
||||
@implementation FLEXBookmarkManager
|
||||
|
||||
+ (void)initialize {
|
||||
if (self == [FLEXBookmarkManager class]) {
|
||||
kFLEXBookmarkManagerBookmarks = [NSMutableArray new];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSMutableArray *)bookmarks {
|
||||
return kFLEXBookmarkManagerBookmarks;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// FLEXBookmarksViewController.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/6/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXTableViewController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FLEXBookmarksViewController : FLEXTableViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// FLEXBookmarksViewController.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/6/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXBookmarksViewController.h"
|
||||
#import "FLEXExplorerViewController.h"
|
||||
#import "FLEXNavigationController.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXBookmarkManager.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
#import "FLEXColor.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "FLEXTableView.h"
|
||||
|
||||
@interface FLEXBookmarksViewController ()
|
||||
@property (nonatomic, copy) NSArray *bookmarks;
|
||||
@property (nonatomic, readonly) FLEXExplorerViewController *corePresenter;
|
||||
@end
|
||||
|
||||
@implementation FLEXBookmarksViewController
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (id)init {
|
||||
return [self initWithStyle:UITableViewStylePlain];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationController.hidesBarsOnSwipe = NO;
|
||||
self.tableView.allowsMultipleSelectionDuringEditing = YES;
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[self setupDefaultBarItems];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)reloadData {
|
||||
// We assume the bookmarks aren't going to change out from under us, since
|
||||
// presenting any other tool via keyboard shortcuts should dismiss us first
|
||||
self.bookmarks = FLEXBookmarkManager.bookmarks;
|
||||
self.title = [NSString stringWithFormat:@"Bookmarks (%@)", @(self.bookmarks.count)];
|
||||
}
|
||||
|
||||
- (void)setupDefaultBarItems {
|
||||
self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated));
|
||||
self.toolbarItems = @[
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)),
|
||||
];
|
||||
|
||||
// Disable editing if no bookmarks available
|
||||
self.toolbarItems.lastObject.enabled = self.bookmarks.count > 0;
|
||||
}
|
||||
|
||||
- (void)setupEditingBarItems {
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
self.toolbarItems = @[
|
||||
[UIBarButtonItem itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed)],
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
// We use a non-system done item because we change its title dynamically
|
||||
[UIBarButtonItem doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)]
|
||||
];
|
||||
|
||||
self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor;
|
||||
}
|
||||
|
||||
- (FLEXExplorerViewController *)corePresenter {
|
||||
// We must be presented by a FLEXExplorerViewController, or presented
|
||||
// by another view controller that was presented by FLEXExplorerViewController
|
||||
FLEXExplorerViewController *presenter = (id)self.presentingViewController;
|
||||
presenter = (id)presenter.presentingViewController ?: presenter;
|
||||
presenter = (id)presenter.presentingViewController ?: presenter;
|
||||
NSAssert(
|
||||
[presenter isKindOfClass:[FLEXExplorerViewController class]],
|
||||
@"The bookmarks view controller expects to be presented by the explorer controller"
|
||||
);
|
||||
return presenter;
|
||||
}
|
||||
|
||||
#pragma mark Button Actions
|
||||
|
||||
- (void)dismissAnimated {
|
||||
[self dismissAnimated:nil];
|
||||
}
|
||||
|
||||
- (void)dismissAnimated:(id)selectedObject {
|
||||
if (selectedObject) {
|
||||
UIViewController *explorer = [FLEXObjectExplorerFactory
|
||||
explorerViewControllerForObject:selectedObject
|
||||
];
|
||||
if ([self.presentingViewController isKindOfClass:[FLEXNavigationController class]]) {
|
||||
// I am presented on an existing navigation stack, so
|
||||
// dismiss myself and push the bookmark there
|
||||
UINavigationController *presenter = (id)self.presentingViewController;
|
||||
[presenter dismissViewControllerAnimated:YES completion:^{
|
||||
[presenter pushViewController:explorer animated:YES];
|
||||
}];
|
||||
} else {
|
||||
// Dismiss myself and present explorer
|
||||
UIViewController *presenter = self.corePresenter;
|
||||
[presenter dismissViewControllerAnimated:YES completion:^{
|
||||
[presenter presentViewController:[FLEXNavigationController
|
||||
withRootViewController:explorer
|
||||
] animated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
// Just dismiss myself
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)toggleEditing {
|
||||
NSArray<NSIndexPath *> *selected = self.tableView.indexPathsForSelectedRows;
|
||||
self.editing = !self.editing;
|
||||
|
||||
if (self.isEditing) {
|
||||
[self setupEditingBarItems];
|
||||
} else {
|
||||
[self setupDefaultBarItems];
|
||||
|
||||
// Get index set of bookmarks to close
|
||||
NSMutableIndexSet *indexes = [NSMutableIndexSet new];
|
||||
for (NSIndexPath *ip in selected) {
|
||||
[indexes addIndex:ip.row];
|
||||
}
|
||||
|
||||
if (selected.count) {
|
||||
// Close bookmarks and update data source
|
||||
[FLEXBookmarkManager.bookmarks removeObjectsAtIndexes:indexes];
|
||||
[self reloadData];
|
||||
|
||||
// Remove deleted rows
|
||||
[self.tableView deleteRowsAtIndexPaths:selected withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeAllButtonPressed {
|
||||
[FLEXAlert makeSheet:^(FLEXAlert *make) {
|
||||
NSInteger count = self.bookmarks.count;
|
||||
NSString *title = FLEXPluralFormatString(count, @"Remove %@ bookmarks", @"Remove %@ bookmark");
|
||||
make.button(title).destructiveStyle().handler(^(NSArray<NSString *> *strings) {
|
||||
[self closeAll];
|
||||
[self toggleEditing];
|
||||
});
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
- (void)closeAll {
|
||||
NSInteger rowCount = self.bookmarks.count;
|
||||
|
||||
// Close bookmarks and update data source
|
||||
[FLEXBookmarkManager.bookmarks removeAllObjects];
|
||||
[self reloadData];
|
||||
|
||||
// Delete rows from table view
|
||||
NSArray<NSIndexPath *> *allRows = [NSArray flex_forEachUpTo:rowCount map:^id(NSUInteger row) {
|
||||
return [NSIndexPath indexPathForRow:row inSection:0];
|
||||
}];
|
||||
[self.tableView deleteRowsAtIndexPaths:allRows withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table View Data Source
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.bookmarks.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(FLEXTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
|
||||
|
||||
id object = self.bookmarks[indexPath.row];
|
||||
cell.textLabel.text = [FLEXRuntimeUtility safeDescriptionForObject:object];
|
||||
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ — %p", [object class], object];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table View Delegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.editing) {
|
||||
// Case: editing with multi-select
|
||||
self.toolbarItems.lastObject.title = @"Remove Selected";
|
||||
self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor;
|
||||
} else {
|
||||
// Case: selected a bookmark
|
||||
[self dismissAnimated:self.bookmarks[indexPath.row]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSParameterAssert(self.editing);
|
||||
|
||||
if (tableView.indexPathsForSelectedRows.count == 0) {
|
||||
self.toolbarItems.lastObject.title = @"Done";
|
||||
self.toolbarItems.lastObject.tintColor = self.view.tintColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)table
|
||||
commitEditingStyle:(UITableViewCellEditingStyle)edit
|
||||
forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSParameterAssert(edit == UITableViewCellEditingStyleDelete);
|
||||
|
||||
// Remove bookmark and update data source
|
||||
[FLEXBookmarkManager.bookmarks removeObjectAtIndex:indexPath.row];
|
||||
[self reloadData];
|
||||
|
||||
// Delete row from table view
|
||||
[table deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -11,12 +11,15 @@
|
||||
#import "FLEXToolbarItem.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXWindow.h"
|
||||
#import "FLEXTabList.h"
|
||||
#import "FLEXNavigationController.h"
|
||||
#import "FLEXHierarchyViewController.h"
|
||||
#import "FLEXGlobalsViewController.h"
|
||||
#import "FLEXObjectExplorerViewController.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXNetworkHistoryTableViewController.h"
|
||||
#import "FLEXNetworkMITMViewController.h"
|
||||
#import "FLEXTabsViewController.h"
|
||||
#import "FLEXWindowManagerController.h"
|
||||
|
||||
static NSString *const kFLEXToolbarTopMarginDefaultsKey = @"com.flex.FLEXToolbar.topMargin";
|
||||
|
||||
@@ -416,23 +419,33 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
#pragma mark - Toolbar Dragging
|
||||
|
||||
- (void)setupToolbarGestures {
|
||||
FLEXExplorerToolbar *toolbar = self.explorerToolbar;
|
||||
|
||||
// Pan gesture for dragging.
|
||||
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc]
|
||||
[toolbar.dragHandle addGestureRecognizer:[[UIPanGestureRecognizer alloc]
|
||||
initWithTarget:self action:@selector(handleToolbarPanGesture:)
|
||||
];
|
||||
[self.explorerToolbar.dragHandle addGestureRecognizer:panGR];
|
||||
]];
|
||||
|
||||
// Tap gesture for hinting.
|
||||
UITapGestureRecognizer *hintTapGR = [[UITapGestureRecognizer alloc]
|
||||
[toolbar.dragHandle addGestureRecognizer:[[UITapGestureRecognizer alloc]
|
||||
initWithTarget:self action:@selector(handleToolbarHintTapGesture:)
|
||||
];
|
||||
[self.explorerToolbar.dragHandle addGestureRecognizer:hintTapGR];
|
||||
]];
|
||||
|
||||
// Tap gesture for showing additional details
|
||||
self.detailsTapGR = [[UITapGestureRecognizer alloc]
|
||||
initWithTarget:self action:@selector(handleToolbarDetailsTapGesture:)
|
||||
];
|
||||
[self.explorerToolbar.selectedViewDescriptionContainer addGestureRecognizer:self.detailsTapGR];
|
||||
[toolbar.selectedViewDescriptionContainer addGestureRecognizer:self.detailsTapGR];
|
||||
|
||||
// Long press gesture to present tabs manager
|
||||
[toolbar.globalsItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc]
|
||||
initWithTarget:self action:@selector(handleToolbarShowTabsGesture:)
|
||||
]];
|
||||
|
||||
// Long press gesture to present window manager
|
||||
[toolbar.selectItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc]
|
||||
initWithTarget:self action:@selector(handleToolbarWindowManagerGesture:)
|
||||
]];
|
||||
}
|
||||
|
||||
- (void)handleToolbarPanGesture:(UIPanGestureRecognizer *)panGR {
|
||||
@@ -501,12 +514,30 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
- (void)handleToolbarDetailsTapGesture:(UITapGestureRecognizer *)tapGR {
|
||||
if (tapGR.state == UIGestureRecognizerStateRecognized && self.selectedView) {
|
||||
UIViewController *topStackVC = [FLEXObjectExplorerFactory explorerViewControllerForObject:self.selectedView];
|
||||
[self makeKeyAndPresentViewController:
|
||||
[self presentViewController:
|
||||
[FLEXNavigationController withRootViewController:topStackVC]
|
||||
animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleToolbarShowTabsGesture:(UILongPressGestureRecognizer *)sender {
|
||||
// Back up the UIMenuController items since dismissViewController: will attempt to replace them
|
||||
self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
|
||||
|
||||
[super presentViewController:[[UINavigationController alloc]
|
||||
initWithRootViewController:[FLEXTabsViewController new]
|
||||
] animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)handleToolbarWindowManagerGesture:(UILongPressGestureRecognizer *)sender {
|
||||
// Back up the UIMenuController items since dismissViewController: will attempt to replace them
|
||||
self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
|
||||
|
||||
[super presentViewController:[[UINavigationController alloc]
|
||||
initWithRootViewController:[FLEXWindowManagerController new]
|
||||
] animated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - View Selection
|
||||
|
||||
@@ -737,20 +768,9 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Modal Dismissal
|
||||
|
||||
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController {
|
||||
[self presentedViewControllerDidDismiss];
|
||||
}
|
||||
|
||||
- (void)presentedViewControllerDidDismiss {
|
||||
[self resignKeyAndDismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Modal Presentation and Window Management
|
||||
|
||||
- (void)makeKeyAndPresentViewController:(UINavigationController *)toPresent
|
||||
- (void)presentViewController:(UIViewController *)toPresent
|
||||
animated:(BOOL)animated
|
||||
completion:(void (^)(void))completion {
|
||||
// Make our window key to correctly handle input.
|
||||
@@ -771,29 +791,11 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
UIMenuController.sharedMenuController.menuItems = @[copyObjectAddress];
|
||||
[UIMenuController.sharedMenuController update];
|
||||
|
||||
// Add the "Done" button to the navigation controller's view controller if it doesn't have one
|
||||
// (the hierarchy screen adds it's own done button in order to pass data between us)
|
||||
if (!toPresent.topViewController.navigationItem.rightBarButtonItem) {
|
||||
toPresent.topViewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
|
||||
target:self
|
||||
action:@selector(presentedViewControllerDidDismiss)
|
||||
];
|
||||
}
|
||||
|
||||
// Make myself the delegate for sheets presented modally so we can do the
|
||||
// proper cleanup when sheets are dragged to dismiss without the done button
|
||||
if (@available(iOS 13, *)) {
|
||||
toPresent.presentationController.delegate = self;
|
||||
}
|
||||
|
||||
// Show the view controller.
|
||||
[self presentViewController:toPresent animated:animated completion:completion];
|
||||
[super presentViewController:toPresent animated:animated completion:completion];
|
||||
}
|
||||
|
||||
- (void)resignKeyAndDismissViewControllerAnimated:(BOOL)animated
|
||||
completion:(void (^)(void))completion
|
||||
{
|
||||
- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion {
|
||||
UIWindow *appWindow = self.window.previousKeyWindow;
|
||||
[appWindow makeKeyWindow];
|
||||
[appWindow.rootViewController setNeedsStatusBarAppearanceUpdate];
|
||||
@@ -809,7 +811,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
// scroll to top, but below FLEX otherwise for exploration.
|
||||
[self statusWindow].windowLevel = UIWindowLevelStatusBar;
|
||||
|
||||
[self dismissViewControllerAnimated:animated completion:completion];
|
||||
[super dismissViewControllerAnimated:animated completion:completion];
|
||||
}
|
||||
|
||||
- (BOOL)wantsWindowToBecomeKey
|
||||
@@ -820,9 +822,9 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
- (void)toggleToolWithViewControllerProvider:(UINavigationController *(^)(void))future
|
||||
completion:(void(^)(void))completion {
|
||||
if (self.presentedViewController) {
|
||||
[self resignKeyAndDismissViewControllerAnimated:YES completion:completion];
|
||||
[self dismissViewControllerAnimated:YES completion:completion];
|
||||
} else if (future) {
|
||||
[self makeKeyAndPresentViewController:future() animated:YES completion:completion];
|
||||
[self presentViewController:future() animated:YES completion:completion];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// FLEXWindowManagerController.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/6/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXTableViewController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FLEXWindowManagerController : FLEXTableViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,303 @@
|
||||
//
|
||||
// FLEXWindowManagerController.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/6/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXWindowManagerController.h"
|
||||
#import "FLEXManager+Private.h"
|
||||
#import "FLEXUtility.h"
|
||||
|
||||
@interface FLEXWindowManagerController ()
|
||||
@property (nonatomic) UIWindow *keyWindow;
|
||||
@property (nonatomic, copy) NSString *keyWindowSubtitle;
|
||||
@property (nonatomic, copy) NSArray<UIWindow *> *windows;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *windowSubtitles;
|
||||
@property (nonatomic, copy) NSArray<UIScene *> *scenes API_AVAILABLE(ios(13));
|
||||
@property (nonatomic, copy) NSArray<NSString *> *sceneSubtitles;
|
||||
@end
|
||||
|
||||
@implementation FLEXWindowManagerController
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (id)init {
|
||||
return [self initWithStyle:UITableViewStylePlain];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.title = @"Windows";
|
||||
if (@available(iOS 13, *)) {
|
||||
self.title = @"Windows and Scenes";
|
||||
}
|
||||
|
||||
[self disableToolbar];
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissAnimated)
|
||||
];
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)reloadData {
|
||||
self.keyWindow = UIApplication.sharedApplication.keyWindow;
|
||||
self.windows = UIApplication.sharedApplication.windows;
|
||||
self.windowSubtitles = [self.windows flex_mapped:^id(UIWindow *window, NSUInteger idx) {
|
||||
return [NSString stringWithFormat:@"Level: %@ — Root: %@",
|
||||
@(window.windowLevel), window.rootViewController
|
||||
];
|
||||
}];
|
||||
self.keyWindowSubtitle = self.windowSubtitles[[self.windows indexOfObject:self.keyWindow]];
|
||||
if (@available(iOS 13, *)) {
|
||||
self.scenes = UIApplication.sharedApplication.connectedScenes.allObjects;
|
||||
self.sceneSubtitles = [self.scenes flex_mapped:^id(UIScene *scene, NSUInteger idx) {
|
||||
return [self sceneDescription:scene];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismissAnimated {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)showRevertOrDismissAlert:(void(^)())revertBlock {
|
||||
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
|
||||
[self reloadData];
|
||||
[self.tableView reloadData];
|
||||
|
||||
UIWindow *highestWindow = UIApplication.sharedApplication.keyWindow;
|
||||
UIWindowLevel maxLevel = 0;
|
||||
for (UIWindow *window in UIApplication.sharedApplication.windows) {
|
||||
if (window.windowLevel > maxLevel) {
|
||||
maxLevel = window.windowLevel;
|
||||
highestWindow = window;
|
||||
}
|
||||
}
|
||||
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Keep Changes?");
|
||||
make.message(@"If you do not wish to keep these settings, choose 'Revert Changes' below.");
|
||||
|
||||
make.button(@"Keep Changes").destructiveStyle();
|
||||
make.button(@"Keep Changes and Dismiss").destructiveStyle().handler(^(NSArray<NSString *> *strings) {
|
||||
[self dismissAnimated];
|
||||
});
|
||||
make.button(@"Revert Changes").cancelStyle().handler(^(NSArray<NSString *> *strings) {
|
||||
revertBlock();
|
||||
[self reloadData];
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
} showFrom:[FLEXUtility topViewControllerInWindow:highestWindow]];
|
||||
}
|
||||
|
||||
- (NSString *)sceneDescription:(UIScene *)scene API_AVAILABLE(ios(13)) {
|
||||
NSString *state = [self stringFromSceneState:scene.activationState];
|
||||
NSString *title = scene.title.length ? scene.title : nil;
|
||||
NSString *suffix = nil;
|
||||
|
||||
if ([scene isKindOfClass:[UIWindowScene class]]) {
|
||||
UIWindowScene *windowScene = (id)scene;
|
||||
suffix = FLEXPluralString(windowScene.windows.count, @"windows", @"window");
|
||||
}
|
||||
|
||||
NSMutableString *description = state.mutableCopy;
|
||||
if (title) {
|
||||
[description appendFormat:@" — %@", title];
|
||||
}
|
||||
if (suffix) {
|
||||
[description appendFormat:@" — %@", suffix];
|
||||
}
|
||||
|
||||
return description.copy;
|
||||
}
|
||||
|
||||
- (NSString *)stringFromSceneState:(UISceneActivationState)state API_AVAILABLE(ios(13)) {
|
||||
switch (state) {
|
||||
case UISceneActivationStateUnattached:
|
||||
return @"Unattached";
|
||||
case UISceneActivationStateForegroundActive:
|
||||
return @"Active";
|
||||
case UISceneActivationStateForegroundInactive:
|
||||
return @"Inactive";
|
||||
case UISceneActivationStateBackground:
|
||||
return @"Backgrounded";
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"Unknown state: %@", @(state)];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table View Data Source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
if (@available(iOS 13, *)) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
return self.windows.count;
|
||||
case 2:
|
||||
if (@available(iOS 13, *)) {
|
||||
return self.scenes.count;
|
||||
}
|
||||
}
|
||||
|
||||
@throw NSInternalInconsistencyException;
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
||||
switch (section) {
|
||||
case 0: return @"Key Window";
|
||||
case 1: return @"Windows";
|
||||
case 2: return @"Connected Scenes";
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
|
||||
UIWindow *window = nil;
|
||||
NSString *subtitle = nil;
|
||||
|
||||
switch (indexPath.section) {
|
||||
case 0:
|
||||
window = self.keyWindow;
|
||||
subtitle = self.keyWindowSubtitle;
|
||||
break;
|
||||
case 1:
|
||||
window = self.windows[indexPath.row];
|
||||
subtitle = self.windowSubtitles[indexPath.row];
|
||||
break;
|
||||
case 2:
|
||||
if (@available(iOS 13, *)) {
|
||||
UIScene *scene = self.scenes[indexPath.row];
|
||||
cell.textLabel.text = scene.description;
|
||||
cell.detailTextLabel.text = self.sceneSubtitles[indexPath.row];
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
cell.textLabel.text = window.description;
|
||||
cell.detailTextLabel.text = [NSString
|
||||
stringWithFormat:@"Level: %@ — Root: %@", @(window.windowLevel), window.rootViewController
|
||||
];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table View Delegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UIWindow *window = nil;
|
||||
NSString *subtitle = nil;
|
||||
FLEXWindow *flex = FLEXManager.sharedManager.explorerWindow;
|
||||
|
||||
id cancelHandler = ^{
|
||||
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
|
||||
};
|
||||
|
||||
switch (indexPath.section) {
|
||||
case 0:
|
||||
window = self.keyWindow;
|
||||
subtitle = self.keyWindowSubtitle;
|
||||
break;
|
||||
case 1:
|
||||
window = self.windows[indexPath.row];
|
||||
subtitle = self.windowSubtitles[indexPath.row];
|
||||
break;
|
||||
case 2:
|
||||
if (@available(iOS 13, *)) {
|
||||
UIScene *scene = self.scenes[indexPath.row];
|
||||
UIWindowScene *oldScene = flex.windowScene;
|
||||
BOOL isWindowScene = [scene isKindOfClass:[UIWindowScene class]];
|
||||
BOOL isFLEXScene = isWindowScene ? flex.windowScene == scene : NO;
|
||||
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(NSStringFromClass(scene.class));
|
||||
|
||||
if (isWindowScene) {
|
||||
if (isFLEXScene) {
|
||||
make.message(@"Already the FLEX window scene");
|
||||
}
|
||||
|
||||
make.button(@"Set as FLEX Window Scene")
|
||||
.handler(^(NSArray<NSString *> *strings) {
|
||||
flex.windowScene = (id)scene;
|
||||
[self showRevertOrDismissAlert:^{
|
||||
flex.windowScene = oldScene;
|
||||
}];
|
||||
}).enabled(!isFLEXScene);
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} else {
|
||||
make.message(@"Not a UIWindowScene");
|
||||
make.button(@"Dismiss").cancelStyle().handler(cancelHandler);
|
||||
}
|
||||
} showFrom:self];
|
||||
}
|
||||
}
|
||||
|
||||
__block UIWindow *targetWindow = nil, *oldKeyWindow = nil;
|
||||
__block UIWindowLevel oldLevel;
|
||||
__block BOOL wasVisible;
|
||||
|
||||
subtitle = [subtitle stringByAppendingString:
|
||||
@"\n\n1) Adjust the FLEX window level relative to this window,\n"
|
||||
"2) adjust this window's level relative to the FLEX window,\n"
|
||||
"3) set this window's level to a specific value, or\n"
|
||||
"4) make this window the key window if it isn't already."
|
||||
];
|
||||
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(NSStringFromClass(window.class)).message(subtitle);
|
||||
make.button(@"Adjust FLEX Window Level").handler(^(NSArray<NSString *> *strings) {
|
||||
targetWindow = flex; oldLevel = flex.windowLevel;
|
||||
flex.windowLevel = window.windowLevel + strings.firstObject.integerValue;
|
||||
|
||||
[self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
|
||||
});
|
||||
make.button(@"Adjust This Window's Level").handler(^(NSArray<NSString *> *strings) {
|
||||
targetWindow = window; oldLevel = window.windowLevel;
|
||||
window.windowLevel = flex.windowLevel + strings.firstObject.integerValue;
|
||||
|
||||
[self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
|
||||
});
|
||||
make.button(@"Set This Window's Level").handler(^(NSArray<NSString *> *strings) {
|
||||
targetWindow = window; oldLevel = window.windowLevel;
|
||||
window.windowLevel = strings.firstObject.integerValue;
|
||||
|
||||
[self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
|
||||
});
|
||||
make.button(@"Make Key And Visible").handler(^(NSArray<NSString *> *strings) {
|
||||
oldKeyWindow = UIApplication.sharedApplication.keyWindow;
|
||||
wasVisible = window.hidden;
|
||||
[window makeKeyAndVisible];
|
||||
|
||||
[self showRevertOrDismissAlert:^{
|
||||
window.hidden = wasVisible;
|
||||
[oldKeyWindow makeKeyWindow];
|
||||
}];
|
||||
}).enabled(!window.isKeyWindow && !window.hidden);
|
||||
make.button(@"Cancel").cancelStyle().handler(cancelHandler);
|
||||
|
||||
make.textField(@"+/- window level, i.e. 5 or -10");
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// FLEXTabList.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/1/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FLEXTabList : NSObject
|
||||
|
||||
@property (nonatomic, readonly, class) FLEXTabList *sharedList;
|
||||
|
||||
@property (nonatomic, readonly, nullable) UINavigationController *activeTab;
|
||||
@property (nonatomic, readonly) NSArray<UINavigationController *> *openTabs;
|
||||
/// Snapshots of each tab when they were last active.
|
||||
@property (nonatomic, readonly) NSArray<UIImage *> *openTabSnapshots;
|
||||
/// \c NSNotFound if no tabs are present.
|
||||
/// Setting this property changes the active tab to one of the already open tabs.
|
||||
@property (nonatomic) NSInteger activeTabIndex;
|
||||
|
||||
/// Adds a new tab and sets the new tab as the active tab.
|
||||
- (void)addTab:(UINavigationController *)newTab;
|
||||
/// Closes the given tab. If this tab was the active tab,
|
||||
/// the most recent tab before that becomes the active tab.
|
||||
- (void)closeTab:(UINavigationController *)tab;
|
||||
/// Closes a tab at the given index. If this tab was the active tab,
|
||||
/// the most recent tab before that becomes the active tab.
|
||||
- (void)closeTabAtIndex:(NSInteger)idx;
|
||||
/// Closes all of the tabs at the given indexes. If the active tab
|
||||
/// is included, the most recent still-open tab becomes the active tab.
|
||||
- (void)closeTabsAtIndexes:(NSIndexSet *)indexes;
|
||||
/// A shortcut to close the active tab.
|
||||
- (void)closeActiveTab;
|
||||
/// A shortcut to close \e every tab.
|
||||
- (void)closeAllTabs;
|
||||
|
||||
- (void)updateSnapshotForActiveTab;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// FLEXTabList.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/1/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXTabList.h"
|
||||
#import "FLEXUtility.h"
|
||||
|
||||
@interface FLEXTabList () {
|
||||
NSMutableArray *_openTabs;
|
||||
NSMutableArray *_openTabSnapshots;
|
||||
}
|
||||
@end
|
||||
#pragma mark -
|
||||
@implementation FLEXTabList
|
||||
|
||||
#pragma mark Initialization
|
||||
|
||||
+ (FLEXTabList *)sharedList {
|
||||
static FLEXTabList *sharedList = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedList = [self new];
|
||||
});
|
||||
|
||||
return sharedList;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_openTabs = [NSMutableArray new];
|
||||
_openTabSnapshots = [NSMutableArray new];
|
||||
_activeTabIndex = NSNotFound;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (void)chooseNewActiveTab {
|
||||
if (self.openTabs.count) {
|
||||
self.activeTabIndex = self.openTabs.count - 1;
|
||||
} else {
|
||||
self.activeTabIndex = NSNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
- (void)setActiveTabIndex:(NSInteger)idx {
|
||||
NSParameterAssert(idx < self.openTabs.count || idx == NSNotFound);
|
||||
if (_activeTabIndex == idx) return;
|
||||
|
||||
_activeTabIndex = idx;
|
||||
_activeTab = (idx == NSNotFound) ? nil : self.openTabs[idx];
|
||||
}
|
||||
|
||||
- (void)addTab:(UINavigationController *)newTab {
|
||||
NSParameterAssert(newTab);
|
||||
|
||||
// Update snapshot of the last active tab
|
||||
if (self.activeTab) {
|
||||
[self updateSnapshotForActiveTab];
|
||||
}
|
||||
|
||||
// Add new tab and snapshot,
|
||||
// update active tab and index
|
||||
[_openTabs addObject:newTab];
|
||||
[_openTabSnapshots addObject:[FLEXUtility previewImageForView:newTab.view]];
|
||||
_activeTab = newTab;
|
||||
_activeTabIndex = self.openTabs.count - 1;
|
||||
}
|
||||
|
||||
- (void)closeTab:(UINavigationController *)tab {
|
||||
NSParameterAssert(tab);
|
||||
NSParameterAssert([self.openTabs containsObject:tab]);
|
||||
NSInteger idx = [self.openTabs indexOfObject:tab];
|
||||
|
||||
[self closeTabAtIndex:idx];
|
||||
}
|
||||
|
||||
- (void)closeTabAtIndex:(NSInteger)idx {
|
||||
NSParameterAssert(idx < self.openTabs.count);
|
||||
|
||||
// Remove old tab and snapshot
|
||||
[_openTabs removeObjectAtIndex:idx];
|
||||
[_openTabSnapshots removeObjectAtIndex:idx];
|
||||
|
||||
// Update active tab and index if needed
|
||||
if (self.activeTabIndex == idx) {
|
||||
[self chooseNewActiveTab];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeTabsAtIndexes:(NSIndexSet *)indexes {
|
||||
// Remove old tabs and snapshot
|
||||
[_openTabs removeObjectsAtIndexes:indexes];
|
||||
[_openTabSnapshots removeObjectsAtIndexes:indexes];
|
||||
|
||||
// Update active tab and index if needed
|
||||
if ([indexes containsIndex:self.activeTabIndex]) {
|
||||
[self chooseNewActiveTab];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeActiveTab {
|
||||
[self closeTab:self.activeTab];
|
||||
}
|
||||
|
||||
- (void)closeAllTabs {
|
||||
// Remove tabs and snapshots
|
||||
[_openTabs removeAllObjects];
|
||||
[_openTabSnapshots removeAllObjects];
|
||||
|
||||
// Update active tab index
|
||||
self.activeTabIndex = NSNotFound;
|
||||
}
|
||||
|
||||
- (void)updateSnapshotForActiveTab {
|
||||
if (self.activeTabIndex != NSNotFound) {
|
||||
UIImage *newSnapshot = [FLEXUtility previewImageForView:self.activeTab.view];
|
||||
[_openTabSnapshots replaceObjectAtIndex:self.activeTabIndex withObject:newSnapshot];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXTabsViewController.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/4/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXTableViewController.h"
|
||||
|
||||
@interface FLEXTabsViewController : FLEXTableViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// FLEXTabsViewController.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/4/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXTabsViewController.h"
|
||||
#import "FLEXNavigationController.h"
|
||||
#import "FLEXTabList.h"
|
||||
#import "FLEXBookmarkManager.h"
|
||||
#import "FLEXTableView.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXColor.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
#import "FLEXExplorerViewController.h"
|
||||
#import "FLEXGlobalsViewController.h"
|
||||
#import "FLEXBookmarksViewController.h"
|
||||
|
||||
@interface FLEXTabsViewController ()
|
||||
@property (nonatomic, copy) NSArray<UINavigationController *> *openTabs;
|
||||
@property (nonatomic, copy) NSArray<UIImage *> *tabSnapshots;
|
||||
@property (nonatomic) NSInteger activeIndex;
|
||||
@property (nonatomic) BOOL presentNewActiveTabOnDismiss;
|
||||
|
||||
@property (nonatomic, readonly) FLEXExplorerViewController *corePresenter;
|
||||
@end
|
||||
|
||||
@implementation FLEXTabsViewController
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (id)init {
|
||||
return [self initWithStyle:UITableViewStylePlain];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.title = @"Open Tabs";
|
||||
self.navigationController.hidesBarsOnSwipe = NO;
|
||||
self.tableView.allowsMultipleSelectionDuringEditing = YES;
|
||||
|
||||
[FLEXTabList.sharedList updateSnapshotForActiveTab];
|
||||
[self reloadData:NO];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[self setupDefaultBarItems];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
/// @param trackActiveTabDelta whether to check if the active
|
||||
/// tab changed and needs to be presented upon "Done" dismissal.
|
||||
/// @return whether the active tab changed or not (if there are any tabs left)
|
||||
- (BOOL)reloadData:(BOOL)trackActiveTabDelta {
|
||||
BOOL activeTabDidChange = NO;
|
||||
FLEXTabList *list = FLEXTabList.sharedList;
|
||||
|
||||
// Flag to enable check to determine whether
|
||||
if (trackActiveTabDelta) {
|
||||
NSInteger oldActiveIndex = self.activeIndex;
|
||||
if (oldActiveIndex != list.activeTabIndex && list.activeTabIndex != NSNotFound) {
|
||||
self.presentNewActiveTabOnDismiss = YES;
|
||||
activeTabDidChange = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// We assume the tabs aren't going to change out from under us, since
|
||||
// presenting any other tool via keyboard shortcuts should dismiss us first
|
||||
self.openTabs = list.openTabs;
|
||||
self.tabSnapshots = list.openTabSnapshots;
|
||||
self.activeIndex = list.activeTabIndex;
|
||||
|
||||
return activeTabDidChange;
|
||||
}
|
||||
|
||||
- (void)reloadActiveTabRowIfChanged:(BOOL)activeTabChanged {
|
||||
// Refresh the newly active tab row if needed
|
||||
if (activeTabChanged) {
|
||||
NSIndexPath *active = [NSIndexPath
|
||||
indexPathForRow:self.activeIndex inSection:0
|
||||
];
|
||||
[self.tableView reloadRowsAtIndexPaths:@[active] withRowAnimation:UITableViewRowAnimationNone];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupDefaultBarItems {
|
||||
self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated));
|
||||
self.toolbarItems = @[
|
||||
UIBarButtonItem.flex_fixedSpace,
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
FLEXBarButtonItemSystem(Add, self, @selector(addTabButtonPressed)),
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)),
|
||||
];
|
||||
|
||||
// Disable editing if no tabs available
|
||||
self.toolbarItems.lastObject.enabled = self.openTabs.count > 0;
|
||||
}
|
||||
|
||||
- (void)setupEditingBarItems {
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
self.toolbarItems = @[
|
||||
[UIBarButtonItem itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed)],
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
[UIBarButtonItem disabledSystemItem:UIBarButtonSystemItemAdd],
|
||||
UIBarButtonItem.flex_flexibleSpace,
|
||||
// We use a non-system done item because we change its title dynamically
|
||||
[UIBarButtonItem doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)]
|
||||
];
|
||||
|
||||
self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor;
|
||||
}
|
||||
|
||||
- (FLEXExplorerViewController *)corePresenter {
|
||||
// We must be presented by a FLEXExplorerViewController, or presented
|
||||
// by another view controller that was presented by FLEXExplorerViewController
|
||||
FLEXExplorerViewController *presenter = (id)self.presentingViewController;
|
||||
presenter = (id)presenter.presentingViewController ?: presenter;
|
||||
NSAssert(
|
||||
[presenter isKindOfClass:[FLEXExplorerViewController class]],
|
||||
@"The tabs view controller expects to be presented by the explorer controller"
|
||||
);
|
||||
return presenter;
|
||||
}
|
||||
|
||||
#pragma mark Button Actions
|
||||
|
||||
- (void)dismissAnimated {
|
||||
if (self.presentNewActiveTabOnDismiss) {
|
||||
// The active tab was closed so we need to present the new one
|
||||
UIViewController *activeTab = FLEXTabList.sharedList.activeTab;
|
||||
FLEXExplorerViewController *presenter = self.corePresenter;
|
||||
[presenter dismissViewControllerAnimated:YES completion:^{
|
||||
[presenter presentViewController:activeTab animated:YES completion:nil];
|
||||
}];
|
||||
} else if (self.activeIndex == NSNotFound) {
|
||||
// The only tab was closed, so dismiss everything
|
||||
[self.corePresenter dismissViewControllerAnimated:YES completion:nil];
|
||||
} else {
|
||||
// Simple dismiss with the same active tab, only dismiss myself
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)toggleEditing {
|
||||
NSArray<NSIndexPath *> *selected = self.tableView.indexPathsForSelectedRows;
|
||||
self.editing = !self.editing;
|
||||
|
||||
if (self.isEditing) {
|
||||
[self setupEditingBarItems];
|
||||
} else {
|
||||
[self setupDefaultBarItems];
|
||||
|
||||
// Get index set of tabs to close
|
||||
NSMutableIndexSet *indexes = [NSMutableIndexSet new];
|
||||
for (NSIndexPath *ip in selected) {
|
||||
[indexes addIndex:ip.row];
|
||||
}
|
||||
|
||||
if (selected.count) {
|
||||
// Close tabs and update data source
|
||||
[FLEXTabList.sharedList closeTabsAtIndexes:indexes];
|
||||
BOOL activeTabChanged = [self reloadData:YES];
|
||||
|
||||
// Remove deleted rows
|
||||
[self.tableView deleteRowsAtIndexPaths:selected withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
|
||||
// Refresh the newly active tab row if needed
|
||||
[self reloadActiveTabRowIfChanged:activeTabChanged];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addTabButtonPressed {
|
||||
if (FLEXBookmarkManager.bookmarks.count) {
|
||||
[FLEXAlert makeSheet:^(FLEXAlert *make) {
|
||||
make.title(@"New Tab");
|
||||
make.button(@"Main Menu").handler(^(NSArray<NSString *> *strings) {
|
||||
[self addTabAndDismiss:[FLEXNavigationController
|
||||
withRootViewController:[FLEXGlobalsViewController new]
|
||||
]];
|
||||
});
|
||||
make.button(@"Choose from Bookmarks").handler(^(NSArray<NSString *> *strings) {
|
||||
[self presentViewController:[FLEXNavigationController
|
||||
withRootViewController:[FLEXBookmarksViewController new]
|
||||
] animated:YES completion:nil];
|
||||
});
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:self];
|
||||
} else {
|
||||
// No bookmarks, just open the main menu
|
||||
[self addTabAndDismiss:[FLEXNavigationController
|
||||
withRootViewController:[FLEXGlobalsViewController new]
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addTabAndDismiss:(UINavigationController *)newTab {
|
||||
FLEXExplorerViewController *presenter = self.corePresenter;
|
||||
[presenter dismissViewControllerAnimated:YES completion:^{
|
||||
[presenter presentViewController:newTab animated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)closeAllButtonPressed {
|
||||
[FLEXAlert makeSheet:^(FLEXAlert *make) {
|
||||
NSInteger count = self.openTabs.count;
|
||||
NSString *title = FLEXPluralFormatString(count, @"Close %@ tabs", @"Close %@ tab");
|
||||
make.button(title).destructiveStyle().handler(^(NSArray<NSString *> *strings) {
|
||||
[self closeAll];
|
||||
[self toggleEditing];
|
||||
});
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
- (void)closeAll {
|
||||
NSInteger rowCount = self.openTabs.count;
|
||||
|
||||
// Close tabs and update data source
|
||||
[FLEXTabList.sharedList closeAllTabs];
|
||||
[self reloadData:YES];
|
||||
|
||||
// Delete rows from table view
|
||||
NSArray<NSIndexPath *> *allRows = [NSArray flex_forEachUpTo:rowCount map:^id(NSUInteger row) {
|
||||
return [NSIndexPath indexPathForRow:row inSection:0];
|
||||
}];
|
||||
[self.tableView deleteRowsAtIndexPaths:allRows withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
#pragma mark - Table View Data Source
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.openTabs.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
|
||||
|
||||
UINavigationController *tab = self.openTabs[indexPath.row];
|
||||
cell.imageView.image = self.tabSnapshots[indexPath.row];
|
||||
cell.textLabel.text = tab.topViewController.title;
|
||||
cell.detailTextLabel.text = FLEXPluralString(tab.viewControllers.count, @"pages", @"page");
|
||||
|
||||
if (!cell.tag) {
|
||||
cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
|
||||
cell.detailTextLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
|
||||
cell.tag = 1;
|
||||
}
|
||||
|
||||
if (indexPath.row == self.activeIndex) {
|
||||
cell.backgroundColor = FLEXColor.secondaryBackgroundColor;
|
||||
} else {
|
||||
cell.backgroundColor = FLEXColor.primaryBackgroundColor;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - Table View Delegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.editing) {
|
||||
// Case: editing with multi-select
|
||||
self.toolbarItems.lastObject.title = @"Close Selected";
|
||||
self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor;
|
||||
} else {
|
||||
if (self.activeIndex == indexPath.row && self.corePresenter != self.presentingViewController) {
|
||||
// Case: selected the already active tab
|
||||
[self dismissAnimated];
|
||||
} else {
|
||||
// Case: selected a different tab,
|
||||
// or selected a tab when presented from the FLEX toolbar
|
||||
FLEXTabList.sharedList.activeTabIndex = indexPath.row;
|
||||
self.presentNewActiveTabOnDismiss = YES;
|
||||
[self dismissAnimated];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSParameterAssert(self.editing);
|
||||
|
||||
if (tableView.indexPathsForSelectedRows.count == 0) {
|
||||
self.toolbarItems.lastObject.title = @"Done";
|
||||
self.toolbarItems.lastObject.tintColor = self.view.tintColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)table
|
||||
commitEditingStyle:(UITableViewCellEditingStyle)edit
|
||||
forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSParameterAssert(edit == UITableViewCellEditingStyleDelete);
|
||||
|
||||
// Close tab and update data source
|
||||
[FLEXTabList.sharedList closeTab:self.openTabs[indexPath.row]];
|
||||
BOOL activeTabChanged = [self reloadData:YES];
|
||||
|
||||
// Delete row from table view
|
||||
[table deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
|
||||
// Refresh the newly active tab row if needed
|
||||
[self reloadActiveTabRowIfChanged:activeTabChanged];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -77,10 +77,19 @@
|
||||
}
|
||||
|
||||
+ (instancetype)instancesTableViewControllerForInstancesReferencingObject:(id)object {
|
||||
static Class SwiftObjectClass = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
SwiftObjectClass = NSClassFromString(@"SwiftObject");
|
||||
if (!SwiftObjectClass) {
|
||||
SwiftObjectClass = NSClassFromString(@"Swift._SwiftObject");
|
||||
}
|
||||
});
|
||||
|
||||
NSMutableArray<FLEXObjectRef *> *instances = [NSMutableArray array];
|
||||
[FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id tryObject, __unsafe_unretained Class actualClass) {
|
||||
// Skip Swift objects
|
||||
if ([actualClass isKindOfClass:NSClassFromString(@"SwiftObject")]) {
|
||||
if ([actualClass isKindOfClass:SwiftObjectClass]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#import "FLEXGlobalsEntry.h"
|
||||
#import "FLEXManager+Private.h"
|
||||
#import "FLEXSystemLogTableViewController.h"
|
||||
#import "FLEXNetworkHistoryTableViewController.h"
|
||||
#import "FLEXNetworkMITMViewController.h"
|
||||
#import "FLEXAddressExplorerCoordinator.h"
|
||||
#import "FLEXGlobalsSection.h"
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
@implementation FLEXGlobalsViewController
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
+ (NSString *)globalsTitleForSection:(FLEXGlobalsSectionKind)section {
|
||||
switch (section) {
|
||||
case FLEXGlobalsSectionProcessAndEvents:
|
||||
@@ -66,7 +68,7 @@
|
||||
case FLEXGlobalsRowSystemLog:
|
||||
return [FLEXSystemLogTableViewController flex_concreteGlobalsEntry:row];
|
||||
case FLEXGlobalsRowNetworkHistory:
|
||||
return [FLEXNetworkHistoryTableViewController flex_concreteGlobalsEntry:row];
|
||||
return [FLEXNetworkMITMViewController flex_concreteGlobalsEntry:row];
|
||||
case FLEXGlobalsRowKeyWindow:
|
||||
case FLEXGlobalsRowRootViewController:
|
||||
case FLEXGlobalsRowProcessInfo:
|
||||
@@ -128,6 +130,7 @@
|
||||
return sections;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - UIViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@@ -151,6 +154,13 @@
|
||||
self.sections = self.allSections;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[self disableToolbar];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Search Bar
|
||||
|
||||
- (void)updateSearchResults:(NSString *)newText {
|
||||
@@ -168,6 +178,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (NSArray<FLEXGlobalsSection *> *)nonemptySections {
|
||||
@@ -176,6 +187,7 @@
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table View Data Source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
@@ -206,6 +218,7 @@
|
||||
|
||||
#pragma mark - Table View Delegate
|
||||
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FLEXTableViewSection *section = self.sections[indexPath.section];
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXKeyboardShortcutManager.h"
|
||||
#import "FLEXExplorerViewController.h"
|
||||
#import "FLEXNetworkHistoryTableViewController.h"
|
||||
#import "FLEXNetworkMITMViewController.h"
|
||||
#import "FLEXKeyboardHelpViewController.h"
|
||||
#import "FLEXFileBrowserTableViewController.h"
|
||||
|
||||
@@ -106,9 +106,20 @@
|
||||
} description:@"Toggle move tool"];
|
||||
|
||||
[self registerSimulatorShortcutWithKey:@"n" modifiers:0 action:^{
|
||||
[self toggleTopViewControllerOfClass:[FLEXNetworkHistoryTableViewController class]];
|
||||
[self toggleTopViewControllerOfClass:[FLEXNetworkMITMViewController class]];
|
||||
} description:@"Toggle network history view"];
|
||||
|
||||
// 't' is for testing: quickly present an object explorer for debugging
|
||||
[self registerSimulatorShortcutWithKey:@"t" modifiers:0 action:^{
|
||||
[self showExplorerIfNeeded];
|
||||
|
||||
[self.explorerViewController toggleToolWithViewControllerProvider:^UINavigationController *{
|
||||
return [FLEXNavigationController withRootViewController:[FLEXObjectExplorerFactory
|
||||
explorerViewControllerForObject:NSBundle.mainBundle
|
||||
]];
|
||||
} completion:nil];
|
||||
} description:@"Present an object explorer for debugging"];
|
||||
|
||||
[self registerSimulatorShortcutWithKey:UIKeyInputDownArrow modifiers:0 action:^{
|
||||
if (self.isHidden || ![self.explorerViewController handleDownArrowKeyPressed]) {
|
||||
[self tryScrollDown];
|
||||
@@ -210,11 +221,7 @@
|
||||
}
|
||||
|
||||
- (UIViewController *)topViewController {
|
||||
UIViewController *topViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
|
||||
while (topViewController.presentedViewController) {
|
||||
topViewController = topViewController.presentedViewController;
|
||||
}
|
||||
return topViewController;
|
||||
return [FLEXUtility topViewControllerInWindow:UIApplication.sharedApplication.keyWindow];
|
||||
}
|
||||
|
||||
- (void)toggleTopViewControllerOfClass:(Class)class {
|
||||
@@ -234,7 +241,7 @@
|
||||
}
|
||||
} else {
|
||||
// Present it in an entirely new navigation controller
|
||||
[topViewController presentViewController:
|
||||
[self.explorerViewController presentViewController:
|
||||
[FLEXNavigationController withRootViewController:[class new]]
|
||||
animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
//
|
||||
|
||||
#import "FLEXManager.h"
|
||||
#import "FLEXWindow.h"
|
||||
|
||||
@class FLEXGlobalsEntry, FLEXExplorerViewController;
|
||||
|
||||
@interface FLEXManager (Private)
|
||||
|
||||
//@property (nonatomic) FLEXWindow *explorerWindow;
|
||||
@property (nonatomic, readonly) FLEXWindow *explorerWindow;
|
||||
@property (nonatomic, readonly) FLEXExplorerViewController *explorerViewController;
|
||||
|
||||
/// An array of FLEXGlobalsEntry objects that have been registered by the user.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// FLEXNetworkHistoryTableViewController.h
|
||||
// FLEXNetworkMITMViewController.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 2/8/15.
|
||||
@@ -9,6 +9,6 @@
|
||||
#import "FLEXTableViewController.h"
|
||||
#import "FLEXGlobalsEntry.h"
|
||||
|
||||
@interface FLEXNetworkHistoryTableViewController : FLEXTableViewController <FLEXGlobalsEntry>
|
||||
@interface FLEXNetworkMITMViewController : FLEXTableViewController <FLEXGlobalsEntry>
|
||||
|
||||
@end
|
||||
+68
-27
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// FLEXNetworkHistoryTableViewController.m
|
||||
// FLEXNetworkMITMViewController.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 2/8/15.
|
||||
@@ -8,15 +8,16 @@
|
||||
|
||||
#import "FLEXColor.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXNetworkHistoryTableViewController.h"
|
||||
#import "FLEXNetworkMITMViewController.h"
|
||||
#import "FLEXNetworkTransaction.h"
|
||||
#import "FLEXNetworkTransactionTableViewCell.h"
|
||||
#import "FLEXNetworkRecorder.h"
|
||||
#import "FLEXNetworkTransactionDetailTableViewController.h"
|
||||
#import "FLEXNetworkObserver.h"
|
||||
#import "FLEXNetworkTransactionTableViewCell.h"
|
||||
#import "FLEXNetworkTransactionDetailTableViewController.h"
|
||||
#import "FLEXNetworkSettingsTableViewController.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
|
||||
@interface FLEXNetworkHistoryTableViewController ()
|
||||
@interface FLEXNetworkMITMViewController ()
|
||||
|
||||
/// Backing model
|
||||
@property (nonatomic, copy) NSArray<FLEXNetworkTransaction *> *networkTransactions;
|
||||
@@ -26,10 +27,13 @@
|
||||
|
||||
@property (nonatomic) BOOL rowInsertInProgress;
|
||||
@property (nonatomic) BOOL isPresentingSearch;
|
||||
@property (nonatomic) BOOL pendingReload;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXNetworkHistoryTableViewController
|
||||
@implementation FLEXNetworkMITMViewController
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (id)init {
|
||||
self = [super initWithStyle:UITableViewStylePlain];
|
||||
@@ -52,6 +56,8 @@
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
@@ -67,19 +73,39 @@
|
||||
[self updateTransactions];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Reload the table if we received updates while not on-screen
|
||||
if (self.pendingReload) {
|
||||
[self.tableView reloadData];
|
||||
self.pendingReload = NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
#pragma mark Button Actions
|
||||
|
||||
- (void)settingsButtonTapped:(id)sender {
|
||||
FLEXNetworkSettingsTableViewController *settingsViewController = [FLEXNetworkSettingsTableViewController new];
|
||||
settingsViewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(settingsViewControllerDoneTapped:)];
|
||||
settingsViewController.title = @"Network Debugging Settings";
|
||||
UIViewController *settings = [FLEXNetworkSettingsTableViewController new];
|
||||
settings.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(
|
||||
Done, self, @selector(settingsViewControllerDoneTapped:)
|
||||
);
|
||||
settings.title = @"Network Debugging Settings";
|
||||
|
||||
// This is not a FLEXNavigationController because it is not intended as a new tab
|
||||
UINavigationController *wrapperNavigationController = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
|
||||
[self presentViewController:wrapperNavigationController animated:YES completion:nil];
|
||||
UIViewController *nav = [[UINavigationController alloc] initWithRootViewController:settings];
|
||||
[self presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)settingsViewControllerDoneTapped:(id)sender {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark Transactions
|
||||
|
||||
- (void)updateTransactions {
|
||||
self.networkTransactions = [[FLEXNetworkRecorder defaultRecorder] networkTransactions];
|
||||
}
|
||||
@@ -101,9 +127,9 @@
|
||||
[self updateFirstSectionHeader];
|
||||
}
|
||||
|
||||
- (void)setFilteredNetworkTransactions:(NSArray<FLEXNetworkTransaction *> *)filteredNetworkTransactions {
|
||||
if (![_filteredNetworkTransactions isEqual:filteredNetworkTransactions]) {
|
||||
_filteredNetworkTransactions = filteredNetworkTransactions;
|
||||
- (void)setFilteredNetworkTransactions:(NSArray<FLEXNetworkTransaction *> *)networkTransactions {
|
||||
if (![_filteredNetworkTransactions isEqual:networkTransactions]) {
|
||||
_filteredNetworkTransactions = networkTransactions;
|
||||
[self updateFilteredBytesReceived];
|
||||
}
|
||||
}
|
||||
@@ -117,6 +143,8 @@
|
||||
[self updateFirstSectionHeader];
|
||||
}
|
||||
|
||||
#pragma mark Header
|
||||
|
||||
- (void)updateFirstSectionHeader {
|
||||
UIView *view = [self.tableView headerViewForSection:0];
|
||||
if ([view isKindOfClass:[UITableViewHeaderFooterView class]]) {
|
||||
@@ -147,6 +175,7 @@
|
||||
return headerText;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - FLEXGlobalsEntry
|
||||
|
||||
+ (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row {
|
||||
@@ -157,15 +186,19 @@
|
||||
return [self new];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Notification Handlers
|
||||
|
||||
- (void)handleNewTransactionRecordedNotification:(NSNotification *)notification {
|
||||
if (self.viewIfLoaded.window) {
|
||||
[self tryUpdateTransactions];
|
||||
}
|
||||
[self tryUpdateTransactions];
|
||||
}
|
||||
|
||||
- (void)tryUpdateTransactions {
|
||||
// Don't do any updating if we aren't in the view hierarchy
|
||||
if (!self.viewIfLoaded.window) {
|
||||
self.pendingReload = YES;
|
||||
return;
|
||||
}
|
||||
// Let the previous row insert animation finish before starting a new one to avoid stomping.
|
||||
// We'll try calling the method again when the insertion completes, and we properly no-op if there haven't been changes.
|
||||
if (self.rowInsertInProgress) {
|
||||
@@ -240,6 +273,7 @@
|
||||
[self updateFirstSectionHeader];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table view data source
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
@@ -278,6 +312,7 @@
|
||||
[self.navigationController pushViewController:detailViewController animated:YES];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Menu Actions
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
@@ -326,19 +361,25 @@
|
||||
return self.searchController.isActive ? self.filteredNetworkTransactions[indexPath.row] : self.networkTransactions[indexPath.row];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Search Bar
|
||||
|
||||
- (void)updateSearchResults:(NSString *)searchString {
|
||||
[self onBackgroundQueue:^NSArray *{
|
||||
return [self.networkTransactions filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(FLEXNetworkTransaction *transaction, NSDictionary<NSString *, id> *bindings) {
|
||||
return [[transaction.request.URL absoluteString] rangeOfString:searchString options:NSCaseInsensitiveSearch].length > 0;
|
||||
}]];
|
||||
} thenOnMainQueue:^(NSArray *filteredNetworkTransactions) {
|
||||
if ([self.searchText isEqual:searchString]) {
|
||||
self.filteredNetworkTransactions = filteredNetworkTransactions;
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
}];
|
||||
if (!searchString) {
|
||||
self.filteredNetworkTransactions = self.networkTransactions;
|
||||
[self.tableView reloadData];
|
||||
} else {
|
||||
[self onBackgroundQueue:^NSArray *{
|
||||
return [self.networkTransactions flex_filtered:^BOOL(FLEXNetworkTransaction *entry, NSUInteger idx) {
|
||||
return [entry.request.URL.absoluteString localizedCaseInsensitiveContainsString:searchString];
|
||||
}];
|
||||
} thenOnMainQueue:^(NSArray *filteredNetworkTransactions) {
|
||||
if ([self.searchText isEqual:searchString]) {
|
||||
self.filteredNetworkTransactions = filteredNetworkTransactions;
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark UISearchControllerDelegate
|
||||
@@ -9,11 +9,14 @@
|
||||
#import "FLEXObjectExplorerViewController.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
#import "FLEXMultilineTableViewCell.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXFieldEditorViewController.h"
|
||||
#import "FLEXMethodCallingViewController.h"
|
||||
#import "FLEXInstancesViewController.h"
|
||||
#import "FLEXTabsViewController.h"
|
||||
#import "FLEXBookmarkManager.h"
|
||||
#import "FLEXTableView.h"
|
||||
#import "FLEXTableViewCell.h"
|
||||
#import "FLEXScopeCarousel.h"
|
||||
@@ -71,7 +74,6 @@
|
||||
#pragma mark - View controller lifecycle
|
||||
|
||||
- (void)loadView {
|
||||
// TODO: grouped with rounded corners or not?
|
||||
FLEXTableView *tableView = [[FLEXTableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
|
||||
self.tableView = tableView;
|
||||
|
||||
@@ -86,6 +88,8 @@
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.showsShareToolbarItem = YES;
|
||||
|
||||
// Use [object class] here rather than object_getClass
|
||||
// to avoid the KVO prefix for observed objects
|
||||
self.title = [[self.object class] description];
|
||||
@@ -223,7 +227,22 @@
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 {
|
||||
return [g2 isKindOfClass:[UIPanGestureRecognizer class]];
|
||||
return [g2 isKindOfClass:[UIPanGestureRecognizer class]] && g2 != self.navigationController.interactivePopGestureRecognizer;
|
||||
}
|
||||
|
||||
- (void)shareButtonPressed {
|
||||
[FLEXAlert makeSheet:^(FLEXAlert *make) {
|
||||
make.button(@"Add to Bookmarks").handler(^(NSArray<NSString *> *strings) {
|
||||
[FLEXBookmarkManager.bookmarks addObject:self.object];
|
||||
});
|
||||
make.button(@"Copy Description").handler(^(NSArray<NSString *> *strings) {
|
||||
UIPasteboard.generalPasteboard.string = self.explorer.objectDescription;
|
||||
});
|
||||
make.button(@"Copy Address").handler(^(NSArray<NSString *> *strings) {
|
||||
[self copyObjectAddress:nil];
|
||||
});
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
#pragma mark - Description
|
||||
@@ -239,6 +258,7 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Search
|
||||
|
||||
- (void)updateSearchResults:(NSString *)newText; {
|
||||
@@ -266,6 +286,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Reloading
|
||||
|
||||
- (void)reloadData {
|
||||
@@ -406,6 +427,7 @@
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - UIMenuController
|
||||
|
||||
/// Prevent the search bar from trying to use us as a responder
|
||||
|
||||
@@ -81,7 +81,7 @@ typedef NS_ENUM(NSUInteger, FLEXCollectionType) {
|
||||
case FLEXUnorderedCollection:
|
||||
return [self describe:[self objectForRow:row]];
|
||||
case FLEXKeyedCollection:
|
||||
return [self describe:self.collection.allKeys[row]];
|
||||
return [self describe:self.cachedCollection.allKeys[row]];
|
||||
|
||||
case FLEXUnsupportedCollection:
|
||||
return nil;
|
||||
@@ -112,11 +112,11 @@ typedef NS_ENUM(NSUInteger, FLEXCollectionType) {
|
||||
- (id)objectForRow:(NSInteger)row {
|
||||
switch (self.collectionType) {
|
||||
case FLEXOrderedCollection:
|
||||
return self.collection[row];
|
||||
return self.cachedCollection[row];
|
||||
case FLEXUnorderedCollection:
|
||||
return self.collection.allObjects[row];
|
||||
return self.cachedCollection.allObjects[row];
|
||||
case FLEXKeyedCollection:
|
||||
return self.collection[self.collection.allKeys[row]];
|
||||
return self.cachedCollection[self.cachedCollection.allKeys[row]];
|
||||
|
||||
case FLEXUnsupportedCollection:
|
||||
return nil;
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
+ (instancetype)forDefaults:(NSUserDefaults *)userDefaults {
|
||||
FLEXDefaultsContentSection *section = [self forReusableFuture:^id(FLEXDefaultsContentSection *section) {
|
||||
section.defaults = userDefaults;
|
||||
section.onlyShowKeysForAppPrefs = YES;
|
||||
return section.whitelistedDefaults;
|
||||
}];
|
||||
|
||||
section.defaults = userDefaults;
|
||||
return section;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,12 @@ NSString * FLEXTypeEncodingString(const char *returnType, NSUInteger count, ...)
|
||||
+ (void)load {
|
||||
// We need to get all of the methods in this file and add them to NSProxy.
|
||||
// To do this we we need the class itself and it's metaclass.
|
||||
// Edit: also add them to Swift._SwiftObject
|
||||
Class NSProxyClass = [NSProxy class];
|
||||
Class NSProxy_meta = object_getClass(NSProxyClass);
|
||||
Class SwiftObjectClass = (
|
||||
NSClassFromString(@"SwiftObject") ?: NSClassFromString(@"Swift._SwiftObject")
|
||||
);
|
||||
|
||||
// Copy all of the "flex_" methods from NSObject
|
||||
id filterFunc = ^BOOL(FLEXMethod *method, NSUInteger idx) {
|
||||
@@ -54,10 +58,18 @@ NSString * FLEXTypeEncodingString(const char *returnType, NSUInteger count, ...)
|
||||
NSArray *instanceMethods = [[NSObject flex_allInstanceMethods] flex_filtered:filterFunc];
|
||||
NSArray *classMethods = [[NSObject flex_allClassMethods] flex_filtered:filterFunc];
|
||||
|
||||
FLEXClassBuilder *proxy = [FLEXClassBuilder builderForClass:NSProxyClass];
|
||||
FLEXClassBuilder *meta = [FLEXClassBuilder builderForClass:NSProxy_meta];
|
||||
FLEXClassBuilder *proxy = [FLEXClassBuilder builderForClass:NSProxyClass];
|
||||
FLEXClassBuilder *proxyMeta = [FLEXClassBuilder builderForClass:NSProxy_meta];
|
||||
[proxy addMethods:instanceMethods];
|
||||
[meta addMethods:classMethods];
|
||||
[proxyMeta addMethods:classMethods];
|
||||
|
||||
if (SwiftObjectClass) {
|
||||
Class SwiftObject_meta = object_getClass(SwiftObjectClass);
|
||||
FLEXClassBuilder *swiftObject = [FLEXClassBuilder builderForClass:SwiftObjectClass];
|
||||
FLEXClassBuilder *swiftObjectMeta = [FLEXClassBuilder builderForClass:SwiftObject_meta];
|
||||
[swiftObject addMethods:instanceMethods];
|
||||
[swiftObjectMeta addMethods:classMethods];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// UIBarButtonItem+FLEX.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/4/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#define FLEXBarButtonItemSystem(item, tgt, sel) \
|
||||
[UIBarButtonItem systemItem:UIBarButtonSystemItem##item target:tgt action:sel]
|
||||
|
||||
@interface UIBarButtonItem (FLEX)
|
||||
|
||||
@property (nonatomic, readonly, class) UIBarButtonItem *flex_flexibleSpace;
|
||||
@property (nonatomic, readonly, class) UIBarButtonItem *flex_fixedSpace;
|
||||
|
||||
+ (instancetype)itemWithCustomView:(UIView *)customView;
|
||||
|
||||
+ (instancetype)systemItem:(UIBarButtonSystemItem)item target:(id)target action:(SEL)action;
|
||||
|
||||
+ (instancetype)itemWithTitle:(NSString *)title target:(id)target action:(SEL)action;
|
||||
+ (instancetype)doneStyleitemWithTitle:(NSString *)title target:(id)target action:(SEL)action;
|
||||
|
||||
+ (instancetype)itemWithImage:(UIImage *)image
|
||||
style:(UIBarButtonItemStyle)style
|
||||
target:(id)target
|
||||
action:(SEL)action;
|
||||
|
||||
+ (instancetype)disabledSystemItem:(UIBarButtonSystemItem)item;
|
||||
+ (instancetype)disabledItemWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style;
|
||||
+ (instancetype)disabledItemWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// UIBarButtonItem+FLEX.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/4/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
|
||||
@implementation UIBarButtonItem (FLEX)
|
||||
|
||||
+ (UIBarButtonItem *)flex_flexibleSpace {
|
||||
return [self systemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
||||
}
|
||||
|
||||
+ (UIBarButtonItem *)flex_fixedSpace {
|
||||
return [self systemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)systemItem:(UIBarButtonSystemItem)item target:(id)target action:(SEL)action {
|
||||
return [[self alloc] initWithBarButtonSystemItem:item target:target action:action];
|
||||
}
|
||||
|
||||
+ (instancetype)itemWithCustomView:(UIView *)customView {
|
||||
return [[self alloc] initWithCustomView:customView];
|
||||
}
|
||||
|
||||
+ (instancetype)itemWithTitle:(NSString *)title target:(id)target action:(SEL)action {
|
||||
return [[self alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:target action:action];
|
||||
}
|
||||
|
||||
+ (instancetype)doneStyleitemWithTitle:(NSString *)title target:(id)target action:(SEL)action {
|
||||
return [[self alloc] initWithTitle:title style:UIBarButtonItemStyleDone target:target action:action];
|
||||
}
|
||||
|
||||
+ (instancetype)itemWithImage:(UIImage *)image
|
||||
style:(UIBarButtonItemStyle)style
|
||||
target:(id)target
|
||||
action:(SEL)action {
|
||||
return [[self alloc] initWithImage:image style:style target:target action:action];
|
||||
}
|
||||
|
||||
+ (instancetype)disabledSystemItem:(UIBarButtonSystemItem)system {
|
||||
UIBarButtonItem *item = [self systemItem:system target:nil action:nil];
|
||||
item.enabled = NO;
|
||||
return item;
|
||||
}
|
||||
|
||||
+ (instancetype)disabledItemWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style {
|
||||
UIBarButtonItem *item = [self itemWithTitle:title target:nil action:nil];
|
||||
item.enabled = NO;
|
||||
return item;
|
||||
}
|
||||
|
||||
+ (instancetype)disabledItemWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style {
|
||||
UIBarButtonItem *item = [self itemWithImage:image style:style target:nil action:nil];
|
||||
item.enabled = NO;
|
||||
return item;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -40,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (readonly, class) UIColor *toolbarItemHighlightedColor;
|
||||
@property (readonly, class) UIColor *toolbarItemSelectedColor;
|
||||
@property (readonly, class) UIColor *hairlineColor;
|
||||
@property (readonly, class) UIColor *destructiveColor;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -133,4 +133,8 @@
|
||||
return FLEXDynamicColor(systemGrayColor, grayColor);
|
||||
}
|
||||
|
||||
+ (UIColor *)destructiveColor {
|
||||
return FLEXDynamicColor(systemRedColor, redColor);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -66,6 +66,10 @@ NS_INLINE CGRect FLEXRectSetHeight(CGRect r, CGFloat height) {
|
||||
stringWithFormat:@"%@ %@", @(count), (count == 1 ? singular : plural) \
|
||||
]
|
||||
|
||||
#define FLEXPluralFormatString(count, pluralFormat, singularFormat) [NSString \
|
||||
stringWithFormat:(count == 1 ? singularFormat : pluralFormat), @(count) \
|
||||
]
|
||||
|
||||
#if !FLEX_AT_LEAST_IOS13_SDK
|
||||
@class UIWindowScene;
|
||||
#endif
|
||||
@@ -77,6 +81,8 @@ NS_INLINE CGRect FLEXRectSetHeight(CGRect r, CGFloat height) {
|
||||
@property (nonatomic, readonly, class) UIWindow *appKeyWindow;
|
||||
/// The first active \c UIWindowScene of the app.
|
||||
@property (nonatomic, readonly, class) UIWindowScene *activeScene API_AVAILABLE(ios(13.0));
|
||||
/// @return top-most view controller of the given window
|
||||
+ (UIViewController *)topViewControllerInWindow:(UIWindow *)window;
|
||||
|
||||
+ (UIColor *)consistentRandomColorForObject:(id)object;
|
||||
+ (NSString *)descriptionForView:(UIView *)view includingFrame:(BOOL)includeFrame;
|
||||
|
||||
@@ -59,6 +59,14 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (UIViewController *)topViewControllerInWindow:(UIWindow *)window {
|
||||
UIViewController *topViewController = window.rootViewController;
|
||||
while (topViewController.presentedViewController) {
|
||||
topViewController = topViewController.presentedViewController;
|
||||
}
|
||||
return topViewController;
|
||||
}
|
||||
|
||||
+ (UIColor *)consistentRandomColorForObject:(id)object {
|
||||
CGFloat hue = (((NSUInteger)object >> 4) % 256) / 255.0;
|
||||
return [UIColor colorWithHue:hue saturation:1.0 brightness:1.0 alpha:1.0];
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "FLEXObjcInternal.h"
|
||||
//#import "FLEXSwiftRuntimeUtility.h"
|
||||
|
||||
// See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6
|
||||
NSString *const kFLEXPropertyAttributeKeyTypeEncoding = @"T";
|
||||
@@ -296,13 +297,18 @@ const unsigned int kFLEXNumberOfImplicitArgs = 2;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Case: `object` is a Swift object or class. Does not implement methodSignatureForSelector:
|
||||
// if ([FLEXSwiftRuntimeUtility isSwiftObjectOrClass:object]) {
|
||||
// return [FLEXSwiftRuntimeUtility performSelector:selector onSwiftObject:object withArguments:arguments error:error];
|
||||
// }
|
||||
|
||||
// Probably an unsupported type encoding, like bitfields
|
||||
// or inline arrays. In the future, we could calculate
|
||||
// the return length on our own. For now, we abort.
|
||||
//
|
||||
// For future reference, the code here will get the true type encoding.
|
||||
// NSMethodSignature will convert {?=b8b4b1b1b18[8S]} to {?}
|
||||
// A solution might involve hooking NSGetSizeAndAlignment.
|
||||
// NSMethodSignature will convert `{?=b8b4b1b1b18[8S]}` to `{?}`.
|
||||
// A solution might involve hooking NSGetSizeAndAlignment or writing our own..
|
||||
//
|
||||
// returnType = method_getTypeEncoding(class_getInstanceMethod([object class], selector));
|
||||
NSMethodSignature *methodSignature = [object methodSignatureForSelector:selector];
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// FLEXPointers.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#ifndef FLEXPointers_h
|
||||
#define FLEXPointers_h
|
||||
|
||||
constexpr BOOL is64bit() {
|
||||
#if __LP64__
|
||||
return YES;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr ssize_t existentialHeaderSize() {
|
||||
#if __LP64__
|
||||
return 16;
|
||||
#else
|
||||
return 8;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr void * bridge(id object) {
|
||||
return (__bridge void *)object;
|
||||
}
|
||||
|
||||
//struct RelativePointer<P, T> {
|
||||
// P offset;
|
||||
//
|
||||
// T get() {
|
||||
//
|
||||
// }
|
||||
//}
|
||||
|
||||
//void * valuePointer(void *value, void *typeInfo) {
|
||||
//
|
||||
// let kind = Kind(type: Value.self)
|
||||
//
|
||||
// switch kind {
|
||||
// case .struct:
|
||||
// return try withUnsafePointer(to: &value) { try body(valuePtr.mutable.raw) }
|
||||
// case .class:
|
||||
// return try withClassValuePointer(of: &value, body)
|
||||
// case .existential:
|
||||
// return try withExistentialValuePointer(of: &value, body)
|
||||
// default:
|
||||
// throw RuntimeError.couldNotGetPointer(type: Value.self, value: value)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func existentialValuePointer<Value, Result>(of value: inout Value, _ body: (UMRPointer)) {
|
||||
// // value is boxed as Any
|
||||
// let container = valuePtr.withMemoryRebound(to: ExistentialContainer.self, capacity: 1) {valuePtr.pointee}
|
||||
// let info = try metadata(of: container.type)
|
||||
// if info.kind == .class || info.size > ExistentialContainerBuffer.size() {
|
||||
// let base = valuePtr.withMemoryRebound(to: UMRPointer.self, capacity: 1) {valuePtr.pointee}
|
||||
// if info.kind == .struct {
|
||||
// return try body(base.advanced(by: existentialHeaderSize))
|
||||
// } else {
|
||||
// return try body(base)
|
||||
// }
|
||||
// } else {
|
||||
// return try body(valuePtr.mutable.raw)
|
||||
// }
|
||||
//}
|
||||
|
||||
#endif /* FLEXPointers_h */
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXSwiftMethod.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FLEXSwiftMethod : NSObject
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXSwiftMethod.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXSwiftMethod.h"
|
||||
|
||||
@implementation FLEXSwiftMethod
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// FLEXSwiftMirror.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FLEXSwiftMirror : NSObject
|
||||
|
||||
+ (BOOL)isSwiftObjectOrClass:(id)objectOrClass;
|
||||
|
||||
+ (instancetype)reflecting:(id)objectOrClass;
|
||||
|
||||
@property (nonatomic, readonly) id target;
|
||||
@property (nonatomic, readonly) FLEXSwiftMirror *classMirror;
|
||||
|
||||
- (NSString *)typeNameForIvarAtOffset:(NSUInteger)offset;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// FLEXSwiftMirror.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXSwiftMirror.h"
|
||||
#import "FLEXSwiftRuntimeUtility.h"
|
||||
#import "SwiftMetadata.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface FLEXSwiftMirror ()
|
||||
@property (nonatomic, readonly) ClassMetadata *metadata;
|
||||
@end
|
||||
|
||||
@implementation FLEXSwiftMirror
|
||||
|
||||
+ (BOOL)isSwiftObjectOrClass:(id)objectOrClass {
|
||||
assert(objectOrClass);
|
||||
|
||||
Class cls = objectOrClass;
|
||||
if (!object_isClass(objectOrClass)) {
|
||||
cls = object_getClass(objectOrClass);
|
||||
}
|
||||
|
||||
ClassMetadata *swiftClass = (__bridge ClassMetadata *)(cls);
|
||||
return cls == NSClassFromString(@"SwiftObject") || (uintptr_t)swiftClass->rodata & (0x1);
|
||||
}
|
||||
|
||||
+ (instancetype)reflecting:(id)objectOrClass {
|
||||
NSAssert([FLEXSwiftRuntimeUtility isSwiftObjectOrClass:objectOrClass],
|
||||
@"Attempted to reflect a non-Swift instance: %@", objectOrClass);
|
||||
return [[self alloc] initWithTarget:objectOrClass];
|
||||
}
|
||||
|
||||
- (id)initWithTarget:(id)target {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
if (!object_isClass(target)) {
|
||||
_classMirror = [FLEXSwiftMirror reflecting:object_getClass(target)];
|
||||
}
|
||||
_target = target;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)typeNameForIvarAtOffset:(NSUInteger)offset {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// FLEXSwiftRuntimeUtility.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FLEXSwiftRuntimeUtility : NSObject
|
||||
|
||||
+ (BOOL)swiftRuntimeAvailable;
|
||||
+ (Class)SwiftObjectClass;
|
||||
+ (BOOL)isSwiftObjectOrClass:(id)objectOrClass;
|
||||
|
||||
+ (id)performSelector:(SEL)selector onSwiftObject:(id)object withArguments:(NSArray *)arguments error:(NSError * __autoreleasing *)error;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// FLEXSwiftRuntimeUtility.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXSwiftRuntimeUtility.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "SwiftMetadata.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation FLEXSwiftRuntimeUtility
|
||||
|
||||
+ (BOOL)swiftRuntimeAvailable {
|
||||
return [self SwiftObjectClass] != nil;
|
||||
}
|
||||
|
||||
+ (Class)SwiftObjectClass {
|
||||
static Class SwiftObject = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
SwiftObject = NSClassFromString(@"SwiftObject");
|
||||
});
|
||||
|
||||
return SwiftObject;
|
||||
}
|
||||
|
||||
+ (BOOL)isSwiftObjectOrClass:(id)objectOrClass {
|
||||
// Automatic NO if Swift is not in use
|
||||
Class SwiftObject = [self SwiftObjectClass];
|
||||
if (!SwiftObject) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Make sure we have the Class
|
||||
Class cls = objectOrClass;
|
||||
if (!object_isClass(objectOrClass)) {
|
||||
cls = object_getClass(objectOrClass);
|
||||
}
|
||||
|
||||
// Determine if inherits from SwiftObject
|
||||
while (cls != nil && cls != SwiftObject) {
|
||||
cls = class_getSuperclass(cls);
|
||||
}
|
||||
|
||||
return cls == SwiftObject;
|
||||
}
|
||||
|
||||
+ (id)performSelector:(SEL)selector onSwiftObject:(id)object withArguments:(NSArray *)arguments error:(NSError *__autoreleasing *)error {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXSwiftType.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FLEXSwiftType : NSObject
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXSwiftType.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXSwiftType.h"
|
||||
|
||||
@implementation FLEXSwiftType
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// ExistentialContainer.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ExistentialContainer_h
|
||||
#define ExistentialContainer_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/// AKA "Any"
|
||||
struct ExistentialContainerBuffer {
|
||||
NSInteger a, b, c;
|
||||
|
||||
ssize_t size() {
|
||||
return sizeof(ExistentialContainerBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
struct ExistentialContainer {
|
||||
ExistentialContainerBuffer buffer;
|
||||
void *type;
|
||||
uintptr_t witnessTable;
|
||||
}
|
||||
|
||||
#endif /* ExistentialContainer_h */
|
||||
@@ -0,0 +1,218 @@
|
||||
//
|
||||
// SwiftMetadata.h
|
||||
// Swift Test
|
||||
//
|
||||
// Created by Tanner on 10/28/17.
|
||||
// Copyright © 2017 Tanner Bennett. All rights reserved.
|
||||
//
|
||||
|
||||
#include "FLEXPointers.h"
|
||||
#include "RelativePointer.h"
|
||||
#include "MetadataValues.h"
|
||||
#include <MacTypes.h>
|
||||
|
||||
using namespace swift;
|
||||
|
||||
#pragma mark - Integral Types
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MetadataKind) {
|
||||
MetadataKindStruct = 1,
|
||||
MetadataKindEnum,
|
||||
MetadataKindOptional,
|
||||
MetadataKindOpaque = 8,
|
||||
MetadataKindTuple,
|
||||
MetadataKindFunction,
|
||||
MetadataKindExistential = 12,
|
||||
MetadataKindMetatype,
|
||||
MetadataKindObjcClassWrapper,
|
||||
MetadataKindExistentialMetatype,
|
||||
MetadataKindForeignClass,
|
||||
MetadataKindHeapLocalVariable = 64,
|
||||
MetadataKindHeapGenericLocalVariable = 65,
|
||||
MetadataKindErrorObject = 128
|
||||
// MetadataKindClass = isa
|
||||
};
|
||||
|
||||
#pragma mark - IDK
|
||||
|
||||
typedef union _CommonMetadata {
|
||||
MetadataKind kind;
|
||||
Class isa;
|
||||
} Metadata;
|
||||
|
||||
#pragma mark - Type Descriptors
|
||||
|
||||
struct ContextDescriptor {
|
||||
ContextDescriptorFlags flags;
|
||||
RelativeIndirectablePointer<ContextDescriptor, true> parent;
|
||||
|
||||
bool isGeneric() const { return flags.isGeneric(); }
|
||||
bool isUnique() const { return flags.isUnique(); }
|
||||
ContextDescriptorKind getKind() const { return flags.getKind(); }
|
||||
|
||||
/// Get the generic context information for this context, or null if the
|
||||
/// context is not generic.
|
||||
const TargetGenericContext<Runtime> *getGenericContext() const;
|
||||
|
||||
unsigned genericParamCount() const {
|
||||
auto *genericContext = getGenericContext();
|
||||
return genericContext
|
||||
? genericContext->getGenericContextHeader().NumParams
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// For classes and structs
|
||||
typedef struct _StructureDescriptor {
|
||||
NSInteger fieldCount;
|
||||
NSInteger fieldOffsetVectorOffset;
|
||||
const char **names;
|
||||
Metadata **(*fieldTypesAccessor)(void *typeMetadata);
|
||||
} StructureDescriptor;
|
||||
|
||||
/// For enums
|
||||
typedef struct _EnumDescriptor {
|
||||
NSUInteger payloadInfo;
|
||||
NSUInteger noPayloadCaseCount;
|
||||
const char **caseNames;
|
||||
Metadata **(*fieldTypesAccessor)(void *typeMetadata);
|
||||
} EnumDescriptor;
|
||||
|
||||
typedef struct ClassTypeDescriptor {
|
||||
ContextDescriptorFlags flags;
|
||||
int32_t parent;
|
||||
RelativeDirectPointer<char> mangledName;
|
||||
RelativeDirectPointer<int64_t> fieldTypesAccessor;
|
||||
RelativeDirectPointer<void *> fieldDescriptor; // FieldDescriptor
|
||||
RelativeDirectPointer<Class> superClass; // id
|
||||
int32_t negativeSizeAndBoundsUnion;
|
||||
int32_t metadataPositiveSizeInWords;
|
||||
int32_t numImmediateMembers;
|
||||
int32_t numberOfFields;
|
||||
RelativeDirectPointer<int64_t> offsetToTheFieldOffsetVector; // int64_t[]
|
||||
// TargetTypeGenericContextDescriptorHeader genericContextHeader;
|
||||
} ClassTypeDescriptor;
|
||||
|
||||
struct NominalTypeDescriptor {
|
||||
enum Kind : NSUInteger {
|
||||
Class = 0,
|
||||
Struct = 1,
|
||||
Enum = 2
|
||||
};
|
||||
|
||||
NominalTypeDescriptor::Kind kind;
|
||||
RelativeDirectPointer<char> mangledName;
|
||||
union {
|
||||
StructureDescriptor ivars; // structs / classes
|
||||
EnumDescriptor cases; // enums
|
||||
} typeInfo;
|
||||
struct {
|
||||
void *metadataPattern;
|
||||
NSUInteger parameterVectorOffset;
|
||||
NSUInteger typeParamCount; // Includes associated types
|
||||
NSUInteger formalTypeParamCount;
|
||||
NSUInteger witnessTableCount[1]; // Variable-length with size .typeParamCount
|
||||
} generic;
|
||||
};
|
||||
|
||||
struct ClassTypeDescriptor : NominalTypeDescriptor {
|
||||
RelativeDirectPointer<NSInteger> fieldTypes;
|
||||
|
||||
}
|
||||
|
||||
#define _High8BitMask 0xFF00000000000000
|
||||
#define _High8Offset 24
|
||||
|
||||
#define LowBits(highbits) 8*sizeof(NSUInteger) - highbits
|
||||
|
||||
#define ArgumentIsInOut(arg) (arg & 0x1)
|
||||
#define EnumPayloadGetCaseCount(enum) (enum->payloadInfo & 0xFFFFFF)
|
||||
#define EnumPayloadGetSizeOffset(enum) ((enum->payloadInfo & _High8BitMask) >> _High8Offset)
|
||||
|
||||
typedef struct _SwiftClassMetadata {
|
||||
Class metaclass;
|
||||
struct _SwiftClassMetadata *superclass;
|
||||
uintptr_t reserved[2];
|
||||
uintptr_t rodata; // (rodata & 0x1) -> isSwiftClass, except SwiftObject
|
||||
UInt32 classFlags;
|
||||
UInt32 instanceAddressOffset;
|
||||
UInt32 instanceSize;
|
||||
UInt16 instanceAlignmentMask;
|
||||
UInt16 reserved_;
|
||||
UInt32 classObjectSize;
|
||||
UInt32 classObjectAddressPoint;
|
||||
NominalTypeDescriptor *nominalTypeDescriptor;
|
||||
|
||||
// Inline variable-sized arrays
|
||||
struct ClassHierarchyInfo {
|
||||
struct _SwiftClassMetadata *parent; // Currently always nil
|
||||
// struct GenericParameterVector {
|
||||
// TypeMetadata *T, *U, *V;
|
||||
// GenericWitnessTable *T_wt, *U_wt, *V_wt;
|
||||
// } genericParameters[genericCount];
|
||||
// IMP vtable[methodCount];
|
||||
// idk FieldOffsetVector;
|
||||
} classHierarchy[1]; // Sized to superclass count
|
||||
} ClassMetadata;
|
||||
|
||||
typedef struct _SwiftStructMetadata {
|
||||
MetadataKind kind;
|
||||
NominalTypeDescriptor *type;
|
||||
Metadata *parent; // Always nil for now
|
||||
NSUInteger fieldOffsets[1]; // Sized to ivar count
|
||||
// struct GenericParameterVector {
|
||||
// TypeMetadata *T, *U, *V;
|
||||
// GenericWitnessTable *T_wt, *U_wt, *V_wt;
|
||||
// } genericParameters[genericCount];
|
||||
} StructMetadata;
|
||||
|
||||
typedef struct _SwiftEnumMetadata {
|
||||
MetadataKind kind;
|
||||
NominalTypeDescriptor *type;
|
||||
Metadata *parent; // Always nil for now
|
||||
// struct GenericParameterVector {
|
||||
// TypeMetadata *T, *U, *V;
|
||||
// GenericWitnessTable *T_wt, *U_wt, *V_wt;
|
||||
// } genericParameters[genericCount];
|
||||
} EnumMetadata;
|
||||
|
||||
typedef struct _SwiftTupleMetadata {
|
||||
MetadataKind kind;
|
||||
NSUInteger numberOfArguments;
|
||||
const char *names; // Inline array of names like ["foo", "bar", etc]
|
||||
struct {
|
||||
NominalTypeDescriptor *type;
|
||||
NSUInteger offset;
|
||||
} arguments[1]; // Sized to .numberOfArguments
|
||||
} TupleMetadata;
|
||||
|
||||
typedef struct _SwiftFunctionMetadata {
|
||||
MetadataKind kind;
|
||||
UInt8 throws;
|
||||
UInt8 metadataConvention;
|
||||
NSUInteger numberOfArguments : LowBits(16);
|
||||
Metadata *arguments[1]; // Sized to numberOfArguments
|
||||
// Metadata *returnType;
|
||||
} FunctionMetadata;
|
||||
|
||||
typedef struct _SwiftProtocolMetadata {
|
||||
MetadataKind kind;
|
||||
union {
|
||||
struct {
|
||||
BOOL classConstrained : 1;
|
||||
NSUInteger witnessTableCount : 31;
|
||||
} flags;
|
||||
NSUInteger unused;
|
||||
} layout;
|
||||
NSUInteger conformedCount;
|
||||
void *protocolDescriptors;
|
||||
} ProtocolMetadata;
|
||||
|
||||
typedef struct _SwiftMetatypeMetadata {
|
||||
MetadataKind kind;
|
||||
Metadata *instanceType;
|
||||
} MetatypeMetadata;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// Metadata.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Metadata_h
|
||||
#define Metadata_h
|
||||
|
||||
#import "SwiftMetadata.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
/// See https://github.com/apple/swift/blob/master/docs/ABI/TypeMetadata.rst#class-metadata
|
||||
|
||||
class Kind {
|
||||
public:
|
||||
enum Value : short {
|
||||
struct_,
|
||||
enum_,
|
||||
optional,
|
||||
opaque,
|
||||
tuple,
|
||||
function,
|
||||
existential,
|
||||
metatype,
|
||||
objCClassWrapper,
|
||||
existentialMetatype,
|
||||
foreignClass,
|
||||
heapLocalVariable,
|
||||
heapGenericLocalVariable,
|
||||
errorObject,
|
||||
class_,
|
||||
};
|
||||
|
||||
private:
|
||||
Value value;
|
||||
|
||||
public:
|
||||
|
||||
enum : uintptr_t {
|
||||
NonHeap = 0x200,
|
||||
RuntimePrivate = 0x100,
|
||||
NonType = 0x400,
|
||||
};
|
||||
|
||||
Kind() = default;
|
||||
constexpr Kind(Value kind) : value(kind) { }
|
||||
|
||||
Kind(uintptr_t flag) {
|
||||
switch (flag) {
|
||||
case 0:
|
||||
value = class_; break;
|
||||
case 1:
|
||||
value = struct_; break;
|
||||
case (0 | NonHeap):
|
||||
value = struct_; break;
|
||||
case 2:
|
||||
value = enum_; break;
|
||||
case (1 | NonHeap):
|
||||
value = enum_; break;
|
||||
case 3:
|
||||
value = optional; break;
|
||||
case (2 | NonHeap):
|
||||
value = optional; break;
|
||||
case 8:
|
||||
value = opaque; break;
|
||||
case (3 | NonHeap):
|
||||
value = foreignClass; break;
|
||||
case 9:
|
||||
value = tuple; break;
|
||||
case (0 | RuntimePrivate | NonHeap):
|
||||
value = opaque; break;
|
||||
case 10:
|
||||
value = function; break;
|
||||
case (1 | RuntimePrivate | NonHeap):
|
||||
value = tuple; break;
|
||||
case 12:
|
||||
value = existential; break;
|
||||
case (2 | RuntimePrivate | NonHeap):
|
||||
value = function; break;
|
||||
case 13:
|
||||
value = metatype; break;
|
||||
case (3 | RuntimePrivate | NonHeap):
|
||||
value = existential; break;
|
||||
case 14:
|
||||
value = objCClassWrapper; break;
|
||||
case (4 | RuntimePrivate | NonHeap):
|
||||
value = metatype; break;
|
||||
case 15:
|
||||
value = existentialMetatype; break;
|
||||
case (5 | RuntimePrivate | NonHeap):
|
||||
value = objCClassWrapper; break;
|
||||
case 16:
|
||||
value = foreignClass; break;
|
||||
case (6 | RuntimePrivate | NonHeap):
|
||||
value = existentialMetatype; break;
|
||||
case 64:
|
||||
value = heapLocalVariable; break;
|
||||
case (0 | NonType):
|
||||
value = heapLocalVariable; break;
|
||||
case 65:
|
||||
value = heapGenericLocalVariable; break;
|
||||
case (0 | NonType | RuntimePrivate):
|
||||
value = heapGenericLocalVariable; break;
|
||||
case 128:
|
||||
value = errorObject; break;
|
||||
case (1 | NonType | RuntimePrivate):
|
||||
value = errorObject; break;
|
||||
default:
|
||||
if (flag > 4096) {
|
||||
value = class_; break;
|
||||
} else {
|
||||
@throw NSInvalidArgumentException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator Value() const { return value; } // Allow switch and comparisons.
|
||||
// note: Putting constexpr here causes
|
||||
// clang to stop warning on incomplete
|
||||
// case handling.
|
||||
explicit operator bool() = delete; // Prevent usage: if(fruit)
|
||||
|
||||
constexpr bool myMethod() const { return value == class_; }
|
||||
};
|
||||
|
||||
constexpr NSInteger * metadataPointer(void *type) {
|
||||
return (NSInteger *)type;
|
||||
}
|
||||
|
||||
NSString * getObjectClassName(id object) {
|
||||
Class cls = object_getClass(object);
|
||||
ClassMetadata *metadata = (__bridge ClassMetadata *)cls;
|
||||
const char *name = metadata->nominalTypeDescriptor->mangledName.get();
|
||||
return @(name);
|
||||
}
|
||||
|
||||
//func swiftObject() -> Any.Type {
|
||||
// class Temp {}
|
||||
// let md = ClassMetadata(type: Temp.self)
|
||||
// return md.pointer.pointee.superClass
|
||||
//}
|
||||
|
||||
NSInteger classIsSwiftMask() {
|
||||
if (@available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* Metadata_h */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// MetadataValues.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Taken from swift/ABI/MetadataValues.h
|
||||
//
|
||||
|
||||
#ifndef MetadataValues_h
|
||||
#define MetadataValues_h
|
||||
|
||||
/// Kinds of context descriptor.
|
||||
enum class ContextDescriptorKind : uint8_t {
|
||||
/// This context descriptor represents a module.
|
||||
Module = 0,
|
||||
|
||||
/// This context descriptor represents an extension.
|
||||
Extension = 1,
|
||||
|
||||
/// This context descriptor represents an anonymous possibly-generic context
|
||||
/// such as a function body.
|
||||
Anonymous = 2,
|
||||
|
||||
/// This context descriptor represents a protocol context.
|
||||
Protocol = 3,
|
||||
|
||||
/// This context descriptor represents an opaque type alias.
|
||||
OpaqueType = 4,
|
||||
|
||||
/// First kind that represents a type of any sort.
|
||||
Type_First = 16,
|
||||
|
||||
/// This context descriptor represents a class.
|
||||
Class = Type_First,
|
||||
|
||||
/// This context descriptor represents a struct.
|
||||
Struct = Type_First + 1,
|
||||
|
||||
/// This context descriptor represents an enum.
|
||||
Enum = Type_First + 2,
|
||||
|
||||
/// Last kind that represents a type of any sort.
|
||||
Type_Last = 31,
|
||||
};
|
||||
|
||||
/// Common flags stored in the first 32-bit word of any context descriptor.
|
||||
struct ContextDescriptorFlags {
|
||||
private:
|
||||
uint32_t value;
|
||||
|
||||
explicit constexpr ContextDescriptorFlags(uint32_t value) : value(value) {}
|
||||
|
||||
public:
|
||||
constexpr ContextDescriptorFlags() : value(0) {}
|
||||
constexpr ContextDescriptorFlags(ContextDescriptorKind kind,
|
||||
bool isGeneric,
|
||||
bool isUnique,
|
||||
uint8_t version,
|
||||
uint16_t kindSpecificFlags)
|
||||
: ContextDescriptorFlags(ContextDescriptorFlags()
|
||||
.withKind(kind)
|
||||
.withGeneric(isGeneric)
|
||||
.withUnique(isUnique)
|
||||
.withVersion(version)
|
||||
.withKindSpecificFlags(kindSpecificFlags))
|
||||
{ }
|
||||
|
||||
/// The kind of context this descriptor describes.
|
||||
constexpr ContextDescriptorKind getKind() const {
|
||||
return ContextDescriptorKind(value & 0x1Fu);
|
||||
}
|
||||
|
||||
/// Whether the context being described is generic.
|
||||
constexpr bool isGeneric() const {
|
||||
return (value & 0x80u) != 0;
|
||||
}
|
||||
|
||||
/// Whether this is a unique record describing the referenced context.
|
||||
constexpr bool isUnique() const {
|
||||
return (value & 0x40u) != 0;
|
||||
}
|
||||
|
||||
/// The format version of the descriptor. Higher version numbers may have
|
||||
/// additional fields that aren't present in older versions.
|
||||
constexpr uint8_t getVersion() const {
|
||||
return (value >> 8u) & 0xFFu;
|
||||
}
|
||||
|
||||
/// The most significant two bytes of the flags word, which can have
|
||||
/// kind-specific meaning.
|
||||
constexpr uint16_t getKindSpecificFlags() const {
|
||||
return (value >> 16u) & 0xFFFFu;
|
||||
}
|
||||
|
||||
constexpr ContextDescriptorFlags withKind(ContextDescriptorKind kind) const {
|
||||
return assert((uint8_t(kind) & 0x1F) == uint8_t(kind)),
|
||||
ContextDescriptorFlags((value & 0xFFFFFFE0u) | uint8_t(kind));
|
||||
}
|
||||
|
||||
constexpr ContextDescriptorFlags withGeneric(bool isGeneric) const {
|
||||
return ContextDescriptorFlags((value & 0xFFFFFF7Fu)
|
||||
| (isGeneric ? 0x80u : 0));
|
||||
}
|
||||
|
||||
constexpr ContextDescriptorFlags withUnique(bool isUnique) const {
|
||||
return ContextDescriptorFlags((value & 0xFFFFFFBFu)
|
||||
| (isUnique ? 0x40u : 0));
|
||||
}
|
||||
|
||||
constexpr ContextDescriptorFlags withVersion(uint8_t version) const {
|
||||
return ContextDescriptorFlags((value & 0xFFFF00FFu) | (version << 8u));
|
||||
}
|
||||
|
||||
constexpr ContextDescriptorFlags
|
||||
withKindSpecificFlags(uint16_t flags) const {
|
||||
return ContextDescriptorFlags((value & 0xFFFFu) | (flags << 16u));
|
||||
}
|
||||
|
||||
constexpr uint32_t getIntValue() const {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* MetadataValues_h */
|
||||
@@ -0,0 +1,562 @@
|
||||
//===--- RelativePointer.h - Relative Pointer Support -----------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
///
|
||||
/// Some data structures emitted by the Swift compiler use relative indirect
|
||||
/// addresses in order to minimize startup cost for a process. By referring to
|
||||
/// the offset of the global offset table entry for a symbol, instead of
|
||||
/// directly referring to the symbol, compiler-emitted data structures avoid
|
||||
/// requiring unnecessary relocation at dynamic linking time. This header
|
||||
/// contains types to help dereference these relative addresses.
|
||||
///
|
||||
/// Theory of references to objects
|
||||
/// -------------------------------
|
||||
///
|
||||
/// A reference can be absolute or relative:
|
||||
///
|
||||
/// - An absolute reference is a pointer to the object.
|
||||
///
|
||||
/// - A relative reference is a (signed) offset from the address of the
|
||||
/// reference to the address of its direct referent.
|
||||
///
|
||||
/// A relative reference can be direct, indirect, or symbolic.
|
||||
///
|
||||
/// In a direct reference, the direct referent is simply the target object.
|
||||
/// Generally, a statically-emitted relative reference can only be direct
|
||||
/// if it can be resolved to a constant offset by the linker, because loaders
|
||||
/// do not support forming relative references. This means that either the
|
||||
/// reference and object must lie within the same linkage unit or the
|
||||
/// difference must be computed at runtime by code.
|
||||
///
|
||||
/// In a symbolic reference, the direct referent is a string holding the symbol
|
||||
/// name of the object. A relative reference can only be symbolic if the
|
||||
/// object actually has a symbol at runtime, which may require exporting
|
||||
/// many internal symbols that would otherwise be strippable.
|
||||
///
|
||||
/// In an indirect reference, the direct referent is a variable holding an
|
||||
/// absolute reference to the object. An indirect relative reference may
|
||||
/// refer to an arbitrary symbol, be it anonymous within the linkage unit
|
||||
/// or completely external to it, but it requires the introduction of an
|
||||
/// intermediate absolute reference that requires load-time initialization.
|
||||
/// However, this initialization can be shared among all indirect references
|
||||
/// within the linkage unit, and the linker will generally place all such
|
||||
/// references adjacent to one another to improve load-time locality.
|
||||
///
|
||||
/// A reference can be made a dynamic union of more than one of these options.
|
||||
/// This allows the compiler/linker to use a direct reference when possible
|
||||
/// and a less-efficient option where required. However, it also requires
|
||||
/// the cases to be dynamically distinguished. This can be done by setting
|
||||
/// a low bit of the offset, as long as the difference between the direct
|
||||
/// referent's address and the reference is a multiple of 2. This works well
|
||||
/// for "indirectable" references because most objects are known to be
|
||||
/// well-aligned, and the cases that aren't (chiefly functions and strings)
|
||||
/// rarely need the flexibility of this kind of reference. It does not
|
||||
/// work quite as well for "possibly symbolic" references because C strings
|
||||
/// are not naturally aligned, and making them aligned generally requires
|
||||
/// moving them out of the linker's ordinary string section; however, it's
|
||||
/// still workable.
|
||||
///
|
||||
/// Finally, a relative reference can be near or far. A near reference
|
||||
/// is potentially smaller, but it requires the direct referent to lie
|
||||
/// within a certain distance of the reference, even if dynamically
|
||||
/// initialized.
|
||||
///
|
||||
/// In Swift, we always prefer to use a near direct relative reference
|
||||
/// when it is possible to do so: that is, when the relationship is always
|
||||
/// between two global objects emitted in the same linkage unit, and there
|
||||
/// is no compatibility constraint requiring the use of an absolute reference.
|
||||
///
|
||||
/// When more flexibility is required, there are several options:
|
||||
///
|
||||
/// 1. Use an absolute reference. Size penalty on 64-bit. Requires
|
||||
/// load-time work.
|
||||
///
|
||||
/// 2. Use a far direct relative reference. Size penalty on 64-bit.
|
||||
/// Requires load-time work when object is outside linkage unit.
|
||||
/// Generally not directly supported by loaders.
|
||||
///
|
||||
/// 3. Use an always-indirect relative reference. Size penalty of one
|
||||
/// pointer (shared). Requires load-time work even when object is
|
||||
/// within linkage unit.
|
||||
///
|
||||
/// 4. Use a near indirectable relative reference. Size penalty of one
|
||||
/// pointer (shared) when reference exceeds range. Runtime / code-size
|
||||
/// penalty on access. Requires load-time work (shared) only when
|
||||
/// object is outside linkage unit.
|
||||
///
|
||||
/// 5. Use a far indirectable relative reference. Size penalty on 64-bit.
|
||||
/// Size penalty of one pointer (shared) when reference exceeds range
|
||||
/// and is initialized statically. Runtime / code-size penalty on access.
|
||||
/// Requires load-time work (shared) only when object is outside linkage
|
||||
/// unit.
|
||||
///
|
||||
/// 6. Use a near or far symbolic relative reference. No load-time work.
|
||||
/// Severe runtime penalty on access. Requires custom logic to statically
|
||||
/// optimize. Requires emission of symbol for target even if private
|
||||
/// to linkage unit.
|
||||
///
|
||||
/// 7. Use a near or far direct-or-symbolic relative reference. No
|
||||
/// load-time work. Severe runtime penalty on access if object is
|
||||
/// outside of linkage unit. Requires custom logic to statically optimize.
|
||||
///
|
||||
/// In general, it's our preference in Swift to use option #4 when there
|
||||
/// is no possibility of initializing the reference dynamically and option #5
|
||||
/// when there is. This is because it is infeasible to actually share the
|
||||
/// memory for the intermediate absolute reference when it must be allocated
|
||||
/// dynamically.
|
||||
///
|
||||
/// Symbolic references are an interesting idea that we have not yet made
|
||||
/// use of. They may be acceptable in reflective metadata cases where it
|
||||
/// is desirable to heavily bias towards never using the metadata. However,
|
||||
/// they're only profitable if there wasn't any other indirect reference
|
||||
/// to the target, and it is likely that their optimal use requires a more
|
||||
/// intelligent toolchain from top to bottom.
|
||||
///
|
||||
/// Note that the cost of load-time work also includes a binary-size penalty
|
||||
/// to store the loader metadata necessary to perform that work. Therefore
|
||||
/// it is better to avoid it even when there are dynamic optimizations in
|
||||
/// place to skip the work itself.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SWIFT_BASIC_RELATIVEPOINTER_H
|
||||
#define SWIFT_BASIC_RELATIVEPOINTER_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace swift {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Apply a relative offset to a base pointer. The offset is applied to the base
|
||||
/// pointer using sign-extended, wrapping arithmetic.
|
||||
template<typename BasePtrTy, typename Offset>
|
||||
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
auto base = reinterpret_cast<uintptr_t>(basePtr);
|
||||
// We want to do wrapping arithmetic, but with a sign-extended
|
||||
// offset. To do this in C, we need to do signed promotion to get
|
||||
// the sign extension, but we need to perform arithmetic on unsigned values,
|
||||
// since signed overflow is undefined behavior.
|
||||
auto extendOffset = (uintptr_t)(intptr_t)offset;
|
||||
return base + extendOffset;
|
||||
}
|
||||
|
||||
/// Measure the relative offset between two pointers. This measures
|
||||
/// (referent - base) using wrapping arithmetic. The result is truncated if
|
||||
/// Offset is smaller than a pointer, with an assertion that the
|
||||
/// pre-truncation result is a sign extension of the truncated result.
|
||||
template<typename Offset, typename A, typename B>
|
||||
static inline Offset measureRelativeOffset(A *referent, B *base) {
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
auto distance = (uintptr_t)referent - (uintptr_t)base;
|
||||
// Truncate as unsigned, then wrap around to signed.
|
||||
auto truncatedDistance =
|
||||
(Offset)(typename std::make_unsigned<Offset>::type)distance;
|
||||
// Assert that the truncation didn't discard any non-sign-extended bits.
|
||||
assert((intptr_t)truncatedDistance == (intptr_t)distance
|
||||
&& "pointers are too far apart to fit in offset type");
|
||||
return truncatedDistance;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// A relative reference to an object stored in memory. The reference may be
|
||||
/// direct or indirect, and uses the low bit of the (assumed at least
|
||||
/// 2-byte-aligned) pointer to differentiate.
|
||||
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t>
|
||||
class RelativeIndirectPointer {
|
||||
private:
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
/// The relative offset of the pointer's memory from the `this` pointer.
|
||||
/// This is an indirect reference.
|
||||
Offset RelativeOffset;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeIndirectPointer() = delete;
|
||||
RelativeIndirectPointer(RelativeIndirectPointer &&) = delete;
|
||||
RelativeIndirectPointer(const RelativeIndirectPointer &) = delete;
|
||||
RelativeIndirectPointer &operator=(RelativeIndirectPointer &&)
|
||||
= delete;
|
||||
RelativeIndirectPointer &operator=(const RelativeIndirectPointer &)
|
||||
= delete;
|
||||
|
||||
public:
|
||||
const ValueTy *get() const & {
|
||||
// Check for null.
|
||||
if (Nullable && RelativeOffset == 0)
|
||||
return nullptr;
|
||||
|
||||
uintptr_t address = detail::applyRelativeOffset(this, RelativeOffset);
|
||||
return *reinterpret_cast<const ValueTy * const *>(address);
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
return RelativeOffset == 0;
|
||||
}
|
||||
|
||||
operator const ValueTy* () const & {
|
||||
return get();
|
||||
}
|
||||
|
||||
const ValueTy *operator->() const & {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
|
||||
/// A relative reference to an object stored in memory. The reference may be
|
||||
/// direct or indirect, and uses the low bit of the (assumed at least
|
||||
/// 2-byte-aligned) pointer to differentiate.
|
||||
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t>
|
||||
class RelativeIndirectablePointer {
|
||||
private:
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
/// The relative offset of the pointer's memory from the `this` pointer.
|
||||
/// If the low bit is clear, this is a direct reference; otherwise, it is
|
||||
/// an indirect reference.
|
||||
Offset RelativeOffsetPlusIndirect;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeIndirectablePointer() = delete;
|
||||
RelativeIndirectablePointer(RelativeIndirectablePointer &&) = delete;
|
||||
RelativeIndirectablePointer(const RelativeIndirectablePointer &) = delete;
|
||||
RelativeIndirectablePointer &operator=(RelativeIndirectablePointer &&)
|
||||
= delete;
|
||||
RelativeIndirectablePointer &operator=(const RelativeIndirectablePointer &)
|
||||
= delete;
|
||||
|
||||
public:
|
||||
/// Allow construction and reassignment from an absolute pointer.
|
||||
/// These always produce a direct relative offset.
|
||||
RelativeIndirectablePointer(ValueTy *absolute)
|
||||
: RelativeOffsetPlusIndirect(
|
||||
Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this)) {
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
}
|
||||
|
||||
RelativeIndirectablePointer &operator=(ValueTy *absolute) & {
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
|
||||
RelativeOffsetPlusIndirect = Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ValueTy *get() const & {
|
||||
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
|
||||
"alignment of value and offset must be at least 2 to "
|
||||
"make room for indirectable flag");
|
||||
|
||||
// Check for null.
|
||||
if (Nullable && RelativeOffsetPlusIndirect == 0)
|
||||
return nullptr;
|
||||
|
||||
Offset offsetPlusIndirect = RelativeOffsetPlusIndirect;
|
||||
uintptr_t address = detail::applyRelativeOffset(this,
|
||||
offsetPlusIndirect & ~1);
|
||||
|
||||
// If the low bit is set, then this is an indirect address. Otherwise,
|
||||
// it's direct.
|
||||
if (offsetPlusIndirect & 1) {
|
||||
return *reinterpret_cast<const ValueTy * const *>(address);
|
||||
} else {
|
||||
return reinterpret_cast<const ValueTy *>(address);
|
||||
}
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
return RelativeOffsetPlusIndirect == 0;
|
||||
}
|
||||
|
||||
operator const ValueTy* () const & {
|
||||
return get();
|
||||
}
|
||||
|
||||
const ValueTy *operator->() const & {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
|
||||
/// A relative reference to an aligned object stored in memory. The reference
|
||||
/// may be direct or indirect, and uses the low bit of the (assumed at least
|
||||
/// 2-byte-aligned) pointer to differentiate. The remaining low bits store
|
||||
/// an additional tiny integer value.
|
||||
template<typename ValueTy, typename IntTy, bool Nullable = false,
|
||||
typename Offset = int32_t>
|
||||
class RelativeIndirectablePointerIntPair {
|
||||
private:
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
/// The relative offset of the pointer's memory from the `this` pointer.
|
||||
/// If the low bit is clear, this is a direct reference; otherwise, it is
|
||||
/// an indirect reference.
|
||||
Offset RelativeOffsetPlusIndirectAndInt;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeIndirectablePointerIntPair() = delete;
|
||||
RelativeIndirectablePointerIntPair(
|
||||
RelativeIndirectablePointerIntPair &&) = delete;
|
||||
RelativeIndirectablePointerIntPair(
|
||||
const RelativeIndirectablePointerIntPair &) = delete;
|
||||
RelativeIndirectablePointerIntPair& operator=(
|
||||
RelativeIndirectablePointerIntPair &&) = delete;
|
||||
RelativeIndirectablePointerIntPair &operator=(
|
||||
const RelativeIndirectablePointerIntPair &) = delete;
|
||||
|
||||
// Retrieve the mask for the stored integer value.
|
||||
static Offset getIntMask() {
|
||||
return (alignof(Offset) - 1) & ~(Offset)0x01;
|
||||
}
|
||||
|
||||
public:
|
||||
const ValueTy *getPointer() const & {
|
||||
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
|
||||
"alignment of value and offset must be at least 2 to "
|
||||
"make room for indirectable flag");
|
||||
|
||||
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
|
||||
|
||||
// Check for null.
|
||||
if (Nullable && offset == 0)
|
||||
return nullptr;
|
||||
|
||||
Offset offsetPlusIndirect = offset;
|
||||
uintptr_t address = detail::applyRelativeOffset(this,
|
||||
offsetPlusIndirect & ~1);
|
||||
|
||||
// If the low bit is set, then this is an indirect address. Otherwise,
|
||||
// it's direct.
|
||||
if (offsetPlusIndirect & 1) {
|
||||
return *reinterpret_cast<const ValueTy * const *>(address);
|
||||
} else {
|
||||
return reinterpret_cast<const ValueTy *>(address);
|
||||
}
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
|
||||
return offset == 0;
|
||||
}
|
||||
|
||||
IntTy getInt() const & {
|
||||
return IntTy((RelativeOffsetPlusIndirectAndInt & getIntMask()) >> 1);
|
||||
}
|
||||
};
|
||||
|
||||
/// A relative reference to a function, intended to reference private metadata
|
||||
/// functions for the current executable or dynamic library image from
|
||||
/// position-independent constant data.
|
||||
template<typename T, bool Nullable, typename Offset>
|
||||
class RelativeDirectPointerImpl {
|
||||
private:
|
||||
/// The relative offset of the function's entry point from *this.
|
||||
Offset RelativeOffset;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeDirectPointerImpl() = delete;
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeDirectPointerImpl(RelativeDirectPointerImpl &&) = delete;
|
||||
RelativeDirectPointerImpl(const RelativeDirectPointerImpl &) = delete;
|
||||
RelativeDirectPointerImpl &operator=(RelativeDirectPointerImpl &&)
|
||||
= delete;
|
||||
RelativeDirectPointerImpl &operator=(const RelativeDirectPointerImpl &)
|
||||
= delete;
|
||||
|
||||
|
||||
public:
|
||||
using ValueTy = T;
|
||||
using PointerTy = T*;
|
||||
|
||||
// Allow construction and reassignment from an absolute pointer.
|
||||
RelativeDirectPointerImpl(PointerTy absolute)
|
||||
: RelativeOffset(Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this))
|
||||
{
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
}
|
||||
explicit constexpr RelativeDirectPointerImpl(std::nullptr_t)
|
||||
: RelativeOffset (0) {
|
||||
static_assert(Nullable, "can't construct non-nullable pointer from null");
|
||||
}
|
||||
|
||||
RelativeDirectPointerImpl &operator=(PointerTy absolute) & {
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
RelativeOffset = Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PointerTy get() const & {
|
||||
// Check for null.
|
||||
if (Nullable && RelativeOffset == 0)
|
||||
return nullptr;
|
||||
|
||||
// The value is addressed relative to `this`.
|
||||
uintptr_t absolute = detail::applyRelativeOffset(this, RelativeOffset);
|
||||
return reinterpret_cast<PointerTy>(absolute);
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
return RelativeOffset == 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// A direct relative reference to an object.
|
||||
template<typename T, bool Nullable = true, typename Offset = int32_t>
|
||||
class RelativeDirectPointer :
|
||||
private RelativeDirectPointerImpl<T, Nullable, Offset>
|
||||
{
|
||||
using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
|
||||
public:
|
||||
using super::get;
|
||||
using super::super;
|
||||
|
||||
RelativeDirectPointer &operator=(T *absolute) & {
|
||||
super::operator=(absolute);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator typename super::PointerTy() const & {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
const typename super::ValueTy *operator->() const & {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
using super::isNull;
|
||||
};
|
||||
|
||||
/// A specialization of RelativeDirectPointer for function pointers,
|
||||
/// allowing for calls.
|
||||
template<typename RetTy, typename...ArgTy, bool Nullable, typename Offset>
|
||||
class RelativeDirectPointer<RetTy (ArgTy...), Nullable, Offset> :
|
||||
private RelativeDirectPointerImpl<RetTy (ArgTy...), Nullable, Offset>
|
||||
{
|
||||
using super = RelativeDirectPointerImpl<RetTy (ArgTy...), Nullable, Offset>;
|
||||
public:
|
||||
using super::get;
|
||||
using super::super;
|
||||
|
||||
RelativeDirectPointer &operator=(RetTy (*absolute)(ArgTy...)) & {
|
||||
super::operator=(absolute);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator typename super::PointerTy() const & {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
RetTy operator()(ArgTy...arg) const {
|
||||
return this->get()(std::forward<ArgTy>(arg)...);
|
||||
}
|
||||
|
||||
using super::isNull;
|
||||
};
|
||||
|
||||
/// A direct relative reference to an aligned object, with an additional
|
||||
/// tiny integer value crammed into its low bits.
|
||||
template<typename PointeeTy, typename IntTy, bool Nullable = false,
|
||||
typename Offset = int32_t>
|
||||
class RelativeDirectPointerIntPair {
|
||||
Offset RelativeOffsetPlusInt;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeDirectPointerIntPair() = delete;
|
||||
RelativeDirectPointerIntPair(RelativeDirectPointerIntPair &&) = delete;
|
||||
RelativeDirectPointerIntPair(const RelativeDirectPointerIntPair &) = delete;
|
||||
RelativeDirectPointerIntPair &operator=(RelativeDirectPointerIntPair &&)
|
||||
= delete;
|
||||
RelativeDirectPointerIntPair &operator=(const RelativeDirectPointerIntPair&)
|
||||
= delete;
|
||||
|
||||
static Offset getMask() {
|
||||
return alignof(Offset) - 1;
|
||||
}
|
||||
|
||||
public:
|
||||
using ValueTy = PointeeTy;
|
||||
using PointerTy = PointeeTy*;
|
||||
|
||||
PointerTy getPointer() const & {
|
||||
Offset offset = (RelativeOffsetPlusInt & ~getMask());
|
||||
|
||||
// Check for null.
|
||||
if (Nullable && offset == 0)
|
||||
return nullptr;
|
||||
|
||||
// The value is addressed relative to `this`.
|
||||
uintptr_t absolute = detail::applyRelativeOffset(this, offset);
|
||||
return reinterpret_cast<PointerTy>(absolute);
|
||||
}
|
||||
|
||||
IntTy getInt() const & {
|
||||
return IntTy(RelativeOffsetPlusInt & getMask());
|
||||
}
|
||||
|
||||
Offset getOpaqueValue() const & {
|
||||
return RelativeOffsetPlusInt;
|
||||
}
|
||||
};
|
||||
|
||||
// Type aliases for "far" relative pointers, which need to be able to reach
|
||||
// across the full address space instead of only across a single small-code-
|
||||
// model image.
|
||||
|
||||
template<typename T, bool Nullable = false>
|
||||
using FarRelativeIndirectablePointer =
|
||||
RelativeIndirectablePointer<T, Nullable, intptr_t>;
|
||||
|
||||
template<typename T, bool Nullable = false>
|
||||
using FarRelativeDirectPointer = RelativeDirectPointer<T, Nullable, intptr_t>;
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
#endif // SWIFT_BASIC_RELATIVEPOINTER_H
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// SwiftExports.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SwiftExports_h
|
||||
#define SwiftExports_h
|
||||
|
||||
extern "C" id swift_allocObject(Class cls, uint32_t requiredSize, uint32_t alignmentMask);
|
||||
|
||||
#endif /* SwiftExports_h */
|
||||
@@ -61,9 +61,9 @@
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "OS_ACTIVITY_MODE"
|
||||
key = "OS_ACTIVITY_DT_MODE"
|
||||
value = "disable"
|
||||
isEnabled = "NO">
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
[[FLEXManager sharedManager] showExplorer];
|
||||
[[FLEXManager sharedManager] setNetworkDebuggingEnabled:YES];
|
||||
[self sendExampleNetworkRequests];
|
||||
self.repeatingLogExampleTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(sendExampleLogMessage) userInfo:nil repeats:YES];
|
||||
// self.repeatingLogExampleTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(sendExampleLogMessage) userInfo:nil repeats:YES];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"foo" forKey:@"FLEXExamplePrefFoo"];
|
||||
|
||||
|
||||
+139
-13
@@ -74,7 +74,7 @@
|
||||
3A4C95231B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A01B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m */; };
|
||||
3A4C95241B5B21410088C3F2 /* FLEXFileBrowserTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserTableViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C95251B5B21410088C3F2 /* FLEXFileBrowserTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A21B5B21410088C3F2 /* FLEXFileBrowserTableViewController.m */; };
|
||||
3A4C95261B5B21410088C3F2 /* FLEXGlobalsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A31B5B21410088C3F2 /* FLEXGlobalsViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C95261B5B21410088C3F2 /* FLEXGlobalsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A31B5B21410088C3F2 /* FLEXGlobalsViewController.h */; };
|
||||
3A4C95271B5B21410088C3F2 /* FLEXGlobalsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A41B5B21410088C3F2 /* FLEXGlobalsViewController.m */; };
|
||||
3A4C95281B5B21410088C3F2 /* FLEXInstancesViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A51B5B21410088C3F2 /* FLEXInstancesViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C95291B5B21410088C3F2 /* FLEXInstancesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A61B5B21410088C3F2 /* FLEXInstancesViewController.m */; };
|
||||
@@ -88,8 +88,8 @@
|
||||
3A4C95331B5B21410088C3F2 /* FLEXSystemLogTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B11B5B21410088C3F2 /* FLEXSystemLogTableViewCell.m */; };
|
||||
3A4C95341B5B21410088C3F2 /* FLEXSystemLogTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B21B5B21410088C3F2 /* FLEXSystemLogTableViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C95351B5B21410088C3F2 /* FLEXSystemLogTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B31B5B21410088C3F2 /* FLEXSystemLogTableViewController.m */; };
|
||||
3A4C95361B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B51B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C95371B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B61B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.m */; };
|
||||
3A4C95361B5B21410088C3F2 /* FLEXNetworkMITMViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B51B5B21410088C3F2 /* FLEXNetworkMITMViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C95371B5B21410088C3F2 /* FLEXNetworkMITMViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B61B5B21410088C3F2 /* FLEXNetworkMITMViewController.m */; };
|
||||
3A4C95381B5B21410088C3F2 /* FLEXNetworkRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B71B5B21410088C3F2 /* FLEXNetworkRecorder.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C953A1B5B21410088C3F2 /* FLEXNetworkSettingsTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B91B5B21410088C3F2 /* FLEXNetworkSettingsTableViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
3A4C953B1B5B21410088C3F2 /* FLEXNetworkSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94BA1B5B21410088C3F2 /* FLEXNetworkSettingsTableViewController.m */; };
|
||||
@@ -142,6 +142,12 @@
|
||||
94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */; };
|
||||
94AAF03A1BAF2F0300DE8760 /* FLEXKeyboardShortcutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 942DCD821BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C309B82D223ED64400B228EC /* FLEXLogController.h */; };
|
||||
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 */; };
|
||||
C312A13523ECBE5800E38049 /* FLEXBookmarksViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A13323ECBE5800E38049 /* FLEXBookmarksViewController.m */; };
|
||||
C312A13C23ECE79000E38049 /* FLEXWindowManagerController.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A13A23ECE79000E38049 /* FLEXWindowManagerController.m */; };
|
||||
C312A13D23ECE79000E38049 /* FLEXWindowManagerController.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A13B23ECE79000E38049 /* FLEXWindowManagerController.h */; };
|
||||
C31C4A6923342A2200C35F12 /* FLEXMetadataSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C31C4A6723342A2200C35F12 /* FLEXMetadataSection.h */; };
|
||||
C31C4A6A23342A2200C35F12 /* FLEXMetadataSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C31C4A6823342A2200C35F12 /* FLEXMetadataSection.m */; };
|
||||
C31D93E423E38CBE005517BF /* FLEXBlockShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C31D93E223E38CBE005517BF /* FLEXBlockShortcuts.h */; };
|
||||
@@ -178,8 +184,14 @@
|
||||
C3531BA223E796BD00A184AD /* FLEXManager+Extensibility.m in Sources */ = {isa = PBXBuildFile; fileRef = C3531BA023E796BD00A184AD /* FLEXManager+Extensibility.m */; };
|
||||
C3531BA523E88A2100A184AD /* FLEXNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3531BA323E88A2100A184AD /* FLEXNavigationController.h */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
C3694DC223EA147F006625D7 /* UIBarButtonItem+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */; };
|
||||
C3694DC323EA147F006625D7 /* UIBarButtonItem+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */; };
|
||||
C36B096523E0D4A1008F5D47 /* UIMenu+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C36B096323E0D4A1008F5D47 /* UIMenu+FLEX.h */; };
|
||||
C36B096623E0D4A1008F5D47 /* UIMenu+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */; };
|
||||
C36B097023E1EDCD008F5D47 /* FLEXTableViewSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C36B096E23E1EDCD008F5D47 /* FLEXTableViewSection.h */; };
|
||||
@@ -276,6 +288,11 @@
|
||||
C3A9424E23C78CFF006871A3 /* FHSViewSnapshot.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9424C23C78CFF006871A3 /* FHSViewSnapshot.m */; };
|
||||
C3BFD070233C23ED0015FB82 /* NSArray+Functional.h in Headers */ = {isa = PBXBuildFile; fileRef = C3BFD06E233C23ED0015FB82 /* NSArray+Functional.h */; };
|
||||
C3BFD071233C23ED0015FB82 /* NSArray+Functional.m in Sources */ = {isa = PBXBuildFile; fileRef = C3BFD06F233C23ED0015FB82 /* NSArray+Functional.m */; };
|
||||
C3CFF19023F1C04A00E14F2C /* FLEXSwiftTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = C3CFF18F23F1C04A00E14F2C /* FLEXSwiftTests.mm */; };
|
||||
C3CFF19223F1C06600E14F2C /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3CFF19123F1C06600E14F2C /* Person.swift */; };
|
||||
C3CFF19423F1C11D00E14F2C /* FLEXSwiftSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3CFF19323F1C11D00E14F2C /* FLEXSwiftSwiftTests.swift */; };
|
||||
C3CFF19B23F1E71A00E14F2C /* RelativePointer.h in Headers */ = {isa = PBXBuildFile; fileRef = C3CFF19A23F1E71A00E14F2C /* RelativePointer.h */; };
|
||||
C3CFF19F23F20BC200E14F2C /* Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = C3CFF19E23F20BC200E14F2C /* Metadata.h */; };
|
||||
C3DB9F642107FC9600B46809 /* FLEXObjectRef.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */; };
|
||||
C3DB9F652107FC9600B46809 /* FLEXObjectRef.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */; };
|
||||
C3DC287C223ED5F200F48AA6 /* FLEXOSLogController.m in Sources */ = {isa = PBXBuildFile; fileRef = C34EE30721CB23CC00BD3A7C /* FLEXOSLogController.m */; };
|
||||
@@ -407,8 +424,8 @@
|
||||
3A4C94B11B5B21410088C3F2 /* FLEXSystemLogTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSystemLogTableViewCell.m; sourceTree = "<group>"; };
|
||||
3A4C94B21B5B21410088C3F2 /* FLEXSystemLogTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSystemLogTableViewController.h; sourceTree = "<group>"; };
|
||||
3A4C94B31B5B21410088C3F2 /* FLEXSystemLogTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSystemLogTableViewController.m; sourceTree = "<group>"; };
|
||||
3A4C94B51B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkHistoryTableViewController.h; sourceTree = "<group>"; };
|
||||
3A4C94B61B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkHistoryTableViewController.m; sourceTree = "<group>"; };
|
||||
3A4C94B51B5B21410088C3F2 /* FLEXNetworkMITMViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkMITMViewController.h; sourceTree = "<group>"; };
|
||||
3A4C94B61B5B21410088C3F2 /* FLEXNetworkMITMViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkMITMViewController.m; sourceTree = "<group>"; };
|
||||
3A4C94B71B5B21410088C3F2 /* FLEXNetworkRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkRecorder.h; sourceTree = "<group>"; };
|
||||
3A4C94B81B5B21410088C3F2 /* FLEXNetworkRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkRecorder.m; sourceTree = "<group>"; };
|
||||
3A4C94B91B5B21410088C3F2 /* FLEXNetworkSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkSettingsTableViewController.h; sourceTree = "<group>"; };
|
||||
@@ -463,6 +480,12 @@
|
||||
94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyboardHelpViewController.h; sourceTree = "<group>"; };
|
||||
94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyboardHelpViewController.m; sourceTree = "<group>"; };
|
||||
C309B82D223ED64400B228EC /* FLEXLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXLogController.h; 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>"; };
|
||||
C312A13323ECBE5800E38049 /* FLEXBookmarksViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBookmarksViewController.m; sourceTree = "<group>"; };
|
||||
C312A13A23ECE79000E38049 /* FLEXWindowManagerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXWindowManagerController.m; sourceTree = "<group>"; };
|
||||
C312A13B23ECE79000E38049 /* FLEXWindowManagerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXWindowManagerController.h; sourceTree = "<group>"; };
|
||||
C31C4A6723342A2200C35F12 /* FLEXMetadataSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXMetadataSection.h; sourceTree = "<group>"; };
|
||||
C31C4A6823342A2200C35F12 /* FLEXMetadataSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXMetadataSection.m; sourceTree = "<group>"; };
|
||||
C31D93E223E38CBE005517BF /* FLEXBlockShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBlockShortcuts.h; sourceTree = "<group>"; };
|
||||
@@ -501,8 +524,14 @@
|
||||
C3531BA023E796BD00A184AD /* FLEXManager+Extensibility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FLEXManager+Extensibility.m"; sourceTree = "<group>"; };
|
||||
C3531BA323E88A2100A184AD /* FLEXNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNavigationController.h; sourceTree = "<group>"; };
|
||||
C3531BA423E88A2100A184AD /* FLEXNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNavigationController.m; sourceTree = "<group>"; };
|
||||
C3531BA823E88FAC00A184AD /* FLEXTabList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTabList.h; sourceTree = "<group>"; };
|
||||
C3531BA923E88FAC00A184AD /* FLEXTabList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTabList.m; sourceTree = "<group>"; };
|
||||
C362AE7F23C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMapTable+FLEX_Subscripting.h"; sourceTree = "<group>"; };
|
||||
C362AE8023C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMapTable+FLEX_Subscripting.m"; sourceTree = "<group>"; };
|
||||
C3694DB823EA1096006625D7 /* FLEXTabsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXTabsViewController.h; sourceTree = "<group>"; };
|
||||
C3694DB923EA1096006625D7 /* FLEXTabsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTabsViewController.m; sourceTree = "<group>"; };
|
||||
C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+FLEX.h"; sourceTree = "<group>"; };
|
||||
C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+FLEX.m"; sourceTree = "<group>"; };
|
||||
C36B096323E0D4A1008F5D47 /* UIMenu+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIMenu+FLEX.h"; sourceTree = "<group>"; };
|
||||
C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIMenu+FLEX.m"; sourceTree = "<group>"; };
|
||||
C36B096E23E1EDCD008F5D47 /* FLEXTableViewSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXTableViewSection.h; sourceTree = "<group>"; };
|
||||
@@ -595,6 +624,17 @@
|
||||
C3A9424C23C78CFF006871A3 /* FHSViewSnapshot.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FHSViewSnapshot.m; sourceTree = "<group>"; };
|
||||
C3BFD06E233C23ED0015FB82 /* NSArray+Functional.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSArray+Functional.h"; sourceTree = "<group>"; };
|
||||
C3BFD06F233C23ED0015FB82 /* NSArray+Functional.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Functional.m"; sourceTree = "<group>"; };
|
||||
C3CFF18E23F1BCCA00E14F2C /* FLEXPointers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXPointers.h; sourceTree = "<group>"; };
|
||||
C3CFF18F23F1C04A00E14F2C /* FLEXSwiftTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FLEXSwiftTests.mm; sourceTree = "<group>"; };
|
||||
C3CFF19123F1C06600E14F2C /* Person.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Person.swift; sourceTree = "<group>"; };
|
||||
C3CFF19323F1C11D00E14F2C /* FLEXSwiftSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FLEXSwiftSwiftTests.swift; sourceTree = "<group>"; };
|
||||
C3CFF19623F1C46B00E14F2C /* MMetadata.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMetadata.h; sourceTree = "<group>"; };
|
||||
C3CFF19823F1CD6E00E14F2C /* ExistentialContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExistentialContainer.h; sourceTree = "<group>"; };
|
||||
C3CFF19923F1E43C00E14F2C /* SwiftMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftMetadata.h; sourceTree = "<group>"; };
|
||||
C3CFF19A23F1E71A00E14F2C /* RelativePointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RelativePointer.h; sourceTree = "<group>"; };
|
||||
C3CFF19C23F1EA5600E14F2C /* SwiftExports.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftExports.h; sourceTree = "<group>"; };
|
||||
C3CFF19D23F202AE00E14F2C /* MetadataValues.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetadataValues.h; sourceTree = "<group>"; };
|
||||
C3CFF19E23F20BC200E14F2C /* Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Metadata.h; sourceTree = "<group>"; };
|
||||
C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectRef.h; sourceTree = "<group>"; };
|
||||
C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXObjectRef.m; sourceTree = "<group>"; };
|
||||
C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXRuntime+Compare.h"; sourceTree = "<group>"; };
|
||||
@@ -654,9 +694,12 @@
|
||||
1C27A8B71F0E5A0400F0D02D /* FLEXTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C27A8B81F0E5A0400F0D02D /* FLEXTestsMethodsList.m */,
|
||||
1C27A8BA1F0E5A0400F0D02D /* Info.plist */,
|
||||
C33C825A23159EAF00DD2451 /* FLEXTests.m */,
|
||||
C3CFF18F23F1C04A00E14F2C /* FLEXSwiftTests.mm */,
|
||||
C3CFF19323F1C11D00E14F2C /* FLEXSwiftSwiftTests.swift */,
|
||||
1C27A8B81F0E5A0400F0D02D /* FLEXTestsMethodsList.m */,
|
||||
C3CFF19123F1C06600E14F2C /* Person.swift */,
|
||||
1C27A8BA1F0E5A0400F0D02D /* Info.plist */,
|
||||
);
|
||||
path = FLEXTests;
|
||||
sourceTree = "<group>";
|
||||
@@ -728,6 +771,7 @@
|
||||
children = (
|
||||
C36FBFB8230F3B52008D95D5 /* Runtime */,
|
||||
C387C88022E0D22600750E58 /* Categories */,
|
||||
C35B6EB722E24DA300907BA4 /* Swift */,
|
||||
3A4C94551B5B21410088C3F2 /* FLEXHeapEnumerator.h */,
|
||||
3A4C94561B5B21410088C3F2 /* FLEXHeapEnumerator.m */,
|
||||
3A4C94591B5B21410088C3F2 /* FLEXResources.h */,
|
||||
@@ -870,8 +914,8 @@
|
||||
3A4C94B41B5B21410088C3F2 /* Network */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3A4C94B51B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.h */,
|
||||
3A4C94B61B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.m */,
|
||||
3A4C94B51B5B21410088C3F2 /* FLEXNetworkMITMViewController.h */,
|
||||
3A4C94B61B5B21410088C3F2 /* FLEXNetworkMITMViewController.m */,
|
||||
3A4C94B71B5B21410088C3F2 /* FLEXNetworkRecorder.h */,
|
||||
3A4C94B81B5B21410088C3F2 /* FLEXNetworkRecorder.m */,
|
||||
3A4C94B91B5B21410088C3F2 /* FLEXNetworkSettingsTableViewController.h */,
|
||||
@@ -963,14 +1007,29 @@
|
||||
94A515121C4C9E7B0063292F /* ExplorerInterface */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94A515191C4CA1F10063292F /* FLEXExplorerViewController.h */,
|
||||
94A5151A1C4CA1F10063292F /* FLEXExplorerViewController.m */,
|
||||
C312A12D23ECB55B00E38049 /* Bookmarks */,
|
||||
C3531BA723E88FAC00A184AD /* Tabs */,
|
||||
94A5151B1C4CA1F10063292F /* FLEXWindow.h */,
|
||||
94A5151C1C4CA1F10063292F /* FLEXWindow.m */,
|
||||
94A515191C4CA1F10063292F /* FLEXExplorerViewController.h */,
|
||||
94A5151A1C4CA1F10063292F /* FLEXExplorerViewController.m */,
|
||||
C312A13B23ECE79000E38049 /* FLEXWindowManagerController.h */,
|
||||
C312A13A23ECE79000E38049 /* FLEXWindowManagerController.m */,
|
||||
);
|
||||
path = ExplorerInterface;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C312A12D23ECB55B00E38049 /* Bookmarks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */,
|
||||
C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */,
|
||||
C312A13223ECBE5800E38049 /* FLEXBookmarksViewController.h */,
|
||||
C312A13323ECBE5800E38049 /* FLEXBookmarksViewController.m */,
|
||||
);
|
||||
path = Bookmarks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C33CF16B22D664E600F9C6C0 /* FileBrowser */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -995,6 +1054,31 @@
|
||||
path = Globals;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C3531BA723E88FAC00A184AD /* Tabs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C3531BA823E88FAC00A184AD /* FLEXTabList.h */,
|
||||
C3531BA923E88FAC00A184AD /* FLEXTabList.m */,
|
||||
C3694DB823EA1096006625D7 /* FLEXTabsViewController.h */,
|
||||
C3694DB923EA1096006625D7 /* FLEXTabsViewController.m */,
|
||||
);
|
||||
path = Tabs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C35B6EB722E24DA300907BA4 /* Swift */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C3CFF19723F1CD5F00E14F2C /* Layouts */,
|
||||
C3CFF18E23F1BCCA00E14F2C /* FLEXPointers.h */,
|
||||
C3CFF19A23F1E71A00E14F2C /* RelativePointer.h */,
|
||||
C3CFF19623F1C46B00E14F2C /* MMetadata.h */,
|
||||
C3CFF19E23F20BC200E14F2C /* Metadata.h */,
|
||||
C3CFF19C23F1EA5600E14F2C /* SwiftExports.h */,
|
||||
C3CFF19D23F202AE00E14F2C /* MetadataValues.h */,
|
||||
);
|
||||
path = Swift;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C36B096723E1E25F008F5D47 /* Carousel */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1098,6 +1182,8 @@
|
||||
C362AE8023C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m */,
|
||||
C36B096323E0D4A1008F5D47 /* UIMenu+FLEX.h */,
|
||||
C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */,
|
||||
C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */,
|
||||
C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
@@ -1190,6 +1276,15 @@
|
||||
path = TreeExplorer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C3CFF19723F1CD5F00E14F2C /* Layouts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C3CFF19823F1CD6E00E14F2C /* ExistentialContainer.h */,
|
||||
C3CFF19923F1E43C00E14F2C /* SwiftMetadata.h */,
|
||||
);
|
||||
path = Layouts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C3D43E9F231D79F60079F6A8 /* Shortcuts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1258,6 +1353,7 @@
|
||||
C3A9424523C641C1006871A3 /* SceneKit+Snapshot.h in Headers */,
|
||||
3A4C94EB1B5B21410088C3F2 /* FLEXImagePreviewViewController.h in Headers */,
|
||||
C34D4EB823A2B17900C1F903 /* FLEXBundleShortcuts.h in Headers */,
|
||||
C3694DC223EA147F006625D7 /* UIBarButtonItem+FLEX.h in Headers */,
|
||||
C38F3F31230C958F004E3731 /* FLEXAlert.h in Headers */,
|
||||
C398624D23AD6C67007E6793 /* TBKeyPathSearchController.h in Headers */,
|
||||
3A4C95381B5B21410088C3F2 /* FLEXNetworkRecorder.h in Headers */,
|
||||
@@ -1265,6 +1361,7 @@
|
||||
C383C3C523B6BB81007A321B /* FLEXCodeFontCell.h in Headers */,
|
||||
C3F527C12318670F009CBA07 /* FLEXImageShortcuts.h in Headers */,
|
||||
3A4C95051B5B21410088C3F2 /* FLEXArgumentInputViewFactory.h in Headers */,
|
||||
C312A13423ECBE5800E38049 /* FLEXBookmarksViewController.h in Headers */,
|
||||
222C88221C7339DC007CA15F /* FLEXRealmDefines.h in Headers */,
|
||||
779B1ED21C0C4D7C001F5E49 /* FLEXTableColumnHeader.h in Headers */,
|
||||
3A4C94FD1B5B21410088C3F2 /* FLEXArgumentInputStructView.h in Headers */,
|
||||
@@ -1280,7 +1377,7 @@
|
||||
C3F646F623A04A7500D4A011 /* FLEXShortcut.h in Headers */,
|
||||
C3531B9D23E69BB200A184AD /* FLEXManager+Networking.h in Headers */,
|
||||
C3878DBB23A749960038FDBE /* FLEXVariableEditorViewController.h in Headers */,
|
||||
3A4C95361B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.h in Headers */,
|
||||
3A4C95361B5B21410088C3F2 /* FLEXNetworkMITMViewController.h in Headers */,
|
||||
3A4C94DD1B5B21410088C3F2 /* FLEXHeapEnumerator.h in Headers */,
|
||||
C3F527BD2318603F009CBA07 /* FLEXShortcutsSection.h in Headers */,
|
||||
C398626B23AD71C1007E6793 /* TBRuntime.h in Headers */,
|
||||
@@ -1304,6 +1401,7 @@
|
||||
C3531BA123E796BD00A184AD /* FLEXManager+Extensibility.h in Headers */,
|
||||
C36FBFCF230F3B98008D95D5 /* FLEXProtocol.h in Headers */,
|
||||
94A515271C4CA2080063292F /* FLEXToolbarItem.h in Headers */,
|
||||
C312A13023ECB5D300E38049 /* FLEXBookmarkManager.h in Headers */,
|
||||
3A4C95341B5B21410088C3F2 /* FLEXSystemLogTableViewController.h in Headers */,
|
||||
C34C9BDD23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.h in Headers */,
|
||||
C398624F23AD6C67007E6793 /* TBKeyPathTokenizer.h in Headers */,
|
||||
@@ -1313,6 +1411,7 @@
|
||||
C3490E1F233BDD73002AE200 /* FLEXSingleRowSection.h in Headers */,
|
||||
3A4C95281B5B21410088C3F2 /* FLEXInstancesViewController.h in Headers */,
|
||||
C36B097023E1EDCD008F5D47 /* FLEXTableViewSection.h in Headers */,
|
||||
C3531BAA23E88FAC00A184AD /* FLEXTabList.h in Headers */,
|
||||
3A4C950F1B5B21410088C3F2 /* FLEXMethodCallingViewController.h in Headers */,
|
||||
3A4C94F51B5B21410088C3F2 /* FLEXArgumentInputObjectView.h in Headers */,
|
||||
C3A9424923C78878006871A3 /* FLEXHierarchyViewController.h in Headers */,
|
||||
@@ -1331,15 +1430,18 @@
|
||||
3A4C95031B5B21410088C3F2 /* FLEXArgumentInputView.h in Headers */,
|
||||
C398627623AD79B7007E6793 /* NSString+FLEX.h in Headers */,
|
||||
94A5151D1C4CA1F10063292F /* FLEXExplorerViewController.h in Headers */,
|
||||
C3CFF19B23F1E71A00E14F2C /* RelativePointer.h in Headers */,
|
||||
C3F31D3F2267D883003C991A /* FLEXMultilineTableViewCell.h in Headers */,
|
||||
71E1C2132307FBB800F5032A /* FLEXKeychain.h in Headers */,
|
||||
C36FBFD7230F3B98008D95D5 /* FLEXMirror.h in Headers */,
|
||||
C3F31D432267D883003C991A /* FLEXTableView.h in Headers */,
|
||||
3A4C95071B5B21410088C3F2 /* FLEXDefaultEditorViewController.h in Headers */,
|
||||
C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */,
|
||||
C3694DBA23EA1096006625D7 /* FLEXTabsViewController.h in Headers */,
|
||||
C31C4A6923342A2200C35F12 /* FLEXMetadataSection.h in Headers */,
|
||||
C3F977832311B38F0032776D /* NSString+ObjcRuntime.h in Headers */,
|
||||
94A5151F1C4CA1F10063292F /* FLEXWindow.h in Headers */,
|
||||
C312A13D23ECE79000E38049 /* FLEXWindowManagerController.h in Headers */,
|
||||
779B1ECE1C0C4D7C001F5E49 /* FLEXDatabaseManager.h in Headers */,
|
||||
C32A19622317378C00EB02AC /* FLEXDefaultsContentSection.h in Headers */,
|
||||
3A4C94D51B5B21410088C3F2 /* FLEXObjectExplorerViewController.h in Headers */,
|
||||
@@ -1362,6 +1464,7 @@
|
||||
3A4C953C1B5B21410088C3F2 /* FLEXNetworkTransaction.h in Headers */,
|
||||
C383C3C223B6B429007A321B /* NSTimer+Blocks.h in Headers */,
|
||||
3A4C952E1B5B21410088C3F2 /* FLEXWebViewController.h in Headers */,
|
||||
C3CFF19F23F20BC200E14F2C /* Metadata.h in Headers */,
|
||||
C3A9424D23C78CFF006871A3 /* FHSViewSnapshot.h in Headers */,
|
||||
C3EE76BF22DFC63600EC0AA0 /* FLEXScopeCarousel.h in Headers */,
|
||||
C398627223AD7951007E6793 /* UIGestureRecognizer+Blocks.h in Headers */,
|
||||
@@ -1456,9 +1559,11 @@
|
||||
TargetAttributes = {
|
||||
1C27A8B51F0E5A0300F0D02D = {
|
||||
DevelopmentTeam = U3LST7M92S;
|
||||
LastSwiftMigration = 1130;
|
||||
};
|
||||
3A4C941E1B5B20570088C3F2 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
LastSwiftMigration = 0900;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
@@ -1505,7 +1610,10 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C3CFF19423F1C11D00E14F2C /* FLEXSwiftSwiftTests.swift in Sources */,
|
||||
C33C825B23159EAF00DD2451 /* FLEXTests.m in Sources */,
|
||||
C3CFF19023F1C04A00E14F2C /* FLEXSwiftTests.mm in Sources */,
|
||||
C3CFF19223F1C06600E14F2C /* Person.swift in Sources */,
|
||||
1C27A8B91F0E5A0400F0D02D /* FLEXTestsMethodsList.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1543,6 +1651,7 @@
|
||||
C3878DBA23A749960038FDBE /* FLEXVariableEditorViewController.m in Sources */,
|
||||
94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */,
|
||||
C398624E23AD6C67007E6793 /* TBKeyPath.m in Sources */,
|
||||
C3694DC323EA147F006625D7 /* UIBarButtonItem+FLEX.m in Sources */,
|
||||
C36FBFD4230F3B98008D95D5 /* FLEXProtocol.m in Sources */,
|
||||
C34D4EB123A2ABD900C1F903 /* FLEXLayerShortcuts.m in Sources */,
|
||||
C38F3F32230C958F004E3731 /* FLEXAlert.m in Sources */,
|
||||
@@ -1582,6 +1691,7 @@
|
||||
3A4C952F1B5B21410088C3F2 /* FLEXWebViewController.m in Sources */,
|
||||
3A4C95041B5B21410088C3F2 /* FLEXArgumentInputView.m in Sources */,
|
||||
C3F977842311B38F0032776D /* NSDictionary+ObjcRuntime.m in Sources */,
|
||||
C312A13523ECBE5800E38049 /* FLEXBookmarksViewController.m in Sources */,
|
||||
3A4C95411B5B21410088C3F2 /* FLEXNetworkTransactionTableViewCell.m in Sources */,
|
||||
C3F527BE2318603F009CBA07 /* FLEXShortcutsSection.m in Sources */,
|
||||
3A4C94D61B5B21410088C3F2 /* FLEXObjectExplorerViewController.m in Sources */,
|
||||
@@ -1595,6 +1705,7 @@
|
||||
3A4C95311B5B21410088C3F2 /* FLEXSystemLogMessage.m in Sources */,
|
||||
C3F31D422267D883003C991A /* FLEXTableViewCell.m in Sources */,
|
||||
3A4C94F41B5B21410088C3F2 /* FLEXArgumentInputFontView.m in Sources */,
|
||||
C3694DBB23EA1096006625D7 /* FLEXTabsViewController.m in Sources */,
|
||||
C3A9424623C641C1006871A3 /* SceneKit+Snapshot.m in Sources */,
|
||||
3A4C953B1B5B21410088C3F2 /* FLEXNetworkSettingsTableViewController.m in Sources */,
|
||||
3A4C94FC1B5B21410088C3F2 /* FLEXArgumentInputStringView.m in Sources */,
|
||||
@@ -1613,6 +1724,7 @@
|
||||
779B1ED31C0C4D7C001F5E49 /* FLEXTableColumnHeader.m in Sources */,
|
||||
C37A0C94218BAC9600848CA7 /* FLEXObjcInternal.mm in Sources */,
|
||||
C31D93E523E38CBE005517BF /* FLEXBlockShortcuts.m in Sources */,
|
||||
C312A13123ECB5D300E38049 /* FLEXBookmarkManager.m in Sources */,
|
||||
C34D4EB523A2AF2A00C1F903 /* FLEXColorPreviewSection.m in Sources */,
|
||||
C36B097123E1EDCD008F5D47 /* FLEXTableViewSection.m in Sources */,
|
||||
C3531BA623E88A2100A184AD /* FLEXNavigationController.m in Sources */,
|
||||
@@ -1636,12 +1748,14 @@
|
||||
C3F31D442267D883003C991A /* FLEXTableView.m in Sources */,
|
||||
3A4C953F1B5B21410088C3F2 /* FLEXNetworkTransactionDetailTableViewController.m in Sources */,
|
||||
224D49AB1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m in Sources */,
|
||||
C312A13C23ECE79000E38049 /* FLEXWindowManagerController.m in Sources */,
|
||||
C36FBFDA230F3B98008D95D5 /* FLEXPropertyAttributes.m in Sources */,
|
||||
779B1ED91C0C4D7C001F5E49 /* FLEXTableLeftCell.m in Sources */,
|
||||
3A4C94E61B5B21410088C3F2 /* FLEXUtility.m in Sources */,
|
||||
3A4C95231B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m in Sources */,
|
||||
C3490E20233BDD73002AE200 /* FLEXSingleRowSection.m in Sources */,
|
||||
C34D4EB923A2B17900C1F903 /* FLEXBundleShortcuts.m in Sources */,
|
||||
C3531BAB23E88FAC00A184AD /* FLEXTabList.m in Sources */,
|
||||
3A4C952D1B5B21410088C3F2 /* FLEXLiveObjectsTableViewController.m in Sources */,
|
||||
C33E46B0223B02CD004BD0E6 /* FLEXASLLogController.m in Sources */,
|
||||
C398625023AD6C67007E6793 /* FLEXObjcRuntimeViewController.m in Sources */,
|
||||
@@ -1655,7 +1769,7 @@
|
||||
71E1C2182307FBB800F5032A /* FLEXKeychainTableViewController.m in Sources */,
|
||||
94A5151E1C4CA1F10063292F /* FLEXExplorerViewController.m in Sources */,
|
||||
C398625A23AD6C88007E6793 /* TBToken.m in Sources */,
|
||||
3A4C95371B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.m in Sources */,
|
||||
3A4C95371B5B21410088C3F2 /* FLEXNetworkMITMViewController.m in Sources */,
|
||||
C3511B9222D7C99E0057BAB7 /* FLEXGlobalsSection.m in Sources */,
|
||||
C32A19632317378C00EB02AC /* FLEXDefaultsContentSection.m in Sources */,
|
||||
C3EE76C022DFC63600EC0AA0 /* FLEXScopeCarousel.m in Sources */,
|
||||
@@ -1676,8 +1790,10 @@
|
||||
1C27A8BE1F0E5A0400F0D02D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = U3LST7M92S;
|
||||
@@ -1686,14 +1802,19 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.FLEXTestsMethodsList;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_SWIFT_SYMBOLS = NO;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1C27A8BF1F0E5A0400F0D02D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
DEVELOPMENT_TEAM = U3LST7M92S;
|
||||
INFOPLIST_FILE = FLEXTests/Info.plist;
|
||||
@@ -1701,6 +1822,8 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.FLEXTestsMethodsList;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_SWIFT_SYMBOLS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -1844,6 +1967,8 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
WARNING_CFLAGS = "-Wno-unsupported-availability-guard";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1873,6 +1998,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
WARNING_CFLAGS = "-Wno-unsupported-availability-guard";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// FLEXSwiftSwiftTests.swift
|
||||
// FLEXTests
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class FLEXSwiftSwiftTests: XCTestCase {
|
||||
|
||||
func testSwiftClassKind() {
|
||||
let str = NSStringFromClass(Person.self)
|
||||
let cls = NSClassFromString("FLEXTests.Person")
|
||||
XCTAssertNotEqual(metadataPointer(type: Person.self).pointee, 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// FLEXSwiftTests.m
|
||||
// FLEXTests
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "Metadata.h"
|
||||
#import "SwiftExports.h"
|
||||
#import "FLEXTests-Swift.h"
|
||||
|
||||
@interface FLEXSwiftTests : XCTestCase
|
||||
@property (nonatomic, readonly) Class personClass;
|
||||
@end
|
||||
|
||||
@implementation FLEXSwiftTests
|
||||
|
||||
- (void)setUp {
|
||||
_personClass = NSClassFromString(@"FLEXTests.Person");
|
||||
}
|
||||
|
||||
- (void)testPersonExists {
|
||||
XCTAssertNotNil(self.personClass);
|
||||
}
|
||||
|
||||
- (void)testPerson {
|
||||
uint32_t size = (uint32_t)class_getInstanceSize(self.personClass);
|
||||
id person = swift_allocObject(self.personClass, size, alignof(void *));
|
||||
NSString *name = getObjectClassName(person);
|
||||
XCTAssertEqualObjects(name, @"Person");
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// Person.swift
|
||||
// FLEXTests
|
||||
//
|
||||
// Created by Tanner on 2/10/20.
|
||||
// Copyright © 2020 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class Person {
|
||||
public var age: Int = 5
|
||||
public var name: String = "Billy"
|
||||
|
||||
public init() { }
|
||||
|
||||
// init(age: Int, name: String) {
|
||||
// self.age = age
|
||||
// self.name = name
|
||||
// }
|
||||
}
|
||||
|
||||
enum Kind: Int {
|
||||
case `struct`
|
||||
case `enum`
|
||||
case optional
|
||||
case opaque
|
||||
case tuple
|
||||
case function
|
||||
case existential
|
||||
case metatype
|
||||
case objCClassWrapper
|
||||
case existentialMetatype
|
||||
case foreignClass
|
||||
case heapLocalVariable
|
||||
case heapGenericLocalVariable
|
||||
case errorObject
|
||||
case `class`
|
||||
|
||||
init(flag: Int) {
|
||||
switch flag {
|
||||
case 1: self = .struct
|
||||
case (0 | Flags.kindIsNonHeap): self = .struct
|
||||
case 2: self = .enum
|
||||
case (1 | Flags.kindIsNonHeap): self = .enum
|
||||
case 3: self = .optional
|
||||
case (2 | Flags.kindIsNonHeap): self = .optional
|
||||
case 8: self = .opaque
|
||||
case (3 | Flags.kindIsNonHeap): self = .foreignClass
|
||||
case 9: self = .tuple
|
||||
case (0 | Flags.kindIsRuntimePrivate | Flags.kindIsNonHeap): self = .opaque
|
||||
case 10: self = .function
|
||||
case (1 | Flags.kindIsRuntimePrivate | Flags.kindIsNonHeap): self = .tuple
|
||||
case 12: self = .existential
|
||||
case (2 | Flags.kindIsRuntimePrivate | Flags.kindIsNonHeap): self = .function
|
||||
case 13: self = .metatype
|
||||
case (3 | Flags.kindIsRuntimePrivate | Flags.kindIsNonHeap): self = .existential
|
||||
case 14: self = .objCClassWrapper
|
||||
case (4 | Flags.kindIsRuntimePrivate | Flags.kindIsNonHeap): self = .metatype
|
||||
case 15: self = .existentialMetatype
|
||||
case (5 | Flags.kindIsRuntimePrivate | Flags.kindIsNonHeap): self = .objCClassWrapper
|
||||
case 16: self = .foreignClass
|
||||
case (6 | Flags.kindIsRuntimePrivate | Flags.kindIsNonHeap): self = .existentialMetatype
|
||||
case 64: self = .heapLocalVariable
|
||||
case (0 | Flags.kindIsNonType): self = .heapLocalVariable
|
||||
case 65: self = .heapGenericLocalVariable
|
||||
case (0 | Flags.kindIsNonType | Flags.kindIsRuntimePrivate): self = .heapGenericLocalVariable
|
||||
case 128: self = .errorObject
|
||||
case (1 | Flags.kindIsNonType | Flags.kindIsRuntimePrivate): self = .errorObject
|
||||
default: self = .class
|
||||
}
|
||||
}
|
||||
|
||||
init(type: Any.Type) {
|
||||
let pointer = metadataPointer(type: type)
|
||||
self.init(flag: pointer.pointee)
|
||||
}
|
||||
|
||||
struct Flags {
|
||||
static let kindIsNonHeap = 0x200
|
||||
static let kindIsRuntimePrivate = 0x100
|
||||
static let kindIsNonType = 0x400
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func metadataPointer(type: Any.Type) -> UnsafeMutablePointer<Int> {
|
||||
return unsafeBitCast(type, to: UnsafeMutablePointer<Int>.self)
|
||||
}
|
||||
Reference in New Issue
Block a user