mirror of
				https://github.com/SoPat712/YTLitePlus.git
				synced 2025-10-31 04:44:14 -04:00 
			
		
		
		
	added files via upload
This commit is contained in:
		
							
								
								
									
										15
									
								
								Tweaks/FLEX/Utility/Categories/CALayer+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Tweaks/FLEX/Utility/Categories/CALayer+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| // | ||||
| //  CALayer+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 2/28/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <QuartzCore/QuartzCore.h> | ||||
|  | ||||
| @interface CALayer (FLEX) | ||||
|  | ||||
| @property (nonatomic) BOOL flex_continuousCorners; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										46
									
								
								Tweaks/FLEX/Utility/Categories/CALayer+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Tweaks/FLEX/Utility/Categories/CALayer+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| // | ||||
| //  CALayer+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 2/28/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "CALayer+FLEX.h" | ||||
|  | ||||
| @interface CALayer (Private) | ||||
| @property (nonatomic) BOOL continuousCorners; | ||||
| @end | ||||
|  | ||||
| @implementation CALayer (FLEX) | ||||
|  | ||||
| static BOOL respondsToContinuousCorners = NO; | ||||
|  | ||||
| + (void)load { | ||||
|     respondsToContinuousCorners = [CALayer | ||||
|         instancesRespondToSelector:@selector(setContinuousCorners:) | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_continuousCorners { | ||||
|     if (respondsToContinuousCorners) { | ||||
|         return self.continuousCorners; | ||||
|     } | ||||
|      | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_continuousCorners:(BOOL)enabled { | ||||
|     if (respondsToContinuousCorners) { | ||||
|         if (@available(iOS 13, *)) { | ||||
|             self.cornerCurve = kCACornerCurveContinuous; | ||||
|         } else { | ||||
|             self.continuousCorners = enabled; | ||||
| //            self.masksToBounds = NO; | ||||
|     //        self.allowsEdgeAntialiasing = YES; | ||||
|     //        self.edgeAntialiasingMask = kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										29
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+Compare.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+Compare.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| // | ||||
| //  FLEXRuntime+Compare.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 8/28/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
| #import "FLEXProperty.h" | ||||
| #import "FLEXIvar.h" | ||||
| #import "FLEXMethodBase.h" | ||||
| #import "FLEXProtocol.h" | ||||
|  | ||||
| @interface FLEXProperty (Compare) | ||||
| - (NSComparisonResult)compare:(FLEXProperty *)other; | ||||
| @end | ||||
|  | ||||
| @interface FLEXIvar (Compare) | ||||
| - (NSComparisonResult)compare:(FLEXIvar *)other; | ||||
| @end | ||||
|  | ||||
| @interface FLEXMethodBase (Compare) | ||||
| - (NSComparisonResult)compare:(FLEXMethodBase *)other; | ||||
| @end | ||||
|  | ||||
| @interface FLEXProtocol (Compare) | ||||
| - (NSComparisonResult)compare:(FLEXProtocol *)other; | ||||
| @end | ||||
							
								
								
									
										47
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+Compare.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+Compare.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| // | ||||
| //  FLEXRuntime+Compare.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 8/28/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "FLEXRuntime+Compare.h" | ||||
|  | ||||
| @implementation FLEXProperty (Compare) | ||||
|  | ||||
| - (NSComparisonResult)compare:(FLEXProperty *)other { | ||||
|     NSComparisonResult r = [self.name caseInsensitiveCompare:other.name]; | ||||
|     if (r == NSOrderedSame) { | ||||
|         // TODO make sure empty image name sorts above an image name | ||||
|         return [self.imageName ?: @"" compare:other.imageName]; | ||||
|     } | ||||
|  | ||||
|     return r; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation FLEXIvar (Compare) | ||||
|  | ||||
| - (NSComparisonResult)compare:(FLEXIvar *)other { | ||||
|     return [self.name caseInsensitiveCompare:other.name]; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation FLEXMethodBase (Compare) | ||||
|  | ||||
| - (NSComparisonResult)compare:(FLEXMethodBase *)other { | ||||
|     return [self.name caseInsensitiveCompare:other.name]; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation FLEXProtocol (Compare) | ||||
|  | ||||
| - (NSComparisonResult)compare:(FLEXProtocol *)other { | ||||
|     return [self.name caseInsensitiveCompare:other.name]; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										90
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+UIKitHelpers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+UIKitHelpers.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| // | ||||
| //  FLEXRuntime+UIKitHelpers.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/16/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
| #import "FLEXProperty.h" | ||||
| #import "FLEXIvar.h" | ||||
| #import "FLEXMethod.h" | ||||
| #import "FLEXProtocol.h" | ||||
| #import "FLEXTableViewSection.h" | ||||
|  | ||||
| @class FLEXObjectExplorerDefaults; | ||||
|  | ||||
| /// Model objects of an object explorer screen adopt this | ||||
| /// protocol in order respond to user defaults changes  | ||||
| @protocol FLEXObjectExplorerItem <NSObject> | ||||
| /// Current explorer settings. Set when settings change. | ||||
| @property (nonatomic) FLEXObjectExplorerDefaults *defaults; | ||||
|  | ||||
| /// YES for properties and ivars which surely support editing, NO for all methods. | ||||
| @property (nonatomic, readonly) BOOL isEditable; | ||||
| /// NO for ivars, YES for supported methods and properties | ||||
| @property (nonatomic, readonly) BOOL isCallable; | ||||
| @end | ||||
|  | ||||
| @protocol FLEXRuntimeMetadata <FLEXObjectExplorerItem> | ||||
| /// Used as the main title of the row | ||||
| - (NSString *)description; | ||||
| /// Used to compare metadata objects for uniqueness | ||||
| @property (nonatomic, readonly) NSString *name; | ||||
|  | ||||
| /// For internal use | ||||
| @property (nonatomic) id tag; | ||||
|  | ||||
| /// Should return \c nil if not applicable | ||||
| - (id)currentValueWithTarget:(id)object; | ||||
| /// Used as the subtitle or description of a property, ivar, or method | ||||
| - (NSString *)previewWithTarget:(id)object; | ||||
| /// For methods, a method calling screen. For all else, an object explorer. | ||||
| - (UIViewController *)viewerWithTarget:(id)object; | ||||
| /// For methods and protocols, nil. For all else, an a field editor screen. | ||||
| /// The given section is reloaded on commit of any changes. | ||||
| - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section; | ||||
| /// Used to determine present which interactions are possible to the user | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object; | ||||
| /// Return nil to use the default reuse identifier | ||||
| - (NSString *)reuseIdentifierWithTarget:(id)object; | ||||
|  | ||||
| /// An array of actions to place in the first section of the context menu. | ||||
| - (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender API_AVAILABLE(ios(13.0)); | ||||
| /// An array where every 2 elements are a key-value pair. The key is a description | ||||
| /// of what to copy like "Name" and the values are what will be copied. | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object; | ||||
| /// Properties and ivars return the address of an object, if they hold one. | ||||
| - (NSString *)contextualSubtitleWithTarget:(id)object; | ||||
|  | ||||
| @end | ||||
|  | ||||
| // Even if a property is readonly, it still may be editable | ||||
| // via a setter. Checking isEditable will not reflect that | ||||
| // unless the property was initialized with a class. | ||||
| @interface FLEXProperty (UIKitHelpers) <FLEXRuntimeMetadata> @end | ||||
| @interface FLEXIvar (UIKitHelpers) <FLEXRuntimeMetadata> @end | ||||
| @interface FLEXMethodBase (UIKitHelpers) <FLEXRuntimeMetadata> @end | ||||
| @interface FLEXMethod (UIKitHelpers) <FLEXRuntimeMetadata> @end | ||||
| @interface FLEXProtocol (UIKitHelpers) <FLEXRuntimeMetadata> @end | ||||
|  | ||||
| typedef NS_ENUM(NSUInteger, FLEXStaticMetadataRowStyle) { | ||||
|     FLEXStaticMetadataRowStyleSubtitle, | ||||
|     FLEXStaticMetadataRowStyleKeyValue, | ||||
|     FLEXStaticMetadataRowStyleDefault = FLEXStaticMetadataRowStyleSubtitle, | ||||
| }; | ||||
|  | ||||
| /// Displays a small row as a static key-value pair of information. | ||||
| @interface FLEXStaticMetadata : NSObject <FLEXRuntimeMetadata> | ||||
|  | ||||
| + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title string:(NSString *)string; | ||||
| + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title number:(NSNumber *)number; | ||||
|  | ||||
| + (NSArray<FLEXStaticMetadata *> *)classHierarchy:(NSArray<Class> *)classes; | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| /// This is assigned to the \c tag property of each metadata. | ||||
|  | ||||
							
								
								
									
										634
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+UIKitHelpers.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										634
									
								
								Tweaks/FLEX/Utility/Categories/FLEXRuntime+UIKitHelpers.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,634 @@ | ||||
| // | ||||
| //  FLEXRuntime+UIKitHelpers.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/16/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "FLEXRuntime+UIKitHelpers.h" | ||||
| #import "FLEXRuntimeUtility.h" | ||||
| #import "FLEXPropertyAttributes.h" | ||||
| #import "FLEXArgumentInputViewFactory.h" | ||||
| #import "FLEXObjectExplorerFactory.h" | ||||
| #import "FLEXFieldEditorViewController.h" | ||||
| #import "FLEXMethodCallingViewController.h" | ||||
| #import "FLEXObjectListViewController.h" | ||||
| #import "FLEXTableView.h" | ||||
| #import "FLEXUtility.h" | ||||
| #import "NSArray+FLEX.h" | ||||
| #import "NSString+FLEX.h" | ||||
|  | ||||
| #define FLEXObjectExplorerDefaultsImpl \ | ||||
| - (FLEXObjectExplorerDefaults *)defaults { \ | ||||
|     return self.tag; \ | ||||
| } \ | ||||
|  \ | ||||
| - (void)setDefaults:(FLEXObjectExplorerDefaults *)defaults { \ | ||||
|     self.tag = defaults; \ | ||||
| } | ||||
|  | ||||
| #pragma mark FLEXProperty | ||||
| @implementation FLEXProperty (UIKitHelpers) | ||||
| FLEXObjectExplorerDefaultsImpl | ||||
|  | ||||
| /// Decide whether to use potentialTarget or [potentialTarget class] to get or set property | ||||
| - (id)appropriateTargetForPropertyType:(id)potentialTarget { | ||||
|     if (!object_isClass(potentialTarget)) { | ||||
|         if (self.isClassProperty) { | ||||
|             return [potentialTarget class]; | ||||
|         } else { | ||||
|             return potentialTarget; | ||||
|         } | ||||
|     } else { | ||||
|         if (self.isClassProperty) { | ||||
|             return potentialTarget; | ||||
|         } else { | ||||
|             // Instance property with a class object | ||||
|             return nil; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (BOOL)isEditable { | ||||
|     if (self.attributes.isReadOnly) { | ||||
|         return self.likelySetterExists; | ||||
|     } | ||||
|      | ||||
|     const FLEXTypeEncoding *typeEncoding = self.attributes.typeEncoding.UTF8String; | ||||
|     return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:nil]; | ||||
| } | ||||
|  | ||||
| - (BOOL)isCallable { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| - (id)currentValueWithTarget:(id)object { | ||||
|     return [self getPotentiallyUnboxedValue: | ||||
|         [self appropriateTargetForPropertyType:object] | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (id)currentValueBeforeUnboxingWithTarget:(id)object { | ||||
|     return [self getValue: | ||||
|         [self appropriateTargetForPropertyType:object] | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (NSString *)previewWithTarget:(id)object { | ||||
|     if (object_isClass(object) && !self.isClassProperty) { | ||||
|         return self.attributes.fullDeclaration; | ||||
|     } else if (self.defaults.wantsDynamicPreviews) { | ||||
|         return [FLEXRuntimeUtility | ||||
|             summaryForObject:[self currentValueWithTarget:object] | ||||
|         ]; | ||||
|     } | ||||
|      | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)viewerWithTarget:(id)object { | ||||
|     id value = [self currentValueWithTarget:object]; | ||||
|     return [FLEXObjectExplorerFactory explorerViewControllerForObject:value]; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { | ||||
|     id target = [self appropriateTargetForPropertyType:object]; | ||||
|     return [FLEXFieldEditorViewController target:target property:self commitHandler:^{ | ||||
|         [section reloadData:YES]; | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { | ||||
|     id targetForValueCheck = [self appropriateTargetForPropertyType:object]; | ||||
|     if (!targetForValueCheck) { | ||||
|         // Instance property with a class object | ||||
|         return UITableViewCellAccessoryNone; | ||||
|     } | ||||
|  | ||||
|     // We use .tag to store the cached value of .isEditable that is | ||||
|     // initialized by FLEXObjectExplorer in -reloadMetada | ||||
|     if ([self getPotentiallyUnboxedValue:targetForValueCheck]) { | ||||
|         if (self.defaults.isEditable) { | ||||
|             // Editable non-nil value, both | ||||
|             return UITableViewCellAccessoryDetailDisclosureButton; | ||||
|         } else { | ||||
|             // Uneditable non-nil value, chevron only | ||||
|             return UITableViewCellAccessoryDisclosureIndicator; | ||||
|         } | ||||
|     } else { | ||||
|         if (self.defaults.isEditable) { | ||||
|             // Editable nil value, just (i) | ||||
|             return UITableViewCellAccessoryDetailButton; | ||||
|         } else { | ||||
|             // Non-editable nil value, neither | ||||
|             return UITableViewCellAccessoryNone; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } | ||||
|  | ||||
| - (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { | ||||
|     BOOL returnsObject = self.attributes.typeEncoding.flex_typeIsObjectOrClass; | ||||
|     BOOL targetNotNil = [self appropriateTargetForPropertyType:object] != nil; | ||||
|      | ||||
|     // "Explore PropertyClass" for properties with a concrete class name | ||||
|     if (returnsObject) { | ||||
|         NSMutableArray<UIAction *> *actions = [NSMutableArray new]; | ||||
|          | ||||
|         // Action for exploring class of this property | ||||
|         Class propertyClass = self.attributes.typeEncoding.flex_typeClass; | ||||
|         if (propertyClass) { | ||||
|             NSString *title = [NSString stringWithFormat:@"Explore %@", NSStringFromClass(propertyClass)]; | ||||
|             [actions addObject:[UIAction actionWithTitle:title image:nil identifier:nil handler:^(UIAction *action) { | ||||
|                 UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:propertyClass]; | ||||
|                 [sender.navigationController pushViewController:explorer animated:YES]; | ||||
|             }]]; | ||||
|         } | ||||
|          | ||||
|         // Action for exploring references to this object | ||||
|         if (targetNotNil) { | ||||
|             // Since the property holder is not nil, check if the property value is nil | ||||
|             id value = [self currentValueBeforeUnboxingWithTarget:object]; | ||||
|             if (value) { | ||||
|                 NSString *title = @"List all references"; | ||||
|                 [actions addObject:[UIAction actionWithTitle:title image:nil identifier:nil handler:^(UIAction *action) { | ||||
|                     UIViewController *list = [FLEXObjectListViewController | ||||
|                         objectsWithReferencesToObject:value | ||||
|                         retained:NO | ||||
|                     ]; | ||||
|                     [sender.navigationController pushViewController:list animated:YES]; | ||||
|                 }]]; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return actions; | ||||
|     } | ||||
|      | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object { | ||||
|     BOOL returnsObject = self.attributes.typeEncoding.flex_typeIsObjectOrClass; | ||||
|     BOOL targetNotNil = [self appropriateTargetForPropertyType:object] != nil; | ||||
|      | ||||
|     NSMutableArray *items = [NSMutableArray arrayWithArray:@[ | ||||
|         @"Name",                      self.name ?: @"", | ||||
|         @"Type",                      self.attributes.typeEncoding ?: @"", | ||||
|         @"Declaration",               self.fullDescription ?: @"", | ||||
|     ]]; | ||||
|      | ||||
|     if (targetNotNil) { | ||||
|         id value = [self currentValueBeforeUnboxingWithTarget:object]; | ||||
|         [items addObjectsFromArray:@[ | ||||
|             @"Value Preview",         [self previewWithTarget:object] ?: @"", | ||||
|             @"Value Address",         returnsObject ? [FLEXUtility addressOfObject:value] : @"", | ||||
|         ]]; | ||||
|     } | ||||
|      | ||||
|     [items addObjectsFromArray:@[ | ||||
|         @"Getter",                    NSStringFromSelector(self.likelyGetter) ?: @"", | ||||
|         @"Setter",                    self.likelySetterExists ? NSStringFromSelector(self.likelySetter) : @"", | ||||
|         @"Image Name",                self.imageName ?: @"", | ||||
|         @"Attributes",                self.attributes.string ?: @"", | ||||
|         @"objc_property",             [FLEXUtility pointerToString:self.objc_property], | ||||
|         @"objc_property_attribute_t", [FLEXUtility pointerToString:self.attributes.list], | ||||
|     ]]; | ||||
|      | ||||
|     return items; | ||||
| } | ||||
|  | ||||
| - (NSString *)contextualSubtitleWithTarget:(id)object { | ||||
|     id target = [self appropriateTargetForPropertyType:object]; | ||||
|     if (target && self.attributes.typeEncoding.flex_typeIsObjectOrClass) { | ||||
|         return [FLEXUtility addressOfObject:[self currentValueBeforeUnboxingWithTarget:target]]; | ||||
|     } | ||||
|      | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark FLEXIvar | ||||
| @implementation FLEXIvar (UIKitHelpers) | ||||
| FLEXObjectExplorerDefaultsImpl | ||||
|  | ||||
| - (BOOL)isEditable { | ||||
|     const FLEXTypeEncoding *typeEncoding = self.typeEncoding.UTF8String; | ||||
|     return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:nil]; | ||||
| } | ||||
|  | ||||
| - (BOOL)isCallable { | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (id)currentValueWithTarget:(id)object { | ||||
|     if (!object_isClass(object)) { | ||||
|         return [self getPotentiallyUnboxedValue:object]; | ||||
|     } | ||||
|  | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSString *)previewWithTarget:(id)object { | ||||
|     if (object_isClass(object)) { | ||||
|         return self.details; | ||||
|     } else if (self.defaults.wantsDynamicPreviews) { | ||||
|         return [FLEXRuntimeUtility | ||||
|             summaryForObject:[self currentValueWithTarget:object] | ||||
|         ]; | ||||
|     } | ||||
|      | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)viewerWithTarget:(id)object { | ||||
|     NSAssert(!object_isClass(object), @"Unreachable state: viewing ivar on class object"); | ||||
|     id value = [self currentValueWithTarget:object]; | ||||
|     return [FLEXObjectExplorerFactory explorerViewControllerForObject:value]; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { | ||||
|     NSAssert(!object_isClass(object), @"Unreachable state: editing ivar on class object"); | ||||
|     return [FLEXFieldEditorViewController target:object ivar:self commitHandler:^{ | ||||
|         [section reloadData:YES]; | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { | ||||
|     if (object_isClass(object)) { | ||||
|         return UITableViewCellAccessoryNone; | ||||
|     } | ||||
|  | ||||
|     // Could use .isEditable here, but we use .tag for speed since it is cached | ||||
|     if ([self getPotentiallyUnboxedValue:object]) { | ||||
|         if (self.defaults.isEditable) { | ||||
|             // Editable non-nil value, both | ||||
|             return UITableViewCellAccessoryDetailDisclosureButton; | ||||
|         } else { | ||||
|             // Uneditable non-nil value, chevron only | ||||
|             return UITableViewCellAccessoryDisclosureIndicator; | ||||
|         } | ||||
|     } else { | ||||
|         if (self.defaults.isEditable) { | ||||
|             // Editable nil value, just (i) | ||||
|             return UITableViewCellAccessoryDetailButton; | ||||
|         } else { | ||||
|             // Non-editable nil value, neither | ||||
|             return UITableViewCellAccessoryNone; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } | ||||
|  | ||||
| - (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { | ||||
|     Class ivarClass = self.typeEncoding.flex_typeClass; | ||||
|      | ||||
|     // "Explore PropertyClass" for properties with a concrete class name | ||||
|     if (ivarClass) { | ||||
|         NSString *title = [NSString stringWithFormat:@"Explore %@", NSStringFromClass(ivarClass)]; | ||||
|         return @[[UIAction actionWithTitle:title image:nil identifier:nil handler:^(UIAction *action) { | ||||
|             UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:ivarClass]; | ||||
|             [sender.navigationController pushViewController:explorer animated:YES]; | ||||
|         }]]; | ||||
|     } | ||||
|      | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object { | ||||
|     BOOL isInstance = !object_isClass(object); | ||||
|     BOOL returnsObject = self.typeEncoding.flex_typeIsObjectOrClass; | ||||
|     id value = isInstance ? [self getValue:object] : nil; | ||||
|      | ||||
|     NSMutableArray *items = [NSMutableArray arrayWithArray:@[ | ||||
|         @"Name",          self.name ?: @"", | ||||
|         @"Type",          self.typeEncoding ?: @"", | ||||
|         @"Declaration",   self.description ?: @"", | ||||
|     ]]; | ||||
|      | ||||
|     if (isInstance) { | ||||
|         [items addObjectsFromArray:@[ | ||||
|             @"Value Preview", isInstance ? [self previewWithTarget:object] : @"", | ||||
|             @"Value Address", returnsObject ? [FLEXUtility addressOfObject:value] : @"", | ||||
|         ]]; | ||||
|     } | ||||
|      | ||||
|     [items addObjectsFromArray:@[ | ||||
|         @"Size",          @(self.size).stringValue, | ||||
|         @"Offset",        @(self.offset).stringValue, | ||||
|         @"objc_ivar",     [FLEXUtility pointerToString:self.objc_ivar], | ||||
|     ]]; | ||||
|      | ||||
|     return items; | ||||
| } | ||||
|  | ||||
| - (NSString *)contextualSubtitleWithTarget:(id)object { | ||||
|     if (!object_isClass(object) && self.typeEncoding.flex_typeIsObjectOrClass) { | ||||
|         return [FLEXUtility addressOfObject:[self getValue:object]]; | ||||
|     } | ||||
|      | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark FLEXMethod | ||||
| @implementation FLEXMethodBase (UIKitHelpers) | ||||
| FLEXObjectExplorerDefaultsImpl | ||||
|  | ||||
| - (BOOL)isEditable { | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (BOOL)isCallable { | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (id)currentValueWithTarget:(id)object { | ||||
|     // Methods can't be "edited" and have no "value" | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSString *)previewWithTarget:(id)object { | ||||
|     return [self.selectorString stringByAppendingFormat:@"  —  %@", self.typeEncoding]; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)viewerWithTarget:(id)object { | ||||
|     // We disallow calling of FLEXMethodBase methods | ||||
|     @throw NSInternalInconsistencyException; | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { | ||||
|     // Methods cannot be edited | ||||
|     @throw NSInternalInconsistencyException; | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { | ||||
|     // We shouldn't be using any FLEXMethodBase objects for this | ||||
|     @throw NSInternalInconsistencyException; | ||||
|     return UITableViewCellAccessoryNone; | ||||
| } | ||||
|  | ||||
| - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } | ||||
|  | ||||
| - (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object { | ||||
|     return @[ | ||||
|         @"Selector",      self.name ?: @"", | ||||
|         @"Type Encoding", self.typeEncoding ?: @"", | ||||
|         @"Declaration",   self.description ?: @"", | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (NSString *)contextualSubtitleWithTarget:(id)object { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation FLEXMethod (UIKitHelpers) | ||||
|  | ||||
| - (BOOL)isCallable { | ||||
|     return self.signature != nil; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)viewerWithTarget:(id)object { | ||||
|     object = self.isInstanceMethod ? object : (object_isClass(object) ? object : [object class]); | ||||
|     return [FLEXMethodCallingViewController target:object method:self]; | ||||
| } | ||||
|  | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { | ||||
|     if (self.isInstanceMethod) { | ||||
|         if (object_isClass(object)) { | ||||
|             // Instance method from class, can't call | ||||
|             return UITableViewCellAccessoryNone; | ||||
|         } else { | ||||
|             // Instance method from instance, can call | ||||
|             return UITableViewCellAccessoryDisclosureIndicator; | ||||
|         } | ||||
|     } else { | ||||
|         return UITableViewCellAccessoryDisclosureIndicator; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object { | ||||
|     return [[super copiableMetadataWithTarget:object] arrayByAddingObjectsFromArray:@[ | ||||
|         @"NSMethodSignature *", [FLEXUtility addressOfObject:self.signature], | ||||
|         @"Signature String",    self.signatureString ?: @"", | ||||
|         @"Number of Arguments", @(self.numberOfArguments).stringValue, | ||||
|         @"Return Type",         @(self.returnType ?: ""), | ||||
|         @"Return Size",         @(self.returnSize).stringValue, | ||||
|         @"objc_method",       [FLEXUtility pointerToString:self.objc_method], | ||||
|     ]]; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark FLEXProtocol | ||||
| @implementation FLEXProtocol (UIKitHelpers) | ||||
| FLEXObjectExplorerDefaultsImpl | ||||
|  | ||||
| - (BOOL)isEditable { | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (BOOL)isCallable { | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (id)currentValueWithTarget:(id)object { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSString *)previewWithTarget:(id)object { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)viewerWithTarget:(id)object { | ||||
|     return [FLEXObjectExplorerFactory explorerViewControllerForObject:self]; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { | ||||
|     // Protocols cannot be edited | ||||
|     @throw NSInternalInconsistencyException; | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { | ||||
|     return UITableViewCellAccessoryDisclosureIndicator; | ||||
| } | ||||
|  | ||||
| - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } | ||||
|  | ||||
| - (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object { | ||||
|     NSArray<NSString *> *conformanceNames = [self.protocols valueForKeyPath:@"name"]; | ||||
|     NSString *conformances = [conformanceNames componentsJoinedByString:@"\n"]; | ||||
|     return @[ | ||||
|         @"Name",         self.name ?: @"", | ||||
|         @"Conformances", conformances ?: @"", | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (NSString *)contextualSubtitleWithTarget:(id)object { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark FLEXStaticMetadata | ||||
| @interface FLEXStaticMetadata () { | ||||
|     @protected | ||||
|     NSString *_name; | ||||
| } | ||||
| @property (nonatomic) FLEXTableViewCellReuseIdentifier reuse; | ||||
| @property (nonatomic) NSString *subtitle; | ||||
| @property (nonatomic) id metadata; | ||||
| @end | ||||
|  | ||||
| @interface FLEXStaticMetadata_Class : FLEXStaticMetadata | ||||
| + (instancetype)withClass:(Class)cls; | ||||
| @end | ||||
|  | ||||
| @implementation FLEXStaticMetadata | ||||
| @synthesize name = _name; | ||||
| @synthesize tag = _tag; | ||||
|  | ||||
| FLEXObjectExplorerDefaultsImpl | ||||
|  | ||||
| + (NSArray<FLEXStaticMetadata *> *)classHierarchy:(NSArray<Class> *)classes { | ||||
|     return [classes flex_mapped:^id(Class cls, NSUInteger idx) { | ||||
|         return [FLEXStaticMetadata_Class withClass:cls]; | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title string:(NSString *)string { | ||||
|     return [[self alloc] initWithStyle:style title:title subtitle:string]; | ||||
| } | ||||
|  | ||||
| + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title number:(NSNumber *)number { | ||||
|     return [[self alloc] initWithStyle:style title:title subtitle:number.stringValue]; | ||||
| } | ||||
|  | ||||
| - (id)initWithStyle:(FLEXStaticMetadataRowStyle)style title:(NSString *)title subtitle:(NSString *)subtitle  { | ||||
|     self = [super init]; | ||||
|     if (self) { | ||||
|         if (style == FLEXStaticMetadataRowStyleKeyValue) { | ||||
|             _reuse = kFLEXKeyValueCell; | ||||
|         } else { | ||||
|             _reuse = kFLEXMultilineDetailCell; | ||||
|         } | ||||
|  | ||||
|         _name = title; | ||||
|         _subtitle = subtitle; | ||||
|     } | ||||
|  | ||||
|     return self; | ||||
| } | ||||
|  | ||||
| - (NSString *)description { | ||||
|     return self.name; | ||||
| } | ||||
|  | ||||
| - (NSString *)reuseIdentifierWithTarget:(id)object { | ||||
|     return self.reuse; | ||||
| } | ||||
|  | ||||
| - (BOOL)isEditable { | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (BOOL)isCallable { | ||||
|     return NO; | ||||
| } | ||||
|  | ||||
| - (id)currentValueWithTarget:(id)object { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSString *)previewWithTarget:(id)object { | ||||
|     return self.subtitle; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)viewerWithTarget:(id)object { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { | ||||
|     // Static metadata cannot be edited | ||||
|     @throw NSInternalInconsistencyException; | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { | ||||
|     return UITableViewCellAccessoryNone; | ||||
| } | ||||
|  | ||||
| - (NSArray<UIAction *> *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object { | ||||
|     return @[self.name, self.subtitle]; | ||||
| } | ||||
|  | ||||
| - (NSString *)contextualSubtitleWithTarget:(id)object { | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark FLEXStaticMetadata_Class | ||||
| @implementation FLEXStaticMetadata_Class | ||||
|  | ||||
| + (instancetype)withClass:(Class)cls { | ||||
|     NSParameterAssert(cls); | ||||
|      | ||||
|     FLEXStaticMetadata_Class *metadata = [self new]; | ||||
|     metadata.metadata = cls; | ||||
|     metadata->_name = NSStringFromClass(cls); | ||||
|     metadata.reuse = kFLEXDefaultCell; | ||||
|     return metadata; | ||||
| } | ||||
|  | ||||
| - (id)initWithStyle:(FLEXStaticMetadataRowStyle)style title:(NSString *)title subtitle:(NSString *)subtitle { | ||||
|     @throw NSInternalInconsistencyException; | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| - (UIViewController *)viewerWithTarget:(id)object { | ||||
|     return [FLEXObjectExplorerFactory explorerViewControllerForObject:self.metadata]; | ||||
| } | ||||
|  | ||||
| - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { | ||||
|     return UITableViewCellAccessoryDisclosureIndicator; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)copiableMetadataWithTarget:(id)object { | ||||
|     return @[ | ||||
|         @"Class Name", self.name, | ||||
|         @"Class", [FLEXUtility addressOfObject:self.metadata] | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (NSString *)contextualSubtitleWithTarget:(id)object { | ||||
|     return [FLEXUtility addressOfObject:self.metadata]; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										40
									
								
								Tweaks/FLEX/Utility/Categories/NSArray+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								Tweaks/FLEX/Utility/Categories/NSArray+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| // | ||||
| //  NSArray+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 9/25/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
|  | ||||
| @interface NSArray<T> (Functional) | ||||
|  | ||||
| /// Actually more like flatmap, but it seems like the objc way to allow returning nil to omit objects. | ||||
| /// So, return nil from the block to omit objects, and return an object to include it in the new array. | ||||
| /// Unlike flatmap, however, this will not flatten arrays of arrays into a single array. | ||||
| - (__kindof NSArray *)flex_mapped:(id(^)(T obj, NSUInteger idx))mapFunc; | ||||
| /// Like flex_mapped, but expects arrays to be returned, and flattens them into one array. | ||||
| - (__kindof NSArray *)flex_flatmapped:(NSArray *(^)(id, NSUInteger idx))block; | ||||
| - (instancetype)flex_filtered:(BOOL(^)(T obj, NSUInteger idx))filterFunc; | ||||
| - (void)flex_forEach:(void(^)(T obj, NSUInteger idx))block; | ||||
|  | ||||
| /// Unlike \c subArrayWithRange: this will not throw an exception if \c maxLength | ||||
| /// is greater than the size of the array. If the array has one element and | ||||
| /// \c maxLength is greater than 1, you get an array with 1 element back. | ||||
| - (instancetype)flex_subArrayUpto:(NSUInteger)maxLength; | ||||
|  | ||||
| + (instancetype)flex_forEachUpTo:(NSUInteger)bound map:(T(^)(NSUInteger i))block; | ||||
| + (instancetype)flex_mapped:(id<NSFastEnumeration>)collection block:(id(^)(T obj, NSUInteger idx))mapFunc; | ||||
|  | ||||
| - (instancetype)flex_sortedUsingSelector:(SEL)selector; | ||||
|  | ||||
| - (T)flex_firstWhere:(BOOL(^)(T obj))meetingCriteria; | ||||
|  | ||||
| @end | ||||
|  | ||||
| @interface NSMutableArray<T> (Functional) | ||||
|  | ||||
| - (void)flex_filter:(BOOL(^)(T obj, NSUInteger idx))filterFunc; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										143
									
								
								Tweaks/FLEX/Utility/Categories/NSArray+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								Tweaks/FLEX/Utility/Categories/NSArray+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| // | ||||
| //  NSArray+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 9/25/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "NSArray+FLEX.h" | ||||
|  | ||||
| #define FLEXArrayClassIsMutable(me) ([[self class] isSubclassOfClass:[NSMutableArray class]]) | ||||
|  | ||||
| @implementation NSArray (Functional) | ||||
|  | ||||
| - (__kindof NSArray *)flex_mapped:(id (^)(id, NSUInteger))mapFunc { | ||||
|     NSMutableArray *map = [NSMutableArray new]; | ||||
|     [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { | ||||
|         id ret = mapFunc(obj, idx); | ||||
|         if (ret) { | ||||
|             [map addObject:ret]; | ||||
|         } | ||||
|     }]; | ||||
|  | ||||
|     if (self.count < 2048 && !FLEXArrayClassIsMutable(self)) { | ||||
|         return map.copy; | ||||
|     } | ||||
|  | ||||
|     return map; | ||||
| } | ||||
|  | ||||
| - (__kindof NSArray *)flex_flatmapped:(NSArray *(^)(id, NSUInteger))block { | ||||
|     NSMutableArray *array = [NSMutableArray new]; | ||||
|     [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { | ||||
|         NSArray *toAdd = block(obj, idx); | ||||
|         if (toAdd) { | ||||
|             [array addObjectsFromArray:toAdd]; | ||||
|         } | ||||
|     }]; | ||||
|  | ||||
|     if (array.count < 2048 && !FLEXArrayClassIsMutable(self)) { | ||||
|         return array.copy; | ||||
|     } | ||||
|  | ||||
|     return array; | ||||
| } | ||||
|  | ||||
| - (NSArray *)flex_filtered:(BOOL (^)(id, NSUInteger))filterFunc { | ||||
|     return [self flex_mapped:^id(id obj, NSUInteger idx) { | ||||
|         return filterFunc(obj, idx) ? obj : nil; | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| - (void)flex_forEach:(void(^)(id, NSUInteger))block { | ||||
|     [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { | ||||
|         block(obj, idx); | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| - (instancetype)flex_subArrayUpto:(NSUInteger)maxLength { | ||||
|     if (maxLength > self.count) { | ||||
|         if (FLEXArrayClassIsMutable(self)) { | ||||
|             return self.mutableCopy; | ||||
|         } | ||||
|          | ||||
|         return self; | ||||
|     } | ||||
|      | ||||
|     return [self subarrayWithRange:NSMakeRange(0, maxLength)]; | ||||
| } | ||||
|  | ||||
| + (__kindof NSArray *)flex_forEachUpTo:(NSUInteger)bound map:(id(^)(NSUInteger))block { | ||||
|     NSMutableArray *array = [NSMutableArray new]; | ||||
|     for (NSUInteger i = 0; i < bound; i++) { | ||||
|         id obj = block(i); | ||||
|         if (obj) { | ||||
|             [array addObject:obj]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // For performance reasons, don't copy large arrays | ||||
|     if (bound < 2048 && !FLEXArrayClassIsMutable(self)) { | ||||
|         return array.copy; | ||||
|     } | ||||
|  | ||||
|     return array; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_mapped:(id<NSFastEnumeration>)collection block:(id(^)(id obj, NSUInteger idx))mapFunc { | ||||
|     NSMutableArray *array = [NSMutableArray new]; | ||||
|     NSInteger idx = 0; | ||||
|     for (id obj in collection) { | ||||
|         id ret = mapFunc(obj, idx++); | ||||
|         if (ret) { | ||||
|             [array addObject:ret]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // For performance reasons, don't copy large arrays | ||||
|     if (array.count < 2048) { | ||||
|         return array.copy; | ||||
|     } | ||||
|  | ||||
|     return array; | ||||
| } | ||||
|  | ||||
| - (instancetype)flex_sortedUsingSelector:(SEL)selector { | ||||
|     if (FLEXArrayClassIsMutable(self)) { | ||||
|         NSMutableArray *me = (id)self; | ||||
|         [me sortUsingSelector:selector]; | ||||
|         return me; | ||||
|     } else { | ||||
|         return [self sortedArrayUsingSelector:selector]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (id)flex_firstWhere:(BOOL (^)(id))meetsCriteria { | ||||
|     for (id e in self) { | ||||
|         if (meetsCriteria(e)) { | ||||
|             return e; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     return nil; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| @implementation NSMutableArray (Functional) | ||||
|  | ||||
| - (void)flex_filter:(BOOL (^)(id, NSUInteger))keepObject { | ||||
|     NSMutableIndexSet *toRemove = [NSMutableIndexSet new]; | ||||
|      | ||||
|     [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { | ||||
|         if (!keepObject(obj, idx)) { | ||||
|             [toRemove addIndex:idx]; | ||||
|         } | ||||
|     }]; | ||||
|      | ||||
|     [self removeObjectsAtIndexes:toRemove]; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										234
									
								
								Tweaks/FLEX/Utility/Categories/NSObject+FLEX_Reflection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								Tweaks/FLEX/Utility/Categories/NSObject+FLEX_Reflection.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | ||||
| // | ||||
| //  NSObject+FLEX_Reflection.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Derived from MirrorKit. | ||||
| //  Created by Tanner on 6/30/15. | ||||
| //  Copyright (c) 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
| #import <objc/runtime.h> | ||||
| @class FLEXMirror, FLEXMethod, FLEXIvar, FLEXProperty, FLEXMethodBase, FLEXPropertyAttributes, FLEXProtocol; | ||||
|  | ||||
| NS_ASSUME_NONNULL_BEGIN | ||||
|  | ||||
| /// Returns the type encoding string given the encoding for the return type and parameters, if any. | ||||
| /// @discussion Example usage for a \c void returning method which takes | ||||
| /// an \c int: @code FLEXTypeEncoding(@encode(void), @encode(int)); | ||||
| /// @param returnType The encoded return type. \c void for exmaple would be \c @encode(void). | ||||
| /// @param count The number of parameters in this type encoding string. | ||||
| /// @return The type encoding string, or \c nil if \e returnType is \c NULL. | ||||
| NSString * FLEXTypeEncodingString(const char *returnType, NSUInteger count, ...); | ||||
|  | ||||
| NSArray<Class> * _Nullable FLEXGetAllSubclasses(_Nullable Class cls, BOOL includeSelf); | ||||
| NSArray<Class> * _Nullable FLEXGetClassHierarchy(_Nullable Class cls, BOOL includeSelf); | ||||
| NSArray<FLEXProtocol *> * _Nullable FLEXGetConformedProtocols(_Nullable Class cls); | ||||
|  | ||||
| NSArray<FLEXIvar *> * _Nullable FLEXGetAllIvars(_Nullable Class cls); | ||||
| /// @param cls a class object to get instance properties, | ||||
| /// or a metaclass object to get class properties | ||||
| NSArray<FLEXProperty *> * _Nullable FLEXGetAllProperties(_Nullable Class cls); | ||||
| /// @param cls a class object to get instance methods, | ||||
| /// or a metaclass object to get class methods | ||||
| /// @param instance used to mark methods as instance methods or not. | ||||
| /// Not used to determine whether to get instance or class methods.  | ||||
| NSArray<FLEXMethod *> * _Nullable FLEXGetAllMethods(_Nullable Class cls, BOOL instance); | ||||
| /// @param cls a class object to get all instance and class methods. | ||||
| NSArray<FLEXMethod *> * _Nullable FLEXGetAllInstanceAndClassMethods(_Nullable Class cls); | ||||
|  | ||||
|  | ||||
|  | ||||
| #pragma mark Reflection | ||||
| @interface NSObject (Reflection) | ||||
|  | ||||
| @property (nonatomic, readonly       ) FLEXMirror *flex_reflection; | ||||
| @property (nonatomic, readonly, class) FLEXMirror *flex_reflection; | ||||
|  | ||||
| /// Calls into /c FLEXGetAllSubclasses | ||||
| /// @return Every subclass of the receiving class, including the receiver itself. | ||||
| @property (nonatomic, readonly, class) NSArray<Class> *flex_allSubclasses; | ||||
|  | ||||
| /// @return The \c Class object for the metaclass of the recieving class, or \c Nil if the class is Nil or not registered. | ||||
| @property (nonatomic, readonly, class) Class flex_metaclass; | ||||
| /// @return The size in bytes of instances of the recieving class, or \c 0 if \e cls is \c Nil. | ||||
| @property (nonatomic, readonly, class) size_t flex_instanceSize; | ||||
|  | ||||
| /// Changes the class of an object instance. | ||||
| /// @return The previous value of the objects \c class, or \c Nil if the object is \c nil. | ||||
| - (Class)flex_setClass:(Class)cls; | ||||
| /// Sets the recieving class's superclass. "You should not use this method" — Apple. | ||||
| /// @return The old superclass. | ||||
| + (Class)flex_setSuperclass:(Class)superclass; | ||||
|  | ||||
| /// Calls into \c FLEXGetClassHierarchy() | ||||
| /// @return a list of classes going up the class hierarchy, | ||||
| /// starting with the receiver and ending with the root class. | ||||
| @property (nonatomic, readonly, class) NSArray<Class> *flex_classHierarchy; | ||||
|  | ||||
| /// Calls into \c FLEXGetConformedProtocols | ||||
| /// @return a list of protocols this class itself conforms to. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXProtocol *> *flex_protocols; | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark Methods | ||||
| @interface NSObject (Methods) | ||||
|  | ||||
| /// All instance and class methods specific to the recieving class. | ||||
| /// @discussion This method will only retrieve methods specific to the recieving class. | ||||
| /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. | ||||
| /// @return An array of \c FLEXMethod objects. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXMethod *> *flex_allMethods; | ||||
| /// All instance methods specific to the recieving class. | ||||
| /// @discussion This method will only retrieve methods specific to the recieving class. | ||||
| /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. | ||||
| /// @return An array of \c FLEXMethod objects. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXMethod *> *flex_allInstanceMethods; | ||||
| /// All class methods specific to the recieving class. | ||||
| /// @discussion This method will only retrieve methods specific to the recieving class. | ||||
| /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. | ||||
| /// @return An array of \c FLEXMethod objects. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXMethod *> *flex_allClassMethods; | ||||
|  | ||||
| /// Retrieves the class's instance method with the given name. | ||||
| /// @return An initialized \c FLEXMethod object, or \c nil if the method wasn't found. | ||||
| + (FLEXMethod *)flex_methodNamed:(NSString *)name; | ||||
|  | ||||
| /// Retrieves the class's class method with the given name. | ||||
| /// @return An initialized \c FLEXMethod object, or \c nil if the method wasn't found. | ||||
| + (FLEXMethod *)flex_classMethodNamed:(NSString *)name; | ||||
|  | ||||
| /// Adds a new method to the recieving class with a given name and implementation. | ||||
| /// @discussion This method will add an override of a superclass's implementation, | ||||
| /// but will not replace an existing implementation in the class. | ||||
| /// To change an existing implementation, use \c replaceImplementationOfMethod:with:. | ||||
| /// | ||||
| /// Type encodings start with the return type and end with the parameter types in order. | ||||
| /// The type encoding for \c NSArray's \c count property getter looks like this: | ||||
| /// @code [NSString stringWithFormat:@"%s%s%s%s", @encode(void), @encode(id), @encode(SEL), @encode(NSUInteger)] @endcode | ||||
| /// Using the \c FLEXTypeEncoding function for the same method looks like this: | ||||
| /// @code FLEXTypeEncodingString(@encode(void), 1, @encode(NSUInteger)) @endcode | ||||
| /// @param typeEncoding The type encoding string. Consider using the \c FLEXTypeEncodingString() function. | ||||
| /// @param instanceMethod NO to add the method to the class itself or YES to add it as an instance method. | ||||
| /// @return YES if the method was added successfully, \c NO otherwise | ||||
| /// (for example, the class already contains a method implementation with that name). | ||||
| + (BOOL)addMethod:(SEL)selector | ||||
|      typeEncoding:(NSString *)typeEncoding | ||||
|    implementation:(IMP)implementaiton | ||||
|       toInstances:(BOOL)instanceMethod; | ||||
|  | ||||
| /// Replaces the implementation of a method in the recieving class. | ||||
| /// @param instanceMethod YES to replace the instance method, NO to replace the class method. | ||||
| /// @note This function behaves in two different ways: | ||||
| /// | ||||
| /// - If the method does not yet exist in the recieving class, it is added as if | ||||
| /// \c addMethod:typeEncoding:implementation were called. | ||||
| /// | ||||
| /// - If the method does exist, its \c IMP is replaced. | ||||
| /// @return The previous \c IMP of \e method. | ||||
| + (IMP)replaceImplementationOfMethod:(FLEXMethodBase *)method with:(IMP)implementation useInstance:(BOOL)instanceMethod; | ||||
| /// Swaps the implementations of the given methods. | ||||
| /// @discussion If one or neither of the given methods exist in the recieving class, | ||||
| /// they are added to the class with their implementations swapped as if each method did exist. | ||||
| /// This method will not fail if each \c FLEXSimpleMethod contains a valid selector. | ||||
| /// @param instanceMethod YES to swizzle the instance method, NO to swizzle the class method. | ||||
| + (void)swizzle:(FLEXMethodBase *)original with:(FLEXMethodBase *)other onInstance:(BOOL)instanceMethod; | ||||
| /// Swaps the implementations of the given methods. | ||||
| /// @param instanceMethod YES to swizzle the instance method, NO to swizzle the class method. | ||||
| /// @return \c YES if successful, and \c NO if selectors could not be retrieved from the given strings. | ||||
| + (BOOL)swizzleByName:(NSString *)original with:(NSString *)other onInstance:(BOOL)instanceMethod; | ||||
| /// Swaps the implementations of methods corresponding to the given selectors. | ||||
| + (void)swizzleBySelector:(SEL)original with:(SEL)other onInstance:(BOOL)instanceMethod; | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark Properties | ||||
| @interface NSObject (Ivars) | ||||
|  | ||||
| /// All of the instance variables specific to the recieving class. | ||||
| /// @discussion This method will only retrieve instance varibles specific to the recieving class. | ||||
| /// To retrieve instance variables on a parent class, simply call \c [[self superclass] allIvars]. | ||||
| /// @return An array of \c FLEXIvar objects. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXIvar *> *flex_allIvars; | ||||
|  | ||||
| /// Retrieves an instance variable with the corresponding name. | ||||
| /// @return An initialized \c FLEXIvar object, or \c nil if the Ivar wasn't found. | ||||
| + (FLEXIvar *)flex_ivarNamed:(NSString *)name; | ||||
|  | ||||
| /// @return The address of the given ivar in the recieving object in memory, | ||||
| /// or \c NULL if it could not be found. | ||||
| - (void *)flex_getIvarAddress:(FLEXIvar *)ivar; | ||||
| /// @return The address of the given ivar in the recieving object in memory, | ||||
| /// or \c NULL if it could not be found. | ||||
| - (void *)flex_getIvarAddressByName:(NSString *)name; | ||||
| /// @discussion This method faster than creating an \c FLEXIvar and calling | ||||
| /// \c -getIvarAddress: if you already have an \c Ivar on hand | ||||
| /// @return The address of the given ivar in the recieving object in memory, | ||||
| /// or \c NULL if it could not be found\. | ||||
| - (void *)flex_getObjcIvarAddress:(Ivar)ivar; | ||||
|  | ||||
| /// Sets the value of the given instance variable on the recieving object. | ||||
| /// @discussion Use only when the target instance variable is an object. | ||||
| - (void)flex_setIvar:(FLEXIvar *)ivar object:(id)value; | ||||
| /// Sets the value of the given instance variable on the recieving object. | ||||
| /// @discussion Use only when the target instance variable is an object. | ||||
| /// @return \c YES if successful, or \c NO if the instance variable could not be found. | ||||
| - (BOOL)flex_setIvarByName:(NSString *)name object:(id)value; | ||||
| /// @discussion Use only when the target instance variable is an object. | ||||
| /// This method is faster than creating an \c FLEXIvar and calling | ||||
| /// \c -setIvar: if you already have an \c Ivar on hand. | ||||
| - (void)flex_setObjcIvar:(Ivar)ivar object:(id)value; | ||||
|  | ||||
| /// Sets the value of the given instance variable on the recieving object to the | ||||
| /// \e size number of bytes of data at \e value. | ||||
| /// @discussion Use one of the other methods if you can help it. | ||||
| - (void)flex_setIvar:(FLEXIvar *)ivar value:(void *)value size:(size_t)size; | ||||
| /// Sets the value of the given instance variable on the recieving object to the | ||||
| /// \e size number of bytes of data at \e value. | ||||
| /// @discussion Use one of the other methods if you can help it | ||||
| /// @return \c YES if successful, or \c NO if the instance variable could not be found. | ||||
| - (BOOL)flex_setIvarByName:(NSString *)name value:(void *)value size:(size_t)size; | ||||
| /// Sets the value of the given instance variable on the recieving object to the | ||||
| /// \e size number of bytes of data at \e value. | ||||
| /// @discussion This is faster than creating an \c FLEXIvar and calling | ||||
| /// \c -setIvar:value:size if you already have an \c Ivar on hand. | ||||
| - (void)flex_setObjcIvar:(Ivar)ivar value:(void *)value size:(size_t)size; | ||||
|  | ||||
| @end | ||||
|  | ||||
| #pragma mark Properties | ||||
| @interface NSObject (Properties) | ||||
|  | ||||
| /// All instance and class properties specific to the recieving class. | ||||
| /// @discussion This method will only retrieve properties specific to the recieving class. | ||||
| /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. | ||||
| /// @return An array of \c FLEXProperty objects. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXProperty *> *flex_allProperties; | ||||
| /// All instance properties specific to the recieving class. | ||||
| /// @discussion This method will only retrieve properties specific to the recieving class. | ||||
| /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. | ||||
| /// @return An array of \c FLEXProperty objects. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXProperty *> *flex_allInstanceProperties; | ||||
| /// All class properties specific to the recieving class. | ||||
| /// @discussion This method will only retrieve properties specific to the recieving class. | ||||
| /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. | ||||
| /// @return An array of \c FLEXProperty objects. | ||||
| @property (nonatomic, readonly, class) NSArray<FLEXProperty *> *flex_allClassProperties; | ||||
|  | ||||
| /// Retrieves the class's property with the given name. | ||||
| /// @return An initialized \c FLEXProperty object, or \c nil if the property wasn't found. | ||||
| + (FLEXProperty *)flex_propertyNamed:(NSString *)name; | ||||
| /// @return An initialized \c FLEXProperty object, or \c nil if the property wasn't found. | ||||
| + (FLEXProperty *)flex_classPropertyNamed:(NSString *)name; | ||||
|  | ||||
| /// Replaces the given property on the recieving class. | ||||
| + (void)flex_replaceProperty:(FLEXProperty *)property; | ||||
| /// Replaces the given property on the recieving class. Useful for changing a property's attributes. | ||||
| + (void)flex_replaceProperty:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes; | ||||
|  | ||||
| @end | ||||
|  | ||||
| NS_ASSUME_NONNULL_END | ||||
							
								
								
									
										426
									
								
								Tweaks/FLEX/Utility/Categories/NSObject+FLEX_Reflection.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								Tweaks/FLEX/Utility/Categories/NSObject+FLEX_Reflection.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,426 @@ | ||||
| // | ||||
| //  NSObject+FLEX_Reflection.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Derived from MirrorKit. | ||||
| //  Created by Tanner on 6/30/15. | ||||
| //  Copyright (c) 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "NSObject+FLEX_Reflection.h" | ||||
| #import "FLEXClassBuilder.h" | ||||
| #import "FLEXMirror.h" | ||||
| #import "FLEXProperty.h" | ||||
| #import "FLEXMethod.h" | ||||
| #import "FLEXIvar.h" | ||||
| #import "FLEXProtocol.h" | ||||
| #import "FLEXPropertyAttributes.h" | ||||
| #import "NSArray+FLEX.h" | ||||
| #import "FLEXUtility.h" | ||||
|  | ||||
|  | ||||
| NSString * FLEXTypeEncodingString(const char *returnType, NSUInteger count, ...) { | ||||
|     if (!returnType) return nil; | ||||
|      | ||||
|     NSMutableString *encoding = [NSMutableString new]; | ||||
|     [encoding appendFormat:@"%s%s%s", returnType, @encode(id), @encode(SEL)]; | ||||
|      | ||||
|     va_list args; | ||||
|     va_start(args, count); | ||||
|     char *type = va_arg(args, char *); | ||||
|     for (NSUInteger i = 0; i < count; i++, type = va_arg(args, char *)) { | ||||
|         [encoding appendFormat:@"%s", type]; | ||||
|     } | ||||
|     va_end(args); | ||||
|      | ||||
|     return encoding.copy; | ||||
| } | ||||
|  | ||||
| NSArray<Class> *FLEXGetAllSubclasses(Class cls, BOOL includeSelf) { | ||||
|     if (!cls) return nil; | ||||
|      | ||||
|     Class *buffer = NULL; | ||||
|      | ||||
|     int count, size; | ||||
|     do { | ||||
|         count  = objc_getClassList(NULL, 0); | ||||
|         buffer = (Class *)realloc(buffer, count * sizeof(*buffer)); | ||||
|         size   = objc_getClassList(buffer, count); | ||||
|     } while (size != count); | ||||
|      | ||||
|     NSMutableArray *classes = [NSMutableArray new]; | ||||
|     if (includeSelf) { | ||||
|         [classes addObject:cls]; | ||||
|     } | ||||
|      | ||||
|     for (int i = 0; i < count; i++) { | ||||
|         Class candidate = buffer[i]; | ||||
|         Class superclass = candidate; | ||||
|         while ((superclass = class_getSuperclass(superclass))) { | ||||
|             if (superclass == cls) { | ||||
|                 [classes addObject:candidate]; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     free(buffer); | ||||
|     return classes.copy; | ||||
| } | ||||
|  | ||||
| NSArray<Class> *FLEXGetClassHierarchy(Class cls, BOOL includeSelf) { | ||||
|     if (!cls) return nil; | ||||
|      | ||||
|     NSMutableArray *classes = [NSMutableArray new]; | ||||
|     if (includeSelf) { | ||||
|         [classes addObject:cls]; | ||||
|     } | ||||
|      | ||||
|     while ((cls = [cls superclass])) { | ||||
|         [classes addObject:cls]; | ||||
|     }; | ||||
|  | ||||
|     return classes.copy; | ||||
| } | ||||
|  | ||||
| NSArray<FLEXProtocol *> *FLEXGetConformedProtocols(Class cls) { | ||||
|     if (!cls) return nil; | ||||
|      | ||||
|     unsigned int count = 0; | ||||
|     Protocol *__unsafe_unretained *list = class_copyProtocolList(cls, &count); | ||||
|     NSArray<Protocol *> *protocols = [NSArray arrayWithObjects:list count:count]; | ||||
|     free(list); | ||||
|      | ||||
|     return [protocols flex_mapped:^id(Protocol *pro, NSUInteger idx) { | ||||
|         return [FLEXProtocol protocol:pro]; | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| NSArray<FLEXIvar *> *FLEXGetAllIvars(_Nullable Class cls) { | ||||
|     if (!cls) return nil; | ||||
|      | ||||
|     unsigned int ivcount; | ||||
|     Ivar *objcivars = class_copyIvarList(cls, &ivcount); | ||||
|     NSArray *ivars = [NSArray flex_forEachUpTo:ivcount map:^id(NSUInteger i) { | ||||
|         return [FLEXIvar ivar:objcivars[i]]; | ||||
|     }]; | ||||
|  | ||||
|     free(objcivars); | ||||
|     return ivars; | ||||
| } | ||||
|  | ||||
| NSArray<FLEXProperty *> *FLEXGetAllProperties(_Nullable Class cls) { | ||||
|     if (!cls) return nil; | ||||
|      | ||||
|     unsigned int pcount; | ||||
|     objc_property_t *objcproperties = class_copyPropertyList(cls, &pcount); | ||||
|     NSArray *properties = [NSArray flex_forEachUpTo:pcount map:^id(NSUInteger i) { | ||||
|         return [FLEXProperty property:objcproperties[i] onClass:cls]; | ||||
|     }]; | ||||
|  | ||||
|     free(objcproperties); | ||||
|     return properties; | ||||
| } | ||||
|  | ||||
| NSArray<FLEXMethod *> *FLEXGetAllMethods(_Nullable Class cls, BOOL instance) { | ||||
|     if (!cls) return nil; | ||||
|  | ||||
|     unsigned int mcount; | ||||
|     Method *objcmethods = class_copyMethodList(cls, &mcount); | ||||
|     NSArray *methods = [NSArray flex_forEachUpTo:mcount map:^id(NSUInteger i) { | ||||
|         return [FLEXMethod method:objcmethods[i] isInstanceMethod:instance]; | ||||
|     }]; | ||||
|      | ||||
|     free(objcmethods); | ||||
|     return methods; | ||||
| } | ||||
|  | ||||
|  | ||||
| #pragma mark NSProxy | ||||
|  | ||||
| @interface NSProxy (AnyObjectAdditions) @end | ||||
| @implementation NSProxy (AnyObjectAdditions) | ||||
|  | ||||
| + (void)load { FLEX_EXIT_IF_NO_CTORS() | ||||
|     // We need to get all of the methods in this file and add them to NSProxy.  | ||||
|     // To do this we we need the class itself and it's metaclass. | ||||
|     // Edit: also add them to Swift._SwiftObject | ||||
|     Class NSProxyClass = [NSProxy class]; | ||||
|     Class NSProxy_meta = object_getClass(NSProxyClass); | ||||
|     Class SwiftObjectClass = ( | ||||
|         NSClassFromString(@"SwiftObject") ?: NSClassFromString(@"Swift._SwiftObject") | ||||
|     ); | ||||
|      | ||||
|     // Copy all of the "flex_" methods from NSObject | ||||
|     id filterFunc = ^BOOL(FLEXMethod *method, NSUInteger idx) { | ||||
|         return [method.name hasPrefix:@"flex_"]; | ||||
|     }; | ||||
|     NSArray *instanceMethods = [NSObject.flex_allInstanceMethods flex_filtered:filterFunc]; | ||||
|     NSArray *classMethods = [NSObject.flex_allClassMethods flex_filtered:filterFunc]; | ||||
|      | ||||
|     FLEXClassBuilder *proxy     = [FLEXClassBuilder builderForClass:NSProxyClass]; | ||||
|     FLEXClassBuilder *proxyMeta = [FLEXClassBuilder builderForClass:NSProxy_meta]; | ||||
|     [proxy addMethods:instanceMethods]; | ||||
|     [proxyMeta addMethods:classMethods]; | ||||
|      | ||||
|     if (SwiftObjectClass) { | ||||
|         Class SwiftObject_meta = object_getClass(SwiftObjectClass); | ||||
|         FLEXClassBuilder *swiftObject = [FLEXClassBuilder builderForClass:SwiftObjectClass]; | ||||
|         FLEXClassBuilder *swiftObjectMeta = [FLEXClassBuilder builderForClass:SwiftObject_meta]; | ||||
|         [swiftObject addMethods:instanceMethods]; | ||||
|         [swiftObjectMeta addMethods:classMethods]; | ||||
|          | ||||
|         // So we can put Swift objects into dictionaries... | ||||
|         [swiftObjectMeta addMethods:@[ | ||||
|             [NSObject flex_classMethodNamed:@"copyWithZone:"]] | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| #pragma mark Reflection | ||||
|  | ||||
| @implementation NSObject (Reflection) | ||||
|  | ||||
| + (FLEXMirror *)flex_reflection { | ||||
|     return [FLEXMirror reflect:self]; | ||||
| } | ||||
|  | ||||
| - (FLEXMirror *)flex_reflection { | ||||
|     return [FLEXMirror reflect:self]; | ||||
| } | ||||
|  | ||||
| /// Code borrowed from MAObjCRuntime by Mike Ash | ||||
| + (NSArray *)flex_allSubclasses { | ||||
|     return FLEXGetAllSubclasses(self, YES); | ||||
| } | ||||
|  | ||||
| - (Class)flex_setClass:(Class)cls { | ||||
|     return object_setClass(self, cls); | ||||
| } | ||||
|  | ||||
| + (Class)flex_metaclass { | ||||
|     return objc_getMetaClass(NSStringFromClass(self.class).UTF8String); | ||||
| } | ||||
|  | ||||
| + (size_t)flex_instanceSize { | ||||
|     return class_getInstanceSize(self.class); | ||||
| } | ||||
|  | ||||
| + (Class)flex_setSuperclass:(Class)superclass { | ||||
|     #pragma clang diagnostic push | ||||
|     #pragma clang diagnostic ignored "-Wdeprecated-declarations" | ||||
|     return class_setSuperclass(self, superclass); | ||||
|     #pragma clang diagnostic pop | ||||
| } | ||||
|  | ||||
| + (NSArray<Class> *)flex_classHierarchy { | ||||
|     return FLEXGetClassHierarchy(self, YES); | ||||
| } | ||||
|  | ||||
| + (NSArray<FLEXProtocol *> *)flex_protocols { | ||||
|     return FLEXGetConformedProtocols(self); | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark Methods | ||||
|  | ||||
| @implementation NSObject (Methods) | ||||
|  | ||||
| + (NSArray<FLEXMethod *> *)flex_allMethods { | ||||
|     NSMutableArray *instanceMethods = self.flex_allInstanceMethods.mutableCopy; | ||||
|     [instanceMethods addObjectsFromArray:self.flex_allClassMethods]; | ||||
|     return instanceMethods; | ||||
| } | ||||
|  | ||||
| + (NSArray<FLEXMethod *> *)flex_allInstanceMethods { | ||||
|     return FLEXGetAllMethods(self, YES); | ||||
| } | ||||
|  | ||||
| + (NSArray<FLEXMethod *> *)flex_allClassMethods { | ||||
|     return FLEXGetAllMethods(self.flex_metaclass, NO) ?: @[]; | ||||
| } | ||||
|  | ||||
| + (FLEXMethod *)flex_methodNamed:(NSString *)name { | ||||
|     Method m = class_getInstanceMethod([self class], NSSelectorFromString(name)); | ||||
|     if (m == NULL) { | ||||
|         return nil; | ||||
|     } | ||||
|  | ||||
|     return [FLEXMethod method:m isInstanceMethod:YES]; | ||||
| } | ||||
|  | ||||
| + (FLEXMethod *)flex_classMethodNamed:(NSString *)name { | ||||
|     Method m = class_getClassMethod([self class], NSSelectorFromString(name)); | ||||
|     if (m == NULL) { | ||||
|         return nil; | ||||
|     } | ||||
|  | ||||
|     return [FLEXMethod method:m isInstanceMethod:NO]; | ||||
| } | ||||
|  | ||||
| + (BOOL)addMethod:(SEL)selector | ||||
|      typeEncoding:(NSString *)typeEncoding | ||||
|    implementation:(IMP)implementaiton | ||||
|       toInstances:(BOOL)instance { | ||||
|     return class_addMethod(instance ? self.class : self.flex_metaclass, selector, implementaiton, typeEncoding.UTF8String); | ||||
| } | ||||
|  | ||||
| + (IMP)replaceImplementationOfMethod:(FLEXMethodBase *)method with:(IMP)implementation useInstance:(BOOL)instance { | ||||
|     return class_replaceMethod(instance ? self.class : self.flex_metaclass, method.selector, implementation, method.typeEncoding.UTF8String); | ||||
| } | ||||
|  | ||||
| + (void)swizzle:(FLEXMethodBase *)original with:(FLEXMethodBase *)other onInstance:(BOOL)instance { | ||||
|     [self swizzleBySelector:original.selector with:other.selector onInstance:instance]; | ||||
| } | ||||
|  | ||||
| + (BOOL)swizzleByName:(NSString *)original with:(NSString *)other onInstance:(BOOL)instance { | ||||
|     SEL originalMethod = NSSelectorFromString(original); | ||||
|     SEL newMethod      = NSSelectorFromString(other); | ||||
|     if (originalMethod == 0 || newMethod == 0) { | ||||
|         return NO; | ||||
|     } | ||||
|  | ||||
|     [self swizzleBySelector:originalMethod with:newMethod onInstance:instance]; | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| + (void)swizzleBySelector:(SEL)original with:(SEL)other onInstance:(BOOL)instance { | ||||
|     Class cls = instance ? self.class : self.flex_metaclass; | ||||
|     Method originalMethod = class_getInstanceMethod(cls, original); | ||||
|     Method newMethod = class_getInstanceMethod(cls, other); | ||||
|     if (class_addMethod(cls, original, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { | ||||
|         class_replaceMethod(cls, other, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); | ||||
|     } else { | ||||
|         method_exchangeImplementations(originalMethod, newMethod); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark Ivars | ||||
|  | ||||
| @implementation NSObject (Ivars) | ||||
|  | ||||
| + (NSArray<FLEXIvar *> *)flex_allIvars { | ||||
|     return FLEXGetAllIvars(self); | ||||
| } | ||||
|  | ||||
| + (FLEXIvar *)flex_ivarNamed:(NSString *)name { | ||||
|     Ivar i = class_getInstanceVariable([self class], name.UTF8String); | ||||
|     if (i == NULL) { | ||||
|         return nil; | ||||
|     } | ||||
|  | ||||
|     return [FLEXIvar ivar:i]; | ||||
| } | ||||
|  | ||||
| #pragma mark Get address | ||||
| - (void *)flex_getIvarAddress:(FLEXIvar *)ivar { | ||||
|     return (uint8_t *)(__bridge void *)self + ivar.offset; | ||||
| } | ||||
|  | ||||
| - (void *)flex_getObjcIvarAddress:(Ivar)ivar { | ||||
|     return (uint8_t *)(__bridge void *)self + ivar_getOffset(ivar); | ||||
| } | ||||
|  | ||||
| - (void *)flex_getIvarAddressByName:(NSString *)name { | ||||
|     Ivar ivar = class_getInstanceVariable(self.class, name.UTF8String); | ||||
|     if (!ivar) return 0; | ||||
|      | ||||
|     return (uint8_t *)(__bridge void *)self + ivar_getOffset(ivar); | ||||
| } | ||||
|  | ||||
| #pragma mark Set ivar object | ||||
| - (void)flex_setIvar:(FLEXIvar *)ivar object:(id)value { | ||||
|     object_setIvar(self, ivar.objc_ivar, value); | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_setIvarByName:(NSString *)name object:(id)value { | ||||
|     Ivar ivar = class_getInstanceVariable(self.class, name.UTF8String); | ||||
|     if (!ivar) return NO; | ||||
|      | ||||
|     object_setIvar(self, ivar, value); | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| - (void)flex_setObjcIvar:(Ivar)ivar object:(id)value { | ||||
|     object_setIvar(self, ivar, value); | ||||
| } | ||||
|  | ||||
| #pragma mark Set ivar value | ||||
| - (void)flex_setIvar:(FLEXIvar *)ivar value:(void *)value size:(size_t)size { | ||||
|     void *address = [self flex_getIvarAddress:ivar]; | ||||
|     memcpy(address, value, size); | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_setIvarByName:(NSString *)name value:(void *)value size:(size_t)size { | ||||
|     Ivar ivar = class_getInstanceVariable(self.class, name.UTF8String); | ||||
|     if (!ivar) return NO; | ||||
|      | ||||
|     [self flex_setObjcIvar:ivar value:value size:size]; | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| - (void)flex_setObjcIvar:(Ivar)ivar value:(void *)value size:(size_t)size { | ||||
|     void *address = [self flex_getObjcIvarAddress:ivar]; | ||||
|     memcpy(address, value, size); | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
| #pragma mark Properties | ||||
|  | ||||
| @implementation NSObject (Properties) | ||||
|  | ||||
| + (NSArray<FLEXProperty *> *)flex_allProperties { | ||||
|     NSMutableArray *instanceProperties = self.flex_allInstanceProperties.mutableCopy; | ||||
|     [instanceProperties addObjectsFromArray:self.flex_allClassProperties]; | ||||
|     return instanceProperties; | ||||
| } | ||||
|  | ||||
| + (NSArray<FLEXProperty *> *)flex_allInstanceProperties { | ||||
|     return FLEXGetAllProperties(self); | ||||
| } | ||||
|  | ||||
| + (NSArray<FLEXProperty *> *)flex_allClassProperties { | ||||
|     return FLEXGetAllProperties(self.flex_metaclass) ?: @[]; | ||||
| } | ||||
|  | ||||
| + (FLEXProperty *)flex_propertyNamed:(NSString *)name { | ||||
|     objc_property_t p = class_getProperty([self class], name.UTF8String); | ||||
|     if (p == NULL) { | ||||
|         return nil; | ||||
|     } | ||||
|  | ||||
|     return [FLEXProperty property:p onClass:self]; | ||||
| } | ||||
|  | ||||
| + (FLEXProperty *)flex_classPropertyNamed:(NSString *)name { | ||||
|     objc_property_t p = class_getProperty(object_getClass(self), name.UTF8String); | ||||
|     if (p == NULL) { | ||||
|         return nil; | ||||
|     } | ||||
|  | ||||
|     return [FLEXProperty property:p onClass:object_getClass(self)]; | ||||
| } | ||||
|  | ||||
| + (void)flex_replaceProperty:(FLEXProperty *)property { | ||||
|     [self flex_replaceProperty:property.name attributes:property.attributes]; | ||||
| } | ||||
|  | ||||
| + (void)flex_replaceProperty:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes { | ||||
|     unsigned int count; | ||||
|     objc_property_attribute_t *objc_attributes = [attributes copyAttributesList:&count]; | ||||
|     class_replaceProperty([self class], name.UTF8String, objc_attributes, count); | ||||
|     free(objc_attributes); | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
|  | ||||
							
								
								
									
										19
									
								
								Tweaks/FLEX/Utility/Categories/NSTimer+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Tweaks/FLEX/Utility/Categories/NSTimer+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| // | ||||
| //  NSTimer+Blocks.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 3/23/17. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
|  | ||||
| typedef void (^VoidBlock)(void); | ||||
|  | ||||
| @interface NSTimer (Blocks) | ||||
|  | ||||
| + (instancetype)flex_fireSecondsFromNow:(NSTimeInterval)delay block:(VoidBlock)block; | ||||
|  | ||||
| // Forward declaration | ||||
| //+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										25
									
								
								Tweaks/FLEX/Utility/Categories/NSTimer+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Tweaks/FLEX/Utility/Categories/NSTimer+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| // | ||||
| //  NSTimer+Blocks.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 3/23/17. | ||||
| // | ||||
|  | ||||
| #import "NSTimer+FLEX.h" | ||||
|  | ||||
| @interface Block : NSObject | ||||
| - (void)invoke; | ||||
| @end | ||||
|  | ||||
| #pragma clang diagnostic ignored "-Wincomplete-implementation" | ||||
| @implementation NSTimer (Blocks) | ||||
|  | ||||
| + (instancetype)flex_fireSecondsFromNow:(NSTimeInterval)delay block:(VoidBlock)block { | ||||
|     if (@available(iOS 10, *)) { | ||||
|         return [self scheduledTimerWithTimeInterval:delay repeats:NO block:(id)block]; | ||||
|     } else { | ||||
|         return [self scheduledTimerWithTimeInterval:delay target:block selector:@selector(invoke) userInfo:nil repeats:NO]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										51
									
								
								Tweaks/FLEX/Utility/Categories/NSUserDefaults+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								Tweaks/FLEX/Utility/Categories/NSUserDefaults+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| // | ||||
| //  NSUserDefaults+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 3/10/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
|  | ||||
| // Only use these if the getters and setters aren't good enough for whatever reason | ||||
| extern NSString * const kFLEXDefaultsToolbarTopMarginKey; | ||||
| extern NSString * const kFLEXDefaultsiOSPersistentOSLogKey; | ||||
| extern NSString * const kFLEXDefaultsHidePropertyIvarsKey; | ||||
| extern NSString * const kFLEXDefaultsHidePropertyMethodsKey; | ||||
| extern NSString * const kFLEXDefaultsHidePrivateMethodsKey; | ||||
| extern NSString * const kFLEXDefaultsShowMethodOverridesKey; | ||||
| extern NSString * const kFLEXDefaultsHideVariablePreviewsKey; | ||||
| extern NSString * const kFLEXDefaultsNetworkObserverEnabledKey; | ||||
| extern NSString * const kFLEXDefaultsNetworkHostDenylistKey; | ||||
| extern NSString * const kFLEXDefaultsDisableOSLogForceASLKey; | ||||
| extern NSString * const kFLEXDefaultsRegisterJSONExplorerKey; | ||||
|  | ||||
| /// All BOOL preferences are NO by default | ||||
| @interface NSUserDefaults (FLEX) | ||||
|  | ||||
| - (void)flex_toggleBoolForKey:(NSString *)key; | ||||
|  | ||||
| @property (nonatomic) double flex_toolbarTopMargin; | ||||
|  | ||||
| @property (nonatomic) BOOL flex_networkObserverEnabled; | ||||
| // Not actually stored in defaults, but written to a file | ||||
| @property (nonatomic) NSArray<NSString *> *flex_networkHostDenylist; | ||||
|  | ||||
| /// Whether or not to register the object explorer as a JSON viewer on launch | ||||
| @property (nonatomic) BOOL flex_registerDictionaryJSONViewerOnLaunch; | ||||
|  | ||||
| /// The last selected screen in the network observer | ||||
| @property (nonatomic) NSInteger flex_lastNetworkObserverMode; | ||||
|  | ||||
| /// Disable os_log and re-enable ASL. May break Console.app output. | ||||
| @property (nonatomic) BOOL flex_disableOSLog; | ||||
| @property (nonatomic) BOOL flex_cacheOSLogMessages; | ||||
|  | ||||
| @property (nonatomic) BOOL flex_explorerHidesPropertyIvars; | ||||
| @property (nonatomic) BOOL flex_explorerHidesPropertyMethods; | ||||
| @property (nonatomic) BOOL flex_explorerHidesPrivateMethods; | ||||
| @property (nonatomic) BOOL flex_explorerShowsMethodOverrides; | ||||
| @property (nonatomic) BOOL flex_explorerHidesVariablePreviews; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										188
									
								
								Tweaks/FLEX/Utility/Categories/NSUserDefaults+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								Tweaks/FLEX/Utility/Categories/NSUserDefaults+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| // | ||||
| //  NSUserDefaults+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 3/10/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "NSUserDefaults+FLEX.h" | ||||
|  | ||||
| NSString * const kFLEXDefaultsToolbarTopMarginKey = @"com.flex.FLEXToolbar.topMargin"; | ||||
| NSString * const kFLEXDefaultsiOSPersistentOSLogKey = @"com.flipborad.flex.enable_persistent_os_log"; | ||||
| NSString * const kFLEXDefaultsHidePropertyIvarsKey = @"com.flipboard.FLEX.hide_property_ivars"; | ||||
| NSString * const kFLEXDefaultsHidePropertyMethodsKey = @"com.flipboard.FLEX.hide_property_methods"; | ||||
| NSString * const kFLEXDefaultsHidePrivateMethodsKey = @"com.flipboard.FLEX.hide_private_or_namespaced_methods"; | ||||
| NSString * const kFLEXDefaultsShowMethodOverridesKey = @"com.flipboard.FLEX.show_method_overrides"; | ||||
| NSString * const kFLEXDefaultsHideVariablePreviewsKey = @"com.flipboard.FLEX.hide_variable_previews"; | ||||
| NSString * const kFLEXDefaultsNetworkObserverEnabledKey = @"com.flex.FLEXNetworkObserver.enableOnLaunch"; | ||||
| 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 kFLEXDefaultsRegisterJSONExplorerKey = @"com.flipboard.FLEX.view_json_as_object"; | ||||
|  | ||||
| #define FLEXDefaultsPathForFile(name) ({ \ | ||||
|     NSArray *paths = NSSearchPathForDirectoriesInDomains( \ | ||||
|         NSLibraryDirectory, NSUserDomainMask, YES \ | ||||
|     ); \ | ||||
|     [paths[0] stringByAppendingPathComponent:@"Preferences"]; \ | ||||
| }) | ||||
|  | ||||
| @implementation NSUserDefaults (FLEX) | ||||
|  | ||||
| #pragma mark Internal | ||||
|  | ||||
| /// @param filename the name of a plist file without any extension | ||||
| - (NSString *)flex_defaultsPathForFile:(NSString *)filename { | ||||
|     filename = [filename stringByAppendingPathExtension:@"plist"]; | ||||
|      | ||||
|     NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains( | ||||
|         NSLibraryDirectory, NSUserDomainMask, YES | ||||
|     ); | ||||
|     NSString *preferences = [paths[0] stringByAppendingPathComponent:@"Preferences"]; | ||||
|     return [preferences stringByAppendingPathComponent:filename]; | ||||
| } | ||||
|  | ||||
| #pragma mark Helper | ||||
|  | ||||
| - (void)flex_toggleBoolForKey:(NSString *)key { | ||||
|     [self setBool:![self boolForKey:key] forKey:key]; | ||||
|     [NSNotificationCenter.defaultCenter postNotificationName:key object:nil]; | ||||
| } | ||||
|  | ||||
| #pragma mark Misc | ||||
|  | ||||
| - (double)flex_toolbarTopMargin { | ||||
|     if ([self objectForKey:kFLEXDefaultsToolbarTopMarginKey]) { | ||||
|         return [self doubleForKey:kFLEXDefaultsToolbarTopMarginKey]; | ||||
|     } | ||||
|      | ||||
|     return 100; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_toolbarTopMargin:(double)margin { | ||||
|     [self setDouble:margin forKey:kFLEXDefaultsToolbarTopMarginKey]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_networkObserverEnabled { | ||||
|     return [self boolForKey:kFLEXDefaultsNetworkObserverEnabledKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_networkObserverEnabled:(BOOL)enabled { | ||||
|     [self setBool:enabled forKey:kFLEXDefaultsNetworkObserverEnabledKey]; | ||||
| } | ||||
|  | ||||
| - (NSArray<NSString *> *)flex_networkHostDenylist { | ||||
|     return [NSArray arrayWithContentsOfFile:[ | ||||
|         self flex_defaultsPathForFile:kFLEXDefaultsNetworkHostDenylistKey | ||||
|     ]] ?: @[]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_networkHostDenylist:(NSArray<NSString *> *)denylist { | ||||
|     NSParameterAssert(denylist); | ||||
|     [denylist writeToFile:[ | ||||
|         self flex_defaultsPathForFile:kFLEXDefaultsNetworkHostDenylistKey | ||||
|     ] atomically:YES]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_registerDictionaryJSONViewerOnLaunch { | ||||
|     return [self boolForKey:kFLEXDefaultsRegisterJSONExplorerKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_registerDictionaryJSONViewerOnLaunch:(BOOL)enable { | ||||
|     [self setBool:enable forKey:kFLEXDefaultsRegisterJSONExplorerKey]; | ||||
| } | ||||
|  | ||||
| - (NSInteger)flex_lastNetworkObserverMode { | ||||
|     return [self integerForKey:kFLEXDefaultsNetworkObserverLastModeKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_lastNetworkObserverMode:(NSInteger)mode { | ||||
|     [self setInteger:mode forKey:kFLEXDefaultsNetworkObserverLastModeKey]; | ||||
| } | ||||
|  | ||||
| #pragma mark System Log | ||||
|  | ||||
| - (BOOL)flex_disableOSLog { | ||||
|     return [self boolForKey:kFLEXDefaultsDisableOSLogForceASLKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_disableOSLog:(BOOL)disable { | ||||
|     [self setBool:disable forKey:kFLEXDefaultsDisableOSLogForceASLKey]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_cacheOSLogMessages { | ||||
|     return [self boolForKey:kFLEXDefaultsiOSPersistentOSLogKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_cacheOSLogMessages:(BOOL)cache { | ||||
|     [self setBool:cache forKey:kFLEXDefaultsiOSPersistentOSLogKey]; | ||||
|     [NSNotificationCenter.defaultCenter | ||||
|         postNotificationName:kFLEXDefaultsiOSPersistentOSLogKey | ||||
|         object:nil | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| #pragma mark Object Explorer | ||||
|  | ||||
| - (BOOL)flex_explorerHidesPropertyIvars { | ||||
|     return [self boolForKey:kFLEXDefaultsHidePropertyIvarsKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_explorerHidesPropertyIvars:(BOOL)hide { | ||||
|     [self setBool:hide forKey:kFLEXDefaultsHidePropertyIvarsKey]; | ||||
|     [NSNotificationCenter.defaultCenter | ||||
|         postNotificationName:kFLEXDefaultsHidePropertyIvarsKey | ||||
|         object:nil | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_explorerHidesPropertyMethods { | ||||
|     return [self boolForKey:kFLEXDefaultsHidePropertyMethodsKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_explorerHidesPropertyMethods:(BOOL)hide { | ||||
|     [self setBool:hide forKey:kFLEXDefaultsHidePropertyMethodsKey]; | ||||
|     [NSNotificationCenter.defaultCenter | ||||
|         postNotificationName:kFLEXDefaultsHidePropertyMethodsKey | ||||
|         object:nil | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_explorerHidesPrivateMethods { | ||||
|     return [self boolForKey:kFLEXDefaultsHidePrivateMethodsKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_explorerHidesPrivateMethods:(BOOL)show { | ||||
|     [self setBool:show forKey:kFLEXDefaultsHidePrivateMethodsKey]; | ||||
|     [NSNotificationCenter.defaultCenter | ||||
|      postNotificationName:kFLEXDefaultsHidePrivateMethodsKey | ||||
|         object:nil | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_explorerShowsMethodOverrides { | ||||
|     return [self boolForKey:kFLEXDefaultsShowMethodOverridesKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_explorerShowsMethodOverrides:(BOOL)show { | ||||
|     [self setBool:show forKey:kFLEXDefaultsShowMethodOverridesKey]; | ||||
|     [NSNotificationCenter.defaultCenter | ||||
|      postNotificationName:kFLEXDefaultsShowMethodOverridesKey | ||||
|         object:nil | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_explorerHidesVariablePreviews { | ||||
|     return [self boolForKey:kFLEXDefaultsHideVariablePreviewsKey]; | ||||
| } | ||||
|  | ||||
| - (void)setFlex_explorerHidesVariablePreviews:(BOOL)hide { | ||||
|     [self setBool:hide forKey:kFLEXDefaultsHideVariablePreviewsKey]; | ||||
|     [NSNotificationCenter.defaultCenter | ||||
|         postNotificationName:kFLEXDefaultsHideVariablePreviewsKey | ||||
|         object:nil | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										13
									
								
								Tweaks/FLEX/Utility/Categories/Private/Cocoa+FLEXShortcuts.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Tweaks/FLEX/Utility/Categories/Private/Cocoa+FLEXShortcuts.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| // | ||||
| //  Cocoa+FLEXShortcuts.h | ||||
| //  Pods | ||||
| // | ||||
| //  Created by Tanner on 2/24/21. | ||||
| //   | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| @interface UIAlertAction (FLEXShortcuts) | ||||
| @property (nonatomic, readonly) NSString *flex_styleName; | ||||
| @end | ||||
							
								
								
									
										25
									
								
								Tweaks/FLEX/Utility/Categories/Private/Cocoa+FLEXShortcuts.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Tweaks/FLEX/Utility/Categories/Private/Cocoa+FLEXShortcuts.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| // | ||||
| //  Cocoa+FLEXShortcuts.m | ||||
| //  Pods | ||||
| // | ||||
| //  Created by Tanner on 2/24/21. | ||||
| //   | ||||
| // | ||||
|  | ||||
| #import "Cocoa+FLEXShortcuts.h" | ||||
|  | ||||
| @implementation UIAlertAction (FLEXShortcuts) | ||||
| - (NSString *)flex_styleName { | ||||
|     switch (self.style) { | ||||
|         case UIAlertActionStyleDefault: | ||||
|             return @"Default style"; | ||||
|         case UIAlertActionStyleCancel: | ||||
|             return @"Cancel style"; | ||||
|         case UIAlertActionStyleDestructive: | ||||
|             return @"Destructive style"; | ||||
|              | ||||
|         default: | ||||
|             return [NSString stringWithFormat:@"Unknown (%@)", @(self.style)]; | ||||
|     } | ||||
| } | ||||
| @end | ||||
| @@ -0,0 +1,21 @@ | ||||
| // | ||||
| //  NSDictionary+ObjcRuntime.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Derived from MirrorKit. | ||||
| //  Created by Tanner on 7/5/15. | ||||
| //  Copyright (c) 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
| #import <objc/runtime.h> | ||||
|  | ||||
| @interface NSDictionary (ObjcRuntime) | ||||
|  | ||||
| /// \c kFLEXPropertyAttributeKeyTypeEncoding is the only required key. | ||||
| /// Keys representing a boolean value should have a value of \c YES instead of an empty string. | ||||
| - (NSString *)propertyAttributesString; | ||||
|  | ||||
| + (instancetype)attributesDictionaryForProperty:(objc_property_t)property; | ||||
|  | ||||
| @end | ||||
| @@ -0,0 +1,107 @@ | ||||
| // | ||||
| //  NSDictionary+ObjcRuntime.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Derived from MirrorKit. | ||||
| //  Created by Tanner on 7/5/15. | ||||
| //  Copyright (c) 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "NSDictionary+ObjcRuntime.h" | ||||
| #import "FLEXRuntimeUtility.h" | ||||
|  | ||||
| @implementation NSDictionary (ObjcRuntime) | ||||
|  | ||||
| /// See this link on how to construct a proper attributes string: | ||||
| /// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html | ||||
| - (NSString *)propertyAttributesString { | ||||
|     if (!self[kFLEXPropertyAttributeKeyTypeEncoding]) return nil; | ||||
|      | ||||
|     NSMutableString *attributes = [NSMutableString new]; | ||||
|     [attributes appendFormat:@"T%@,", self[kFLEXPropertyAttributeKeyTypeEncoding]]; | ||||
|      | ||||
|     for (NSString *attribute in self.allKeys) { | ||||
|         FLEXPropertyAttribute c = (FLEXPropertyAttribute)[attribute characterAtIndex:0]; | ||||
|         switch (c) { | ||||
|             case FLEXPropertyAttributeTypeEncoding: | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeBackingIvarName: | ||||
|                 [attributes appendFormat:@"%@%@,", | ||||
|                     kFLEXPropertyAttributeKeyBackingIvarName, | ||||
|                     self[kFLEXPropertyAttributeKeyBackingIvarName] | ||||
|                 ]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeCopy: | ||||
|                 if ([self[kFLEXPropertyAttributeKeyCopy] boolValue]) | ||||
|                 [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyCopy]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeCustomGetter: | ||||
|                 [attributes appendFormat:@"%@%@,", | ||||
|                     kFLEXPropertyAttributeKeyCustomGetter, | ||||
|                     self[kFLEXPropertyAttributeKeyCustomGetter] | ||||
|                 ]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeCustomSetter: | ||||
|                 [attributes appendFormat:@"%@%@,", | ||||
|                     kFLEXPropertyAttributeKeyCustomSetter, | ||||
|                     self[kFLEXPropertyAttributeKeyCustomSetter] | ||||
|                 ]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeDynamic: | ||||
|                 if ([self[kFLEXPropertyAttributeKeyDynamic] boolValue]) | ||||
|                 [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyDynamic]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeGarbageCollectible: | ||||
|                 [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyGarbageCollectable]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeNonAtomic: | ||||
|                 if ([self[kFLEXPropertyAttributeKeyNonAtomic] boolValue]) | ||||
|                 [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyNonAtomic]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeOldTypeEncoding: | ||||
|                 [attributes appendFormat:@"%@%@,", | ||||
|                     kFLEXPropertyAttributeKeyOldStyleTypeEncoding, | ||||
|                     self[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] | ||||
|                 ]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeReadOnly: | ||||
|                 if ([self[kFLEXPropertyAttributeKeyReadOnly] boolValue]) | ||||
|                 [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyReadOnly]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeRetain: | ||||
|                 if ([self[kFLEXPropertyAttributeKeyRetain] boolValue]) | ||||
|                 [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyRetain]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeWeak: | ||||
|                 if ([self[kFLEXPropertyAttributeKeyWeak] boolValue]) | ||||
|                 [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyWeak]; | ||||
|                 break; | ||||
|             default: | ||||
|                 return nil; | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     [attributes deleteCharactersInRange:NSMakeRange(attributes.length-1, 1)]; | ||||
|     return attributes.copy; | ||||
| } | ||||
|  | ||||
| + (instancetype)attributesDictionaryForProperty:(objc_property_t)property { | ||||
|     NSMutableDictionary *attrs = [NSMutableDictionary new]; | ||||
|  | ||||
|     for (NSString *key in FLEXRuntimeUtility.allPropertyAttributeKeys) { | ||||
|         char *value = property_copyAttributeValue(property, key.UTF8String); | ||||
|         if (value) { | ||||
|             attrs[key] = [[NSString alloc] | ||||
|                 initWithBytesNoCopy:value | ||||
|                 length:strlen(value) | ||||
|                 encoding:NSUTF8StringEncoding | ||||
|                 freeWhenDone:YES | ||||
|             ]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return attrs.copy; | ||||
| } | ||||
|  | ||||
| @end | ||||
| @@ -0,0 +1,20 @@ | ||||
| // | ||||
| //  NSMapTable+FLEX_Subscripting.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 1/9/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
|  | ||||
| NS_ASSUME_NONNULL_BEGIN | ||||
|  | ||||
| @interface NSMapTable<KeyType, ObjectType> (FLEX_Subscripting) | ||||
|  | ||||
| - (nullable ObjectType)objectForKeyedSubscript:(KeyType)key; | ||||
| - (void)setObject:(nullable ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; | ||||
|  | ||||
| @end | ||||
|  | ||||
| NS_ASSUME_NONNULL_END | ||||
| @@ -0,0 +1,21 @@ | ||||
| // | ||||
| //  NSMapTable+FLEX_Subscripting.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 1/9/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "NSMapTable+FLEX_Subscripting.h" | ||||
|  | ||||
| @implementation NSMapTable (FLEX_Subscripting) | ||||
|  | ||||
| - (id)objectForKeyedSubscript:(id)key { | ||||
|     return [self objectForKey:key]; | ||||
| } | ||||
|  | ||||
| - (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key { | ||||
|     [self setObject:obj forKey:key]; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										33
									
								
								Tweaks/FLEX/Utility/Categories/Private/NSString+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Tweaks/FLEX/Utility/Categories/Private/NSString+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| // | ||||
| //  NSString+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 3/26/17. | ||||
| //  Copyright © 2017 Tanner Bennett. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "FLEXRuntimeConstants.h" | ||||
|  | ||||
| @interface NSString (FLEXTypeEncoding) | ||||
|  | ||||
| ///@return whether this type starts with the const specifier | ||||
| @property (nonatomic, readonly) BOOL flex_typeIsConst; | ||||
| /// @return the first char in the type encoding that is not the const specifier | ||||
| @property (nonatomic, readonly) FLEXTypeEncoding flex_firstNonConstType; | ||||
| /// @return the first char in the type encoding after the pointer specifier, if it is a pointer | ||||
| @property (nonatomic, readonly) FLEXTypeEncoding flex_pointeeType; | ||||
| /// @return whether this type is an objc object of any kind, even if it's const | ||||
| @property (nonatomic, readonly) BOOL flex_typeIsObjectOrClass; | ||||
| /// @return the class named in this type encoding if it is of the form \c @"MYClass" | ||||
| @property (nonatomic, readonly) Class flex_typeClass; | ||||
| /// Includes C strings and selectors as well as regular pointers | ||||
| @property (nonatomic, readonly) BOOL flex_typeIsNonObjcPointer; | ||||
|  | ||||
| @end | ||||
|  | ||||
| @interface NSString (KeyPaths) | ||||
|  | ||||
| - (NSString *)flex_stringByRemovingLastKeyPathComponent; | ||||
| - (NSString *)flex_stringByReplacingLastKeyPathComponent:(NSString *)replacement; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										160
									
								
								Tweaks/FLEX/Utility/Categories/Private/NSString+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								Tweaks/FLEX/Utility/Categories/Private/NSString+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| // | ||||
| //  NSString+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 3/26/17. | ||||
| //  Copyright © 2017 Tanner Bennett. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "NSString+FLEX.h" | ||||
|  | ||||
| @interface NSMutableString (Replacement) | ||||
| - (void)replaceOccurencesOfString:(NSString *)string with:(NSString *)replacement; | ||||
| - (void)removeLastKeyPathComponent; | ||||
| @end | ||||
|  | ||||
| @implementation NSMutableString (Replacement) | ||||
|  | ||||
| - (void)replaceOccurencesOfString:(NSString *)string with:(NSString *)replacement { | ||||
|     [self replaceOccurrencesOfString:string withString:replacement options:0 range:NSMakeRange(0, self.length)]; | ||||
| } | ||||
|  | ||||
| - (void)removeLastKeyPathComponent { | ||||
|     if (![self containsString:@"."]) { | ||||
|         [self deleteCharactersInRange:NSMakeRange(0, self.length)]; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     BOOL putEscapesBack = NO; | ||||
|     if ([self containsString:@"\\."]) { | ||||
|         [self replaceOccurencesOfString:@"\\." with:@"\\~"]; | ||||
|  | ||||
|         // Case like "UIKit\.framework" | ||||
|         if (![self containsString:@"."]) { | ||||
|             [self deleteCharactersInRange:NSMakeRange(0, self.length)]; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         putEscapesBack = YES; | ||||
|     } | ||||
|  | ||||
|     // Case like "Bund" or "Bundle.cla" | ||||
|     if (![self hasSuffix:@"."]) { | ||||
|         NSUInteger len = self.pathExtension.length; | ||||
|         [self deleteCharactersInRange:NSMakeRange(self.length-len, len)]; | ||||
|     } | ||||
|  | ||||
|     if (putEscapesBack) { | ||||
|         [self replaceOccurencesOfString:@"\\~" with:@"\\."]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation NSString (FLEXTypeEncoding) | ||||
|  | ||||
| - (NSCharacterSet *)flex_classNameAllowedCharactersSet { | ||||
|     static NSCharacterSet *classNameAllowedCharactersSet = nil; | ||||
|     static dispatch_once_t onceToken; | ||||
|     dispatch_once(&onceToken, ^{ | ||||
|         NSMutableCharacterSet *temp = NSMutableCharacterSet.alphanumericCharacterSet; | ||||
|         [temp addCharactersInString:@"_"]; | ||||
|         classNameAllowedCharactersSet = temp.copy; | ||||
|     }); | ||||
|      | ||||
|     return classNameAllowedCharactersSet; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_typeIsConst { | ||||
|     if (!self.length) return NO; | ||||
|     return [self characterAtIndex:0] == FLEXTypeEncodingConst; | ||||
| } | ||||
|  | ||||
| - (FLEXTypeEncoding)flex_firstNonConstType { | ||||
|     if (!self.length) return FLEXTypeEncodingNull; | ||||
|     return [self characterAtIndex:(self.flex_typeIsConst ? 1 : 0)]; | ||||
| } | ||||
|  | ||||
| - (FLEXTypeEncoding)flex_pointeeType { | ||||
|     if (!self.length) return FLEXTypeEncodingNull; | ||||
|      | ||||
|     if (self.flex_firstNonConstType == FLEXTypeEncodingPointer) { | ||||
|         return [self characterAtIndex:(self.flex_typeIsConst ? 2 : 1)]; | ||||
|     } | ||||
|      | ||||
|     return FLEXTypeEncodingNull; | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_typeIsObjectOrClass { | ||||
|     FLEXTypeEncoding type = self.flex_firstNonConstType; | ||||
|     return type == FLEXTypeEncodingObjcObject || type == FLEXTypeEncodingObjcClass; | ||||
| } | ||||
|  | ||||
| - (Class)flex_typeClass { | ||||
|     if (!self.flex_typeIsObjectOrClass) { | ||||
|         return nil; | ||||
|     } | ||||
|      | ||||
|     NSScanner *scan = [NSScanner scannerWithString:self]; | ||||
|     // Skip const | ||||
|     [scan scanString:@"r" intoString:nil]; | ||||
|     // Scan leading @" | ||||
|     if (![scan scanString:@"@\"" intoString:nil]) { | ||||
|         return nil; | ||||
|     } | ||||
|      | ||||
|     // Scan class name | ||||
|     NSString *name = nil; | ||||
|     if (![scan scanCharactersFromSet:self.flex_classNameAllowedCharactersSet intoString:&name]) { | ||||
|         return nil; | ||||
|     } | ||||
|     // Scan trailing quote | ||||
|     if (![scan scanString:@"\"" intoString:nil]) { | ||||
|         return nil; | ||||
|     } | ||||
|      | ||||
|     // Return found class | ||||
|     return NSClassFromString(name); | ||||
| } | ||||
|  | ||||
| - (BOOL)flex_typeIsNonObjcPointer { | ||||
|     FLEXTypeEncoding type = self.flex_firstNonConstType; | ||||
|     return type == FLEXTypeEncodingPointer || | ||||
|            type == FLEXTypeEncodingCString || | ||||
|            type == FLEXTypeEncodingSelector; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation NSString (KeyPaths) | ||||
|  | ||||
| - (NSString *)flex_stringByRemovingLastKeyPathComponent { | ||||
|     if (![self containsString:@"."]) { | ||||
|         return @""; | ||||
|     } | ||||
|  | ||||
|     NSMutableString *mself = self.mutableCopy; | ||||
|     [mself removeLastKeyPathComponent]; | ||||
|     return mself; | ||||
| } | ||||
|  | ||||
| - (NSString *)flex_stringByReplacingLastKeyPathComponent:(NSString *)replacement { | ||||
|     // replacement should not have any escaped '.' in it, | ||||
|     // so we escape all '.' | ||||
|     if ([replacement containsString:@"."]) { | ||||
|         replacement = [replacement stringByReplacingOccurrencesOfString:@"." withString:@"\\."]; | ||||
|     } | ||||
|  | ||||
|     // Case like "Foo" | ||||
|     if (![self containsString:@"."]) { | ||||
|         return [replacement stringByAppendingString:@"."]; | ||||
|     } | ||||
|  | ||||
|     NSMutableString *mself = self.mutableCopy; | ||||
|     [mself removeLastKeyPathComponent]; | ||||
|     [mself appendString:replacement]; | ||||
|     [mself appendString:@"."]; | ||||
|     return mself; | ||||
| } | ||||
|  | ||||
| @end | ||||
| @@ -0,0 +1,23 @@ | ||||
| // | ||||
| //  NSString+ObjcRuntime.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Derived from MirrorKit. | ||||
| //  Created by Tanner on 7/1/15. | ||||
| //  Copyright (c) 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
|  | ||||
| @interface NSString (Utilities) | ||||
|  | ||||
| /// A dictionary of property attributes if the receiver is a valid property attributes string. | ||||
| /// Values are either a string or \c YES. Boolean attributes which are false will not be | ||||
| /// present in the dictionary. See this link on how to construct a proper attributes string: | ||||
| /// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html | ||||
| /// | ||||
| /// Note: this method doesn't work properly for certain type encodings, and neither does | ||||
| /// the property_copyAttributeValue function in the runtime itself. Radar: FB7499230 | ||||
| - (NSDictionary *)propertyAttributes; | ||||
|  | ||||
| @end | ||||
| @@ -0,0 +1,75 @@ | ||||
| // | ||||
| //  NSString+ObjcRuntime.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Derived from MirrorKit. | ||||
| //  Created by Tanner on 7/1/15. | ||||
| //  Copyright (c) 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "NSString+ObjcRuntime.h" | ||||
| #import "FLEXRuntimeUtility.h" | ||||
|  | ||||
| @implementation NSString (Utilities) | ||||
|  | ||||
| - (NSString *)stringbyDeletingCharacterAtIndex:(NSUInteger)idx { | ||||
|     NSMutableString *string = self.mutableCopy; | ||||
|     [string replaceCharactersInRange:NSMakeRange(idx, 1) withString:@""]; | ||||
|     return string; | ||||
| } | ||||
|  | ||||
| /// See this link on how to construct a proper attributes string: | ||||
| /// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html | ||||
| - (NSDictionary *)propertyAttributes { | ||||
|     if (!self.length) return nil; | ||||
|      | ||||
|     NSMutableDictionary *attributes = [NSMutableDictionary new]; | ||||
|      | ||||
|     NSArray *components = [self componentsSeparatedByString:@","]; | ||||
|     for (NSString *attribute in components) { | ||||
|         FLEXPropertyAttribute c = (FLEXPropertyAttribute)[attribute characterAtIndex:0]; | ||||
|         switch (c) { | ||||
|             case FLEXPropertyAttributeTypeEncoding: | ||||
|                 // Note: the type encoding here is not always correct. Radar: FB7499230 | ||||
|                 attributes[kFLEXPropertyAttributeKeyTypeEncoding] = [attribute stringbyDeletingCharacterAtIndex:0]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeBackingIvarName: | ||||
|                 attributes[kFLEXPropertyAttributeKeyBackingIvarName] = [attribute stringbyDeletingCharacterAtIndex:0]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeCopy: | ||||
|                 attributes[kFLEXPropertyAttributeKeyCopy] = @YES; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeCustomGetter: | ||||
|                 attributes[kFLEXPropertyAttributeKeyCustomGetter] = [attribute stringbyDeletingCharacterAtIndex:0]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeCustomSetter: | ||||
|                 attributes[kFLEXPropertyAttributeKeyCustomSetter] = [attribute stringbyDeletingCharacterAtIndex:0]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeDynamic: | ||||
|                 attributes[kFLEXPropertyAttributeKeyDynamic] = @YES; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeGarbageCollectible: | ||||
|                 attributes[kFLEXPropertyAttributeKeyGarbageCollectable] = @YES; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeNonAtomic: | ||||
|                 attributes[kFLEXPropertyAttributeKeyNonAtomic] = @YES; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeOldTypeEncoding: | ||||
|                 attributes[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] = [attribute stringbyDeletingCharacterAtIndex:0]; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeReadOnly: | ||||
|                 attributes[kFLEXPropertyAttributeKeyReadOnly] = @YES; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeRetain: | ||||
|                 attributes[kFLEXPropertyAttributeKeyRetain] = @YES; | ||||
|                 break; | ||||
|             case FLEXPropertyAttributeWeak: | ||||
|                 attributes[kFLEXPropertyAttributeKeyWeak] = @YES; | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return attributes; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										23
									
								
								Tweaks/FLEX/Utility/Categories/Private/UIView+FLEX_Layout.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Tweaks/FLEX/Utility/Categories/Private/UIView+FLEX_Layout.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // | ||||
| //  UIView+FLEX_Layout.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 7/18/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| #define Padding(p) UIEdgeInsetsMake(p, p, p, p) | ||||
|  | ||||
| @interface UIView (FLEX_Layout) | ||||
|  | ||||
| - (void)flex_centerInView:(UIView *)view; | ||||
| - (void)flex_pinEdgesTo:(UIView *)view; | ||||
| - (void)flex_pinEdgesTo:(UIView *)view withInsets:(UIEdgeInsets)insets; | ||||
| - (void)flex_pinEdgesToSuperview; | ||||
| - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets; | ||||
| - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets aboveView:(UIView *)sibling; | ||||
| - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets belowView:(UIView *)sibling; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										66
									
								
								Tweaks/FLEX/Utility/Categories/Private/UIView+FLEX_Layout.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								Tweaks/FLEX/Utility/Categories/Private/UIView+FLEX_Layout.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| // | ||||
| //  UIView+FLEX_Layout.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 7/18/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "UIView+FLEX_Layout.h" | ||||
|  | ||||
| @implementation UIView (FLEX_Layout) | ||||
|  | ||||
| - (void)flex_centerInView:(UIView *)view { | ||||
|     [NSLayoutConstraint activateConstraints:@[ | ||||
|         [self.centerXAnchor constraintEqualToAnchor:view.centerXAnchor], | ||||
|         [self.centerYAnchor constraintEqualToAnchor:view.centerYAnchor], | ||||
|     ]]; | ||||
| } | ||||
|  | ||||
| - (void)flex_pinEdgesTo:(UIView *)view { | ||||
|    [NSLayoutConstraint activateConstraints:@[ | ||||
|        [self.topAnchor constraintEqualToAnchor:view.topAnchor], | ||||
|        [self.leftAnchor constraintEqualToAnchor:view.leftAnchor], | ||||
|        [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor], | ||||
|        [self.rightAnchor constraintEqualToAnchor:view.rightAnchor], | ||||
|    ]];  | ||||
| } | ||||
|  | ||||
| - (void)flex_pinEdgesTo:(UIView *)view withInsets:(UIEdgeInsets)i { | ||||
|     [NSLayoutConstraint activateConstraints:@[ | ||||
|         [self.topAnchor constraintEqualToAnchor:view.topAnchor constant:i.top], | ||||
|         [self.leftAnchor constraintEqualToAnchor:view.leftAnchor constant:i.left], | ||||
|         [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:-i.bottom], | ||||
|         [self.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:-i.right], | ||||
|     ]]; | ||||
| } | ||||
|  | ||||
| - (void)flex_pinEdgesToSuperview { | ||||
|     [self flex_pinEdgesTo:self.superview]; | ||||
| } | ||||
|  | ||||
| - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets { | ||||
|     [self flex_pinEdgesTo:self.superview withInsets:insets]; | ||||
| } | ||||
|  | ||||
| - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)i aboveView:(UIView *)sibling { | ||||
|     UIView *view = self.superview; | ||||
|     [NSLayoutConstraint activateConstraints:@[ | ||||
|         [self.topAnchor constraintEqualToAnchor:view.topAnchor constant:i.top], | ||||
|         [self.leftAnchor constraintEqualToAnchor:view.leftAnchor constant:i.left], | ||||
|         [self.bottomAnchor constraintEqualToAnchor:sibling.topAnchor constant:-i.bottom], | ||||
|         [self.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:-i.right], | ||||
|     ]]; | ||||
| } | ||||
|  | ||||
| - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)i belowView:(UIView *)sibling { | ||||
|     UIView *view = self.superview; | ||||
|     [NSLayoutConstraint activateConstraints:@[ | ||||
|         [self.topAnchor constraintEqualToAnchor:sibling.bottomAnchor constant:i.top], | ||||
|         [self.leftAnchor constraintEqualToAnchor:view.leftAnchor constant:i.left], | ||||
|         [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:-i.bottom], | ||||
|         [self.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:-i.right], | ||||
|     ]]; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										40
									
								
								Tweaks/FLEX/Utility/Categories/UIBarButtonItem+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								Tweaks/FLEX/Utility/Categories/UIBarButtonItem+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| // | ||||
| //  UIBarButtonItem+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 2/4/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| #define FLEXBarButtonItem(title, tgt, sel) \ | ||||
|     [UIBarButtonItem flex_itemWithTitle:title target:tgt action:sel] | ||||
| #define FLEXBarButtonItemSystem(item, tgt, sel) \ | ||||
|     [UIBarButtonItem flex_systemItem:UIBarButtonSystemItem##item target:tgt action:sel] | ||||
|  | ||||
| @interface UIBarButtonItem (FLEX) | ||||
|  | ||||
| @property (nonatomic, readonly, class) UIBarButtonItem *flex_flexibleSpace; | ||||
| @property (nonatomic, readonly, class) UIBarButtonItem *flex_fixedSpace; | ||||
|  | ||||
| + (instancetype)flex_itemWithCustomView:(UIView *)customView; | ||||
| + (instancetype)flex_backItemWithTitle:(NSString *)title; | ||||
|  | ||||
| + (instancetype)flex_systemItem:(UIBarButtonSystemItem)item target:(id)target action:(SEL)action; | ||||
|  | ||||
| + (instancetype)flex_itemWithTitle:(NSString *)title target:(id)target action:(SEL)action; | ||||
| + (instancetype)flex_doneStyleitemWithTitle:(NSString *)title target:(id)target action:(SEL)action; | ||||
|  | ||||
| + (instancetype)flex_itemWithImage:(UIImage *)image target:(id)target action:(SEL)action; | ||||
|  | ||||
| + (instancetype)flex_disabledSystemItem:(UIBarButtonSystemItem)item; | ||||
| + (instancetype)flex_disabledItemWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style; | ||||
| + (instancetype)flex_disabledItemWithImage:(UIImage *)image; | ||||
|  | ||||
| /// @return the receiver | ||||
| - (UIBarButtonItem *)flex_withTintColor:(UIColor *)tint; | ||||
|  | ||||
| - (void)_setWidth:(CGFloat)width; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										72
									
								
								Tweaks/FLEX/Utility/Categories/UIBarButtonItem+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								Tweaks/FLEX/Utility/Categories/UIBarButtonItem+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| // | ||||
| //  UIBarButtonItem+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 2/4/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "UIBarButtonItem+FLEX.h" | ||||
|  | ||||
| #pragma clang diagnostic ignored "-Wincomplete-implementation" | ||||
|  | ||||
| @implementation UIBarButtonItem (FLEX) | ||||
|  | ||||
| + (UIBarButtonItem *)flex_flexibleSpace { | ||||
|     return [self flex_systemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; | ||||
| } | ||||
|  | ||||
| + (UIBarButtonItem *)flex_fixedSpace { | ||||
|     UIBarButtonItem *fixed = [self flex_systemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; | ||||
|     fixed.width = 60; | ||||
|     return fixed; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_systemItem:(UIBarButtonSystemItem)item target:(id)target action:(SEL)action { | ||||
|     return [[self alloc] initWithBarButtonSystemItem:item target:target action:action]; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_itemWithCustomView:(UIView *)customView { | ||||
|     return [[self alloc] initWithCustomView:customView]; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_backItemWithTitle:(NSString *)title { | ||||
|     return [self flex_itemWithTitle:title target:nil action:nil]; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_itemWithTitle:(NSString *)title target:(id)target action:(SEL)action { | ||||
|     return [[self alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:target action:action]; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_doneStyleitemWithTitle:(NSString *)title target:(id)target action:(SEL)action { | ||||
|     return [[self alloc] initWithTitle:title style:UIBarButtonItemStyleDone target:target action:action]; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_itemWithImage:(UIImage *)image target:(id)target action:(SEL)action { | ||||
|     return [[self alloc] initWithImage:image style:UIBarButtonItemStylePlain target:target action:action]; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_disabledSystemItem:(UIBarButtonSystemItem)system { | ||||
|     UIBarButtonItem *item = [self flex_systemItem:system target:nil action:nil]; | ||||
|     item.enabled = NO; | ||||
|     return item; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_disabledItemWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style { | ||||
|     UIBarButtonItem *item = [self flex_itemWithTitle:title target:nil action:nil]; | ||||
|     item.enabled = NO; | ||||
|     return item; | ||||
| } | ||||
|  | ||||
| + (instancetype)flex_disabledItemWithImage:(UIImage *)image { | ||||
|     UIBarButtonItem *item = [self flex_itemWithImage:image target:nil action:nil]; | ||||
|     item.enabled = NO; | ||||
|     return item; | ||||
| } | ||||
|  | ||||
| - (UIBarButtonItem *)flex_withTintColor:(UIColor *)tint { | ||||
|     self.tintColor = tint; | ||||
|     return self; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										17
									
								
								Tweaks/FLEX/Utility/Categories/UIFont+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Tweaks/FLEX/Utility/Categories/UIFont+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| // | ||||
| //  UIFont+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/20/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| @interface UIFont (FLEX) | ||||
|  | ||||
| @property (nonatomic, readonly, class) UIFont *flex_defaultTableCellFont; | ||||
| @property (nonatomic, readonly, class) UIFont *flex_codeFont; | ||||
| @property (nonatomic, readonly, class) UIFont *flex_smallCodeFont; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										43
									
								
								Tweaks/FLEX/Utility/Categories/UIFont+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Tweaks/FLEX/Utility/Categories/UIFont+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| // | ||||
| //  UIFont+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/20/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "UIFont+FLEX.h" | ||||
|  | ||||
| #define kFLEXDefaultCellFontSize 12.0 | ||||
|  | ||||
| @implementation UIFont (FLEX) | ||||
|  | ||||
| + (UIFont *)flex_defaultTableCellFont { | ||||
|     static UIFont *defaultTableCellFont = nil; | ||||
|     static dispatch_once_t onceToken; | ||||
|     dispatch_once(&onceToken, ^{ | ||||
|         defaultTableCellFont = [UIFont systemFontOfSize:kFLEXDefaultCellFontSize]; | ||||
|     }); | ||||
|  | ||||
|     return defaultTableCellFont; | ||||
| } | ||||
|  | ||||
| + (UIFont *)flex_codeFont { | ||||
|     // Actually only available in iOS 13, the SDK headers are wrong | ||||
|     if (@available(iOS 13, *)) { | ||||
|         return [self monospacedSystemFontOfSize:kFLEXDefaultCellFontSize weight:UIFontWeightRegular]; | ||||
|     } else { | ||||
|         return [self fontWithName:@"Menlo-Regular" size:kFLEXDefaultCellFontSize]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| + (UIFont *)flex_smallCodeFont { | ||||
|         // Actually only available in iOS 13, the SDK headers are wrong | ||||
|     if (@available(iOS 13, *)) { | ||||
|         return [self monospacedSystemFontOfSize:self.smallSystemFontSize weight:UIFontWeightRegular]; | ||||
|     } else { | ||||
|         return [self fontWithName:@"Menlo-Regular" size:self.smallSystemFontSize]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										21
									
								
								Tweaks/FLEX/Utility/Categories/UIGestureRecognizer+Blocks.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Tweaks/FLEX/Utility/Categories/UIGestureRecognizer+Blocks.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| // | ||||
| //  UIGestureRecognizer+Blocks.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/20/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| typedef void (^GestureBlock)(UIGestureRecognizer *gesture); | ||||
|  | ||||
|  | ||||
| @interface UIGestureRecognizer (Blocks) | ||||
|  | ||||
| + (instancetype)flex_action:(GestureBlock)action; | ||||
|  | ||||
| @property (nonatomic, setter=flex_setAction:) GestureBlock flex_action; | ||||
|  | ||||
| @end | ||||
|  | ||||
							
								
								
									
										36
									
								
								Tweaks/FLEX/Utility/Categories/UIGestureRecognizer+Blocks.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								Tweaks/FLEX/Utility/Categories/UIGestureRecognizer+Blocks.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| // | ||||
| //  UIGestureRecognizer+Blocks.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/20/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "UIGestureRecognizer+Blocks.h" | ||||
| #import <objc/runtime.h> | ||||
|  | ||||
|  | ||||
| @implementation UIGestureRecognizer (Blocks) | ||||
|  | ||||
| static void * actionKey; | ||||
|  | ||||
| + (instancetype)flex_action:(GestureBlock)action { | ||||
|     UIGestureRecognizer *gesture = [[self alloc] initWithTarget:nil action:nil]; | ||||
|     [gesture addTarget:gesture action:@selector(flex_invoke)]; | ||||
|     gesture.flex_action = action; | ||||
|     return gesture; | ||||
| } | ||||
|  | ||||
| - (void)flex_invoke { | ||||
|     self.flex_action(self); | ||||
| } | ||||
|  | ||||
| - (GestureBlock)flex_action { | ||||
|     return objc_getAssociatedObject(self, &actionKey); | ||||
| } | ||||
|  | ||||
| - (void)flex_setAction:(GestureBlock)action { | ||||
|     objc_setAssociatedObject(self, &actionKey, action, OBJC_ASSOCIATION_COPY); | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										19
									
								
								Tweaks/FLEX/Utility/Categories/UIMenu+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Tweaks/FLEX/Utility/Categories/UIMenu+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| // | ||||
| //  UIMenu+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 1/28/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| @interface UIMenu (FLEX) | ||||
|  | ||||
| + (instancetype)flex_inlineMenuWithTitle:(NSString *)title | ||||
|                                    image:(UIImage *)image | ||||
|                                 children:(NSArray<UIMenuElement *> *)children; | ||||
|  | ||||
| - (instancetype)flex_collapsed; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										39
									
								
								Tweaks/FLEX/Utility/Categories/UIMenu+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Tweaks/FLEX/Utility/Categories/UIMenu+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| // | ||||
| //  UIMenu+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 1/28/20. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "UIMenu+FLEX.h" | ||||
|  | ||||
| @implementation UIMenu (FLEX) | ||||
|  | ||||
| + (instancetype)flex_inlineMenuWithTitle:(NSString *)title image:(UIImage *)image children:(NSArray *)children { | ||||
|     return [UIMenu | ||||
|         menuWithTitle:title | ||||
|         image:image | ||||
|         identifier:nil | ||||
|         options:UIMenuOptionsDisplayInline | ||||
|         children:children | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| - (instancetype)flex_collapsed { | ||||
|     return [UIMenu | ||||
|         menuWithTitle:@"" | ||||
|         image:nil | ||||
|         identifier:nil | ||||
|         options:UIMenuOptionsDisplayInline | ||||
|         children:@[[UIMenu | ||||
|             menuWithTitle:self.title | ||||
|             image:self.image | ||||
|             identifier:self.identifier | ||||
|             options:0 | ||||
|             children:self.children | ||||
|         ]] | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										16
									
								
								Tweaks/FLEX/Utility/Categories/UIPasteboard+FLEX.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Tweaks/FLEX/Utility/Categories/UIPasteboard+FLEX.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| // | ||||
| //  UIPasteboard+FLEX.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/9/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| @interface UIPasteboard (FLEX) | ||||
|  | ||||
| /// For copying an object which could be a string, data, or number | ||||
| - (void)flex_copy:(id)unknownType; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										31
									
								
								Tweaks/FLEX/Utility/Categories/UIPasteboard+FLEX.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Tweaks/FLEX/Utility/Categories/UIPasteboard+FLEX.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| // | ||||
| //  UIPasteboard+FLEX.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 12/9/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "UIPasteboard+FLEX.h" | ||||
|  | ||||
| @implementation UIPasteboard (FLEX) | ||||
|  | ||||
| - (void)flex_copy:(id)object { | ||||
|     if (!object) { | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     if ([object isKindOfClass:[NSString class]]) { | ||||
|         UIPasteboard.generalPasteboard.string = object; | ||||
|     } else if([object isKindOfClass:[NSData class]]) { | ||||
|         [UIPasteboard.generalPasteboard setData:object forPasteboardType:@"public.data"]; | ||||
|     } else if ([object isKindOfClass:[NSNumber class]]) { | ||||
|         UIPasteboard.generalPasteboard.string = [object stringValue]; | ||||
|     } else { | ||||
|         // TODO: make this an alert instead of an exception | ||||
|         [NSException raise:NSInternalInconsistencyException | ||||
|                     format:@"Tried to copy unsupported type: %@", [object class]]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										14
									
								
								Tweaks/FLEX/Utility/Categories/UITextField+Range.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Tweaks/FLEX/Utility/Categories/UITextField+Range.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| // | ||||
| //  UITextField+Range.h | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 6/13/17. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| @interface UITextField (Range) | ||||
|  | ||||
| @property (nonatomic, readonly) NSRange flex_selectedRange; | ||||
|  | ||||
| @end | ||||
							
								
								
									
										23
									
								
								Tweaks/FLEX/Utility/Categories/UITextField+Range.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Tweaks/FLEX/Utility/Categories/UITextField+Range.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // | ||||
| //  UITextField+Range.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner on 6/13/17. | ||||
| // | ||||
|  | ||||
| #import "UITextField+Range.h" | ||||
|  | ||||
| @implementation UITextField (Range) | ||||
|  | ||||
| - (NSRange)flex_selectedRange { | ||||
|     UITextRange *r = self.selectedTextRange; | ||||
|     if (r) { | ||||
|         NSInteger loc = [self offsetFromPosition:self.beginningOfDocument toPosition:r.start]; | ||||
|         NSInteger len = [self offsetFromPosition:r.start toPosition:r.end]; | ||||
|         return NSMakeRange(loc, len); | ||||
|     } | ||||
|  | ||||
|     return NSMakeRange(NSNotFound, 0); | ||||
| } | ||||
|  | ||||
| @end | ||||
		Reference in New Issue
	
	Block a user
	 Balackburn
					Balackburn