Просмотр исходного кода

Consolidate metadata row logic

Consolidate the logic between FLEXShortcut and FLEXMetadataSection for how metadata rows are displayed.

There was a lot of duplication between these two classes before this. Now there is one source of truth: the object itself will tell you how to display itself via our categories in FLEXRuntime+UIKitHelpers.
Tanner Bennett лет назад: 6
Родитель
Сommit
fc9874ece6

+ 0 - 3
Classes/ObjectExplorers/FLEXObjectExplorer.h

@@ -52,7 +52,4 @@
 - (void)reloadMetadata;
 - (void)reloadClassHierarchy;
 
-- (id)valueForProperty:(FLEXProperty *)property;
-- (id)valueForIvar:(FLEXIvar *)ivar;
-
 @end

+ 0 - 27
Classes/ObjectExplorers/FLEXObjectExplorer.m

@@ -195,33 +195,6 @@
 }
 
 
-#pragma mark Values
-
-- (id)valueForProperty:(FLEXProperty *)property {
-    if (self.objectIsInstance) {
-        if (property.isClassProperty) {
-            return [property getPotentiallyUnboxedValue:[self.object class]];
-        }
-        return [property getPotentiallyUnboxedValue:self.object];
-    } else {
-        if (property.isClassProperty) {
-            return [property getPotentiallyUnboxedValue:self.object];
-        } else {
-            // Instance property with a class object
-            return nil;
-        }
-    }
-}
-
-- (id)valueForIvar:(FLEXIvar *)ivar {
-    if (self.objectIsInstance) {
-        return [ivar getPotentiallyUnboxedValue:self.object];
-    }
-    
-    return nil;
-}
-
-
 #pragma mark - Superclasses
 
 - (void)reloadClassHierarchy {

+ 6 - 112
Classes/ObjectExplorers/Sections/FLEXMetadataSection.m

@@ -57,56 +57,6 @@
     }
 }
 
-- (__kindof UIViewController *)detailScreenForMetadata:(id)metadata {
-    id target = self.explorer.object;
-
-    switch (self.metadataKind) {
-        case FLEXMetadataKindClassProperties:
-            if (self.explorer.objectIsInstance) {
-                target = [target class];
-            }
-            // Fallthrough
-        case FLEXMetadataKindProperties:
-        case FLEXMetadataKindIvars: {
-            // This works for both properties and ivars, we just
-            // cast so the compiler knows what return type to expect since
-            // this method has the same name as -[NSValue getValue:] (void *)
-            id currentValue = [(FLEXIvar *)metadata getPotentiallyUnboxedValue:target];
-            return [FLEXObjectExplorerFactory explorerViewControllerForObject:currentValue];
-        }
-        case FLEXMetadataKindClassMethods:
-            if (self.explorer.objectIsInstance) {
-                target = [target class];
-            }
-            // Fallthrough
-        case FLEXMetadataKindMethods: {
-            return [FLEXMethodCallingViewController target:target method:metadata];
-        }
-    }
-}
-
-// Only for properties or ivars
-- (id)valueForRow:(NSInteger)row {
-    id metadata = self.metadata[row];
-
-    // We use -[FLEXObjectExplorer valueFor...:] instead of getValue: below
-    // because we want to "preview" what object is being stored if this is
-    // a void * or something and we're given an NSValue back from getValue:
-    switch (self.metadataKind) {
-        case FLEXMetadataKindProperties:
-            return [self.explorer valueForProperty:metadata];
-        case FLEXMetadataKindClassProperties:
-            return [self.explorer valueForProperty:metadata];
-        case FLEXMetadataKindIvars:
-            return [self.explorer valueForIvar:metadata];
-
-        // Methods: nil
-        case FLEXMetadataKindMethods:
-        case FLEXMetadataKindClassMethods:
-            return nil;
-    }
-}
-
 - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row {
     return [self.metadata[row] suggestedAccessoryTypeWithTarget:self.explorer.object];
 }
