Compare commits

...

10 Commits

Author SHA1 Message Date
Tanner Bennett 6c0a3b43eb Well, this quickly became a pain.
This will be near impossible without importing thousands of lines of code from the compiler. Swift interop is already probably far too difficult to achieve without writing C++, and in Joe's own words:

"If you're writing code in C++ already then definitely just using the headers or MetadataReader as is will keep you better in sync with future additions to the runtime."

Another option to consider is libSwiftRemoteMirror.

This commit leaves the project in an incomplete and broken state.
2020-02-10 16:57:58 -06:00
Tanner Bennett b6914ff990 Skeleton files for SwiftObject support 2020-02-09 18:26:21 -06:00
Tanner Bennett f601018373 Fix crash in network screen while searching 2020-02-09 18:12:40 -06:00
Tanner Bennett a171eedc40 SwiftObject-related fixes 2020-02-08 22:16:09 -06:00
Tanner Bennett 8acbc804b4 Add window and scene managemenet screen
Also fix potential bugs in FLEXExplorerViewController
2020-02-06 19:01:22 -06:00
Tanner Bennett 30a9f5628d Add basic support for bookmarking objects 2020-02-06 19:01:22 -06:00
Tanner Bennett 7362870570 Fix bugs in collection and defaults content sections
FLEXCollectionContentSection and FLEXDefaultsContentSection
2020-02-06 19:01:22 -06:00
Tanner Bennett 26333cbb25 Fix bugs in FLEXNetworkHistoryTableViewController
Also, rename it to FLEXNetworkMITMViewController
2020-02-06 19:01:22 -06:00
Tanner Bennett 15377eabbc Change OS_ACTIVITY_MODE to OS_ACTIVITY_DT_MODE
Silences annoying internal crap in the console
2020-02-06 19:01:22 -06:00
Tanner Bennett b211a845d2 Add basic support for tabs
Other changes:
- Editor/caller view controllers use a toolbar for the call/set button now
- FLEXNavigationController adds the Done button to it's root view controller instead of FLEXExplorerViewController
- FLEXExplorerViewController now overrides presentViewController: and dismissViewControllerAnimated: to toggle its window's key status instead of using new methods to do it
- Adds a 't' simulator shortcut to quickly present an explorer screen for testing
2020-02-06 19:01:22 -06:00
56 changed files with 7761 additions and 125 deletions
@@ -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];
+15 -8
View File
@@ -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];
}
+2 -1
View File
@@ -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.
@@ -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
@@ -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
+1
View File
@@ -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
+4
View File
@@ -133,4 +133,8 @@
return FLEXDynamicColor(systemGrayColor, grayColor);
}
+ (UIColor *)destructiveColor {
return FLEXDynamicColor(systemRedColor, redColor);
}
@end
+6
View File
@@ -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;
+8
View File
@@ -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];
+8 -2
View File
@@ -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];
+74
View File
@@ -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 */
+13
View File
@@ -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
+13
View File
@@ -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
+22
View File
@@ -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
+54
View File
@@ -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
+13
View File
@@ -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
+13
View File
@@ -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;
+154
View File
@@ -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
+124
View File
@@ -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 */
+562
View File
@@ -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
+14
View File
@@ -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>
+1 -1
View File
@@ -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
View File
@@ -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;
+18
View File
@@ -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)
}
}
+35
View File
@@ -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
+89
View File
@@ -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)
}