Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bfba6715e | |||
| 0c87d94b0d | |||
| 401a94f81b | |||
| f92dd73442 | |||
| 3ccf4b8c59 | |||
| e036f964d8 | |||
| a1c3e86ce7 | |||
| 079f2d87a5 | |||
| 462c507df9 | |||
| a19f1acbed | |||
| 1b983160cc | |||
| 6616242d9c | |||
| 377a9a5f3f | |||
| 6f4d9c3ba1 | |||
| 56935533be | |||
| f0b2dc971e | |||
| 346301b628 | |||
| 692146831e | |||
| 81f748a33e | |||
| 038106f954 | |||
| 1bbf8dbea1 | |||
| 343eba92b6 | |||
| d6232baf03 | |||
| 9808b89d6d | |||
| de1cbfae4a | |||
| b036fb18a5 | |||
| c6ed5098cf | |||
| d7283cd22d | |||
| 785e181d75 | |||
| dd66f46a7b | |||
| 55bae8b53e | |||
| 5edca3fdad | |||
| 9bc319a38e | |||
| deac628b06 | |||
| 8e44d88c36 | |||
| 73e4a712e8 | |||
| 9e1f60b2f4 | |||
| 3c385143ee | |||
| e0acf59d06 | |||
| f00af28418 | |||
| 2bd809ae6c | |||
| 003b8f88e8 | |||
| 5e780e6d0c | |||
| 17ea4976c1 | |||
| fbda722f11 | |||
| b351b788d5 | |||
| a195840b67 | |||
| 9b946dda5d | |||
| 806cdc6acb | |||
| aadfc69f01 | |||
| 6e0135d4a0 | |||
| 2599a1eb0d | |||
| c96df6d35c | |||
| fff3059099 | |||
| 4bee6d17bc | |||
| 3d8a12027b | |||
| bc96542856 | |||
| 24526a6a0a | |||
| d33d909fa0 | |||
| 0a9db69419 | |||
| 2db78c3bef | |||
| 1b91bc963e | |||
| 56d90748cb | |||
| 9997721e0a | |||
| bedbd0b8c8 | |||
| cb64c88c59 | |||
| be733c30cb | |||
| e46d26034b | |||
| 3809ece1f9 | |||
| b2410750fd | |||
| 72ccd007b3 | |||
| 4375f1a67f | |||
| 393a43754c | |||
| b56d6f6d33 | |||
| 0d985b504b | |||
| 148890914d | |||
| c734ad16e5 | |||
| ac6301437b | |||
| b45c655ba2 | |||
| 83fe11210e | |||
| e80e3bb5b7 | |||
| 833b8e7534 | |||
| 56e8693c41 | |||
| 49eeb28464 | |||
| a24d752313 | |||
| 643523a779 | |||
| 65744675db | |||
| f994346919 | |||
| a7a8b8e33e | |||
| f254720408 | |||
| e6fd5ed24e | |||
| d13f263492 | |||
| ba36647398 | |||
| 9d97533649 | |||
| 96b13e66b6 | |||
| 9712a4c869 | |||
| 8f9956308f | |||
| 69e88acbee | |||
| 07587986df | |||
| e6641a5f9d | |||
| 111fb6c9e6 | |||
| 995e7eadbe | |||
| ac50a6d36b | |||
| af40ce0909 |
@@ -21,3 +21,4 @@ DerivedData
|
||||
Podfile.lock
|
||||
IDEWorkspaceChecks.plist
|
||||
*.xcworkspace
|
||||
.build
|
||||
|
||||
@@ -16,4 +16,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface UINavigationController (FLEXObjectExploring)
|
||||
|
||||
/// Push an object explorer view controller onto the navigation stack
|
||||
- (void)pushExplorerForObject:(id)object;
|
||||
/// Push an object explorer view controller onto the navigation stack
|
||||
- (void)pushExplorerForObject:(id)object animated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#import "FLEXNavigationController.h"
|
||||
#import "FLEXExplorerViewController.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXTabList.h"
|
||||
|
||||
@interface UINavigationController (Private) <UIGestureRecognizerDelegate>
|
||||
@@ -28,7 +29,8 @@
|
||||
@implementation FLEXNavigationController
|
||||
|
||||
+ (instancetype)withRootViewController:(UIViewController *)rootVC {
|
||||
return [[self alloc] initWithRootViewController:rootVC];
|
||||
FLEXNavigationController *nav = [[self alloc] initWithRootViewController:rootVC];
|
||||
return nav;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@@ -65,6 +67,17 @@
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (@available(iOS 15.0, *)) {
|
||||
UISheetPresentationController *presenter = self.sheetPresentationController;
|
||||
presenter.detents = @[
|
||||
UISheetPresentationControllerDetent.mediumDetent,
|
||||
UISheetPresentationControllerDetent.largeDetent,
|
||||
];
|
||||
presenter.prefersScrollingExpandsWhenScrolledToEdge = NO;
|
||||
presenter.selectedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge;
|
||||
presenter.largestUndimmedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge;
|
||||
}
|
||||
|
||||
if (self.beingPresented && !self.didSetupPendingDismissButtons) {
|
||||
for (UIViewController *vc in self.viewControllers) {
|
||||
[self addNavigationBarItemsToViewController:vc.navigationItem];
|
||||
@@ -93,6 +106,13 @@
|
||||
[self addNavigationBarItemsToViewController:viewController.navigationItem];
|
||||
}
|
||||
|
||||
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
|
||||
// Workaround for UIActivityViewController trying to dismiss us for some reason
|
||||
if (![self.viewControllers.lastObject.presentedViewController isKindOfClass:UIActivityViewController.self]) {
|
||||
[super dismissViewControllerAnimated:flag completion:completion];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismissAnimated {
|
||||
// Tabs are only closed if the done button is pressed; this
|
||||
// allows you to leave a tab open by dragging down to dismiss
|
||||
@@ -194,3 +214,18 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UINavigationController (FLEXObjectExploring)
|
||||
|
||||
- (void)pushExplorerForObject:(id)object {
|
||||
[self pushExplorerForObject:object animated:YES];
|
||||
}
|
||||
|
||||
- (void)pushExplorerForObject:(id)object animated:(BOOL)animated {
|
||||
UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:object];
|
||||
if (explorer) {
|
||||
[self pushViewController:explorer animated:animated];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -96,7 +96,11 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
self.searchController.searchBar.placeholder = @"Filter";
|
||||
self.searchController.searchResultsUpdater = (id)self;
|
||||
self.searchController.delegate = (id)self;
|
||||
self.searchController.dimsBackgroundDuringPresentation = NO;
|
||||
if (@available(iOS 9.1, *)) {
|
||||
self.searchController.obscuresBackgroundDuringPresentation = NO;
|
||||
} else {
|
||||
self.searchController.dimsBackgroundDuringPresentation = NO;
|
||||
}
|
||||
self.searchController.hidesNavigationBarDuringPresentation = NO;
|
||||
/// Not necessary in iOS 13; remove this when iOS 13 is the minimum deployment target
|
||||
self.searchController.searchBar.delegate = self;
|
||||
@@ -210,6 +214,8 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
|
||||
self.tableView.dataSource = self;
|
||||
self.tableView.delegate = self;
|
||||
|
||||
self.tableView.estimatedRowHeight = 10;
|
||||
|
||||
_shareToolbarItem = FLEXBarButtonItemSystem(Action, self, @selector(shareButtonPressed:));
|
||||
_bookmarksToolbarItem = [UIBarButtonItem
|
||||
flex_itemWithImage:FLEXResources.bookmarksIcon target:self action:@selector(showBookmarks)
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#import "FLEXTableViewSection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// A section providing a specific single row.
|
||||
///
|
||||
/// You may optionally provide a view controller to push when the row
|
||||
@@ -16,13 +18,15 @@
|
||||
@interface FLEXSingleRowSection : FLEXTableViewSection
|
||||
|
||||
/// @param reuseIdentifier if nil, kFLEXDefaultCell is used.
|
||||
+ (instancetype)title:(NSString *)sectionTitle
|
||||
reuse:(NSString *)reuseIdentifier
|
||||
+ (instancetype)title:(nullable NSString *)sectionTitle
|
||||
reuse:(nullable NSString *)reuseIdentifier
|
||||
cell:(void(^)(__kindof UITableViewCell *cell))cellConfiguration;
|
||||
|
||||
@property (nonatomic) UIViewController *pushOnSelection;
|
||||
@property (nonatomic) void (^selectionAction)(UIViewController *host);
|
||||
@property (nullable, nonatomic) UIViewController *pushOnSelection;
|
||||
@property (nullable, nonatomic) void (^selectionAction)(UIViewController *host);
|
||||
/// Called to determine whether the single row should display itself or not.
|
||||
@property (nonatomic) BOOL (^filterMatcher)(NSString *filterText);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
- (id)initWithTitle:(NSString *)sectionTitle
|
||||
reuse:(NSString *)reuseIdentifier
|
||||
cell:(void (^)(__kindof UITableViewCell *))cellConfiguration {
|
||||
NSParameterAssert(cellConfiguration);
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_title = sectionTitle;
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (NSArray<UIMenuElement *> *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13)) {
|
||||
- (NSArray<UIMenuElement *> *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13.0)) {
|
||||
NSArray<NSString *> *copyItems = [self copyMenuItemsForRow:row];
|
||||
NSAssert(copyItems.count % 2 == 0, @"copyMenuItemsForRow: should return an even list");
|
||||
|
||||
|
||||
@@ -46,7 +46,13 @@
|
||||
UIPickerView *fontsPicker = [UIPickerView new];
|
||||
fontsPicker.dataSource = self;
|
||||
fontsPicker.delegate = self;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// Deprecated in iOS 13; from then on, selection is always shown
|
||||
fontsPicker.showsSelectionIndicator = YES;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
return fontsPicker;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,4 +10,7 @@
|
||||
|
||||
@interface FLEXArgumentInputStructView : FLEXArgumentInputView
|
||||
|
||||
/// Enable displaying ivar names for custom struct types
|
||||
+ (void)registerFieldNames:(NSArray<NSString *> *)names forTypeEncoding:(NSString *)typeEncoding;
|
||||
|
||||
@end
|
||||
|
||||
@@ -19,6 +19,41 @@
|
||||
|
||||
@implementation FLEXArgumentInputStructView
|
||||
|
||||
static NSMutableDictionary<NSString *, NSArray<NSString *> *> *structFieldNameRegistrar = nil;
|
||||
+ (void)initialize {
|
||||
if (self == [FLEXArgumentInputStructView class]) {
|
||||
structFieldNameRegistrar = [NSMutableDictionary new];
|
||||
[self registerDefaultFieldNames];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)registerDefaultFieldNames {
|
||||
NSDictionary *defaults = @{
|
||||
@(@encode(CGRect)): @[@"CGPoint origin", @"CGSize size"],
|
||||
@(@encode(CGPoint)): @[@"CGFloat x", @"CGFloat y"],
|
||||
@(@encode(CGSize)): @[@"CGFloat width", @"CGFloat height"],
|
||||
@(@encode(CGVector)): @[@"CGFloat dx", @"CGFloat dy"],
|
||||
@(@encode(UIEdgeInsets)): @[@"CGFloat top", @"CGFloat left", @"CGFloat bottom", @"CGFloat right"],
|
||||
@(@encode(UIOffset)): @[@"CGFloat horizontal", @"CGFloat vertical"],
|
||||
@(@encode(NSRange)): @[@"NSUInteger location", @"NSUInteger length"],
|
||||
@(@encode(CATransform3D)): @[@"CGFloat m11", @"CGFloat m12", @"CGFloat m13", @"CGFloat m14",
|
||||
@"CGFloat m21", @"CGFloat m22", @"CGFloat m23", @"CGFloat m24",
|
||||
@"CGFloat m31", @"CGFloat m32", @"CGFloat m33", @"CGFloat m34",
|
||||
@"CGFloat m41", @"CGFloat m42", @"CGFloat m43", @"CGFloat m44"],
|
||||
@(@encode(CGAffineTransform)): @[@"CGFloat a", @"CGFloat b",
|
||||
@"CGFloat c", @"CGFloat d",
|
||||
@"CGFloat tx", @"CGFloat ty"],
|
||||
};
|
||||
|
||||
[structFieldNameRegistrar addEntriesFromDictionary:defaults];
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
structFieldNameRegistrar[@(@encode(NSDirectionalEdgeInsets))] = @[
|
||||
@"CGFloat top", @"CGFloat leading", @"CGFloat bottom", @"CGFloat trailing"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
@@ -181,40 +216,13 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (void)registerFieldNames:(NSArray<NSString *> *)names forTypeEncoding:(NSString *)typeEncoding {
|
||||
NSParameterAssert(typeEncoding); NSParameterAssert(names);
|
||||
structFieldNameRegistrar[typeEncoding] = names;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)customFieldTitlesForTypeEncoding:(const char *)typeEncoding {
|
||||
NSArray<NSString *> *customTitles = nil;
|
||||
if (strcmp(typeEncoding, @encode(CGRect)) == 0) {
|
||||
customTitles = @[@"CGPoint origin", @"CGSize size"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGPoint)) == 0) {
|
||||
customTitles = @[@"CGFloat x", @"CGFloat y"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGSize)) == 0) {
|
||||
customTitles = @[@"CGFloat width", @"CGFloat height"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGVector)) == 0) {
|
||||
customTitles = @[@"CGFloat dx", @"CGFloat dy"];
|
||||
} else if (strcmp(typeEncoding, @encode(UIEdgeInsets)) == 0) {
|
||||
customTitles = @[@"CGFloat top", @"CGFloat left", @"CGFloat bottom", @"CGFloat right"];
|
||||
} else if (strcmp(typeEncoding, @encode(UIOffset)) == 0) {
|
||||
customTitles = @[@"CGFloat horizontal", @"CGFloat vertical"];
|
||||
} else if (strcmp(typeEncoding, @encode(NSRange)) == 0) {
|
||||
customTitles = @[@"NSUInteger location", @"NSUInteger length"];
|
||||
} else if (strcmp(typeEncoding, @encode(CATransform3D)) == 0) {
|
||||
customTitles = @[@"CGFloat m11", @"CGFloat m12", @"CGFloat m13", @"CGFloat m14",
|
||||
@"CGFloat m21", @"CGFloat m22", @"CGFloat m23", @"CGFloat m24",
|
||||
@"CGFloat m31", @"CGFloat m32", @"CGFloat m33", @"CGFloat m34",
|
||||
@"CGFloat m41", @"CGFloat m42", @"CGFloat m43", @"CGFloat m44"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGAffineTransform)) == 0) {
|
||||
customTitles = @[@"CGFloat a", @"CGFloat b",
|
||||
@"CGFloat c", @"CGFloat d",
|
||||
@"CGFloat tx", @"CGFloat ty"];
|
||||
} else {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (strcmp(typeEncoding, @encode(NSDirectionalEdgeInsets)) == 0) {
|
||||
customTitles = @[@"CGFloat top", @"CGFloat leading",
|
||||
@"CGFloat bottom", @"CGFloat trailing"];
|
||||
}
|
||||
}
|
||||
}
|
||||
return customTitles;
|
||||
return structFieldNameRegistrar[@(typeEncoding)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -21,4 +21,7 @@
|
||||
/// Useful when deciding whether to edit or explore a property, ivar, or NSUserDefaults value.
|
||||
+ (BOOL)canEditFieldWithTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue;
|
||||
|
||||
/// Enable displaying ivar names for custom struct types
|
||||
+ (void)registerFieldNames:(NSArray<NSString *> *)names forTypeEncoding:(NSString *)typeEncoding;
|
||||
|
||||
@end
|
||||
|
||||
@@ -67,4 +67,9 @@
|
||||
return [self argumentInputViewSubclassForTypeEncoding:typeEncoding currentValue:currentValue] != nil;
|
||||
}
|
||||
|
||||
/// Enable displaying ivar names for custom struct types
|
||||
+ (void)registerFieldNames:(NSArray<NSString *> *)names forTypeEncoding:(NSString *)typeEncoding {
|
||||
[FLEXArgumentInputStructView registerFieldNames:names forTypeEncoding:typeEncoding];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import "FLEXFieldEditorView.h"
|
||||
#import "FLEXArgumentInputView.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXColor.h"
|
||||
|
||||
@interface FLEXFieldEditorView ()
|
||||
|
||||
@@ -122,7 +123,7 @@
|
||||
}
|
||||
|
||||
+ (UIColor *)dividerColor {
|
||||
return UIColor.lightGrayColor;
|
||||
return FLEXColor.tertiaryBackgroundColor;
|
||||
}
|
||||
|
||||
+ (CGFloat)horizontalPadding {
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
#import "FLEXArgumentInputViewFactory.h"
|
||||
#import "FLEXPropertyAttributes.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "FLEXMetadataExtras.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXColor.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
|
||||
@interface FLEXFieldEditorViewController () <FLEXArgumentInputViewDelegate>
|
||||
|
||||
@property (nonatomic, readonly) id<FLEXMetadataAuxiliaryInfo> auxiliaryInfoProvider;
|
||||
@property (nonatomic) FLEXProperty *property;
|
||||
@property (nonatomic) FLEXIvar *ivar;
|
||||
|
||||
@@ -30,14 +32,14 @@
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
+ (instancetype)target:(id)target property:(nonnull FLEXProperty *)property commitHandler:(void(^_Nullable)(void))onCommit {
|
||||
+ (instancetype)target:(id)target property:(nonnull FLEXProperty *)property commitHandler:(void(^)(void))onCommit {
|
||||
FLEXFieldEditorViewController *editor = [self target:target data:property commitHandler:onCommit];
|
||||
editor.title = [@"Property: " stringByAppendingString:property.name];
|
||||
editor.property = property;
|
||||
return editor;
|
||||
}
|
||||
|
||||
+ (instancetype)target:(id)target ivar:(nonnull FLEXIvar *)ivar commitHandler:(void(^_Nullable)(void))onCommit {
|
||||
+ (instancetype)target:(id)target ivar:(nonnull FLEXIvar *)ivar commitHandler:(void(^)(void))onCommit {
|
||||
FLEXFieldEditorViewController *editor = [self target:target data:ivar commitHandler:onCommit];
|
||||
editor.title = [@"Ivar: " stringByAppendingString:ivar.name];
|
||||
editor.ivar = ivar;
|
||||
@@ -61,6 +63,8 @@
|
||||
self.toolbarItems = @[
|
||||
UIBarButtonItem.flex_flexibleSpace, self.getterButton, self.actionButton
|
||||
];
|
||||
|
||||
[self registerAuxiliaryInfo];
|
||||
|
||||
// Configure input view
|
||||
self.fieldEditorView.fieldDescription = self.fieldDescription;
|
||||
@@ -122,6 +126,17 @@
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)registerAuxiliaryInfo {
|
||||
// This is how Reflex will get Swift struct field names into the editor at runtime
|
||||
NSDictionary<NSString *, NSArray *> *labels = [self.auxiliaryInfoProvider
|
||||
auxiliaryInfoForKey:FLEXAuxiliarynfoKeyFieldLabels
|
||||
];
|
||||
|
||||
for (NSString *type in labels) {
|
||||
[FLEXArgumentInputViewFactory registerFieldNames:labels[type] forTypeEncoding:type];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)currentValue {
|
||||
if (self.property) {
|
||||
return [self.property getValue:self.target];
|
||||
@@ -130,6 +145,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (id<FLEXMetadataAuxiliaryInfo>)auxiliaryInfoProvider {
|
||||
return self.ivar ?: self.property;
|
||||
}
|
||||
|
||||
- (const FLEXTypeEncoding *)typeEncoding {
|
||||
if (self.property) {
|
||||
return self.property.attributes.typeEncoding.UTF8String;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
_commitHandler = onCommit;
|
||||
[NSNotificationCenter.defaultCenter
|
||||
addObserver:self selector:@selector(keyboardDidShow:)
|
||||
name:UIKeyboardDidShowNotification object:nil
|
||||
name:UIKeyboardWillShowNotification object:nil
|
||||
];
|
||||
[NSNotificationCenter.defaultCenter
|
||||
addObserver:self selector:@selector(keyboardWillHide:)
|
||||
|
||||
@@ -129,6 +129,14 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
_selectionFBG = [UISelectionFeedbackGenerator new];
|
||||
}
|
||||
|
||||
// Observe keyboard to move self out of the way
|
||||
[NSNotificationCenter.defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(keyboardShown:)
|
||||
name:UIKeyboardWillShowNotification
|
||||
object:nil
|
||||
];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@@ -162,7 +170,10 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
|
||||
UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
|
||||
UIInterfaceOrientationMask supportedOrientations = FLEXUtility.infoPlistSupportedInterfaceOrientationsMask;
|
||||
if (viewControllerToAsk && ![viewControllerToAsk isKindOfClass:[self class]]) {
|
||||
// We check its class by name because using isKindOfClass will fail for the same class defined
|
||||
// twice in the runtime; and the goal here is to avoid calling -supportedInterfaceOrientations
|
||||
// recursively when I'm inspecting FLEX with itself from a tweak dylib
|
||||
if (viewControllerToAsk && ![NSStringFromClass([viewControllerToAsk class]) hasPrefix:@"FLEX"]) {
|
||||
supportedOrientations = [viewControllerToAsk supportedInterfaceOrientations];
|
||||
}
|
||||
|
||||
@@ -375,6 +386,21 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
return [self.view convertRect:frameInWindow fromView:nil];
|
||||
}
|
||||
|
||||
- (void)keyboardShown:(NSNotification *)notif {
|
||||
CGRect keyboardFrame = [notif.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||
CGRect toolbarFrame = self.explorerToolbar.frame;
|
||||
|
||||
if (CGRectGetMinY(keyboardFrame) < CGRectGetMaxY(toolbarFrame)) {
|
||||
toolbarFrame.origin.y = keyboardFrame.origin.y - toolbarFrame.size.height;
|
||||
// Subtract a little more, to ignore accessory input views
|
||||
toolbarFrame.origin.y -= 50;
|
||||
|
||||
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.5
|
||||
options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
[self updateToolbarPositionWithUnconstrainedFrame:toolbarFrame];
|
||||
} completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Toolbar Buttons
|
||||
|
||||
@@ -403,8 +429,12 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
}
|
||||
|
||||
- (UIWindow *)statusWindow {
|
||||
NSString *statusBarString = [NSString stringWithFormat:@"%@arWindow", @"_statusB"];
|
||||
return [UIApplication.sharedApplication valueForKey:statusBarString];
|
||||
if (!@available(iOS 16, *)) {
|
||||
NSString *statusBarString = [NSString stringWithFormat:@"%@arWindow", @"_statusB"];
|
||||
return [UIApplication.sharedApplication valueForKey:statusBarString];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)recentButtonTapped:(FLEXExplorerToolbarItem *)sender {
|
||||
@@ -436,7 +466,11 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
toolbar.moveItem.selected = self.currentMode == FLEXExplorerModeMove;
|
||||
|
||||
// Recent only enabled when we have a last active tab
|
||||
toolbar.recentItem.enabled = FLEXTabList.sharedList.activeTab != nil;
|
||||
if (!self.presentedViewController) {
|
||||
toolbar.recentItem.enabled = FLEXTabList.sharedList.activeTab != nil;
|
||||
} else {
|
||||
toolbar.recentItem.enabled = NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -818,31 +852,34 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
#pragma mark - Touch Handling
|
||||
|
||||
- (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates {
|
||||
BOOL shouldReceiveTouch = NO;
|
||||
|
||||
CGPoint pointInLocalCoordinates = [self.view convertPoint:pointInWindowCoordinates fromView:nil];
|
||||
|
||||
// Always if it's on the toolbar
|
||||
if (CGRectContainsPoint(self.explorerToolbar.frame, pointInLocalCoordinates)) {
|
||||
shouldReceiveTouch = YES;
|
||||
// If we have a modal presented, is it in the modal?
|
||||
if (self.presentedViewController) {
|
||||
UIView *presentedView = self.presentedViewController.view;
|
||||
CGPoint pipvc = [presentedView convertPoint:pointInLocalCoordinates fromView:self.view];
|
||||
UIView *hit = [presentedView hitTest:pipvc withEvent:nil];
|
||||
if (hit != nil) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Always if we're in selection mode
|
||||
if (!shouldReceiveTouch && self.currentMode == FLEXExplorerModeSelect) {
|
||||
shouldReceiveTouch = YES;
|
||||
if (self.currentMode == FLEXExplorerModeSelect) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Always in move mode too
|
||||
if (!shouldReceiveTouch && self.currentMode == FLEXExplorerModeMove) {
|
||||
shouldReceiveTouch = YES;
|
||||
if (self.currentMode == FLEXExplorerModeMove) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Always if we have a modal presented
|
||||
if (!shouldReceiveTouch && self.presentedViewController) {
|
||||
shouldReceiveTouch = YES;
|
||||
// Always if it's on the toolbar
|
||||
if (CGRectContainsPoint(self.explorerToolbar.frame, pointInLocalCoordinates)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return shouldReceiveTouch;
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@@ -888,8 +925,14 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
// up in case we start replacing them again in the future
|
||||
self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
|
||||
|
||||
[self updateButtonStates];
|
||||
|
||||
// Show the view controller
|
||||
[super presentViewController:toPresent animated:animated completion:completion];
|
||||
[super presentViewController:toPresent animated:animated completion:^{
|
||||
[self updateButtonStates];
|
||||
|
||||
if (completion) completion();
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion {
|
||||
@@ -910,7 +953,11 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
|
||||
|
||||
[self updateButtonStates];
|
||||
|
||||
[super dismissViewControllerAnimated:animated completion:completion];
|
||||
[super dismissViewControllerAnimated:animated completion:^{
|
||||
[self updateButtonStates];
|
||||
|
||||
if (completion) completion();
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)wantsWindowToBecomeKey {
|
||||
|
||||
@@ -18,18 +18,13 @@
|
||||
// Some apps have windows at UIWindowLevelStatusBar + n.
|
||||
// If we make the window level too high, we block out UIAlertViews.
|
||||
// There's a balance between staying above the app's windows and staying below alerts.
|
||||
// UIWindowLevelStatusBar + 100 seems to hit that balance.
|
||||
self.windowLevel = UIWindowLevelStatusBar + 100.0;
|
||||
self.windowLevel = UIWindowLevelAlert - 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
BOOL pointInside = NO;
|
||||
if ([self.eventDelegate shouldHandleTouchAtPoint:point]) {
|
||||
pointInside = [super pointInside:point withEvent:event];
|
||||
}
|
||||
return pointInside;
|
||||
return [self.eventDelegate shouldHandleTouchAtPoint:point];
|
||||
}
|
||||
|
||||
- (BOOL)shouldAffectStatusBarAppearance {
|
||||
|
||||
@@ -84,6 +84,16 @@
|
||||
if (idx != NSNotFound) {
|
||||
[self closeTabAtIndex:idx];
|
||||
}
|
||||
|
||||
// Not sure how this is possible, but it happens sometimes
|
||||
if (self.activeTab == tab) {
|
||||
[self chooseNewActiveTab];
|
||||
}
|
||||
|
||||
// It is possible for an object explorer to form a retain cycle
|
||||
// with its own navigation controller; clearing the view controllers
|
||||
// manually when closing a tab breaks the cycle
|
||||
tab.viewControllers = @[];
|
||||
}
|
||||
|
||||
- (void)closeTabAtIndex:(NSInteger)idx {
|
||||
|
||||
@@ -18,3 +18,4 @@
|
||||
#import "NSArray+FLEX.h"
|
||||
#import "NSUserDefaults+FLEX.h"
|
||||
#import "NSTimer+FLEX.h"
|
||||
#import "NSDateFormatter+FLEX.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "FLEXObjcInternal.h"
|
||||
#import "FLEXSwiftInternal.h"
|
||||
#import "FLEXRuntimeSafety.h"
|
||||
#import "FLEXBlockDescription.h"
|
||||
#import "FLEXTypeEncodingParser.h"
|
||||
@@ -20,6 +21,7 @@
|
||||
#import "FLEXPropertyAttributes.h"
|
||||
#import "FLEXRuntime+Compare.h"
|
||||
#import "FLEXRuntime+UIKitHelpers.h"
|
||||
#import "FLEXMetadataExtras.h"
|
||||
|
||||
#import "FLEXProtocolBuilder.h"
|
||||
#import "FLEXClassBuilder.h"
|
||||
|
||||
@@ -93,16 +93,37 @@
|
||||
}
|
||||
|
||||
- (void)queryButtonPressed {
|
||||
[self showQueryInput:nil];
|
||||
}
|
||||
|
||||
- (void)showQueryInput:(NSString *)prefillQuery {
|
||||
FLEXSQLiteDatabaseManager *database = self.dbm;
|
||||
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Execute an SQL query");
|
||||
make.textField(nil);
|
||||
make.configuredTextField(^(UITextField *textField) {
|
||||
textField.text = prefillQuery;
|
||||
});
|
||||
|
||||
make.button(@"Run").handler(^(NSArray<NSString *> *strings) {
|
||||
FLEXSQLResult *result = [database executeStatement:strings[0]];
|
||||
NSString *query = strings[0];
|
||||
FLEXSQLResult *result = [database executeStatement:query];
|
||||
|
||||
if (result.message) {
|
||||
[FLEXAlert showAlert:@"Message" message:result.message from:self];
|
||||
// Allow users to edit their last query if it had an error
|
||||
if ([result.message containsString:@"error"]) {
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Error").message(result.message);
|
||||
make.button(@"Edit Query").preferred().handler(^(NSArray<NSString *> *_) {
|
||||
// Show query editor again with our last input
|
||||
[self showQueryInput:query];
|
||||
});
|
||||
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:self];
|
||||
} else {
|
||||
[FLEXAlert showAlert:@"Message" message:result.message from:self];
|
||||
}
|
||||
} else {
|
||||
UIViewController *resultsScreen = [FLEXTableContentViewController
|
||||
columns:result.columns rows:result.rows
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// FLEXAPNSViewController.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 6/28/22.
|
||||
// Copyright © 2022 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXGlobalsEntry.h"
|
||||
#import "FLEXFilteringTableViewController.h"
|
||||
|
||||
@interface FLEXAPNSViewController : FLEXFilteringTableViewController <FLEXGlobalsEntry>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,372 @@
|
||||
//
|
||||
// FLEXAPNSViewController.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 6/28/22.
|
||||
// Copyright © 2022 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXAPNSViewController.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXMutableListSection.h"
|
||||
#import "FLEXSingleRowSection.h"
|
||||
#import "NSUserDefaults+FLEX.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
#import "NSDateFormatter+FLEX.h"
|
||||
#import "FLEXResources.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "flex_fishhook.h"
|
||||
#import <dlfcn.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
#define orig(method, ...) if (orig_##method) { orig_##method(__VA_ARGS__); }
|
||||
#define method_lookup(__selector, __cls, __return, ...) \
|
||||
([__cls instancesRespondToSelector:__selector] ? \
|
||||
(__return(*)(__VA_ARGS__))class_getMethodImplementation(__cls, __selector) : nil)
|
||||
|
||||
@interface FLEXAPNSViewController ()
|
||||
@property (nonatomic, readonly, class) Class appDelegateClass;
|
||||
@property (nonatomic, class) NSData *deviceToken;
|
||||
@property (nonatomic, class) NSError *registrationError;
|
||||
@property (nonatomic, readonly, class) NSString *deviceTokenString;
|
||||
@property (nonatomic, readonly, class) NSMutableArray<NSDictionary *> *remoteNotifications;
|
||||
@property (nonatomic, readonly, class) NSMutableArray<UNNotification *> *userNotifications API_AVAILABLE(ios(10.0));
|
||||
|
||||
@property (nonatomic) FLEXSingleRowSection *deviceToken;
|
||||
@property (nonatomic) FLEXMutableListSection<NSDictionary *> *remoteNotifications;
|
||||
@property (nonatomic) FLEXMutableListSection<UNNotification *> *userNotifications API_AVAILABLE(ios(10.0));
|
||||
@end
|
||||
|
||||
@implementation FLEXAPNSViewController
|
||||
|
||||
#pragma mark Swizzles
|
||||
|
||||
/// Hook User Notifications related methods on the app delegate
|
||||
/// and UNUserNotificationCenter delegate classes
|
||||
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
|
||||
if (!NSUserDefaults.standardUserDefaults.flex_enableAPNSCapture) {
|
||||
return;
|
||||
}
|
||||
|
||||
//──────────────────────//
|
||||
// App Delegate //
|
||||
//──────────────────────//
|
||||
|
||||
// Hook UIApplication to intercept app delegate
|
||||
Class uiapp = UIApplication.self;
|
||||
auto orig_uiapp_setDelegate = (void(*)(id, SEL, id))class_getMethodImplementation(
|
||||
uiapp, @selector(setDelegate:)
|
||||
);
|
||||
|
||||
IMP uiapp_setDelegate = imp_implementationWithBlock(^(id _, id delegate) {
|
||||
[self hookAppDelegateClass:[delegate class]];
|
||||
orig_uiapp_setDelegate(_, @selector(setDelegate:), delegate);
|
||||
});
|
||||
|
||||
class_replaceMethod(
|
||||
uiapp,
|
||||
@selector(setDelegate:),
|
||||
uiapp_setDelegate,
|
||||
"v@:@"
|
||||
);
|
||||
|
||||
//───────────────────────────────────────────//
|
||||
// UNUserNotificationCenter Delegate //
|
||||
//───────────────────────────────────────────//
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
Class unusernc = UNUserNotificationCenter.self;
|
||||
auto orig_unusernc_setDelegate = (void(*)(id, SEL, id))class_getMethodImplementation(
|
||||
unusernc, @selector(setDelegate:)
|
||||
);
|
||||
|
||||
IMP unusernc_setDelegate = imp_implementationWithBlock(^(id _, id delegate) {
|
||||
[self hookUNUserNotificationCenterDelegateClass:[delegate class]];
|
||||
orig_unusernc_setDelegate(_, @selector(setDelegate:), delegate);
|
||||
});
|
||||
|
||||
class_replaceMethod(
|
||||
unusernc,
|
||||
@selector(setDelegate:),
|
||||
unusernc_setDelegate,
|
||||
"v@:@"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)hookAppDelegateClass:(Class)appDelegate {
|
||||
// Abort if we already hooked something
|
||||
if (_appDelegateClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
_appDelegateClass = appDelegate;
|
||||
|
||||
// Better documentation for what's happening is in hookUNUserNotificationCenterDelegateClass: below
|
||||
|
||||
auto types_didRegisterForRemoteNotificationsWithDeviceToken = "v@:@@";
|
||||
auto types_didFailToRegisterForRemoteNotificationsWithError = "v@:@@";
|
||||
auto types_didReceiveRemoteNotification = "v@:@@@?";
|
||||
|
||||
auto sel_didRegisterForRemoteNotifications = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:);
|
||||
auto sel_didFailToRegisterForRemoteNotifs = @selector(application:didFailToRegisterForRemoteNotificationsWithError:);
|
||||
auto sel_didReceiveRemoteNotification = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
|
||||
|
||||
auto orig_didRegisterForRemoteNotificationsWithDeviceToken = method_lookup(
|
||||
sel_didRegisterForRemoteNotifications, appDelegate, void, id, SEL, id, id);
|
||||
auto orig_didFailToRegisterForRemoteNotificationsWithError = method_lookup(
|
||||
sel_didFailToRegisterForRemoteNotifs, appDelegate, void, id, SEL, id, id);
|
||||
auto orig_didReceiveRemoteNotification = method_lookup(
|
||||
sel_didReceiveRemoteNotification, appDelegate, void, id, SEL, id, id, id);
|
||||
|
||||
IMP didRegisterForRemoteNotificationsWithDeviceToken = imp_implementationWithBlock(^(id _, id app, NSData *token) {
|
||||
self.deviceToken = token;
|
||||
orig(didRegisterForRemoteNotificationsWithDeviceToken, _, nil, app, token);
|
||||
});
|
||||
IMP didFailToRegisterForRemoteNotificationsWithError = imp_implementationWithBlock(^(id _, id app, NSError *error) {
|
||||
self.registrationError = error;
|
||||
orig(didFailToRegisterForRemoteNotificationsWithError, _, nil, app, error);
|
||||
});
|
||||
IMP didReceiveRemoteNotification = imp_implementationWithBlock(^(id _, id app, NSDictionary *payload, id handler) {
|
||||
// TODO: notify when new notifications are added
|
||||
[self.remoteNotifications addObject:payload];
|
||||
orig(didReceiveRemoteNotification, _, nil, app, payload, handler);
|
||||
});
|
||||
|
||||
class_replaceMethod(
|
||||
appDelegate,
|
||||
sel_didRegisterForRemoteNotifications,
|
||||
didRegisterForRemoteNotificationsWithDeviceToken,
|
||||
types_didRegisterForRemoteNotificationsWithDeviceToken
|
||||
);
|
||||
class_replaceMethod(
|
||||
appDelegate,
|
||||
sel_didFailToRegisterForRemoteNotifs,
|
||||
didFailToRegisterForRemoteNotificationsWithError,
|
||||
types_didFailToRegisterForRemoteNotificationsWithError
|
||||
);
|
||||
class_replaceMethod(
|
||||
appDelegate,
|
||||
sel_didReceiveRemoteNotification,
|
||||
didReceiveRemoteNotification,
|
||||
types_didReceiveRemoteNotification
|
||||
);
|
||||
}
|
||||
|
||||
+ (void)hookUNUserNotificationCenterDelegateClass:(Class)delegate API_AVAILABLE(ios(10.0)) {
|
||||
// Selector
|
||||
auto sel_didReceiveNotification =
|
||||
@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:);
|
||||
// Original implementation (or nil if unimplemented)
|
||||
auto orig_didReceiveNotification = method_lookup(
|
||||
sel_didReceiveNotification, delegate, void, id, SEL, id, id, id);
|
||||
// Our hook (ignores self and other unneeded parameters)
|
||||
IMP didReceiveNotification = imp_implementationWithBlock(^(id _, id __, UNNotification *notification, id ___) {
|
||||
[self.userNotifications addObject:notification];
|
||||
// This macro is a no-op if there is no original implementation
|
||||
orig(didReceiveNotification, _, nil, __, notification, ___);
|
||||
});
|
||||
|
||||
// Set the hook
|
||||
class_replaceMethod(
|
||||
delegate,
|
||||
sel_didReceiveNotification,
|
||||
didReceiveNotification,
|
||||
"v@:@@@?"
|
||||
);
|
||||
}
|
||||
|
||||
#pragma mark Class Properties
|
||||
|
||||
static Class _appDelegateClass = nil;
|
||||
+ (Class)appDelegateClass {
|
||||
return _appDelegateClass;
|
||||
}
|
||||
|
||||
static NSData *_apnsDeviceToken = nil;
|
||||
+ (NSData *)deviceToken {
|
||||
return _apnsDeviceToken;
|
||||
}
|
||||
|
||||
+ (void)setDeviceToken:(NSData *)deviceToken {
|
||||
_apnsDeviceToken = deviceToken;
|
||||
}
|
||||
|
||||
+ (NSString *)deviceTokenString {
|
||||
static NSString *_deviceTokenString = nil;
|
||||
|
||||
if (!_deviceTokenString && self.deviceToken) {
|
||||
NSData *token = self.deviceToken;
|
||||
NSUInteger capacity = token.length * 2;
|
||||
NSMutableString *tokenString = [NSMutableString stringWithCapacity:capacity];
|
||||
|
||||
const UInt8 *tokenData = token.bytes;
|
||||
for (NSUInteger idx = 0; idx < token.length; ++idx) {
|
||||
[tokenString appendFormat:@"%02X", (int)tokenData[idx]];
|
||||
}
|
||||
|
||||
_deviceTokenString = tokenString;
|
||||
}
|
||||
|
||||
return _deviceTokenString;
|
||||
}
|
||||
|
||||
static NSError *_apnsRegistrationError = nil;
|
||||
+ (NSError *)registrationError {
|
||||
return _apnsRegistrationError;
|
||||
}
|
||||
|
||||
+ (void)setRegistrationError:(NSError *)error {
|
||||
_apnsRegistrationError = error;
|
||||
}
|
||||
|
||||
+ (NSMutableArray<NSDictionary *> *)userNotifications {
|
||||
static NSMutableArray *_userNotifications = nil;
|
||||
if (!_userNotifications) {
|
||||
_userNotifications = [NSMutableArray new];
|
||||
}
|
||||
|
||||
return _userNotifications;
|
||||
}
|
||||
|
||||
+ (NSMutableArray<NSDictionary *> *)remoteNotifications {
|
||||
static NSMutableArray *_remoteNotifications = nil;
|
||||
if (!_remoteNotifications) {
|
||||
_remoteNotifications = [NSMutableArray new];
|
||||
}
|
||||
|
||||
return _remoteNotifications;
|
||||
}
|
||||
|
||||
#pragma mark Instance stuff
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.title = @"Push Notifications";
|
||||
|
||||
self.refreshControl = [UIRefreshControl new];
|
||||
[self.refreshControl addTarget:self action:@selector(reloadData) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[self addToolbarItems:@[
|
||||
[UIBarButtonItem
|
||||
flex_itemWithImage:FLEXResources.gearIcon
|
||||
target:self
|
||||
action:@selector(settingsButtonTapped)
|
||||
],
|
||||
]];
|
||||
}
|
||||
|
||||
- (NSArray<FLEXTableViewSection *> *)makeSections {
|
||||
self.deviceToken = [FLEXSingleRowSection title:@"APNS Device Token" reuse:nil cell:^(UITableViewCell *cell) {
|
||||
NSString *tokenString = FLEXAPNSViewController.deviceTokenString;
|
||||
if (tokenString) {
|
||||
cell.textLabel.text = tokenString;
|
||||
cell.textLabel.numberOfLines = 0;
|
||||
}
|
||||
else if (!NSUserDefaults.standardUserDefaults.flex_enableAPNSCapture) {
|
||||
cell.textLabel.text = @"APNS capture disabled";
|
||||
}
|
||||
else {
|
||||
cell.textLabel.text = @"Not yet registered";
|
||||
}
|
||||
}];
|
||||
self.deviceToken.selectionAction = ^(UIViewController *host) {
|
||||
UIPasteboard.generalPasteboard.string = FLEXAPNSViewController.deviceTokenString;
|
||||
[FLEXAlert showQuickAlert:@"Copied to Clipboard" from:host];
|
||||
};
|
||||
|
||||
// Remote Notifications //
|
||||
|
||||
self.remoteNotifications = [FLEXMutableListSection list:FLEXAPNSViewController.remoteNotifications
|
||||
cellConfiguration:^(UITableViewCell *cell, NSDictionary *notif, NSInteger row) {
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
// TODO: date received
|
||||
cell.detailTextLabel.text = [FLEXRuntimeUtility summaryForObject:notif];
|
||||
}
|
||||
filterMatcher:^BOOL(NSString *filterText, NSDictionary *notif) {
|
||||
return [notif.description localizedCaseInsensitiveContainsString:filterText];
|
||||
}
|
||||
];
|
||||
|
||||
self.remoteNotifications.customTitle = @"Remote Notifications";
|
||||
self.remoteNotifications.selectionHandler = ^(UIViewController *host, NSDictionary *notif) {
|
||||
[host.navigationController pushViewController:[
|
||||
FLEXObjectExplorerFactory explorerViewControllerForObject:notif
|
||||
] animated:YES];
|
||||
};
|
||||
|
||||
// User Notifications //
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
self.userNotifications = [FLEXMutableListSection list:FLEXAPNSViewController.userNotifications
|
||||
cellConfiguration:^(UITableViewCell *cell, UNNotification *notif, NSInteger row) {
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
|
||||
// Subtitle is 'subtitle \n date'
|
||||
NSString *dateString = [NSDateFormatter flex_stringFrom:notif.date format:FLEXDateFormatPreciseClock];
|
||||
NSString *subtitle = notif.request.content.subtitle;
|
||||
subtitle = subtitle ? [NSString stringWithFormat:@"%@\n%@", subtitle, dateString] : dateString;
|
||||
|
||||
cell.textLabel.text = notif.request.content.title;
|
||||
cell.detailTextLabel.text = subtitle;
|
||||
}
|
||||
filterMatcher:^BOOL(NSString *filterText, NSDictionary *notif) {
|
||||
return [notif.description localizedCaseInsensitiveContainsString:filterText];
|
||||
}
|
||||
];
|
||||
|
||||
self.userNotifications.customTitle = @"Push Notifications";
|
||||
self.userNotifications.selectionHandler = ^(UIViewController *host, UNNotification *notif) {
|
||||
[host.navigationController pushViewController:[
|
||||
FLEXObjectExplorerFactory explorerViewControllerForObject:notif.request
|
||||
] animated:YES];
|
||||
};
|
||||
|
||||
return @[self.deviceToken, self.remoteNotifications, self.userNotifications];
|
||||
}
|
||||
else {
|
||||
return @[self.deviceToken, self.remoteNotifications];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadData {
|
||||
[self.refreshControl endRefreshing];
|
||||
|
||||
self.remoteNotifications.customTitle = [NSString stringWithFormat:
|
||||
@"%@ notifications", @(self.remoteNotifications.filteredList.count)
|
||||
];
|
||||
[super reloadData];
|
||||
}
|
||||
|
||||
- (void)settingsButtonTapped {
|
||||
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
|
||||
BOOL enabled = defaults.flex_enableAPNSCapture;
|
||||
|
||||
NSString *apnsToggle = enabled ? @"Disable Capture" : @"Enable Capture";
|
||||
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Settings")
|
||||
.message(@"Enable or disable the capture of push notifications.\n\n")
|
||||
.message(@"This will hook UIApplicationMain on launch until it is disabled, ")
|
||||
.message(@"and swizzle some app delegate methods. Restart the app for changes to take effect.");
|
||||
|
||||
make.button(apnsToggle).destructiveStyle().handler(^(NSArray<NSString *> *strings) {
|
||||
[defaults flex_toggleBoolForKey:kFLEXDefaultsAPNSCaptureEnabledKey];
|
||||
});
|
||||
make.button(@"Dismiss").cancelStyle();
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
#pragma mark - FLEXGlobalsEntry
|
||||
|
||||
+ (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row {
|
||||
return @"📌 Push Notifications";
|
||||
}
|
||||
|
||||
+ (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row {
|
||||
return [self new];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -26,6 +26,14 @@
|
||||
self.title = @"Cookies";
|
||||
}
|
||||
|
||||
- (NSString *)headerTitle {
|
||||
return self.cookies.title;
|
||||
}
|
||||
|
||||
- (void)setHeaderTitle:(NSString *)headerTitle {
|
||||
self.cookies.customTitle = headerTitle;
|
||||
}
|
||||
|
||||
- (NSArray<FLEXTableViewSection *> *)makeSections {
|
||||
NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc]
|
||||
initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)
|
||||
|
||||
@@ -158,7 +158,7 @@ typedef NS_ENUM(NSUInteger, FLEXObjectReferenceSection) {
|
||||
}
|
||||
|
||||
+ (instancetype)subclassesOfClassWithName:(NSString *)className {
|
||||
NSArray<FLEXObjectRef *> *references = [FLEXHeapEnumerator subclassesOfClassWithName:className];
|
||||
NSArray<FLEXObjectRef *> *references = [FLEXRuntimeUtility subclassesOfClassWithName:className];
|
||||
FLEXObjectListViewController *controller = [[self alloc] initWithReferences:references];
|
||||
controller.title = [NSString stringWithFormat:@"Subclasses of %@ (%@)",
|
||||
className, @(references.count)
|
||||
@@ -168,15 +168,6 @@ typedef NS_ENUM(NSUInteger, FLEXObjectReferenceSection) {
|
||||
}
|
||||
|
||||
+ (instancetype)objectsWithReferencesToObject:(id)object retained:(BOOL)retain {
|
||||
static Class SwiftObjectClass = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
SwiftObjectClass = NSClassFromString(@"SwiftObject");
|
||||
if (!SwiftObjectClass) {
|
||||
SwiftObjectClass = NSClassFromString(@"Swift._SwiftObject");
|
||||
}
|
||||
});
|
||||
|
||||
NSArray<FLEXObjectRef *> *instances = [FLEXHeapEnumerator
|
||||
objectsWithReferencesToObject:object retained:retain
|
||||
];
|
||||
|
||||
@@ -109,4 +109,10 @@
|
||||
_retainer = nil;
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
return [NSString stringWithFormat:@"<%@: %@>",
|
||||
[self class], self.reference
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// FLEXActivityViewController.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 5/26/22.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Wraps UIActivityViewController so that it can't dismiss other view controllers
|
||||
@interface FLEXActivityViewController : UIActivityViewController
|
||||
|
||||
/// @param source A \c UIVIew, \c UIBarButtonItem, or \c NSValue representing a source rect.
|
||||
+ (id)sharing:(NSArray *)items source:(nullable id)source;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// FLEXActivityViewController.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 5/26/22.
|
||||
//
|
||||
|
||||
#import "FLEXActivityViewController.h"
|
||||
#import "FLEXMacros.h"
|
||||
|
||||
@interface FLEXActivityViewController ()
|
||||
@end
|
||||
|
||||
@implementation FLEXActivityViewController
|
||||
|
||||
+ (id)sharing:(NSArray *)items source:(id)sender {
|
||||
UIViewController *shareSheet = [[UIActivityViewController alloc]
|
||||
initWithActivityItems:items applicationActivities:nil
|
||||
];
|
||||
|
||||
if (sender && UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
UIPopoverPresentationController *popover = shareSheet.popoverPresentationController;
|
||||
|
||||
// Source view
|
||||
if ([sender isKindOfClass:UIView.self]) {
|
||||
popover.sourceView = sender;
|
||||
}
|
||||
// Source bar item
|
||||
if ([sender isKindOfClass:UIBarButtonItem.self]) {
|
||||
popover.barButtonItem = sender;
|
||||
}
|
||||
// Source rect
|
||||
if ([sender isKindOfClass:NSValue.self]) {
|
||||
CGRect rect = [sender CGRectValue];
|
||||
popover.sourceRect = rect;
|
||||
}
|
||||
}
|
||||
|
||||
return shareSheet;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#import "FLEXTableViewController.h"
|
||||
#import "FLEXGlobalsEntry.h"
|
||||
#import "FLEXFileBrowserSearchOperation.h"
|
||||
|
||||
@interface FLEXFileBrowserController : FLEXTableViewController <FLEXGlobalsEntry>
|
||||
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
#import "FLEXFileBrowserController.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXWebViewController.h"
|
||||
#import "FLEXActivityViewController.h"
|
||||
#import "FLEXImagePreviewViewController.h"
|
||||
#import "FLEXTableListViewController.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXObjectExplorerViewController.h"
|
||||
#import <mach-o/loader.h>
|
||||
#import "FLEXFileBrowserSearchOperation.h"
|
||||
|
||||
@interface FLEXFileBrowserTableViewCell : UITableViewCell
|
||||
@end
|
||||
@@ -264,14 +266,19 @@ typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) {
|
||||
if ([pathExtension isEqualToString:@"json"]) {
|
||||
prettyString = [FLEXUtility prettyJSONStringFromData:fileData];
|
||||
} else {
|
||||
// Regardless of file extension...
|
||||
|
||||
id object = nil;
|
||||
@try {
|
||||
// Try to decode an archived object regardless of file extension
|
||||
object = [NSKeyedUnarchiver unarchiveObjectWithData:fileData];
|
||||
} @catch (NSException *e) { }
|
||||
|
||||
// Try to decode an archived object, regardless of file extension
|
||||
NSKeyedUnarchiver *unarchiver = ({
|
||||
NSKeyedUnarchiver *obj = nil;
|
||||
if (@available(iOS 12.0, *)) {
|
||||
obj = [[NSKeyedUnarchiver alloc] initForReadingFromData:fileData error:nil];
|
||||
} else {
|
||||
obj = [[NSKeyedUnarchiver alloc] initForReadingWithData:fileData];
|
||||
}
|
||||
obj.requiresSecureCoding = NO;
|
||||
obj;
|
||||
});
|
||||
id object = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
|
||||
|
||||
// Try to decode other things instead
|
||||
object = object ?: [NSPropertyListSerialization
|
||||
propertyListWithData:fileData
|
||||
@@ -469,10 +476,7 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
[self openFileController:pathString];
|
||||
} else {
|
||||
// Share sheet for files
|
||||
UIActivityViewController *shareSheet = [[UIActivityViewController alloc] initWithActivityItems:@[filePath] applicationActivities:nil];
|
||||
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
shareSheet.popoverPresentationController.sourceView = sender;
|
||||
}
|
||||
UIViewController *shareSheet = [FLEXActivityViewController sharing:@[filePath] source:sender];
|
||||
[self presentViewController:shareSheet animated:true completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
|
||||
FLEXGlobalsRowCookies,
|
||||
FLEXGlobalsRowBrowseRuntime,
|
||||
FLEXGlobalsRowAppKeychainItems,
|
||||
FLEXGlobalsRowPushNotifications,
|
||||
FLEXGlobalsRowAppDelegate,
|
||||
FLEXGlobalsRowRootViewController,
|
||||
FLEXGlobalsRowUserDefaults,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "FLEXObjcRuntimeViewController.h"
|
||||
#import "FLEXKeychainViewController.h"
|
||||
#import "FLEXAPNSViewController.h"
|
||||
#import "FLEXObjectExplorerViewController.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXLiveObjectsController.h"
|
||||
@@ -57,6 +58,8 @@
|
||||
switch (row) {
|
||||
case FLEXGlobalsRowAppKeychainItems:
|
||||
return [FLEXKeychainViewController flex_concreteGlobalsEntry:row];
|
||||
case FLEXGlobalsRowPushNotifications:
|
||||
return [FLEXAPNSViewController flex_concreteGlobalsEntry:row];
|
||||
case FLEXGlobalsRowAddressInspector:
|
||||
return [FLEXAddressExplorerCoordinator flex_concreteGlobalsEntry:row];
|
||||
case FLEXGlobalsRowBrowseRuntime:
|
||||
@@ -94,13 +97,14 @@
|
||||
case FLEXGlobalsRowMainThread:
|
||||
case FLEXGlobalsRowOperationQueue:
|
||||
return [FLEXObjectExplorerFactory flex_concreteGlobalsEntry:row];
|
||||
|
||||
default:
|
||||
@throw [NSException
|
||||
exceptionWithName:NSInternalInconsistencyException
|
||||
reason:@"Missing globals case in switch" userInfo:nil
|
||||
];
|
||||
|
||||
case FLEXGlobalsRowCount: break;
|
||||
}
|
||||
|
||||
@throw [NSException
|
||||
exceptionWithName:NSInternalInconsistencyException
|
||||
reason:@"Missing globals case in switch" userInfo:nil
|
||||
];
|
||||
}
|
||||
|
||||
+ (NSArray<FLEXGlobalsSection *> *)defaultGlobalSections {
|
||||
@@ -122,6 +126,7 @@
|
||||
[self globalsEntryForRow:FLEXGlobalsRowMainBundle],
|
||||
[self globalsEntryForRow:FLEXGlobalsRowUserDefaults],
|
||||
[self globalsEntryForRow:FLEXGlobalsRowAppKeychainItems],
|
||||
[self globalsEntryForRow:FLEXGlobalsRowPushNotifications],
|
||||
[self globalsEntryForRow:FLEXGlobalsRowApplication],
|
||||
[self globalsEntryForRow:FLEXGlobalsRowAppDelegate],
|
||||
[self globalsEntryForRow:FLEXGlobalsRowKeyWindow],
|
||||
|
||||
@@ -28,25 +28,26 @@
|
||||
if (status == errSecSuccess) {//item already exists, update it!
|
||||
query = [[NSMutableDictionary alloc]init];
|
||||
query[(__bridge id)kSecValueData] = self.passwordData;
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
|
||||
if (accessibilityType) {
|
||||
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
|
||||
}else if (status == errSecItemNotFound){//item not found, create it!
|
||||
}
|
||||
else if (status == errSecItemNotFound){//item not found, create it!
|
||||
query = [self query];
|
||||
if (self.label) {
|
||||
query[(__bridge id)kSecAttrLabel] = self.label;
|
||||
}
|
||||
query[(__bridge id)kSecValueData] = self.passwordData;
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
|
||||
if (accessibilityType) {
|
||||
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
||||
}
|
||||
|
||||
@@ -69,9 +70,9 @@
|
||||
}
|
||||
|
||||
NSMutableDictionary *query = [self query];
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IPHONE
|
||||
status = SecItemDelete((__bridge CFDictionaryRef)query);
|
||||
#else
|
||||
#else
|
||||
// On Mac OS, SecItemDelete will not delete a key created in a different
|
||||
// app, nor in a different version of the same app.
|
||||
//
|
||||
@@ -88,7 +89,7 @@
|
||||
status = SecKeychainItemDelete((SecKeychainItemRef)result);
|
||||
CFRelease(result);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (status != errSecSuccess && error != NULL) {
|
||||
*error = [self errorWithCode:status];
|
||||
@@ -102,12 +103,12 @@
|
||||
NSMutableDictionary *query = [self query];
|
||||
query[(__bridge id)kSecReturnAttributes] = @YES;
|
||||
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
|
||||
if (accessibilityType) {
|
||||
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
@@ -149,19 +150,6 @@
|
||||
|
||||
#pragma mark - Accessors
|
||||
|
||||
- (void)setPasswordObject:(id<NSCoding>)object {
|
||||
self.passwordData = [NSKeyedArchiver archivedDataWithRootObject:object];
|
||||
}
|
||||
|
||||
|
||||
- (id<NSCoding>)passwordObject {
|
||||
if (self.passwordData.length) {
|
||||
return [NSKeyedUnarchiver unarchiveObjectWithData:self.passwordData];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)setPassword:(NSString *)password {
|
||||
self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -181,11 +169,11 @@
|
||||
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
+ (BOOL)isSynchronizationAvailable {
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IPHONE
|
||||
return YES;
|
||||
#else
|
||||
#else
|
||||
return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -204,15 +192,15 @@
|
||||
dictionary[(__bridge id)kSecAttrAccount] = self.account;
|
||||
}
|
||||
|
||||
#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
if (self.accessGroup) {
|
||||
dictionary[(__bridge id)kSecAttrAccessGroup] = self.accessGroup;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
if ([[self class] isSynchronizationAvailable]) {
|
||||
id value;
|
||||
|
||||
@@ -233,7 +221,7 @@
|
||||
|
||||
dictionary[(__bridge id)(kSecAttrSynchronizable)] = value;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
@@ -251,7 +239,7 @@
|
||||
case errSecSuccess: return nil;
|
||||
case FLEXKeychainErrorBadArguments: message = NSLocalizedStringFromTableInBundle(@"FLEXKeychainErrorBadArguments", @"FLEXKeychain", resourcesBundle, nil); break;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IPHONE
|
||||
case errSecUnimplemented: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecUnimplemented", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
@@ -291,10 +279,10 @@
|
||||
default: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecDefault", @"FLEXKeychain", resourcesBundle, nil);
|
||||
}
|
||||
#else
|
||||
#else
|
||||
default:
|
||||
message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
NSDictionary *userInfo = message ? @{ NSLocalizedDescriptionKey : message } : nil;
|
||||
|
||||
@@ -122,7 +122,12 @@
|
||||
path = [NSString stringWithFormat:format, path, path];
|
||||
}
|
||||
|
||||
dlopen(path.UTF8String, RTLD_NOW);
|
||||
if (!dlopen(path.UTF8String, RTLD_NOW)) {
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Error").message(@(dlerror()));
|
||||
make.button(@"Dismiss").cancelStyle();
|
||||
}];
|
||||
}
|
||||
});
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
*
|
||||
* See <os/object.h> for details.
|
||||
*/
|
||||
#if !TARGET_OS_MACCATALYST
|
||||
#if !TARGET_OS_MACCATALYST && !__has_include(<xpc/xpc.h>)
|
||||
#if OS_OBJECT_USE_OBJC
|
||||
OS_OBJECT_DECL(xpc_object);
|
||||
#else
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import "FLEXSystemLogCell.h"
|
||||
#import "FLEXSystemLogMessage.h"
|
||||
#import "UIFont+FLEX.h"
|
||||
#import "NSDateFormatter+FLEX.h"
|
||||
|
||||
NSString *const kFLEXSystemLogCellIdentifier = @"FLEXSystemLogCellIdentifier";
|
||||
|
||||
@@ -106,14 +107,7 @@ static const UIEdgeInsets kFLEXLogMessageCellInsets = {10.0, 10.0, 10.0, 10.0};
|
||||
}
|
||||
|
||||
+ (NSString *)logTimeStringFromDate:(NSDate *)date {
|
||||
static NSDateFormatter *formatter = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [NSDateFormatter new];
|
||||
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
|
||||
});
|
||||
|
||||
return [formatter stringFromDate:date];
|
||||
return [NSDateFormatter flex_stringFrom:date format:FLEXDateFormatVerbose];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../../Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../Classes/Utility/Runtime/Objc/Reflection/FLEXMetadataExtras.h
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../Classes/Utility/Runtime/Objc/FLEXSwiftInternal.h
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../Classes/Utility/Categories/NSDateFormatter+FLEX.h
|
||||
@@ -52,6 +52,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// Removes all registered global entries.
|
||||
- (void)clearGlobalEntries;
|
||||
|
||||
#pragma mark - Editing
|
||||
|
||||
/// Enable displaying ivar names for custom struct types
|
||||
+ (void)registerFieldNames:(NSArray<NSString *> *)names forTypeEncoding:(NSString *)typeEncoding;
|
||||
|
||||
#pragma mark - Simulator Shortcuts
|
||||
|
||||
/// Simulator keyboard shortcuts are enabled by default.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "FLEXNetworkMITMViewController.h"
|
||||
#import "FLEXKeyboardHelpViewController.h"
|
||||
#import "FLEXFileBrowserController.h"
|
||||
#import "FLEXArgumentInputStructView.h"
|
||||
#import "FLEXUtility.h"
|
||||
|
||||
@interface FLEXManager (ExtensibilityPrivate)
|
||||
@@ -75,6 +76,13 @@
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Editing
|
||||
|
||||
+ (void)registerFieldNames:(NSArray<NSString *> *)names forTypeEncoding:(NSString *)typeEncoding {
|
||||
[FLEXArgumentInputStructView registerFieldNames:names forTypeEncoding:typeEncoding];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Simulator Shortcuts
|
||||
|
||||
- (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description {
|
||||
|
||||
@@ -23,12 +23,22 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Programmatically dismiss anything presented by FLEX, leaving only the toolbar visible.
|
||||
- (void)dismissAnyPresentedTools:(void (^_Nullable)(void))completion;
|
||||
|
||||
/// Programmatically present something on top of the FLEX toolbar.
|
||||
/// This method will automatically dismiss any currently presented tool,
|
||||
/// so you do not need to call \c dismissAnyPresentedTools: yourself.
|
||||
- (void)presentTool:(UINavigationController *(^)(void))viewControllerFuture
|
||||
completion:(void (^_Nullable)(void))completion;
|
||||
|
||||
/// Programmatically presents a new navigation controller with the given view controller.
|
||||
/// The completion block is passed this new navigation controller.
|
||||
- (void)presentEmbeddedTool:(UIViewController *)viewController
|
||||
completion:(void (^_Nullable)(UINavigationController *))completion;
|
||||
|
||||
/// Programmatically presents a new navigation controller exploring the given object.
|
||||
/// The completion block is passed this new navigation controller.
|
||||
- (void)presentObjectExplorer:(id)object completion:(void (^_Nullable)(UINavigationController *))completion;
|
||||
|
||||
/// Use this to present the explorer in a specific scene when the one
|
||||
/// it chooses by default is not the one you wish to display it in.
|
||||
- (void)showExplorerFromScene:(UIWindowScene *)scene API_AVAILABLE(ios(13.0));
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXExplorerViewController.h"
|
||||
#import "FLEXWindow.h"
|
||||
#import "FLEXObjectExplorerViewController.h"
|
||||
#import "FLEXNavigationController.h"
|
||||
#import "FLEXObjectExplorerFactory.h"
|
||||
#import "FLEXFileBrowserController.h"
|
||||
|
||||
@interface FLEXManager () <FLEXWindowEventDelegate, FLEXExplorerViewControllerDelegate>
|
||||
@@ -106,6 +107,20 @@
|
||||
[self.explorerViewController presentTool:future completion:completion];
|
||||
}
|
||||
|
||||
- (void)presentEmbeddedTool:(UIViewController *)tool completion:(void (^)(UINavigationController *))completion {
|
||||
FLEXNavigationController *nav = [FLEXNavigationController withRootViewController:tool];
|
||||
[self presentTool:^UINavigationController *{
|
||||
return nav;
|
||||
} completion:^{
|
||||
if (completion) completion(nav);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)presentObjectExplorer:(id)object completion:(void (^)(UINavigationController *))completion {
|
||||
UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:object];
|
||||
[self presentEmbeddedTool:explorer completion:completion];
|
||||
}
|
||||
|
||||
- (void)showExplorerFromScene:(UIWindowScene *)scene {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
self.explorerWindow.windowScene = scene;
|
||||
|
||||
@@ -15,9 +15,6 @@ typedef std::string (*ReturnsString)(void *);
|
||||
@implementation FLEXFirebaseSetDataInfo
|
||||
|
||||
+ (instancetype)data:(NSDictionary *)data merge:(NSNumber *)merge mergeFields:(NSArray *)mergeFields {
|
||||
NSParameterAssert(data);
|
||||
NSParameterAssert(merge || mergeFields);
|
||||
|
||||
FLEXFirebaseSetDataInfo *info = [self new];
|
||||
info->_documentData = data;
|
||||
info->_merge = merge;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#import "FLEXManager+Private.h"
|
||||
#import "FLEXTableView.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
#import "NSDateFormatter+FLEX.h"
|
||||
|
||||
typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
|
||||
|
||||
@@ -289,7 +290,8 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
|
||||
postBodyRow.selectionFuture = ^UIViewController * () {
|
||||
// Show the body if we can
|
||||
NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"];
|
||||
UIViewController *detailViewController = [self detailViewControllerForMIMEType:contentType data:[self postBodyDataForTransaction:transaction]];
|
||||
NSData *body = [self postBodyDataForTransaction:transaction];
|
||||
UIViewController *detailViewController = [self detailViewControllerForMIMEType:contentType data:body];
|
||||
if (detailViewController) {
|
||||
detailViewController.title = @"Request Body";
|
||||
return detailViewController;
|
||||
@@ -297,8 +299,13 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
|
||||
|
||||
// We can't show the body, alert user
|
||||
return [FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Can't View HTTP Body Data");
|
||||
make.message(@"FLEX does not have a viewer for request body data with MIME type: ");
|
||||
if (!body) {
|
||||
make.title(@"Empty HTTP Body");
|
||||
} else {
|
||||
make.title(@"Cannot View HTTP Body Data");
|
||||
make.message(@"FLEX does not have a viewer for request body data with MIME type: ");
|
||||
}
|
||||
|
||||
make.message(contentType);
|
||||
make.button(@"Dismiss").cancelStyle();
|
||||
}];
|
||||
@@ -375,19 +382,14 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
|
||||
mechanismRow.detailText = transaction.requestMechanism;
|
||||
[rows addObject:mechanismRow];
|
||||
|
||||
NSDateFormatter *startTimeFormatter = [NSDateFormatter new];
|
||||
startTimeFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
|
||||
|
||||
FLEXNetworkDetailRow *localStartTimeRow = [FLEXNetworkDetailRow new];
|
||||
localStartTimeRow.title = [NSString stringWithFormat:@"Start Time (%@)", [NSTimeZone.localTimeZone abbreviationForDate:transaction.startTime]];
|
||||
localStartTimeRow.detailText = [startTimeFormatter stringFromDate:transaction.startTime];
|
||||
localStartTimeRow.detailText = [NSDateFormatter flex_stringFrom:transaction.startTime format:FLEXDateFormatVerbose];
|
||||
[rows addObject:localStartTimeRow];
|
||||
|
||||
startTimeFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
|
||||
|
||||
FLEXNetworkDetailRow *utcStartTimeRow = [FLEXNetworkDetailRow new];
|
||||
utcStartTimeRow.title = @"Start Time (UTC)";
|
||||
utcStartTimeRow.detailText = [startTimeFormatter stringFromDate:transaction.startTime];
|
||||
utcStartTimeRow.detailText = [NSDateFormatter flex_stringFrom:transaction.startTime format:FLEXDateFormatVerbose];
|
||||
[rows addObject:utcStartTimeRow];
|
||||
|
||||
FLEXNetworkDetailRow *unixStartTime = [FLEXNetworkDetailRow new];
|
||||
@@ -486,6 +488,10 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
|
||||
}
|
||||
|
||||
+ (UIViewController *)detailViewControllerForMIMEType:(NSString *)mimeType data:(NSData *)data {
|
||||
if (!data) {
|
||||
return nil; // An alert will be presented in place of this screen
|
||||
}
|
||||
|
||||
FLEXCustomContentViewerFuture makeCustomViewer = FLEXManager.sharedManager.customContentTypeViewers[mimeType.lowercaseString];
|
||||
|
||||
if (makeCustomViewer) {
|
||||
@@ -523,11 +529,8 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
|
||||
|
||||
+ (NSData *)postBodyDataForTransaction:(FLEXHTTPTransaction *)transaction {
|
||||
NSData *bodyData = transaction.cachedRequestBody;
|
||||
if (bodyData.length > 0) {
|
||||
NSString *contentEncoding = [transaction.request valueForHTTPHeaderField:@"Content-Encoding"];
|
||||
if ([contentEncoding rangeOfString:@"deflate" options:NSCaseInsensitiveSearch].length > 0 || [contentEncoding rangeOfString:@"gzip" options:NSCaseInsensitiveSearch].length > 0) {
|
||||
bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData];
|
||||
}
|
||||
if (bodyData.length > 0 && [FLEXUtility hasCompressedContentEncoding:transaction.request]) {
|
||||
bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData];
|
||||
}
|
||||
return bodyData;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
#import "FLEXNetworkCurlLogger.h"
|
||||
#import "FLEXUtility.h"
|
||||
|
||||
@implementation FLEXNetworkCurlLogger
|
||||
|
||||
@@ -28,8 +29,22 @@
|
||||
}
|
||||
|
||||
if (request.HTTPBody) {
|
||||
NSString *body = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
|
||||
[curlCommandString appendFormat:@"-d \'%@\'", body];
|
||||
NSData *bodyData = request.HTTPBody;
|
||||
if ([FLEXUtility hasCompressedContentEncoding:request]) {
|
||||
bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData];
|
||||
}
|
||||
NSString *body = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding];
|
||||
|
||||
if (body != nil) {
|
||||
[curlCommandString appendFormat:@"-d \'%@\'", body];
|
||||
} else {
|
||||
// Fallback to using base64 encoding
|
||||
[curlCommandString appendString:@"--data-binary @-"];
|
||||
|
||||
NSString *base64 = [request.HTTPBody base64EncodedStringWithOptions:0];
|
||||
NSString *prefix = [NSString stringWithFormat:@"echo -n '%@' | base64 -D | ", base64];
|
||||
[curlCommandString insertString:prefix atIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
return curlCommandString;
|
||||
|
||||
@@ -332,13 +332,13 @@ typedef NS_ENUM(NSInteger, FLEXNetworkObserverMode) {
|
||||
make.title(@"Network Monitor Disabled");
|
||||
make.message(@"You must enable network monitoring to proceed.");
|
||||
|
||||
make.button(@"Turn On").handler(^(NSArray<NSString *> *strings) {
|
||||
make.button(@"Turn On").preferred().handler(^(NSArray<NSString *> *strings) {
|
||||
FLEXNetworkObserver.enabled = YES;
|
||||
[host.navigationController pushViewController:[
|
||||
self globalsEntryViewController:row
|
||||
] animated:YES];
|
||||
}).cancelStyle();
|
||||
make.button(@"Dismiss");
|
||||
});
|
||||
make.button(@"Dismiss").cancelStyle();
|
||||
} showFrom:host];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
#import "NSUserDefaults+FLEX.h"
|
||||
#import "OSCache.h"
|
||||
|
||||
#define Synchronized(queue, obj) ({ \
|
||||
__block id __synchronized_retval = nil; \
|
||||
dispatch_sync(queue, ^{ __synchronized_retval = obj; }); \
|
||||
__synchronized_retval; \
|
||||
})
|
||||
|
||||
|
||||
NSString *const kFLEXNetworkRecorderNewTransactionNotification = @"kFLEXNetworkRecorderNewTransactionNotification";
|
||||
NSString *const kFLEXNetworkRecorderTransactionUpdatedNotification = @"kFLEXNetworkRecorderTransactionUpdatedNotification";
|
||||
NSString *const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction";
|
||||
@@ -84,15 +91,15 @@ NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.r
|
||||
}
|
||||
|
||||
- (NSArray<FLEXHTTPTransaction *> *)HTTPTransactions {
|
||||
return self.orderedHTTPTransactions.copy;
|
||||
return Synchronized(self.queue, self.orderedHTTPTransactions.copy);
|
||||
}
|
||||
|
||||
- (NSArray<FLEXWebsocketTransaction *> *)websocketTransactions {
|
||||
return self.orderedWSTransactions.copy;
|
||||
return Synchronized(self.queue, self.orderedWSTransactions.copy);
|
||||
}
|
||||
|
||||
- (NSArray<FLEXFirebaseTransaction *> *)firebaseTransactions {
|
||||
return self.orderedFirebaseTransactions.copy;
|
||||
return Synchronized(self.queue, self.orderedFirebaseTransactions.copy);
|
||||
}
|
||||
|
||||
- (NSData *)cachedResponseBodyForTransaction:(FLEXHTTPTransaction *)transaction {
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
#pragma mark - Table View Data Source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return self.hostDenylist.count ? 2 : 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
|
||||
@@ -90,7 +90,7 @@ typedef NS_ENUM(NSUInteger, FLEXWebsocketMessageDirection) {
|
||||
@property (nonatomic) NSTimeInterval latency;
|
||||
@property (nonatomic) NSTimeInterval duration;
|
||||
|
||||
/// Populated lazily. Handles both normal HTTPBody data and HTTPBodyStreams.
|
||||
/// Populated lazily, nullable. Handles both normal HTTPBody data and HTTPBodyStreams.
|
||||
@property (nonatomic, readonly) NSData *cachedRequestBody;
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import "FLEXNetworkTransaction.h"
|
||||
#import "FLEXResources.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "NSDateFormatter+FLEX.h"
|
||||
|
||||
@implementation FLEXNetworkTransaction
|
||||
|
||||
@@ -45,14 +46,7 @@
|
||||
}
|
||||
|
||||
- (NSString *)timestampStringFromRequestDate:(NSDate *)date {
|
||||
static NSDateFormatter *dateFormatter = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
dateFormatter = [NSDateFormatter new];
|
||||
dateFormatter.dateFormat = @"HH:mm:ss";
|
||||
});
|
||||
|
||||
return [dateFormatter stringFromDate:date];
|
||||
return [NSDateFormatter flex_stringFrom:date format:FLEXDateFormatPreciseClock];
|
||||
}
|
||||
|
||||
- (void)setState:(FLEXNetworkTransactionState)transactionState {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#import "FLEXMethod.h"
|
||||
#import "Firestore.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <dispatch/queue.h>
|
||||
@@ -237,7 +238,9 @@ static void _logos_method$_ungrouped$FIRDocumentReference$getDocumentWithComplet
|
||||
FIRDocumentSnapshotBlock orig = completion;
|
||||
completion = ^(FIRDocumentSnapshot *document, NSError *error) {
|
||||
[FLEXNetworkRecorder.defaultRecorder recordFIRDocumentDidFetch:document error:error transactionID:requestID];
|
||||
orig(document, error);
|
||||
if (orig != nil) {
|
||||
orig(document, error);
|
||||
}
|
||||
};
|
||||
|
||||
// Forward invocation
|
||||
@@ -256,7 +259,9 @@ static void _logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$(
|
||||
FIRQuerySnapshotBlock orig = completion;
|
||||
completion = ^(FIRQuerySnapshot *query, NSError *error) {
|
||||
[FLEXNetworkRecorder.defaultRecorder recordFIRQueryDidFetch:query error:error transactionID:requestID];
|
||||
orig(query, error);
|
||||
if (orig != nil) {
|
||||
orig(query, error);
|
||||
}
|
||||
};
|
||||
|
||||
// Forward invocation
|
||||
@@ -285,7 +290,9 @@ static void _logos_method$_ungrouped$FIRDocumentReference$setData$merge$completi
|
||||
void (^orig)(NSError *) = completion;
|
||||
completion = ^(NSError *error) {
|
||||
[FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID];
|
||||
orig(error);
|
||||
if (orig != nil) {
|
||||
orig(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Forward invocation
|
||||
@@ -313,7 +320,9 @@ static void _logos_method$_ungrouped$FIRDocumentReference$setData$mergeFields$co
|
||||
void (^orig)(NSError *) = completion;
|
||||
completion = ^(NSError *error) {
|
||||
[FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID];
|
||||
orig(error);
|
||||
if (orig != nil) {
|
||||
orig(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Forward invocation
|
||||
@@ -333,7 +342,9 @@ static void _logos_method$_ungrouped$FIRDocumentReference$updateData$completion$
|
||||
void (^orig)(NSError *) = completion;
|
||||
completion = ^(NSError *error) {
|
||||
[FLEXNetworkRecorder.defaultRecorder recordFIRDidUpdateData:error transactionID:requestID];
|
||||
orig(error);
|
||||
if (orig != nil) {
|
||||
orig(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Forward invocation
|
||||
@@ -353,7 +364,9 @@ static void _logos_method$_ungrouped$FIRDocumentReference$deleteDocumentWithComp
|
||||
void (^orig)(NSError *) = completion;
|
||||
completion = ^(NSError *error) {
|
||||
[FLEXNetworkRecorder.defaultRecorder recordFIRDidDeleteDocument:error transactionID:requestID];
|
||||
orig(error);
|
||||
if (orig != nil) {
|
||||
orig(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Forward invocation
|
||||
@@ -371,7 +384,9 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
|
||||
void (^orig)(NSError *) = completion;
|
||||
completion = ^(NSError *error) {
|
||||
[FLEXNetworkRecorder.defaultRecorder recordFIRDidAddDocument:error transactionID:requestID];
|
||||
orig(error);
|
||||
if (orig != nil) {
|
||||
orig(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Forward invocation
|
||||
@@ -518,8 +533,17 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
|
||||
[self injectIntoNSURLConnectionAsynchronousClassMethod];
|
||||
[self injectIntoNSURLConnectionSynchronousClassMethod];
|
||||
|
||||
[self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods];
|
||||
[self injectIntoNSURLSessionAsyncUploadTaskMethods];
|
||||
Class URLSession = [NSURLSession class];
|
||||
[self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:URLSession];
|
||||
[self injectIntoNSURLSessionAsyncUploadTaskMethods:URLSession];
|
||||
|
||||
// At some point, NSURLSession.sharedSession became an __NSURLSessionLocal,
|
||||
// which is not the class returned by [NSURLSession class], of course
|
||||
Class URLSessionLocal = NSClassFromString(@"__NSURLSessionLocal");
|
||||
if (URLSessionLocal && (URLSession != URLSessionLocal)) {
|
||||
[self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:URLSessionLocal];
|
||||
[self injectIntoNSURLSessionAsyncUploadTaskMethods:URLSessionLocal];
|
||||
}
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
Class websocketTask = NSClassFromString(@"__NSURLSessionWebSocketTask");
|
||||
@@ -643,15 +667,22 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
|
||||
Method originalResume = class_getInstanceMethod(class, selector);
|
||||
IMP implementation = imp_implementationWithBlock(^(NSURLSessionTask *slf) {
|
||||
|
||||
// iOS's internal HTTP parser finalization code is mysteriously not thread safe,
|
||||
// invoking it asynchronously has a chance to cause a `double free` crash.
|
||||
// This line below will ask for HTTPBody synchronously, make the HTTPParser
|
||||
// parse the request, and cache them in advance. After that the HTTPParser
|
||||
// will be finalized. Make sure other threads inspecting the request
|
||||
// won't trigger a race to finalize the parser.
|
||||
[slf.currentRequest HTTPBody];
|
||||
if (@available(iOS 11.0, *)) {
|
||||
// AVAggregateAssetDownloadTask deeply does not like to be looked at. Accessing -currentRequest or
|
||||
// -originalRequest will crash. Do not try to observe these. https://github.com/FLEXTool/FLEX/issues/276
|
||||
if (![slf isKindOfClass:[AVAggregateAssetDownloadTask class]]) {
|
||||
// iOS's internal HTTP parser finalization code is mysteriously not thread safe,
|
||||
// invoking it asynchronously has a chance to cause a `double free` crash.
|
||||
// This line below will ask for HTTPBody synchronously, make the HTTPParser
|
||||
// parse the request, and cache them in advance. After that the HTTPParser
|
||||
// will be finalized. Make sure other threads inspecting the request
|
||||
// won't trigger a race to finalize the parser.
|
||||
[slf.currentRequest HTTPBody];
|
||||
|
||||
[FLEXNetworkObserver.sharedObserver URLSessionTaskWillResume:slf];
|
||||
}
|
||||
}
|
||||
|
||||
[FLEXNetworkObserver.sharedObserver URLSessionTaskWillResume:slf];
|
||||
((void(*)(id, SEL))objc_msgSend)(
|
||||
slf, swizzledSelector
|
||||
);
|
||||
@@ -803,11 +834,11 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods {
|
||||
+ (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:(Class)sessionClass {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Class class = [NSURLSession class];
|
||||
|
||||
Class class = sessionClass;
|
||||
|
||||
// The method signatures here are close enough that
|
||||
// we can use the same logic to inject into all of them.
|
||||
const SEL selectors[] = {
|
||||
@@ -871,11 +902,11 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)injectIntoNSURLSessionAsyncUploadTaskMethods {
|
||||
+ (void)injectIntoNSURLSessionAsyncUploadTaskMethods:(Class)sessionClass {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Class class = [NSURLSession class];
|
||||
|
||||
Class class = sessionClass;
|
||||
|
||||
// The method signatures here are close enough that we can use the same logic to inject into both of them.
|
||||
// Note that they have 3 arguments, so we can't easily combine with the data and download method above.
|
||||
typedef NSURLSessionUploadTask *(^UploadTaskMethod)(
|
||||
@@ -1591,15 +1622,19 @@ static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$ad
|
||||
[FLEXNetworkObserver.sharedObserver
|
||||
websocketTask:slf sendMessagage:message
|
||||
];
|
||||
completion = ^(NSError *error) {
|
||||
|
||||
id completionHook = ^(NSError *error) {
|
||||
[FLEXNetworkObserver.sharedObserver
|
||||
websocketTaskMessageSendCompletion:message
|
||||
error:error
|
||||
];
|
||||
if (completion) {
|
||||
completion(error);
|
||||
}
|
||||
};
|
||||
|
||||
((void(*)(id, SEL, id, id))objc_msgSend)(
|
||||
slf, swizzledSelector, message, completion
|
||||
slf, swizzledSelector, message, completionHook
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1868,6 +1903,12 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask];
|
||||
FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID];
|
||||
|
||||
// Fix for "Response body not in cache" issue reported by developers
|
||||
// See this github comment for detailed explanation on why this happens
|
||||
// https://github.com/FLEXTool/FLEX/issues/568#issuecomment-1141015572
|
||||
if (requestState.dataAccumulator == nil) {
|
||||
requestState.dataAccumulator = [NSMutableData new];
|
||||
}
|
||||
[requestState.dataAccumulator appendData:data];
|
||||
|
||||
[FLEXNetworkRecorder.defaultRecorder
|
||||
@@ -1946,6 +1987,14 @@ didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data
|
||||
}
|
||||
|
||||
- (void)URLSessionTaskWillResume:(NSURLSessionTask *)task {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
// AVAggregateAssetDownloadTask deeply does not like to be looked at. Accessing -currentRequest or
|
||||
// -originalRequest will crash. Do not try to observe these. https://github.com/FLEXTool/FLEX/issues/276
|
||||
if ([task isKindOfClass:[AVAggregateAssetDownloadTask class]]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Since resume can be called multiple times on the same task, only treat the first resume as
|
||||
// the equivalent to connection:willSendRequest:...
|
||||
[self performBlock:^{
|
||||
|
||||
@@ -71,3 +71,12 @@
|
||||
- (void)reloadClassHierarchy;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface FLEXObjectExplorer (Reflex)
|
||||
|
||||
/// Do not enable this property manually; Reflex will flip the switch when it is loaded.
|
||||
/// If you wish, you may \e disable it manually.
|
||||
@property (nonatomic, class) BOOL reflexAvailable;
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#import "FLEXPropertyAttributes.h"
|
||||
#import "FLEXMetadataSection.h"
|
||||
#import "NSUserDefaults+FLEX.h"
|
||||
#import "FLEXMirror.h"
|
||||
#import "FLEXSwiftInternal.h"
|
||||
|
||||
@implementation FLEXObjectExplorerDefaults
|
||||
|
||||
@@ -38,10 +40,18 @@
|
||||
NSMutableArray<FLEXStaticMetadata *> *_allImageNames;
|
||||
NSString *_objectDescription;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) id<FLEXMirror> initialMirror;
|
||||
@end
|
||||
|
||||
@implementation FLEXObjectExplorer
|
||||
|
||||
+ (void)initialize {
|
||||
if (self == FLEXObjectExplorer.class) {
|
||||
FLEXObjectExplorer.reflexAvailable = NSClassFromString(@"FLEXSwiftMirror") != nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
+ (id)forObject:(id)objectOrClass {
|
||||
@@ -62,6 +72,23 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<FLEXMirror>)mirrorForClass:(Class)cls {
|
||||
static Class FLEXSwiftMirror = nil;
|
||||
|
||||
// Should we use Reflex?
|
||||
if (FLEXIsSwiftObjectOrClass(cls) && FLEXObjectExplorer.reflexAvailable) {
|
||||
// Initialize FLEXSwiftMirror class if needed
|
||||
if (!FLEXSwiftMirror) {
|
||||
FLEXSwiftMirror = NSClassFromString(@"FLEXSwiftMirror");
|
||||
}
|
||||
|
||||
return [(id<FLEXMirror>)[FLEXSwiftMirror alloc] initWithSubject:cls];
|
||||
}
|
||||
|
||||
// No; not swift object, or Reflex unavailable
|
||||
return [FLEXMirror reflect:cls];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
@@ -151,40 +178,41 @@
|
||||
NSInteger rootIdx = count - 1;
|
||||
for (NSInteger i = 0; i < count; i++) {
|
||||
Class cls = self.classHierarchyClasses[i];
|
||||
id<FLEXMirror> mirror = [self mirrorForClass:cls];
|
||||
superclass = (i < rootIdx) ? self.classHierarchyClasses[i+1] : nil;
|
||||
|
||||
[allProperties addObject:[self
|
||||
metadataUniquedByName:[cls flex_allInstanceProperties]
|
||||
metadataUniquedByName:mirror.properties
|
||||
superclass:superclass
|
||||
kind:FLEXMetadataKindProperties
|
||||
skip:showMethodOverrides
|
||||
]];
|
||||
[allClassProps addObject:[self
|
||||
metadataUniquedByName:[cls flex_allClassProperties]
|
||||
metadataUniquedByName:mirror.classProperties
|
||||
superclass:superclass
|
||||
kind:FLEXMetadataKindClassProperties
|
||||
skip:showMethodOverrides
|
||||
]];
|
||||
[_allIvars addObject:[self
|
||||
metadataUniquedByName:[cls flex_allIvars]
|
||||
metadataUniquedByName:mirror.ivars
|
||||
superclass:nil
|
||||
kind:FLEXMetadataKindIvars
|
||||
skip:NO
|
||||
]];
|
||||
[allMethods addObject:[self
|
||||
metadataUniquedByName:[cls flex_allInstanceMethods]
|
||||
metadataUniquedByName:mirror.methods
|
||||
superclass:superclass
|
||||
kind:FLEXMetadataKindMethods
|
||||
skip:showMethodOverrides
|
||||
]];
|
||||
[allClassMethods addObject:[self
|
||||
metadataUniquedByName:[cls flex_allClassMethods]
|
||||
metadataUniquedByName:mirror.classMethods
|
||||
superclass:superclass
|
||||
kind:FLEXMetadataKindClassMethods
|
||||
skip:showMethodOverrides
|
||||
]];
|
||||
[_allConformedProtocols addObject:[self
|
||||
metadataUniquedByName:[cls flex_protocols]
|
||||
metadataUniquedByName:mirror.protocols
|
||||
superclass:superclass
|
||||
kind:FLEXMetadataKindProtocols
|
||||
skip:NO
|
||||
@@ -325,6 +353,10 @@
|
||||
if ([names containsObject:name]) {
|
||||
return NO;
|
||||
} else {
|
||||
if (!name) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[names addObject:name];
|
||||
|
||||
// Skip methods and properties which are just overrides,
|
||||
@@ -376,3 +408,13 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - Reflex
|
||||
@implementation FLEXObjectExplorer (Reflex)
|
||||
static BOOL _reflexAvailable = NO;
|
||||
|
||||
+ (BOOL)reflexAvailable { return _reflexAvailable; }
|
||||
+ (void)setReflexAvailable:(BOOL)enable { _reflexAvailable = enable; }
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import "FLEXGlobalsViewController.h"
|
||||
#import "FLEXClassShortcuts.h"
|
||||
#import "FLEXViewShortcuts.h"
|
||||
#import "FLEXWindowShortcuts.h"
|
||||
#import "FLEXViewControllerShortcuts.h"
|
||||
#import "FLEXUIAppShortcuts.h"
|
||||
#import "FLEXImageShortcuts.h"
|
||||
@@ -48,6 +49,7 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
|
||||
ClassKey(UIViewController) : [FLEXViewControllerShortcuts class],
|
||||
ClassKey(UIApplication) : [FLEXUIAppShortcuts class],
|
||||
ClassKey(UIView) : [FLEXViewShortcuts class],
|
||||
ClassKey(UIWindow) : [FLEXWindowShortcuts class],
|
||||
ClassKey(UIImage) : [FLEXImageShortcuts class],
|
||||
ClassKey(CALayer) : [FLEXLayerShortcuts class],
|
||||
ClassKey(UIColor) : [FLEXColorPreviewSection class],
|
||||
@@ -180,7 +182,7 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
|
||||
return [self explorerViewControllerForObject:UIDevice.currentDevice];
|
||||
case FLEXGlobalsRowPasteboard:
|
||||
return [self explorerViewControllerForObject:UIPasteboard.generalPasteboard];
|
||||
case FLEXGlobalsRowURLSession:
|
||||
case FLEXGlobalsRowURLSession:
|
||||
return [self explorerViewControllerForObject:NSURLSession.sharedSession];
|
||||
case FLEXGlobalsRowURLCache:
|
||||
return [self explorerViewControllerForObject:NSURLCache.sharedURLCache];
|
||||
@@ -215,8 +217,22 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
|
||||
|
||||
return nil;
|
||||
}
|
||||
default: return nil;
|
||||
|
||||
case FLEXGlobalsRowNetworkHistory:
|
||||
case FLEXGlobalsRowSystemLog:
|
||||
case FLEXGlobalsRowLiveObjects:
|
||||
case FLEXGlobalsRowAddressInspector:
|
||||
case FLEXGlobalsRowCookies:
|
||||
case FLEXGlobalsRowBrowseRuntime:
|
||||
case FLEXGlobalsRowAppKeychainItems:
|
||||
case FLEXGlobalsRowPushNotifications:
|
||||
case FLEXGlobalsRowBrowseBundle:
|
||||
case FLEXGlobalsRowBrowseContainer:
|
||||
case FLEXGlobalsRowCount:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (FLEXGlobalsEntryRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row {
|
||||
|
||||
@@ -55,4 +55,3 @@ typedef void (^FLEXMutableListCellForElement)(__kindof UITableViewCell *cell, id
|
||||
- (void)mutate:(void(^)(NSMutableArray *list))block;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXWindowShortcuts.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by AnthoPak on 26/09/2022.
|
||||
//
|
||||
|
||||
#import "FLEXShortcutsSection.h"
|
||||
|
||||
/// Adds "Animations Speed" shortcut for all windows
|
||||
@interface FLEXWindowShortcuts : FLEXShortcutsSection
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// FLEXWindowShortcuts.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by AnthoPak on 26/09/2022.
|
||||
//
|
||||
|
||||
#import "FLEXWindowShortcuts.h"
|
||||
#import "FLEXShortcut.h"
|
||||
#import "FLEXAlert.h"
|
||||
#import "FLEXObjectExplorerViewController.h"
|
||||
|
||||
@implementation FLEXWindowShortcuts
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
+ (instancetype)forObject:(UIView *)view {
|
||||
return [self forObject:view additionalRows:@[
|
||||
[FLEXActionShortcut title:@"Animation Speed" subtitle:^NSString *(UIWindow *window) {
|
||||
return [NSString stringWithFormat:@"Current speed: %.2f", window.layer.speed];
|
||||
} selectionHandler:^(UIViewController *host, UIWindow *window) {
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Change Animation Speed");
|
||||
make.message([NSString stringWithFormat:@"Current speed: %.2f", window.layer.speed]);
|
||||
make.configuredTextField(^(UITextField * _Nonnull textField) {
|
||||
textField.placeholder = @"Default: 1.0";
|
||||
textField.keyboardType = UIKeyboardTypeDecimalPad;
|
||||
});
|
||||
|
||||
make.button(@"OK").handler(^(NSArray<NSString *> *strings) {
|
||||
NSNumberFormatter *formatter = [NSNumberFormatter new];
|
||||
formatter.numberStyle = NSNumberFormatterDecimalStyle;
|
||||
CGFloat speedValue = [formatter numberFromString:strings.firstObject].floatValue;
|
||||
window.layer.speed = speedValue;
|
||||
|
||||
// Refresh the host view controller to update the shortcut subtitle, reflecting current speed
|
||||
// TODO: this shouldn't be necessary
|
||||
[(FLEXObjectExplorerViewController *)host reloadData];
|
||||
});
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:host];
|
||||
} accessoryType:^UITableViewCellAccessoryType(id _Nonnull object) {
|
||||
return UITableViewCellAccessoryDisclosureIndicator;
|
||||
}]
|
||||
]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -33,7 +33,7 @@
|
||||
+ (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:(FLEXExplorerToolbarItem *)backupItem {
|
||||
NSParameterAssert(title); NSParameterAssert(image);
|
||||
|
||||
FLEXExplorerToolbarItem *toolbarItem = [self buttonWithType:UIButtonTypeSystem];
|
||||
FLEXExplorerToolbarItem *toolbarItem = [self buttonWithType:UIButtonTypeCustom];
|
||||
toolbarItem.sibling = backupItem;
|
||||
toolbarItem.title = title;
|
||||
toolbarItem.image = image;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// NSDateFormatter+FLEX.h
|
||||
// libflex:FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 7/24/22.
|
||||
// Copyright © 2022 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FLEXDateFormat) {
|
||||
// hour:minute [AM|PM]
|
||||
FLEXDateFormatClock,
|
||||
// hour:minute:second [AM|PM]
|
||||
FLEXDateFormatPreciseClock,
|
||||
// year-month-day hour:minute:second.millisecond
|
||||
FLEXDateFormatVerbose,
|
||||
};
|
||||
|
||||
@interface NSDateFormatter (FLEX)
|
||||
|
||||
+ (NSString *)flex_stringFrom:(NSDate *)date format:(FLEXDateFormat)format;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// NSDateFormatter+FLEX.m
|
||||
// libflex:FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 7/24/22.
|
||||
// Copyright © 2022 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSDateFormatter+FLEX.h"
|
||||
|
||||
@implementation NSDateFormatter (FLEX)
|
||||
|
||||
+ (NSString *)flex_stringFrom:(NSDate *)date format:(FLEXDateFormat)format {
|
||||
static NSDateFormatter *formatter = nil;
|
||||
if (!formatter) {
|
||||
formatter = [NSDateFormatter new];
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case FLEXDateFormatClock:
|
||||
formatter.dateFormat = @"h:mm a";
|
||||
break;
|
||||
case FLEXDateFormatPreciseClock:
|
||||
formatter.dateFormat = @"h:mm:ss a";
|
||||
break;
|
||||
case FLEXDateFormatVerbose:
|
||||
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
|
||||
break;
|
||||
}
|
||||
|
||||
return [formatter stringFromDate:date];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -19,6 +19,7 @@ extern NSString * const kFLEXDefaultsHideVariablePreviewsKey;
|
||||
extern NSString * const kFLEXDefaultsNetworkObserverEnabledKey;
|
||||
extern NSString * const kFLEXDefaultsNetworkHostDenylistKey;
|
||||
extern NSString * const kFLEXDefaultsDisableOSLogForceASLKey;
|
||||
extern NSString * const kFLEXDefaultsAPNSCaptureEnabledKey;
|
||||
extern NSString * const kFLEXDefaultsRegisterJSONExplorerKey;
|
||||
|
||||
/// All BOOL preferences are NO by default
|
||||
@@ -42,6 +43,8 @@ extern NSString * const kFLEXDefaultsRegisterJSONExplorerKey;
|
||||
@property (nonatomic) BOOL flex_disableOSLog;
|
||||
@property (nonatomic) BOOL flex_cacheOSLogMessages;
|
||||
|
||||
@property (nonatomic) BOOL flex_enableAPNSCapture;
|
||||
|
||||
@property (nonatomic) BOOL flex_explorerHidesPropertyIvars;
|
||||
@property (nonatomic) BOOL flex_explorerHidesPropertyMethods;
|
||||
@property (nonatomic) BOOL flex_explorerHidesPrivateMethods;
|
||||
|
||||
@@ -19,6 +19,7 @@ NSString * const kFLEXDefaultsNetworkObserverEnabledKey = @"com.flex.FLEXNetwork
|
||||
NSString * const kFLEXDefaultsNetworkObserverLastModeKey = @"com.flex.FLEXNetworkObserver.lastMode";
|
||||
NSString * const kFLEXDefaultsNetworkHostDenylistKey = @"com.flipboard.FLEX.network_host_denylist";
|
||||
NSString * const kFLEXDefaultsDisableOSLogForceASLKey = @"com.flipboard.FLEX.try_disable_os_log";
|
||||
NSString * const kFLEXDefaultsAPNSCaptureEnabledKey = @"com.flipboard.FLEX.capture_apns";
|
||||
NSString * const kFLEXDefaultsRegisterJSONExplorerKey = @"com.flipboard.FLEX.view_json_as_object";
|
||||
|
||||
#define FLEXDefaultsPathForFile(name) ({ \
|
||||
@@ -123,6 +124,16 @@ NSString * const kFLEXDefaultsRegisterJSONExplorerKey = @"com.flipboard.FLEX.vie
|
||||
];
|
||||
}
|
||||
|
||||
#pragma mark Push Notifications
|
||||
|
||||
- (BOOL)flex_enableAPNSCapture {
|
||||
return [self boolForKey:kFLEXDefaultsAPNSCaptureEnabledKey];
|
||||
}
|
||||
|
||||
- (void)setFlex_enableAPNSCapture:(BOOL)enable {
|
||||
[self setBool:enable forKey:kFLEXDefaultsAPNSCaptureEnabledKey];
|
||||
}
|
||||
|
||||
#pragma mark Object Explorer
|
||||
|
||||
- (BOOL)flex_explorerHidesPropertyIvars {
|
||||
|
||||
@@ -28,6 +28,9 @@ typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionHandler)(void(^handler)(NSAr
|
||||
/// Shows a simple alert with one button which says "Dismiss"
|
||||
+ (void)showAlert:(NSString * _Nullable)title message:(NSString * _Nullable)message from:(UIViewController *)viewController;
|
||||
|
||||
/// Shows a simple alert with no buttons and only a title, for half a second
|
||||
+ (void)showQuickAlert:(NSString *)title from:(UIViewController *)viewController;
|
||||
|
||||
/// Construct and display an alert
|
||||
+ (void)makeAlert:(FLEXAlertBuilder)block showFrom:(UIViewController *)viewController;
|
||||
/// Construct and display an action sheet-style alert
|
||||
@@ -68,8 +71,11 @@ typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionHandler)(void(^handler)(NSAr
|
||||
@property (nonatomic, readonly) FLEXAlertActionStringProperty title;
|
||||
/// Make the action destructive. It appears with red text.
|
||||
@property (nonatomic, readonly) FLEXAlertActionProperty destructiveStyle;
|
||||
/// Make the action cancel-style. It appears with a bolder font.
|
||||
/// Make the action cancel-style. It sometimes appears with a bolder font.
|
||||
@property (nonatomic, readonly) FLEXAlertActionProperty cancelStyle;
|
||||
/// Make the action the preferred action. It appears with a bolder font.
|
||||
/// The first action that is set as preferred will be used as the preferred action.
|
||||
@property (nonatomic, readonly) FLEXAlertActionProperty preferred;
|
||||
/// Enable or disable the action. Enabled by default.
|
||||
@property (nonatomic, readonly) FLEXAlertActionBOOLProperty enabled;
|
||||
/// Give the button an action. The action takes an array of text field strings.
|
||||
|
||||
@@ -22,6 +22,7 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
|
||||
@property (nonatomic) NSString *_title;
|
||||
@property (nonatomic) UIAlertActionStyle _style;
|
||||
@property (nonatomic) BOOL _disable;
|
||||
@property (nonatomic) BOOL _isPreferred;
|
||||
@property (nonatomic) void(^_handler)(UIAlertAction *action);
|
||||
@property (nonatomic) UIAlertAction *_action;
|
||||
@end
|
||||
@@ -34,6 +35,18 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
|
||||
} showFrom:viewController];
|
||||
}
|
||||
|
||||
+ (void)showQuickAlert:(NSString *)title from:(UIViewController *)viewController {
|
||||
UIAlertController *alert = [self makeAlert:^(FLEXAlert *make) {
|
||||
make.title(title);
|
||||
}];
|
||||
|
||||
[viewController presentViewController:alert animated:YES completion:^{
|
||||
flex_dispatch_after(0.5, dispatch_get_main_queue(), ^{
|
||||
[alert dismissViewControllerAnimated:YES completion:nil];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark Initialization
|
||||
|
||||
- (instancetype)initWithController:(UIAlertController *)controller {
|
||||
@@ -60,7 +73,18 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
|
||||
[alert._controller addAction:builder.action];
|
||||
}
|
||||
|
||||
return alert._controller;
|
||||
UIAlertController *controller = alert._controller;
|
||||
|
||||
// Set preferred action on alert controller
|
||||
for (FLEXAlertAction *builder in alert._actions) {
|
||||
UIAlertAction *action = builder.action;
|
||||
if (builder._isPreferred) {
|
||||
controller.preferredAction = action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
+ (void)make:(FLEXAlertBuilder)block
|
||||
@@ -191,6 +215,14 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
|
||||
};
|
||||
}
|
||||
|
||||
- (FLEXAlertActionProperty)preferred {
|
||||
return ^FLEXAlertAction *() {
|
||||
FLEXAlertActionMutationAssertion();
|
||||
self._isPreferred = YES;
|
||||
return self;
|
||||
};
|
||||
}
|
||||
|
||||
- (FLEXAlertActionBOOLProperty)enabled {
|
||||
return ^FLEXAlertAction *(BOOL enabled) {
|
||||
FLEXAlertActionMutationAssertion();
|
||||
|
||||
@@ -9,17 +9,46 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@class FLEXObjectRef;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^flex_object_enumeration_block_t)(__unsafe_unretained id object, __unsafe_unretained Class actualClass);
|
||||
|
||||
/// Counts and identifies all class instances on the heap.
|
||||
@interface FLEXHeapSnapshot : NSObject
|
||||
|
||||
/// The names of every class instance discovered on the heap.
|
||||
@property (nonatomic, readonly) NSArray<NSString *> *classNames;
|
||||
/// A mapping of instance counts to class names.
|
||||
@property (nonatomic, readonly) NSDictionary<NSString *, NSNumber *> *instanceCountsForClassNames;
|
||||
/// A mapping of class instance size to class name.
|
||||
///
|
||||
/// To roughly calculate the memory usage of an entire class, multiply this number by the instance count.
|
||||
@property (nonatomic, readonly) NSDictionary<NSString *, NSNumber *> *instanceSizesForClassNames;
|
||||
|
||||
@end
|
||||
|
||||
@interface FLEXHeapEnumerator : NSObject
|
||||
|
||||
+ (void)enumerateLiveObjectsUsingBlock:(flex_object_enumeration_block_t)block;
|
||||
/// Use carefully; this method puts a global lock on the heap in between callbacks.
|
||||
///
|
||||
/// Inspired by:
|
||||
/// [heap_find.cpp](https://llvm.org/svn/llvm-project/lldb/tags/RELEASE_34/final/examples/darwin/heap_find/heap/heap_find.cpp)
|
||||
/// and [samdmarshall](https://gist.github.com/samdmarshall/17f4e66b5e2e579fd396)
|
||||
+ (void)enumerateLiveObjectsUsingBlock:(flex_object_enumeration_block_t)callback
|
||||
NS_SWIFT_UNAVAILABLE("Use one of the other methods instead.");
|
||||
|
||||
/// Returned references are not validated beyond containing a valid isa.
|
||||
/// To validate them yourself, pass each reference's object to \c FLEXPointerIsValidObjcObject
|
||||
+ (NSArray<FLEXObjectRef *> *)instancesOfClassWithName:(NSString *)className retained:(BOOL)retain;
|
||||
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className;
|
||||
|
||||
/// Returned references have been validated via \c FLEXPointerIsValidObjcObject
|
||||
/// @param object the object to find references to
|
||||
/// @param retain whether to retain the objects referencing \c object
|
||||
+ (NSArray<FLEXObjectRef *> *)objectsWithReferencesToObject:(id)object retained:(BOOL)retain;
|
||||
|
||||
/// Capture all live objects on the heap and do with this information what you will.
|
||||
+ (FLEXHeapSnapshot *)generateHeapSnapshot;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -22,6 +22,18 @@ typedef struct {
|
||||
Class isa;
|
||||
} flex_maybe_object_t;
|
||||
|
||||
@implementation FLEXHeapSnapshot
|
||||
+ (instancetype)snapshotWithCounts:(NSDictionary<NSString *, NSNumber *> *)counts
|
||||
sizes:(NSDictionary<NSString *, NSNumber *> *)sizes {
|
||||
FLEXHeapSnapshot *snapshot = [FLEXHeapSnapshot new];
|
||||
snapshot->_classNames = counts.allKeys;
|
||||
snapshot->_instanceCountsForClassNames = counts;
|
||||
snapshot->_instanceSizesForClassNames = sizes;
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FLEXHeapEnumerator
|
||||
|
||||
static void range_callback(task_t task, void *context, unsigned type, vm_range_t *ranges, unsigned rangeCount) {
|
||||
@@ -138,22 +150,7 @@ static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_add
|
||||
return references;
|
||||
}
|
||||
|
||||
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className {
|
||||
NSArray<Class> *classes = FLEXGetAllSubclasses(NSClassFromString(className), NO);
|
||||
NSArray<FLEXObjectRef *> *references = [FLEXObjectRef referencingClasses:classes];
|
||||
return references;
|
||||
}
|
||||
|
||||
+ (NSArray<FLEXObjectRef *> *)objectsWithReferencesToObject:(id)object retained:(BOOL)retain {
|
||||
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 new];
|
||||
[FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id tryObject, __unsafe_unretained Class actualClass) {
|
||||
// Skip known-invalid objects
|
||||
@@ -194,4 +191,48 @@ static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_add
|
||||
return instances;
|
||||
}
|
||||
|
||||
+ (FLEXHeapSnapshot *)generateHeapSnapshot {
|
||||
// Set up a CFMutableDictionary with class pointer keys and NSUInteger values.
|
||||
// We abuse CFMutableDictionary a little to have primitive keys through judicious casting, but it gets the job done.
|
||||
// The dictionary is intialized with a 0 count for each class so that it doesn't have to expand during enumeration.
|
||||
// While it might be a little cleaner to populate an NSMutableDictionary with class name string keys to NSNumber
|
||||
// counts, we choose the CF/primitives approach because it lets us enumerate the objects in the heap without
|
||||
// allocating any memory during enumeration. The alternative of creating one NSString/NSNumber per object
|
||||
// on the heap ends up polluting the count of live objects quite a bit.
|
||||
unsigned int classCount = 0;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
CFMutableDictionaryRef mutableCountsForClasses = CFDictionaryCreateMutable(NULL, classCount, NULL, NULL);
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
CFDictionarySetValue(mutableCountsForClasses, (__bridge const void *)classes[i], (const void *)0);
|
||||
}
|
||||
|
||||
// Enumerate all objects on the heap to build the counts of instances for each class
|
||||
[FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, __unsafe_unretained Class cls) {
|
||||
NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(
|
||||
mutableCountsForClasses, (__bridge const void *)cls
|
||||
);
|
||||
instanceCount++;
|
||||
CFDictionarySetValue(
|
||||
mutableCountsForClasses, (__bridge const void *)cls, (const void *)instanceCount
|
||||
);
|
||||
}];
|
||||
|
||||
// Convert our CF primitive dictionary into a nicer mapping of class name strings to instance counts
|
||||
NSMutableDictionary<NSString *, NSNumber *> *countsForClassNames = [NSMutableDictionary new];
|
||||
NSMutableDictionary<NSString *, NSNumber *> *sizesForClassNames = [NSMutableDictionary new];
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
Class class = classes[i];
|
||||
NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)(class));
|
||||
NSString *className = @(class_getName(class));
|
||||
|
||||
if (instanceCount > 0) {
|
||||
countsForClassNames[className] = @(instanceCount);
|
||||
sizesForClassNames[className] = @(class_getInstanceSize(class));
|
||||
}
|
||||
}
|
||||
free(classes);
|
||||
|
||||
return [FLEXHeapSnapshot snapshotWithCounts:countsForClassNames sizes:sizesForClassNames];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
#ifndef FLEXMacros_h
|
||||
#define FLEXMacros_h
|
||||
|
||||
#ifndef __cplusplus
|
||||
#ifndef auto
|
||||
#define auto __auto_type
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define flex_keywordify class NSObject;
|
||||
#define ctor flex_keywordify __attribute__((constructor)) void __flex_ctor_##__LINE__()
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
+ (NSString *)prettyJSONStringFromData:(NSData *)data;
|
||||
+ (BOOL)isValidJSONData:(NSData *)data;
|
||||
+ (NSData *)inflatedDataFromCompressedData:(NSData *)compressedData;
|
||||
+ (BOOL)hasCompressedContentEncoding:(NSURLRequest *)request;
|
||||
|
||||
// Swizzling utilities
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#import <objc/runtime.h>
|
||||
#import <zlib.h>
|
||||
|
||||
BOOL FLEXConstructorsShouldRun() {
|
||||
BOOL FLEXConstructorsShouldRun(void) {
|
||||
#if FLEX_DISABLE_CTORS
|
||||
return NO;
|
||||
#else
|
||||
@@ -100,8 +100,9 @@ BOOL FLEXConstructorsShouldRun() {
|
||||
description = [description stringByAppendingFormat:@" %@", [self stringForCGRect:view.frame]];
|
||||
}
|
||||
|
||||
if (view.accessibilityLabel.length > 0) {
|
||||
description = [description stringByAppendingFormat:@" · %@", view.accessibilityLabel];
|
||||
if (view.accessibilityLabel.length > 0 || view.accessibilityIdentifier.length > 0) {
|
||||
description = [description stringByAppendingFormat:@" · %@",
|
||||
view.accessibilityLabel.length > 0 ? view.accessibilityLabel : view.accessibilityIdentifier];
|
||||
}
|
||||
|
||||
return description;
|
||||
@@ -412,6 +413,11 @@ BOOL FLEXConstructorsShouldRun() {
|
||||
return inflatedData;
|
||||
}
|
||||
|
||||
+ (BOOL)hasCompressedContentEncoding:(NSURLRequest *)request {
|
||||
NSString *contentEncoding = [request valueForHTTPHeaderField:@"Content-Encoding"];
|
||||
return ([contentEncoding rangeOfString:@"deflate" options:NSCaseInsensitiveSearch].length > 0 || [contentEncoding rangeOfString:@"gzip" options:NSCaseInsensitiveSearch].length > 0);
|
||||
}
|
||||
|
||||
+ (NSArray<UIWindow *> *)allWindows {
|
||||
BOOL includeInternalWindows = YES;
|
||||
BOOL onlyVisibleWindows = NO;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
self.textView.backgroundColor = UIColor.blackColor;
|
||||
self.textView.textColor = UIColor.whiteColor;
|
||||
self.textView.font = [UIFont boldSystemFontOfSize:14.0];
|
||||
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
|
||||
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
|
||||
|
||||
self.title = @"Simulator Shortcuts";
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed:)];
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "FLEXRuntimeConstants.h"
|
||||
@class FLEXObjectRef;
|
||||
|
||||
#define PropertyKey(suffix) kFLEXPropertyAttributeKey##suffix : @""
|
||||
#define PropertyKeyGetter(getter) kFLEXPropertyAttributeKeyCustomGetter : NSStringFromSelector(@selector(getter))
|
||||
@@ -45,7 +46,9 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
|
||||
|
||||
@interface FLEXRuntimeUtility : NSObject
|
||||
|
||||
// General Helpers
|
||||
#pragma mark - General Helpers
|
||||
|
||||
/// Calls into \c FLEXPointerIsValidObjcObject()
|
||||
+ (BOOL)pointerIsValidObjcObject:(const void *)pointer;
|
||||
/// Unwraps raw pointers to objects stored in NSValue, and re-boxes C strings into NSStrings.
|
||||
+ (id)potentiallyUnwrapBoxedPointer:(id)returnedObjectOrNil type:(const FLEXTypeEncoding *)returnType;
|
||||
@@ -59,6 +62,8 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
|
||||
/// @return The class hierarchy for the given object or class,
|
||||
/// from the current class to the root-most class.
|
||||
+ (NSArray<Class> *)classHierarchyOfObject:(id)objectOrClass;
|
||||
/// @return Every subclass of the given class name.
|
||||
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className;
|
||||
|
||||
/// Used to describe an object in brief within an explorer row
|
||||
+ (NSString *)summaryForObject:(id)value;
|
||||
@@ -69,16 +74,19 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
|
||||
+ (BOOL)safeObject:(id)object isKindOfClass:(Class)cls;
|
||||
+ (BOOL)safeObject:(id)object respondsToSelector:(SEL)sel;
|
||||
|
||||
// Property Helpers
|
||||
#pragma mark - Property Helpers
|
||||
|
||||
+ (BOOL)tryAddPropertyWithName:(const char *)name
|
||||
attributes:(NSDictionary<NSString *, NSString *> *)attributePairs
|
||||
toClass:(__unsafe_unretained Class)theClass;
|
||||
+ (NSArray<NSString *> *)allPropertyAttributeKeys;
|
||||
|
||||
// Method Helpers
|
||||
#pragma mark - Method Helpers
|
||||
|
||||
+ (NSArray *)prettyArgumentComponentsForMethod:(Method)method;
|
||||
|
||||
// Method Calling/Field Editing
|
||||
#pragma mark - Method Calling/Field Editing
|
||||
|
||||
+ (id)performSelector:(SEL)selector onObject:(id)object;
|
||||
+ (id)performSelector:(SEL)selector
|
||||
onObject:(id)object
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "FLEXObjcInternal.h"
|
||||
#import "FLEXObjectRef.h"
|
||||
#import "NSObject+FLEX_Reflection.h"
|
||||
#import "FLEXTypeEncodingParser.h"
|
||||
#import "FLEXMethod.h"
|
||||
|
||||
@@ -92,6 +94,12 @@ NSString * const FLEXRuntimeUtilityErrorDomain = @"FLEXRuntimeUtilityErrorDomain
|
||||
return superClasses;
|
||||
}
|
||||
|
||||
+ (NSArray<FLEXObjectRef *> *)subclassesOfClassWithName:(NSString *)className {
|
||||
NSArray<Class> *classes = FLEXGetAllSubclasses(NSClassFromString(className), NO);
|
||||
NSArray<FLEXObjectRef *> *references = [FLEXObjectRef referencingClasses:classes];
|
||||
return references;
|
||||
}
|
||||
|
||||
+ (NSString *)safeClassNameForObject:(id)object {
|
||||
// Don't assume that we have an NSObject subclass
|
||||
if ([self safeObject:object respondsToSelector:@selector(class)]) {
|
||||
@@ -160,6 +168,7 @@ NSString * const FLEXRuntimeUtilityErrorDomain = @"FLEXRuntimeUtilityErrorDomain
|
||||
// Single line display - replace newlines and tabs with spaces.
|
||||
description = [[self safeDescriptionForObject:value] stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
|
||||
description = [description stringByReplacingOccurrencesOfString:@"\t" withString:@" "];
|
||||
description = [description stringByReplacingOccurrencesOfString:@" " withString:@" "];
|
||||
} @catch (NSException *e) {
|
||||
description = [@"Thrown: " stringByAppendingString:e.reason ?: @"(nil exception reason)"];
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ NS_INLINE BOOL flex_isTaggedPointer(const void *ptr) {
|
||||
|
||||
#define FLEXPointerIsTaggedPointer(obj) flex_isTaggedPointer((__bridge void *)obj)
|
||||
|
||||
/// Whether the given pointer is a valid, readable address.
|
||||
BOOL FLEXPointerIsReadable(const void * ptr);
|
||||
|
||||
/// @brief Assumes memory is valid and readable.
|
||||
|
||||
@@ -19,7 +19,7 @@ CFSetRef FLEXKnownUnsafeIvars = nil;
|
||||
(class_getInstanceVariable([cls class], name) ?: (void *)kCFNull)
|
||||
|
||||
__attribute__((constructor))
|
||||
static void FLEXRuntimeSafteyInit() {
|
||||
static void FLEXRuntimeSafteyInit(void) {
|
||||
FLEXKnownUnsafeClasses = CFSetCreate(
|
||||
kCFAllocatorDefault,
|
||||
(const void **)(uintptr_t)FLEXKnownUnsafeClassList(),
|
||||
@@ -39,7 +39,7 @@ static void FLEXRuntimeSafteyInit() {
|
||||
);
|
||||
}
|
||||
|
||||
const Class * FLEXKnownUnsafeClassList() {
|
||||
const Class * FLEXKnownUnsafeClassList(void) {
|
||||
if (!_UnsafeClasses) {
|
||||
const Class ignored[] = {
|
||||
FLEXClassPointerOrCFNull(@"__ARCLite__"),
|
||||
@@ -74,7 +74,7 @@ const Class * FLEXKnownUnsafeClassList() {
|
||||
return _UnsafeClasses;
|
||||
}
|
||||
|
||||
NSSet * FLEXKnownUnsafeClassNames() {
|
||||
NSSet * FLEXKnownUnsafeClassNames(void) {
|
||||
static NSSet *set = nil;
|
||||
if (!set) {
|
||||
NSArray *ignored = @[
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// FLEXSwiftInternal.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 10/28/21.
|
||||
// Copyright © 2021 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
BOOL FLEXIsSwiftObjectOrClass(id objOrClass);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// FLEXSwiftInternal.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 10/28/21.
|
||||
// Copyright © 2021 Flipboard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXSwiftInternal.h"
|
||||
#import <objc/runtime.h>
|
||||
#include <atomic>
|
||||
|
||||
// class is a Swift class from the pre-stable Swift ABI
|
||||
#define FAST_IS_SWIFT_LEGACY (1UL<<0)
|
||||
// class is a Swift class from the stable Swift ABI
|
||||
#define FAST_IS_SWIFT_STABLE (1UL<<1)
|
||||
// data pointer
|
||||
#define FAST_DATA_MASK 0xfffffffcUL
|
||||
|
||||
typedef uintptr_t class_data_bits_t;
|
||||
#if __LP64__
|
||||
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
|
||||
#else
|
||||
typedef uint16_t mask_t;
|
||||
#endif
|
||||
|
||||
/* dyld_shared_cache_builder and obj-C agree on these definitions */
|
||||
struct preopt_cache_entry_t {
|
||||
uint32_t sel_offs;
|
||||
uint32_t imp_offs;
|
||||
};
|
||||
|
||||
/* dyld_shared_cache_builder and obj-C agree on these definitions */
|
||||
struct preopt_cache_t {
|
||||
int32_t fallback_class_offset;
|
||||
union {
|
||||
struct {
|
||||
uint16_t shift : 5;
|
||||
uint16_t mask : 11;
|
||||
};
|
||||
uint16_t hash_params;
|
||||
};
|
||||
uint16_t occupied : 14;
|
||||
uint16_t has_inlines : 1;
|
||||
uint16_t bit_one : 1;
|
||||
preopt_cache_entry_t entries[];
|
||||
};
|
||||
|
||||
union isa_t {
|
||||
uintptr_t bits;
|
||||
// Accessing the class requires custom ptrauth operations
|
||||
Class cls;
|
||||
};
|
||||
|
||||
struct cache_t {
|
||||
std::atomic<uintptr_t> _bucketsAndMaybeMask;
|
||||
union {
|
||||
struct {
|
||||
std::atomic<mask_t> _maybeMask;
|
||||
#if __LP64__
|
||||
uint16_t _flags;
|
||||
#endif
|
||||
uint16_t _occupied;
|
||||
};
|
||||
std::atomic<preopt_cache_t *> _originalPreoptCache;
|
||||
};
|
||||
};
|
||||
|
||||
struct objc_object_ {
|
||||
union isa_t isa;
|
||||
};
|
||||
|
||||
struct objc_class_ : objc_object_ {
|
||||
Class superclass;
|
||||
cache_t cache; // formerly cache pointer and vtable
|
||||
class_data_bits_t bits;
|
||||
};
|
||||
|
||||
extern "C" BOOL FLEXIsSwiftObjectOrClass(id objOrClass) {
|
||||
Class cls = objOrClass;
|
||||
if (!object_isClass(objOrClass)) {
|
||||
cls = object_getClass(objOrClass);
|
||||
}
|
||||
|
||||
class_data_bits_t rodata = ((__bridge objc_class_ *)(cls))->bits;
|
||||
|
||||
if (@available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *)) {
|
||||
return (rodata & FAST_IS_SWIFT_STABLE) != 0;
|
||||
} else {
|
||||
return (rodata & FAST_IS_SWIFT_LEGACY) != 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// FLEXMetadataExtras.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 4/26/22.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FLEXMethodBase.h"
|
||||
#import "FLEXProperty.h"
|
||||
#import "FLEXIvar.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// A dictionary mapping type encoding strings to an array of field titles
|
||||
extern NSString * const FLEXAuxiliarynfoKeyFieldLabels;
|
||||
|
||||
@protocol FLEXMetadataAuxiliaryInfo <NSObject>
|
||||
|
||||
/// Used to supply arbitrary additional data that need not be exposed by their own properties
|
||||
- (nullable id)auxiliaryInfoForKey:(NSString *)key;
|
||||
|
||||
@end
|
||||
|
||||
@interface FLEXMethodBase (Auxiliary) <FLEXMetadataAuxiliaryInfo> @end
|
||||
@interface FLEXProperty (Auxiliary) <FLEXMetadataAuxiliaryInfo> @end
|
||||
@interface FLEXIvar (Auxiliary) <FLEXMetadataAuxiliaryInfo> @end
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// FLEXMetadataExtras.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by Tanner Bennett on 4/26/22.
|
||||
//
|
||||
|
||||
#import "FLEXMetadataExtras.h"
|
||||
|
||||
NSString * const FLEXAuxiliarynfoKeyFieldLabels = @"FLEXAuxiliarynfoKeyFieldLabels";
|
||||
|
||||
@implementation FLEXMethodBase (Auxiliary)
|
||||
- (id)auxiliaryInfoForKey:(NSString *)key { return nil; }
|
||||
@end
|
||||
|
||||
@implementation FLEXProperty (Auxiliary)
|
||||
- (id)auxiliaryInfoForKey:(NSString *)key { return nil; }
|
||||
@end
|
||||
|
||||
@implementation FLEXIvar (Auxiliary)
|
||||
- (id)auxiliaryInfoForKey:(NSString *)key { return nil; }
|
||||
@end
|
||||
@@ -23,7 +23,7 @@
|
||||
NSString *_flex_description;
|
||||
}
|
||||
|
||||
/// Constructs and returns an \c FLEXSimpleMethod instance with the given name, type encoding, and implementation.
|
||||
/// Constructs and returns a \c FLEXSimpleMethod instance with the given name, type encoding, and implementation.
|
||||
+ (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation;
|
||||
|
||||
/// The selector of the method.
|
||||
|
||||
@@ -18,21 +18,28 @@ NS_SWIFT_NAME(FLEXMirrorProtocol)
|
||||
@protocol FLEXMirror <NSObject>
|
||||
|
||||
/// Swift initializer
|
||||
/// @throws If a metaclass object is passed in.
|
||||
- (instancetype)initWithSubject:(id)objectOrClass NS_SWIFT_NAME(init(reflecting:));
|
||||
|
||||
/// The underlying object or \c Class used to create this \c FLEXMirror instance.
|
||||
/// The underlying object or \c Class used to create this \c FLEXMirror.
|
||||
@property (nonatomic, readonly) id value;
|
||||
/// Whether the reflected thing was a class or a class instance.
|
||||
/// Whether \c value was a class or a class instance.
|
||||
@property (nonatomic, readonly) BOOL isClass;
|
||||
/// The name of the \c Class of the \c value property.
|
||||
@property (nonatomic, readonly) NSString *className;
|
||||
|
||||
@property (nonatomic, readonly) NSArray<FLEXProperty *> *properties;
|
||||
@property (nonatomic, readonly) NSArray<FLEXProperty *> *classProperties;
|
||||
@property (nonatomic, readonly) NSArray<FLEXIvar *> *ivars;
|
||||
@property (nonatomic, readonly) NSArray<FLEXMethod *> *methods;
|
||||
@property (nonatomic, readonly) NSArray<FLEXMethod *> *classMethods;
|
||||
@property (nonatomic, readonly) NSArray<FLEXProtocol *> *protocols;
|
||||
|
||||
/// @return A reflection of \c value.superClass.
|
||||
/// Super mirrors are initialized with the class that corresponds to the value passed in.
|
||||
/// If you passed in an instance of a class, it's superclass is used to create this mirror.
|
||||
/// If you passed in a class, then that class's superclass is used.
|
||||
///
|
||||
/// @note This property should be computed, not cached.
|
||||
@property (nonatomic, readonly, nullable) id<FLEXMirror> superMirror NS_SWIFT_NAME(superMirror);
|
||||
|
||||
@end
|
||||
@@ -45,11 +52,12 @@ NS_SWIFT_NAME(FLEXMirrorProtocol)
|
||||
/// \c NSObject categories provided if your code will only use a few pieces of information,
|
||||
/// or if your code needs to run faster.
|
||||
///
|
||||
/// If you reflect an instance of a class then \c methods and \c properties will be populated
|
||||
/// with instance methods and properties. If you reflect a class itself, then \c methods
|
||||
/// and \c properties will be populated with class methods and properties as you'd expect.
|
||||
/// Regardless of whether you reflect an instance or a class object, \c methods and \c properties
|
||||
/// will be populated with instance methods and properties, and \c classMethods and \c classProperties
|
||||
/// will be populated with class methods and properties.
|
||||
///
|
||||
/// @param objectOrClass An instance of an objct or a \c Class object.
|
||||
/// @throws If a metaclass object is passed in.
|
||||
/// @return An instance of \c FLEXMirror.
|
||||
+ (instancetype)reflect:(id)objectOrClass;
|
||||
|
||||
@@ -58,8 +66,10 @@ NS_SWIFT_NAME(FLEXMirrorProtocol)
|
||||
@property (nonatomic, readonly) NSString *className;
|
||||
|
||||
@property (nonatomic, readonly) NSArray<FLEXProperty *> *properties;
|
||||
@property (nonatomic, readonly) NSArray<FLEXProperty *> *classProperties;
|
||||
@property (nonatomic, readonly) NSArray<FLEXIvar *> *ivars;
|
||||
@property (nonatomic, readonly) NSArray<FLEXMethod *> *methods;
|
||||
@property (nonatomic, readonly) NSArray<FLEXMethod *> *classMethods;
|
||||
@property (nonatomic, readonly) NSArray<FLEXProtocol *> *protocols;
|
||||
|
||||
@property (nonatomic, readonly, nullable) FLEXMirror *superMirror NS_SWIFT_NAME(superMirror);
|
||||
@@ -69,10 +79,14 @@ NS_SWIFT_NAME(FLEXMirrorProtocol)
|
||||
|
||||
@interface FLEXMirror (ExtendedMirror)
|
||||
|
||||
/// @return The method with the given name, or \c nil if one does not exist.
|
||||
/// @return The instance method with the given name, or \c nil if one does not exist.
|
||||
- (nullable FLEXMethod *)methodNamed:(nullable NSString *)name;
|
||||
/// @return The property with the given name, or \c nil if one does not exist.
|
||||
/// @return The class method with the given name, or \c nil if one does not exist.
|
||||
- (nullable FLEXMethod *)classMethodNamed:(nullable NSString *)name;
|
||||
/// @return The instance property with the given name, or \c nil if one does not exist.
|
||||
- (nullable FLEXProperty *)propertyNamed:(nullable NSString *)name;
|
||||
/// @return The class property with the given name, or \c nil if one does not exist.
|
||||
- (nullable FLEXProperty *)classPropertyNamed:(nullable NSString *)name;
|
||||
/// @return The instance variable with the given name, or \c nil if one does not exist.
|
||||
- (nullable FLEXIvar *)ivarNamed:(nullable NSString *)name;
|
||||
/// @return The protocol with the given name, or \c nil if one does not exist.
|
||||
|
||||
@@ -45,64 +45,65 @@
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSString *type = self.isClass ? @"metaclass" : @"class";
|
||||
return [NSString
|
||||
stringWithFormat:@"<%@ %@=%@, %lu properties, %lu ivars, %lu methods, %lu protocols>",
|
||||
return [NSString stringWithFormat:@"<%@ %@=%@>",
|
||||
NSStringFromClass(self.class),
|
||||
type,
|
||||
self.className,
|
||||
(unsigned long)self.properties.count,
|
||||
(unsigned long)self.ivars.count,
|
||||
(unsigned long)self.methods.count,
|
||||
(unsigned long)self.protocols.count
|
||||
self.isClass ? @"metaclass" : @"class",
|
||||
self.className
|
||||
];
|
||||
}
|
||||
|
||||
- (void)examine {
|
||||
// cls is a metaclass if self.value is a class
|
||||
Class cls = object_getClass(self.value);
|
||||
|
||||
unsigned int pcount, mcount, ivcount, pccount;
|
||||
objc_property_t *objcproperties = class_copyPropertyList(cls, &pcount);
|
||||
Protocol*__unsafe_unretained *procs = class_copyProtocolList(cls, &pccount);
|
||||
Method *objcmethods = class_copyMethodList(cls, &mcount);
|
||||
Ivar *objcivars = class_copyIvarList(cls, &ivcount);
|
||||
|
||||
BOOL isClass = object_isClass(self.value);
|
||||
Class cls = isClass ? self.value : object_getClass(self.value);
|
||||
Class meta = object_getClass(cls);
|
||||
_className = NSStringFromClass(cls);
|
||||
_isClass = class_isMetaClass(cls); // or object_isClass(self.value)
|
||||
_isClass = isClass;
|
||||
|
||||
NSMutableArray *properties = [NSMutableArray new];
|
||||
for (int i = 0; i < pcount; i++)
|
||||
[properties addObject:[FLEXProperty property:objcproperties[i]]];
|
||||
_properties = properties;
|
||||
unsigned int pcount, cpcount, mcount, cmcount, ivcount, pccount;
|
||||
Ivar *objcIvars = class_copyIvarList(cls, &ivcount);
|
||||
Method *objcMethods = class_copyMethodList(cls, &mcount);
|
||||
Method *objcClsMethods = class_copyMethodList(meta, &cmcount);
|
||||
objc_property_t *objcProperties = class_copyPropertyList(cls, &pcount);
|
||||
objc_property_t *objcClsProperties = class_copyPropertyList(meta, &cpcount);
|
||||
Protocol *__unsafe_unretained *protos = class_copyProtocolList(cls, &pccount);
|
||||
|
||||
NSMutableArray *methods = [NSMutableArray new];
|
||||
for (int i = 0; i < mcount; i++)
|
||||
[methods addObject:[FLEXMethod method:objcmethods[i]]];
|
||||
_methods = methods;
|
||||
_ivars = [NSArray flex_forEachUpTo:ivcount map:^id(NSUInteger i) {
|
||||
return [FLEXIvar ivar:objcIvars[i]];
|
||||
}];
|
||||
|
||||
NSMutableArray *ivars = [NSMutableArray new];
|
||||
for (int i = 0; i < ivcount; i++)
|
||||
[ivars addObject:[FLEXIvar ivar:objcivars[i]]];
|
||||
_ivars = ivars;
|
||||
_methods = [NSArray flex_forEachUpTo:mcount map:^id(NSUInteger i) {
|
||||
return [FLEXMethod method:objcMethods[i] isInstanceMethod:YES];
|
||||
}];
|
||||
_classMethods = [NSArray flex_forEachUpTo:cmcount map:^id(NSUInteger i) {
|
||||
return [FLEXMethod method:objcClsMethods[i] isInstanceMethod:NO];
|
||||
}];
|
||||
|
||||
NSMutableArray *protocols = [NSMutableArray new];
|
||||
for (int i = 0; i < pccount; i++)
|
||||
[protocols addObject:[FLEXProtocol protocol:procs[i]]];
|
||||
_protocols = protocols;
|
||||
_properties = [NSArray flex_forEachUpTo:pcount map:^id(NSUInteger i) {
|
||||
return [FLEXProperty property:objcProperties[i] onClass:cls];
|
||||
}];
|
||||
_classProperties = [NSArray flex_forEachUpTo:cpcount map:^id(NSUInteger i) {
|
||||
return [FLEXProperty property:objcClsProperties[i] onClass:meta];
|
||||
}];
|
||||
|
||||
_protocols = [NSArray flex_forEachUpTo:pccount map:^id(NSUInteger i) {
|
||||
return [FLEXProtocol protocol:protos[i]];
|
||||
}];
|
||||
|
||||
// Cleanup
|
||||
free(objcproperties);
|
||||
free(objcmethods);
|
||||
free(objcivars);
|
||||
free(procs);
|
||||
procs = NULL;
|
||||
free(objcClsProperties);
|
||||
free(objcProperties);
|
||||
free(objcClsMethods);
|
||||
free(objcMethods);
|
||||
free(objcIvars);
|
||||
free(protos);
|
||||
protos = NULL;
|
||||
}
|
||||
|
||||
#pragma mark Misc
|
||||
|
||||
- (FLEXMirror *)superMirror {
|
||||
return [FLEXMirror reflect:[self.value superclass]];
|
||||
Class cls = _isClass ? _value : object_getClass(_value);
|
||||
return [FLEXMirror reflect:class_getSuperclass(cls)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -121,10 +122,18 @@
|
||||
return [self filter:self.methods forName:name];
|
||||
}
|
||||
|
||||
- (FLEXMethod *)classMethodNamed:(NSString *)name {
|
||||
return [self filter:self.classMethods forName:name];
|
||||
}
|
||||
|
||||
- (FLEXProperty *)propertyNamed:(NSString *)name {
|
||||
return [self filter:self.properties forName:name];
|
||||
}
|
||||
|
||||
- (FLEXProperty *)classPropertyNamed:(NSString *)name {
|
||||
return [self filter:self.classProperties forName:name];
|
||||
}
|
||||
|
||||
- (FLEXIvar *)ivarNamed:(NSString *)name {
|
||||
return [self filter:self.ivars forName:name];
|
||||
}
|
||||
|
||||
@@ -52,9 +52,15 @@
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
return [NSString stringWithFormat:@"<%@ name=%@, %lu properties, %lu required methods, %lu optional methods, %lu protocols>",
|
||||
if (@available(iOS 10.0, *)) {
|
||||
return [NSString stringWithFormat:@"<%@ name=%@, %lu required properties, %lu optional properties %lu required methods, %lu optional methods, %lu protocols>",
|
||||
NSStringFromClass(self.class), self.name, (unsigned long)self.requiredProperties.count, (unsigned long)self.optionalProperties.count,
|
||||
(unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"<%@ name=%@, %lu properties, %lu required methods, %lu optional methods, %lu protocols>",
|
||||
NSStringFromClass(self.class), self.name, (unsigned long)self.properties.count,
|
||||
(unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)examine {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "FLEXImagePreviewViewController.h"
|
||||
#import "FLEXActivityViewController.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXColor.h"
|
||||
#import "FLEXResources.h"
|
||||
@@ -50,6 +51,10 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
|
||||
[super dismissViewControllerAnimated:flag completion:completion];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Lifecycle
|
||||
|
||||
@@ -128,7 +133,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[self.image] applicationActivities:@[]];
|
||||
UIViewController *activityVC = [FLEXActivityViewController sharing:@[self.image] source:sender];
|
||||
|
||||
if (!canSaveToCameraRoll && !didShowWarning) {
|
||||
didShowWarning = YES;
|
||||
|
||||
@@ -88,8 +88,19 @@
|
||||
@implementation FHSView (Snapshotting)
|
||||
|
||||
+ (UIImage *)drawView:(UIView *)view {
|
||||
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
|
||||
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
|
||||
if (CGRectIsEmpty(view.bounds)) {
|
||||
return [UIImage new];
|
||||
}
|
||||
|
||||
CGSize size = view.bounds.size;
|
||||
CGFloat minUnit = 1.f / UIScreen.mainScreen.scale;
|
||||
|
||||
// Every drawn view must not have 0 width or height
|
||||
CGSize minsize = CGSizeMake(MAX(size.width, minUnit), MAX(size.height, minUnit));
|
||||
CGRect minBounds = CGRectMake(0, 0, minsize.width, minsize.height);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(minsize, NO, 0);
|
||||
[view drawViewHierarchyInRect:minBounds afterScreenUpdates:YES];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
|
||||
@@ -24,7 +24,9 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
|
||||
@interface FLEXHierarchyTableViewController ()
|
||||
|
||||
@property (nonatomic) NSArray<UIView *> *allViews;
|
||||
@property (nonatomic) NSMapTable<UIView *, NSNumber *> *depthsForViews;
|
||||
/// Map of view's address to its depth
|
||||
/// @((uintptr_t)(__bridge void *)view) -> depth
|
||||
@property (nonatomic) NSMapTable<NSNumber *, NSNumber *> *depthsForViews;
|
||||
@property (nonatomic) NSArray<UIView *> *viewsAtTap;
|
||||
@property (nonatomic) NSArray<UIView *> *displayedViews;
|
||||
@property (nonatomic, readonly) BOOL showScopeBar;
|
||||
@@ -46,7 +48,7 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
|
||||
- (instancetype)initWithViews:(NSArray<UIView *> *)allViews
|
||||
viewsAtTap:(NSArray<UIView *> *)viewsAtTap
|
||||
selectedView:(UIView *)selectedView
|
||||
depths:(NSMapTable<UIView *, NSNumber *> *)depthsForViews {
|
||||
depths:(NSMapTable<NSNumber *, NSNumber *> *)depthsForViews {
|
||||
NSParameterAssert(allViews);
|
||||
NSParameterAssert(depthsForViews.count == allViews.count);
|
||||
|
||||
@@ -135,7 +137,7 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
|
||||
tryView = tryView.superview;
|
||||
depth++;
|
||||
}
|
||||
depths[(id)view] = @(depth);
|
||||
depths[@((uintptr_t)(__bridge void *)view)] = @(depth);
|
||||
}
|
||||
|
||||
return depths;
|
||||
@@ -223,7 +225,7 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
|
||||
cell.textLabel.text = [FLEXUtility descriptionForView:view includingFrame:NO];
|
||||
cell.detailTextLabel.text = [FLEXUtility detailDescriptionForView:view];
|
||||
cell.randomColorTag = [FLEXUtility consistentRandomColorForObject:view];
|
||||
cell.viewDepth = self.depthsForViews[view].integerValue;
|
||||
cell.viewDepth = self.depthsForViews[@((uintptr_t)(__bridge void *)view)].integerValue;
|
||||
cell.indicatedViewColor = view.backgroundColor;
|
||||
|
||||
if (view.isHidden || view.alpha < 0.01) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A9D70EC640E633FE829EF695 /* libPods-FLEXample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBD699DDBAC5A16D8CFD39AC /* libPods-FLEXample.a */; };
|
||||
C376F92B28DBE503004E88BA /* dogs.realm in Resources */ = {isa = PBXBuildFile; fileRef = C386D706241AA66100699085 /* dogs.realm */; };
|
||||
C386D6D02419975A00699085 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6CF2419975A00699085 /* AppDelegate.swift */; };
|
||||
C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6D12419975A00699085 /* SceneDelegate.swift */; };
|
||||
C386D6D62419975B00699085 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D52419975B00699085 /* Assets.xcassets */; };
|
||||
@@ -238,6 +239,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C376F92B28DBE503004E88BA /* dogs.realm in Resources */,
|
||||
C386D6DC2419975B00699085 /* LaunchScreen.storyboard in Resources */,
|
||||
C386D6FF241AA4B700699085 /* image.png in Resources */,
|
||||
C386D704241AA61600699085 /* music_library.db in Resources */,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1240"
|
||||
LastUpgradeVersion = "1400"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -50,6 +50,13 @@
|
||||
ReferencedContainer = "container:FLEXample-Cocoapods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_MSSafeMode"
|
||||
value = "1"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
C35F0EF4292CC5CA00B728BC /* Reflex in Frameworks */ = {isa = PBXBuildFile; productRef = C35F0EF3292CC5CA00B728BC /* Reflex */; };
|
||||
C386D6D02419975A00699085 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6CF2419975A00699085 /* AppDelegate.swift */; };
|
||||
C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6D12419975A00699085 /* SceneDelegate.swift */; };
|
||||
C386D6D62419975B00699085 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D52419975B00699085 /* Assets.xcassets */; };
|
||||
@@ -24,7 +25,6 @@
|
||||
C3A67856241AB8AD005A4681 /* MiscNetworkRequests.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */; };
|
||||
C3A67858241ADDF7005A4681 /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A67857241ADDF7005A4681 /* Commit.swift */; };
|
||||
C3B3760025B8CDA300AD43AB /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = C3B375FF25B8CDA300AD43AB /* Person.m */; };
|
||||
C3F86C322740DB9400E094BF /* FLEX in Frameworks */ = {isa = PBXBuildFile; productRef = C3F86C312740DB9400E094BF /* FLEX */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -62,7 +62,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C3F86C322740DB9400E094BF /* FLEX in Frameworks */,
|
||||
C35F0EF4292CC5CA00B728BC /* Reflex in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -183,7 +183,7 @@
|
||||
);
|
||||
name = FLEXample;
|
||||
packageProductDependencies = (
|
||||
C3F86C312740DB9400E094BF /* FLEX */,
|
||||
C35F0EF3292CC5CA00B728BC /* Reflex */,
|
||||
);
|
||||
productName = FLEXample;
|
||||
productReference = C386D6CC2419975A00699085 /* FLEXample.app */;
|
||||
@@ -215,7 +215,7 @@
|
||||
);
|
||||
mainGroup = C386D6C32419975A00699085;
|
||||
packageReferences = (
|
||||
C3F86C302740DB9400E094BF /* XCRemoteSwiftPackageReference "FLEX" */,
|
||||
C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */,
|
||||
);
|
||||
productRefGroup = C386D6CD2419975A00699085 /* Products */;
|
||||
projectDirPath = "";
|
||||
@@ -462,9 +462,9 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
C3F86C302740DB9400E094BF /* XCRemoteSwiftPackageReference "FLEX" */ = {
|
||||
C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/FLEXTool/FLEX";
|
||||
repositoryURL = "https://github.com/FLEXTool/Reflex";
|
||||
requirement = {
|
||||
branch = master;
|
||||
kind = branch;
|
||||
@@ -473,10 +473,10 @@
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
C3F86C312740DB9400E094BF /* FLEX */ = {
|
||||
C35F0EF3292CC5CA00B728BC /* Reflex */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C3F86C302740DB9400E094BF /* XCRemoteSwiftPackageReference "FLEX" */;
|
||||
productName = FLEX;
|
||||
package = C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */;
|
||||
productName = Reflex;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
|
||||
@@ -50,6 +50,13 @@
|
||||
ReferencedContainer = "container:FLEXample-SPM.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_MSSafeMode"
|
||||
value = "1"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -44,8 +44,8 @@ public class CommitIdentity: NSObject, Codable {
|
||||
}
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
public class CommitDetails: NSObject, Codable {
|
||||
//@objcMembers
|
||||
public struct CommitDetails: Codable {
|
||||
public let message: String
|
||||
public let url: String
|
||||
|
||||
|
||||
@@ -16,6 +16,24 @@
|
||||
@property (nonatomic, readonly) NSMutableDictionary<NSString *, UIImage *> *avatars;
|
||||
@end
|
||||
|
||||
@interface UIAlertController (Private)
|
||||
@property (nonatomic) UIViewController *contentViewController;
|
||||
@end
|
||||
|
||||
@interface ContentViewController : UIViewController
|
||||
+ (instancetype)customView:(UIView *)view;
|
||||
@end
|
||||
|
||||
@implementation ContentViewController
|
||||
|
||||
+ (instancetype)customView:(UIView *)view {
|
||||
ContentViewController *vc = [self new];
|
||||
vc.view = view;
|
||||
return vc;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CommitListViewController
|
||||
|
||||
- (id)init {
|
||||
@@ -23,6 +41,16 @@
|
||||
return [self initWithStyle:UITableViewStylePlain];
|
||||
}
|
||||
|
||||
- (void)showCustomView {
|
||||
UIAlertController *alert = [FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Custom View").button(@"Dismiss").cancelStyle();
|
||||
}];
|
||||
|
||||
// I like to use this to easily display and interact with custom views I'm working on
|
||||
alert.contentViewController = [ContentViewController customView:[UIView new]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
@@ -35,6 +63,10 @@
|
||||
];
|
||||
self.navigationItem.rightBarButtonItem.accessibilityIdentifier = @"toggle-explorer";
|
||||
|
||||
self.navigationItem.leftBarButtonItem = [UIBarButtonItem
|
||||
flex_itemWithTitle:@"Test" target:self action:@selector(showCustomView)
|
||||
];
|
||||
|
||||
// Load and process commits
|
||||
NSString *commitsURL = @"https://api.github.com/repos/Flipboard/FLEX/commits";
|
||||
[self startDataTask:commitsURL completion:^(NSData *data, NSInteger statusCode, NSError *error) {
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
}
|
||||
|
||||
- (void)sendExampleWebsocketTraffic:(NSURLSession *)sessionWithDelegate {
|
||||
NSString *APIKey = @"oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm";
|
||||
NSString *APIKey = @"VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV";
|
||||
NSString *wsurl = [NSString stringWithFormat:@"wss://demo.piesocket.com/v3/channel_1?api_key=%@¬ify_self", APIKey];
|
||||
NSURLSessionWebSocketTask *task = [sessionWithDelegate webSocketTaskWithURL:[NSURL URLWithString:wsurl]];
|
||||
[task resume];
|
||||
@@ -167,6 +167,8 @@
|
||||
[task sendMessage:message completionHandler:^(NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Error sending WS message: %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"WS message sent.");
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
</dict>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>NSPhotoLibraryAddUsageDescription</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Save images</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "FLEX"
|
||||
spec.version = "4.6.1"
|
||||
spec.version = "5.22.10"
|
||||
spec.summary = "A set of in-app debugging and exploration tools for iOS"
|
||||
spec.description = <<-DESC
|
||||
- Inspect and modify views in the hierarchy.
|
||||
@@ -28,7 +28,6 @@ Pod::Spec.new do |spec|
|
||||
|
||||
spec.license = { :type => "BSD", :file => "LICENSE" }
|
||||
spec.author = { "Tanner Bennett" => "tannerbennett@me.com" }
|
||||
spec.social_media_url = "https://twitter.com/NSExceptional"
|
||||
spec.platform = :ios, "9.0"
|
||||
spec.source = { :git => "https://github.com/FLEXTool/FLEX.git", :tag => "#{spec.version}" }
|
||||
spec.source_files = "Classes/**/*.{h,c,m,mm}"
|
||||
@@ -37,8 +36,8 @@ Pod::Spec.new do |spec|
|
||||
spec.libraries = [ "z", "sqlite3" ]
|
||||
spec.requires_arc = true
|
||||
spec.library = 'stdc++'
|
||||
spec.xcconfig = {
|
||||
'CLANG_CXX_LANGUAGE_STANDARD' => 'compiler-default',
|
||||
spec.pod_target_xcconfig = {
|
||||
'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++11',
|
||||
}
|
||||
spec.compiler_flags = "-Wno-unsupported-availability-guard", "-Wno-deprecated-declarations"
|
||||
spec.public_header_files = [ "Classes/*.h", "Classes/Manager/*.h", "Classes/Toolbar/*.h",
|
||||
@@ -54,6 +53,7 @@ Pod::Spec.new do |spec|
|
||||
"Classes/Utility/FLEXResources.h",
|
||||
"Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h",
|
||||
"Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h",
|
||||
"Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h"
|
||||
"Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h",
|
||||
"Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h"
|
||||
]
|
||||
end
|
||||
|
||||
@@ -178,6 +178,8 @@
|
||||
C33C825F2316DC8600DD2451 /* FLEXObjectExplorer.m in Sources */ = {isa = PBXBuildFile; fileRef = C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */; };
|
||||
C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C33E46AD223B02CD004BD0E6 /* FLEXASLLogController.h */; };
|
||||
C33E46B0223B02CD004BD0E6 /* FLEXASLLogController.m in Sources */ = {isa = PBXBuildFile; fileRef = C33E46AE223B02CD004BD0E6 /* FLEXASLLogController.m */; };
|
||||
C34063B528400A26007B46D3 /* FLEXActivityViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C34063B328400A26007B46D3 /* FLEXActivityViewController.h */; };
|
||||
C34063B628400A26007B46D3 /* FLEXActivityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C34063B428400A26007B46D3 /* FLEXActivityViewController.m */; };
|
||||
C3474C4023DA496400466532 /* FLEXKeyValueTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3474C3E23DA496400466532 /* FLEXKeyValueTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3474C4123DA496400466532 /* FLEXKeyValueTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3474C3F23DA496400466532 /* FLEXKeyValueTableViewCell.m */; };
|
||||
C3490E1F233BDD73002AE200 /* FLEXSingleRowSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3490E1D233BDD73002AE200 /* FLEXSingleRowSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -246,6 +248,8 @@
|
||||
C383C3C523B6BB81007A321B /* FLEXCodeFontCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C383C3C323B6BB81007A321B /* FLEXCodeFontCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C383C3C623B6BB81007A321B /* FLEXCodeFontCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C383C3C423B6BB81007A321B /* FLEXCodeFontCell.m */; };
|
||||
C3854DF023F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C3854DEF23F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m */; };
|
||||
C38568FA272B3BFC00B1E37F /* FLEXSwiftInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38568FB272B3BFC00B1E37F /* FLEXSwiftInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = C38568F9272B3BFC00B1E37F /* FLEXSwiftInternal.mm */; };
|
||||
C386D6A9241995A800699085 /* FLEXTypeEncodingParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C3854DF223F36C9E00FCD1E2 /* FLEXTypeEncodingParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C386D6E924199C1B00699085 /* FLEX-Core.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6E824199C1B00699085 /* FLEX-Core.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C386D6EB24199E9600699085 /* FLEX-ObjectExploring.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6EA24199E9600699085 /* FLEX-ObjectExploring.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -262,6 +266,8 @@
|
||||
C387C87B22DFCD6A00750E58 /* FLEXCarouselCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C387C87922DFCD6A00750E58 /* FLEXCarouselCell.m */; };
|
||||
C387C88322E0D24A00750E58 /* UIView+FLEX_Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
C387C88422E0D24A00750E58 /* UIView+FLEX_Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */; };
|
||||
C38D970228190C93008709D0 /* FLEXMetadataExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = C38D970028190C93008709D0 /* FLEXMetadataExtras.m */; };
|
||||
C38D970328190C93008709D0 /* FLEXMetadataExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = C38D970128190C93008709D0 /* FLEXMetadataExtras.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38DF0EA22CFE4370077B4AD /* FLEXTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38DF0E822CFE4370077B4AD /* FLEXTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38DF0EB22CFE4370077B4AD /* FLEXTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38DF0E922CFE4370077B4AD /* FLEXTableViewController.m */; };
|
||||
C38EF26223A2FCD20047A7EC /* FLEXViewControllerShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF26023A2FCD20047A7EC /* FLEXViewControllerShortcuts.m */; };
|
||||
@@ -303,6 +309,8 @@
|
||||
C39EADC923F37B89005618BE /* FLEXTypeEncodingParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C3854DF123F36C9E00FCD1E2 /* FLEXTypeEncodingParser.m */; };
|
||||
C39ED92822D63F3200B5773A /* FLEXAddressExplorerCoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = C39ED92622D63F3200B5773A /* FLEXAddressExplorerCoordinator.h */; };
|
||||
C39ED92922D63F3200B5773A /* FLEXAddressExplorerCoordinator.m in Sources */ = {isa = PBXBuildFile; fileRef = C39ED92722D63F3200B5773A /* FLEXAddressExplorerCoordinator.m */; };
|
||||
C3A04B64288E007100F2C16D /* NSDateFormatter+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3A04B65288E007100F2C16D /* NSDateFormatter+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */; };
|
||||
C3A9422C23C3DA39006871A3 /* FHSView.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9422823C3DA39006871A3 /* FHSView.h */; };
|
||||
C3A9422D23C3DA39006871A3 /* FHSView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9422923C3DA39006871A3 /* FHSView.m */; };
|
||||
C3A9423423C3F98E006871A3 /* FHSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9423223C3F98E006871A3 /* FHSViewController.h */; };
|
||||
@@ -337,8 +345,12 @@
|
||||
C3E5DA02231700EE00E655DB /* FLEXObjectInfoSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3EB6F8D242E9C83006EA386 /* FLEXRuntimeExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EB6F8B242E9C83006EA386 /* FLEXRuntimeExporter.m */; };
|
||||
C3EB6F8E242E9C83006EA386 /* FLEXRuntimeExporter.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EB6F8C242E9C83006EA386 /* FLEXRuntimeExporter.h */; };
|
||||
C3EBE111287028D600702098 /* FLEXAPNSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */; };
|
||||
C3EBE112287028D600702098 /* FLEXAPNSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EBE110287028D600702098 /* FLEXAPNSViewController.m */; };
|
||||
C3EE76BF22DFC63600EC0AA0 /* FLEXScopeCarousel.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EE76BD22DFC63600EC0AA0 /* FLEXScopeCarousel.h */; };
|
||||
C3EE76C022DFC63600EC0AA0 /* FLEXScopeCarousel.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EE76BE22DFC63600EC0AA0 /* FLEXScopeCarousel.m */; };
|
||||
C3EF290528EE8BC500B337B6 /* FLEXWindowShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */; };
|
||||
C3EF290628EE8BC500B337B6 /* FLEXWindowShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */; };
|
||||
C3F31D3D2267D883003C991A /* FLEXSubtitleTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D342267D883003C991A /* FLEXSubtitleTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3F31D3E2267D883003C991A /* FLEXTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D352267D883003C991A /* FLEXTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3F31D3F2267D883003C991A /* FLEXMultilineTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D362267D883003C991A /* FLEXMultilineTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -554,6 +566,8 @@
|
||||
C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FLEXObjectExplorer.m; path = Classes/ObjectExplorers/FLEXObjectExplorer.m; sourceTree = SOURCE_ROOT; };
|
||||
C33E46AD223B02CD004BD0E6 /* FLEXASLLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXASLLogController.h; sourceTree = "<group>"; };
|
||||
C33E46AE223B02CD004BD0E6 /* FLEXASLLogController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXASLLogController.m; sourceTree = "<group>"; };
|
||||
C34063B328400A26007B46D3 /* FLEXActivityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXActivityViewController.h; sourceTree = "<group>"; };
|
||||
C34063B428400A26007B46D3 /* FLEXActivityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXActivityViewController.m; sourceTree = "<group>"; };
|
||||
C3474C3E23DA496400466532 /* FLEXKeyValueTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXKeyValueTableViewCell.h; sourceTree = "<group>"; };
|
||||
C3474C3F23DA496400466532 /* FLEXKeyValueTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyValueTableViewCell.m; sourceTree = "<group>"; };
|
||||
C3490E1D233BDD73002AE200 /* FLEXSingleRowSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSingleRowSection.h; sourceTree = "<group>"; };
|
||||
@@ -627,6 +641,8 @@
|
||||
C3854DEF23F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTypeEncodingParserTests.m; sourceTree = "<group>"; };
|
||||
C3854DF123F36C9E00FCD1E2 /* FLEXTypeEncodingParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTypeEncodingParser.m; sourceTree = "<group>"; };
|
||||
C3854DF223F36C9E00FCD1E2 /* FLEXTypeEncodingParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTypeEncodingParser.h; sourceTree = "<group>"; };
|
||||
C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSwiftInternal.h; sourceTree = "<group>"; };
|
||||
C38568F9272B3BFC00B1E37F /* FLEXSwiftInternal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FLEXSwiftInternal.mm; sourceTree = "<group>"; };
|
||||
C386D6E824199C1B00699085 /* FLEX-Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEX-Core.h"; sourceTree = "<group>"; };
|
||||
C386D6EA24199E9600699085 /* FLEX-ObjectExploring.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEX-ObjectExploring.h"; sourceTree = "<group>"; };
|
||||
C386D6EC24199EC600699085 /* FLEX-Runtime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEX-Runtime.h"; sourceTree = "<group>"; };
|
||||
@@ -640,6 +656,8 @@
|
||||
C387C87922DFCD6A00750E58 /* FLEXCarouselCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXCarouselCell.m; sourceTree = "<group>"; };
|
||||
C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+FLEX_Layout.h"; sourceTree = "<group>"; };
|
||||
C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+FLEX_Layout.m"; sourceTree = "<group>"; };
|
||||
C38D970028190C93008709D0 /* FLEXMetadataExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMetadataExtras.m; sourceTree = "<group>"; };
|
||||
C38D970128190C93008709D0 /* FLEXMetadataExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMetadataExtras.h; sourceTree = "<group>"; };
|
||||
C38DF0E822CFE4370077B4AD /* FLEXTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXTableViewController.h; sourceTree = "<group>"; };
|
||||
C38DF0E922CFE4370077B4AD /* FLEXTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTableViewController.m; sourceTree = "<group>"; };
|
||||
C38EF26023A2FCD20047A7EC /* FLEXViewControllerShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXViewControllerShortcuts.m; sourceTree = "<group>"; };
|
||||
@@ -678,6 +696,8 @@
|
||||
C398682423AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FLEXShortcutsFactory+Defaults.h"; sourceTree = "<group>"; };
|
||||
C39ED92622D63F3200B5773A /* FLEXAddressExplorerCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXAddressExplorerCoordinator.h; sourceTree = "<group>"; };
|
||||
C39ED92722D63F3200B5773A /* FLEXAddressExplorerCoordinator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXAddressExplorerCoordinator.m; sourceTree = "<group>"; };
|
||||
C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+FLEX.h"; sourceTree = "<group>"; };
|
||||
C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+FLEX.m"; sourceTree = "<group>"; };
|
||||
C3A9422823C3DA39006871A3 /* FHSView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FHSView.h; sourceTree = "<group>"; };
|
||||
C3A9422923C3DA39006871A3 /* FHSView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FHSView.m; sourceTree = "<group>"; };
|
||||
C3A9423223C3F98E006871A3 /* FHSViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FHSViewController.h; sourceTree = "<group>"; };
|
||||
@@ -711,8 +731,12 @@
|
||||
C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectInfoSection.h; sourceTree = "<group>"; };
|
||||
C3EB6F8B242E9C83006EA386 /* FLEXRuntimeExporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeExporter.m; sourceTree = "<group>"; };
|
||||
C3EB6F8C242E9C83006EA386 /* FLEXRuntimeExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeExporter.h; sourceTree = "<group>"; };
|
||||
C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXAPNSViewController.h; sourceTree = "<group>"; };
|
||||
C3EBE110287028D600702098 /* FLEXAPNSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXAPNSViewController.m; sourceTree = "<group>"; };
|
||||
C3EE76BD22DFC63600EC0AA0 /* FLEXScopeCarousel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXScopeCarousel.h; sourceTree = "<group>"; };
|
||||
C3EE76BE22DFC63600EC0AA0 /* FLEXScopeCarousel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXScopeCarousel.m; sourceTree = "<group>"; };
|
||||
C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXWindowShortcuts.h; sourceTree = "<group>"; };
|
||||
C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXWindowShortcuts.m; sourceTree = "<group>"; };
|
||||
C3F31D342267D883003C991A /* FLEXSubtitleTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSubtitleTableViewCell.h; sourceTree = "<group>"; };
|
||||
C3F31D352267D883003C991A /* FLEXTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableViewCell.h; sourceTree = "<group>"; };
|
||||
C3F31D362267D883003C991A /* FLEXMultilineTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMultilineTableViewCell.h; sourceTree = "<group>"; };
|
||||
@@ -950,6 +974,8 @@
|
||||
C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */,
|
||||
3A4C94A91B5B21410088C3F2 /* FLEXLiveObjectsController.h */,
|
||||
3A4C94AA1B5B21410088C3F2 /* FLEXLiveObjectsController.m */,
|
||||
C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */,
|
||||
C3EBE110287028D600702098 /* FLEXAPNSViewController.m */,
|
||||
679F64841BD53B7B00A8C94C /* FLEXCookiesViewController.h */,
|
||||
679F64851BD53B7B00A8C94C /* FLEXCookiesViewController.m */,
|
||||
3A4C94AB1B5B21410088C3F2 /* FLEXWebViewController.h */,
|
||||
@@ -1118,6 +1144,8 @@
|
||||
C383C3B823B6A62A007A321B /* FLEXRuntimeSafety.m */,
|
||||
C37A0C91218BAC9600848CA7 /* FLEXObjcInternal.h */,
|
||||
C37A0C92218BAC9600848CA7 /* FLEXObjcInternal.mm */,
|
||||
C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */,
|
||||
C38568F9272B3BFC00B1E37F /* FLEXSwiftInternal.mm */,
|
||||
C386D6EE2419A2F400699085 /* FLEXRuntimeConstants.h */,
|
||||
C386D6EF2419A33F00699085 /* FLEXRuntimeConstants.m */,
|
||||
C3854DF223F36C9E00FCD1E2 /* FLEXTypeEncodingParser.h */,
|
||||
@@ -1152,6 +1180,8 @@
|
||||
3A4C94A01B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m */,
|
||||
3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserController.h */,
|
||||
3A4C94A21B5B21410088C3F2 /* FLEXFileBrowserController.m */,
|
||||
C34063B328400A26007B46D3 /* FLEXActivityViewController.h */,
|
||||
C34063B428400A26007B46D3 /* FLEXActivityViewController.m */,
|
||||
);
|
||||
path = FileBrowser;
|
||||
sourceTree = "<group>";
|
||||
@@ -1276,6 +1306,8 @@
|
||||
C36FBFC7230F3B98008D95D5 /* FLEXProtocolBuilder.m */,
|
||||
C36FBFC9230F3B98008D95D5 /* FLEXClassBuilder.h */,
|
||||
C36FBFBF230F3B98008D95D5 /* FLEXClassBuilder.m */,
|
||||
C38D970128190C93008709D0 /* FLEXMetadataExtras.h */,
|
||||
C38D970028190C93008709D0 /* FLEXMetadataExtras.m */,
|
||||
);
|
||||
path = Reflection;
|
||||
sourceTree = "<group>";
|
||||
@@ -1286,6 +1318,8 @@
|
||||
C31E4E53259D4A4100712288 /* Private */,
|
||||
C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */,
|
||||
C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */,
|
||||
C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */,
|
||||
C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */,
|
||||
C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */,
|
||||
C398627123AD7951007E6793 /* UIGestureRecognizer+Blocks.m */,
|
||||
C3F646BF239EAA8F00D4A011 /* UIPasteboard+FLEX.h */,
|
||||
@@ -1417,6 +1451,8 @@
|
||||
C3F527C02318670F009CBA07 /* FLEXImageShortcuts.m */,
|
||||
C3F527C3231891F6009CBA07 /* FLEXViewShortcuts.h */,
|
||||
C3F527C4231891F6009CBA07 /* FLEXViewShortcuts.m */,
|
||||
C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */,
|
||||
C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */,
|
||||
C3F646F023A045DB00D4A011 /* FLEXClassShortcuts.h */,
|
||||
C3F646F123A045DB00D4A011 /* FLEXClassShortcuts.m */,
|
||||
C34D4EAE23A2ABD900C1F903 /* FLEXLayerShortcuts.h */,
|
||||
@@ -1496,6 +1532,7 @@
|
||||
3A4C94ED1B5B21410088C3F2 /* FLEXArgumentInputColorView.h in Headers */,
|
||||
C3A9424523C641C1006871A3 /* SceneKit+Snapshot.h in Headers */,
|
||||
3A4C94EB1B5B21410088C3F2 /* FLEXImagePreviewViewController.h in Headers */,
|
||||
C38D970328190C93008709D0 /* FLEXMetadataExtras.h in Headers */,
|
||||
C34D4EB823A2B17900C1F903 /* FLEXBundleShortcuts.h in Headers */,
|
||||
C3694DC223EA147F006625D7 /* UIBarButtonItem+FLEX.h in Headers */,
|
||||
C38F3F31230C958F004E3731 /* FLEXAlert.h in Headers */,
|
||||
@@ -1575,6 +1612,8 @@
|
||||
3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */,
|
||||
C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */,
|
||||
C34EE30821CB23CC00BD3A7C /* FLEXOSLogController.h in Headers */,
|
||||
C38568FA272B3BFC00B1E37F /* FLEXSwiftInternal.h in Headers */,
|
||||
C3EBE111287028D600702098 /* FLEXAPNSViewController.h in Headers */,
|
||||
3A4C94FF1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.h in Headers */,
|
||||
C398625423AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.h in Headers */,
|
||||
3A4C94E71B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h in Headers */,
|
||||
@@ -1592,6 +1631,7 @@
|
||||
C3F31D432267D883003C991A /* FLEXTableView.h in Headers */,
|
||||
3A4C95071B5B21410088C3F2 /* FLEXDefaultEditorViewController.h in Headers */,
|
||||
C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */,
|
||||
C34063B528400A26007B46D3 /* FLEXActivityViewController.h in Headers */,
|
||||
C3694DBA23EA1096006625D7 /* FLEXTabsViewController.h in Headers */,
|
||||
C31C4A6923342A2200C35F12 /* FLEXMetadataSection.h in Headers */,
|
||||
C3F977832311B38F0032776D /* NSString+ObjcRuntime.h in Headers */,
|
||||
@@ -1640,6 +1680,7 @@
|
||||
C397E318240EC98F0091E4EC /* FLEXSQLResult.h in Headers */,
|
||||
7349FD6A22B93CDF00051810 /* FLEXColor.h in Headers */,
|
||||
C36FBFD3230F3B98008D95D5 /* FLEXMethod.h in Headers */,
|
||||
C3EF290528EE8BC500B337B6 /* FLEXWindowShortcuts.h in Headers */,
|
||||
C36FBFD8230F3B98008D95D5 /* FLEXPropertyAttributes.h in Headers */,
|
||||
3A4C94FB1B5B21410088C3F2 /* FLEXArgumentInputStringView.h in Headers */,
|
||||
C38EF26323A2FCD20047A7EC /* FLEXViewControllerShortcuts.h in Headers */,
|
||||
@@ -1664,6 +1705,7 @@
|
||||
94A515181C4CA1D70063292F /* FLEXManager+Private.h in Headers */,
|
||||
C383C3B923B6A62A007A321B /* FLEXRuntimeSafety.h in Headers */,
|
||||
C39ED92822D63F3200B5773A /* FLEXAddressExplorerCoordinator.h in Headers */,
|
||||
C3A04B64288E007100F2C16D /* NSDateFormatter+FLEX.h in Headers */,
|
||||
C313854923F5F7D50046E63C /* flex_fishhook.h in Headers */,
|
||||
C3A9424023C5443A006871A3 /* FHSSnapshotNodes.h in Headers */,
|
||||
C32A195E231732E800EB02AC /* FLEXCollectionContentSection.h in Headers */,
|
||||
@@ -1719,7 +1761,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = FLEX;
|
||||
LastUpgradeCheck = 1240;
|
||||
LastUpgradeCheck = 1400;
|
||||
ORGANIZATIONNAME = Flipboard;
|
||||
TargetAttributes = {
|
||||
1C27A8B51F0E5A0300F0D02D = {
|
||||
@@ -1808,6 +1850,7 @@
|
||||
3A4C95101B5B21410088C3F2 /* FLEXMethodCallingViewController.m in Sources */,
|
||||
C362AE8223C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m in Sources */,
|
||||
C3F646C2239EAA8F00D4A011 /* UIPasteboard+FLEX.m in Sources */,
|
||||
C38568FB272B3BFC00B1E37F /* FLEXSwiftInternal.mm in Sources */,
|
||||
C3EB6F8D242E9C83006EA386 /* FLEXRuntimeExporter.m in Sources */,
|
||||
3A4C94F61B5B21410088C3F2 /* FLEXArgumentInputObjectView.m in Sources */,
|
||||
3A4C94EC1B5B21410088C3F2 /* FLEXImagePreviewViewController.m in Sources */,
|
||||
@@ -1819,6 +1862,7 @@
|
||||
94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */,
|
||||
C398624E23AD6C67007E6793 /* FLEXRuntimeKeyPath.m in Sources */,
|
||||
C3694DC323EA147F006625D7 /* UIBarButtonItem+FLEX.m in Sources */,
|
||||
C38D970228190C93008709D0 /* FLEXMetadataExtras.m in Sources */,
|
||||
C36FBFD4230F3B98008D95D5 /* FLEXProtocol.m in Sources */,
|
||||
C34D4EB123A2ABD900C1F903 /* FLEXLayerShortcuts.m in Sources */,
|
||||
C38F3F32230C958F004E3731 /* FLEXAlert.m in Sources */,
|
||||
@@ -1857,6 +1901,7 @@
|
||||
3A4C94E41B5B21410088C3F2 /* FLEXRuntimeUtility.m in Sources */,
|
||||
3A4C94D41B5B21410088C3F2 /* FLEXObjectExplorerFactory.m in Sources */,
|
||||
94A515171C4CA1D70063292F /* FLEXManager.m in Sources */,
|
||||
C34063B628400A26007B46D3 /* FLEXActivityViewController.m in Sources */,
|
||||
C398626623AD70F5007E6793 /* FLEXKBToolbarButton.m in Sources */,
|
||||
3A4C952F1B5B21410088C3F2 /* FLEXWebViewController.m in Sources */,
|
||||
3A4C95041B5B21410088C3F2 /* FLEXArgumentInputView.m in Sources */,
|
||||
@@ -1877,12 +1922,15 @@
|
||||
3A4C95311B5B21410088C3F2 /* FLEXSystemLogMessage.m in Sources */,
|
||||
C3F31D422267D883003C991A /* FLEXTableViewCell.m in Sources */,
|
||||
3A4C94F41B5B21410088C3F2 /* FLEXArgumentInputFontView.m in Sources */,
|
||||
C3EBE112287028D600702098 /* FLEXAPNSViewController.m in Sources */,
|
||||
C3694DBB23EA1096006625D7 /* FLEXTabsViewController.m in Sources */,
|
||||
C3A9424623C641C1006871A3 /* SceneKit+Snapshot.m in Sources */,
|
||||
C3A04B65288E007100F2C16D /* NSDateFormatter+FLEX.m in Sources */,
|
||||
3A4C953B1B5B21410088C3F2 /* FLEXNetworkSettingsController.m in Sources */,
|
||||
3A4C94FC1B5B21410088C3F2 /* FLEXArgumentInputStringView.m in Sources */,
|
||||
3A4C94F81B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.m in Sources */,
|
||||
3A4C95351B5B21410088C3F2 /* FLEXSystemLogViewController.m in Sources */,
|
||||
C3EF290628EE8BC500B337B6 /* FLEXWindowShortcuts.m in Sources */,
|
||||
C3DBFD0C26CE2FAF00E0466A /* OSCache.m in Sources */,
|
||||
C383C3C623B6BB81007A321B /* FLEXCodeFontCell.m in Sources */,
|
||||
3A4C95271B5B21410088C3F2 /* FLEXGlobalsViewController.m in Sources */,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1240"
|
||||
LastUpgradeVersion = "1400"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1240"
|
||||
LastUpgradeVersion = "1400"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
+9
-1
@@ -7,9 +7,17 @@ enum FLEXBuildOptions {
|
||||
static let silenceWarnings = false
|
||||
}
|
||||
|
||||
#if swift(>=5.9)
|
||||
let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v12)]
|
||||
#elseif swift(>=5.7)
|
||||
let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v11)]
|
||||
#else
|
||||
let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v10)]
|
||||
#endif
|
||||
|
||||
let package = Package(
|
||||
name: "FLEX",
|
||||
platforms: [.iOS(.v10)],
|
||||
platforms: platforms,
|
||||
products: [
|
||||
.library(name: "FLEX", targets: ["FLEX"])
|
||||
],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user