@@ -125,24 +75,7 @@
 }
 
 - (NSString *)subtitleForRow:(NSInteger)row {
-    id metadata = self.metadata[row];
-
-    // We use -[FLEXObjectExplorer valueFor...:] instead of getValue: below
-    // because we want to "preview" what object is being stored if this is
-    // a void * or something and we're given an NSValue back from getValue:
-    switch (self.metadataKind) {
-        case FLEXMetadataKindClassProperties:
-            return [FLEXRuntimeUtility summaryForObject:[self valueForRow:row]];
-        case FLEXMetadataKindProperties:
-        case FLEXMetadataKindIvars:
-            if (!self.explorer.objectIsInstance) {
-                return nil;
-            }
-            return [FLEXRuntimeUtility summaryForObject:[self valueForRow:row]];
-        case FLEXMetadataKindMethods:
-        case FLEXMetadataKindClassMethods:
-            return [metadata selectorString];
-    }
+    return [self.metadata[row] previewWithTarget:self.explorer.object];
 }
 
 - (NSString *)title {
@@ -212,18 +145,9 @@
 }
 
 - (BOOL)canSelectRow:(NSInteger)row {
-    switch (self.metadataKind) {
-        case FLEXMetadataKindProperties:
-        case FLEXMetadataKindIvars:
-            if (![self valueForRow:row]) {
-                return NO;
-            }
-        case FLEXMetadataKindMethods:
-            return self.explorer.objectIsInstance;
-        case FLEXMetadataKindClassMethods:
-        case FLEXMetadataKindClassProperties:
-            return YES;
-    }
+    UITableViewCellAccessoryType accessory = [self accessoryTypeForRow:row];
+    return accessory == UITableViewCellAccessoryDisclosureIndicator ||
+        accessory == UITableViewCellAccessoryDetailDisclosureButton;
 }
 
 - (NSString *)reuseIdentifierForRow:(NSInteger)row {
@@ -231,7 +155,7 @@
 }
 
 - (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
-    return [self detailScreenForMetadata:self.metadata[row]];
+    return [self.metadata[row] viewerWithTarget:self.explorer.object];
 }
 
 - (void (^)(UIViewController *))didPressInfoButtonAction:(NSInteger)row {
@@ -241,37 +165,7 @@
 }
 
 - (UIViewController *)editorForRow:(NSInteger)row {
-    NSAssert(
-        self.metadataKind == FLEXMetadataKindIvars ||
-        self.metadataKind == FLEXMetadataKindProperties ||
-        self.metadataKind == FLEXMetadataKindClassProperties,
-        @"Only ivars or properties can be edited"
-    );
-
-    id metadata = self.metadata[row];
-
-    // Nil editor means unsupported ivar or property type, or nil value
-    switch (self.metadataKind) {
-        case FLEXMetadataKindProperties:
-            NSAssert(self.explorer.objectIsInstance, @"Unreachable state: class object editing instance property");
-            return [FLEXFieldEditorViewController target:self.explorer.object property:metadata];
-        case FLEXMetadataKindClassProperties:
-            if (self.explorer.objectIsInstance) {
-                return [FLEXFieldEditorViewController target:[self.explorer.object class] property:metadata];
-            } else {
-                return [FLEXFieldEditorViewController target:self.explorer.object property:metadata];
-            }
-        case FLEXMetadataKindIvars:
-            NSAssert(self.explorer.objectIsInstance, @"Unreachable state: class object editing ivar");
-            return [FLEXFieldEditorViewController target:self.explorer.object ivar:metadata];
-
-        default:
-            break;
-    }
-
-    // Methods can't be edited
-    @throw NSInternalInconsistencyException;
-    return nil;
+    return [self.metadata[row] editorWithTarget:self.explorer.object];
 }
 
 - (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row {

+ 18 - 118
Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.m

@@ -57,46 +57,7 @@
 }
 
 - (id)propertyOrIvarValue:(id)object {
-    BOOL objectIsInstance = !object_isClass(object);
-
-    // We use -[FLEXObjectExplorer valueFor...:] instead of getValue: below
-    // because we want to "preview" what object is being stored if this is
-    // a void * or something and we're given an NSValue back from getValue:
-    switch (self.metadataKind) {
-        case FLEXMetadataKindProperties:
-        case FLEXMetadataKindClassProperties: {
-            // Decide whether to use object or [object class] to check for a potential value
-            id targetForValueCheck = nil;
-            if (objectIsInstance) {
-                if (self.property.isClassProperty) {
-                    targetForValueCheck = [object class];
-                } else {
-                    targetForValueCheck = object;
-                }
-            } else {
-                if (self.property.isClassProperty) {
-                    targetForValueCheck = object;
-                } else {
-                    // Instance property with a class object
-                    return nil;
-                }
-            }
-
-            return [self.property getPotentiallyUnboxedValue:targetForValueCheck];
-        }
-        case FLEXMetadataKindIvars: {
-            if (objectIsInstance) {
-                return [self.ivar getPotentiallyUnboxedValue:object];
-            }
-
-            return nil;
-        }
-
-        // Methods: nil
-        case FLEXMetadataKindMethods:
-        case FLEXMetadataKindClassMethods:
-            return nil;
-    }
+    return [self.metadata currentValueWithTarget:object];
 }
 
 - (NSString *)titleWith:(id)object {
@@ -105,99 +66,38 @@
         case FLEXMetadataKindProperties:
             // Since we're outside of the "properties" section, prepend @property for clarity.
             return [@"@property " stringByAppendingString:[_item description]];
-        case FLEXMetadataKindIvars:
-        case FLEXMetadataKindMethods:
-            return [_item description];
 
         default:
-            break;
+            return [_item description];
     }
 
-    if ([_item isKindOfClass:[NSString class]]) {
-        return _item;
-    }
+    NSAssert(
+        [_item isKindOfClass:[NSString class]],
+        @"Unexpected type: %@", [_item class]
+    );
 
-    [NSException
-        raise:NSInvalidArgumentException
-        format:@"Unsupported shortcut '%@':\n%@",
-        [_item class], [_item description]
-    ];
-    return nil;
+    return _item;
 }
 
-- (NSString *)subtitleWith:(id)fromObject {
-    switch (self.metadataKind) {
-        case FLEXMetadataKindProperties:
-        case FLEXMetadataKindIvars:
-            return [FLEXRuntimeUtility
-                summaryForObject:[self propertyOrIvarValue:fromObject]
-            ];
-        case FLEXMetadataKindMethods:
-        case FLEXMetadataKindClassMethods:
-            return [_item selectorString];
-
-        default:
-            break;
-    }
-
-    if ([_item isKindOfClass:[NSString class]]) {
-        // Must return empty string since these will be
-        // gathered into an array. If the object is a
-        // just a string, it doesn't get a subtitle.
-        return @"";
+- (NSString *)subtitleWith:(id)object {
+    if (self.metadataKind) {
+        return [self.metadata previewWithTarget:object];
     }
 
-    [NSException
-        raise:NSInvalidArgumentException
-        format:@"Unsupported shortcut '%@':\n%@",
-        [_item class], [_item description]
-    ];
-    return nil;
+    // Item is probably a string; must return empty string since
+    // these will be gathered into an array. If the object is a
+    // just a string, it doesn't get a subtitle.
+    return @"";
 }
 
 - (UIViewController *)viewerWith:(id)object {
-    // View or edit a property or ivar
-    switch (self.metadataKind) {
-        case FLEXMetadataKindProperties:
-        case FLEXMetadataKindIvars:
-            return [FLEXObjectExplorerFactory
-                explorerViewControllerForObject:[self propertyOrIvarValue:object]
-            ];
-        case FLEXMetadataKindMethods:
-        case FLEXMetadataKindClassMethods:
-            object = self.method.isInstanceMethod ? object : (object_isClass(object) ? object : [object class]);
-            return [FLEXMethodCallingViewController target:object method:self.method];
-
-        default:
-            return nil;
-    }
-
-    return nil;
+    NSAssert(self.metadataKind, @"Static titles cannot be viewed");
+    return [self.metadata viewerWithTarget:object];
 }
 
 - (UIViewController *)editorWith:(id)object {
-    BOOL objectIsInstance = !object_isClass(object);
-    switch (self.metadataKind) {
-        case FLEXMetadataKindProperties:
-            NSAssert(objectIsInstance, @"Unreachable state: class object editing instance property");
-            return [FLEXFieldEditorViewController target:object property:self.property];
-        case FLEXMetadataKindClassProperties:
-            if (objectIsInstance) {
-                return [FLEXFieldEditorViewController target:[object class] property:self.property];
-            } else {
-                return [FLEXFieldEditorViewController target:object property:self.property];
-            }
-        case FLEXMetadataKindIvars:
-            NSAssert(objectIsInstance, @"Unreachable state: class object editing ivar");
-            return [FLEXFieldEditorViewController target:object ivar:self.ivar];
-
-        default:
-            break;
-    }
-
-    // Methods can't be edited
-    @throw NSInternalInconsistencyException;
-    return nil;
+    NSAssert(self.metadataKind, @"Static titles cannot be edited");
+    return [self.metadata editorWithTarget:object];
 }
 
 - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {

+ 3 - 18
Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.m

@@ -198,19 +198,13 @@
 }
 
 - (NSString *)reuseIdentifierForRow:(NSInteger)row {
-    if (self.subtitles[row].length) {
-        // Title+subtitle: properties, ivars, methods, custom
-        return kFLEXMultilineDetailCell;
-    }
-
-    // Just a title string
-    return kFLEXMultilineCell;
+    return kFLEXMultilineDetailCell;
 }
 
 - (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row {
-    cell.titleLabel.text = self.titles[row];
+    cell.titleLabel.text = [self titleForRow:row];
     cell.titleLabel.numberOfLines = self.numberOfLines;
-    cell.subtitleLabel.text = self.subtitles[row];
+    cell.subtitleLabel.text = [self subtitleForRow:row];
     cell.subtitleLabel.numberOfLines = self.numberOfLines;
     cell.accessoryType = [self accessoryTypeForRow:row];
 }
@@ -229,15 +223,6 @@
     return self.subtitles[row];
 }
 
-// Not sure what this was for, maybe I added filterText after the fact?
-//- (BOOL)row:(NSInteger)row matchesFilter:(NSString *)query {
-//    if ([self.titles[row] localizedCaseInsensitiveContainsString:query]) {
-//        return YES;
-//    }
-//
-//    return [self.subtitles[row] localizedCaseInsensitiveContainsString:query];
-//}
-
 @end
 
 

+ 10 - 0
Classes/Utility/Categories/FLEXRuntime+UIKitHelpers.h

@@ -20,7 +20,17 @@
 /// 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, nil. For all else, an a field editor screen.
+- (UIViewController *)editorWithTarget:(id)object;
+/// Used to determine present which interactions are possible to the user
 - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object;
+
 @end
 
 // Even if a property is readonly, it still may be editable

+ 98 - 17
Classes/Utility/Categories/FLEXRuntime+UIKitHelpers.m

@@ -10,9 +10,30 @@
 #import "FLEXRuntimeUtility.h"
 #import "FLEXPropertyAttributes.h"
 #import "FLEXArgumentInputViewFactory.h"
+#import "FLEXObjectExplorerFactory.h"
+#import "FLEXFieldEditorViewController.h"
+#import "FLEXMethodCallingViewController.h"
 
 @implementation FLEXProperty (UIKitHelpers)
 
+/// Decide whether to use object or [object class] to get or set property
+- (id)targetForPropertyTypeGivenObject:(id)object {
+    if (!object_isClass(object)) {
+        if (self.isClassProperty) {
+            return [object class];
+        } else {
+            return object;
+        }
+    } else {
+        if (self.isClassProperty) {
+            return object;
+        } else {
+            // Instance property with a class object
+            return nil;
+        }
+    }
+}
+
 - (BOOL)isEditable {
     if (self.attributes.isReadOnly) {
         return self.likelySetterExists;
@@ -26,24 +47,33 @@
     return YES;
 }
 
-- (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
-    BOOL objectIsInstance = !object_isClass(object);
+- (id)currentValueWithTarget:(id)object {
+    return [self getPotentiallyUnboxedValue:
+        [self targetForPropertyTypeGivenObject:object]
+    ];
+}
 
-    // Decide whether to use object or [object class] to check for a potential value
-    id targetForValueCheck = nil;
-    if (objectIsInstance) {
-        if (self.isClassProperty) {
-            targetForValueCheck = [object class];
-        } else {
-            targetForValueCheck = object;
-        }
-    } else {
-        if (self.isClassProperty) {
-            targetForValueCheck = object;
-        } else {
-            // Instance property with a class object
-            return UITableViewCellAccessoryNone;
-        }
+- (NSString *)previewWithTarget:(id)object {
+    return [FLEXRuntimeUtility
+        summaryForObject:[self currentValueWithTarget:object]
+    ];
+}
+
+- (UIViewController *)viewerWithTarget:(id)object {
+    id value = [self currentValueWithTarget:object];
+    return [FLEXObjectExplorerFactory explorerViewControllerForObject:value];
+}
+
+- (UIViewController *)editorWithTarget:(id)object {
+    id target = [self targetForPropertyTypeGivenObject:object];
+    return [FLEXFieldEditorViewController target:target property:self];
+}
+
+- (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
+    id targetForValueCheck = [self targetForPropertyTypeGivenObject:object];
+    if (!targetForValueCheck) {
+        // Instance property with a class object
+        return UITableViewCellAccessoryNone;
     }
 
     // We use .tag to store the cached value of .isEditable that is
@@ -81,6 +111,31 @@
     return NO;
 }
 
+- (id)currentValueWithTarget:(id)object {
+    if (!object_isClass(object)) {
+        return [self getPotentiallyUnboxedValue:object];
+    }
+
+    return nil;
+}
+
+- (NSString *)previewWithTarget:(id)object {
+    return [FLEXRuntimeUtility
+        summaryForObject:[self currentValueWithTarget:object]
+    ];
+}
+
+- (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 {
+    NSAssert(!object_isClass(object), @"Unreachable state: editing ivar on class object");
+    return [FLEXFieldEditorViewController target:object ivar:self];
+}
+
 - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
     if (object_isClass(object)) {
         return UITableViewCellAccessoryNone;
@@ -119,6 +174,27 @@
     return NO;
 }
 
+- (id)currentValueWithTarget:(id)object {
+    // Methods can't be "edited" and have no "value"
+    return nil;
+}
+
+- (NSString *)previewWithTarget:(id)object {
+    return self.selectorString;
+}
+
+- (UIViewController *)viewerWithTarget:(id)object {
+    // We disallow calling of FLEXMethodBase methods
+    @throw NSInternalInconsistencyException;
+    return nil;
+}
+
+- (UIViewController *)editorWithTarget:(id)object {
+    // Methods cannot be edited
+    @throw NSInternalInconsistencyException;
+    return nil;
+}
+
 - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object {
     // We shouldn't be using any FLEXMethodBase objects for this
     @throw NSInternalInconsistencyException;
@@ -133,6 +209,11 @@
     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)) {

+ 4 - 0
Classes/Utility/Runtime/FLEXProperty.m

@@ -216,6 +216,8 @@
 }
 
 - (id)getValue:(id)target {
+    if (!target) return nil;
+    
     // We don't care about checking dynamically whether the getter
     // _now_ exists on this object. If the getter doesn't exist
     // when this property is initialized, it will never call it.
@@ -234,6 +236,8 @@
 }
 
 - (id)getPotentiallyUnboxedValue:(id)target {
+    if (!target) return nil;
+
     return [FLEXRuntimeUtility
         potentiallyUnwrapBoxedPointer:[self getValue:target]
         type:self.attributes.typeEncoding.UTF8String