Skip to content

Commit

Permalink
added a new metadata property that will hold all the key/values for t…
Browse files Browse the repository at this point in the history
…hat node. Also code refactoring (#27)
  • Loading branch information
yarneo authored Mar 12, 2018
1 parent 62f5b0b commit 6c44e44
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 166 deletions.
10 changes: 5 additions & 5 deletions example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ EXTERNAL SOURCES:
:path: components/Resistor

SPEC CHECKSUMS:
CatalogByConvention: 1df2d770271921f668a99245c7c4c129e78941ee
CatalogExamples: cafe3e4eae3abc948d96beb626657455c1dfb327
CatalogUnitTests: b7a746f12abb31a905654521ee926ea007ab7275
Resistor: 36a9ae98666be3b4f34d8133fad442fa87fdbce2
CatalogByConvention: f4b95f8905470807a5022eabd1d3d9ce07f6a66f
CatalogExamples: 7a95e6ea7befbd43c5ceb1427a9b161f6d8fc34e
CatalogUnitTests: 2fbf7f2e894dd3777f11573a7a5314adb1e4fad7
Resistor: a17e39cab5f42993c2b3ede22ce3829b707a9ac8

PODFILE CHECKSUM: bb59c09c71f8777bbe79af5ae920e3d58849ab41

COCOAPODS: 1.3.1
COCOAPODS: 1.4.0
30 changes: 21 additions & 9 deletions src/CBCCatalogExample.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,47 @@
*/
@protocol CBCCatalogExample <NSObject>

/**
Returns a dictionary with metaata information for the example.
*/
+ (nonnull NSDictionary<NSString *, NSObject *> *)catalogMetadata;

@optional

/** Return a list of breadcrumbs defining the navigation path taken to reach this example. */
+ (nonnull NSArray<NSString *> *)catalogBreadcrumbs;
+ (nonnull NSArray<NSString *> *)catalogBreadcrumbs
__attribute__((deprecated("use catalogMetadata[CBCBreadcrumbs] instead.")));

/**
Return a BOOL stating whether this example should be treated as the primary demo of the component.
*/
+ (BOOL)catalogIsPrimaryDemo;
+ (BOOL)catalogIsPrimaryDemo
__attribute__((deprecated("use catalogMetadata[CBCIsPrimaryDemo] instead.")));;

/**
Return a BOOL stating whether this example is presentable and should be part of the catalog app.
*/
+ (BOOL)catalogIsPresentable;
+ (BOOL)catalogIsPresentable
__attribute__((deprecated("use catalogMetadata[CBCIsPresentable] instead.")));

/**
Return a BOOL stating whether this example is in debug mode and should appear as the initial view controller.
*/
+ (BOOL)catalogIsDebug;

@optional
+ (BOOL)catalogIsDebug
__attribute__((deprecated("use catalogMetadata[CBCIsDebug] instead.")));

/**
Return the name of a UIStoryboard from which the example's view controller should be instantiated.
*/
- (nonnull NSString *)catalogStoryboardName;
- (nonnull NSString *)catalogStoryboardName
__attribute__((deprecated("use catalogMetadata[CBCStoryboardName] instead.")));

/** Return a description of the example. */
- (nonnull NSString *)catalogDescription;
- (nonnull NSString *)catalogDescription
__attribute__((deprecated("use catalogMetadata[CBCDescription] instead.")));

/** Return a link to related information or resources. */
- (nonnull NSURL *)catalogRelatedInfo;
- (nonnull NSURL *)catalogRelatedInfo
__attribute__((deprecated("use catalogMetadata[CBCRelatedInfo] instead.")));

@end
27 changes: 23 additions & 4 deletions src/CBCNodeListViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@

#import <UIKit/UIKit.h>

/** This key represents a strings array of the breadcrumbs showing the hierarchy of the example */
FOUNDATION_EXTERN NSString *_Nonnull const CBCBreadcrumbs;
/** This key represents a boolean value if the example is for debugging */
FOUNDATION_EXTERN NSString *_Nonnull const CBCIsDebug;
/** This key represents a string for the description for the example */
FOUNDATION_EXTERN NSString *_Nonnull const CBCDescription;
/** This key represents a boolean value if to present the example in the Catalog app or not */
FOUNDATION_EXTERN NSString *_Nonnull const CBCIsPresentable;
/** This key represents a boolean value if the example is the primary demo */
FOUNDATION_EXTERN NSString *_Nonnull const CBCIsPrimaryDemo;
/** This key represents an NSURL value providing related info for the example */
FOUNDATION_EXTERN NSString *_Nonnull const CBCRelatedInfo;
/** This key represents a string value of the storyboard name for the example */
FOUNDATION_EXTERN NSString *_Nonnull const CBCStoryboardName;

@class CBCNode;

/**
Expand Down Expand Up @@ -69,9 +84,6 @@ FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreatePresentableNavigationTree(void);
/** The title for this node. */
@property(nonatomic, copy, nonnull, readonly) NSString *title;

/** The description for this node. */
@property(nonatomic, copy, nonnull, readonly) NSString *nodeDescription;

/** The children of this node. */
@property(nonatomic, strong, nonnull) NSArray<CBCNode *> *children;

Expand All @@ -83,6 +95,13 @@ FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreatePresentableNavigationTree(void);
*/
@property(nonatomic, strong, nullable) CBCNode *debugLeaf;

/**
This NSDictionary holds all the metadata related to this CBCNode.
If it is an example noe, a primary demo, related info,
if presentable in Catalog, etc.
*/
@property(nonatomic, strong, nonnull) NSDictionary *metadata;

/** Returns YES if this is an example node. */
- (BOOL)isExample;

Expand Down Expand Up @@ -111,7 +130,7 @@ FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreatePresentableNavigationTree(void);
Check that isExample returns YES before invoking.
*/
- (nonnull NSString *)exampleDescription;
- (nullable NSString *)exampleDescription;

/** Returns a link to related information for the example. */
- (nullable NSURL *)exampleRelatedInfo;
Expand Down
162 changes: 84 additions & 78 deletions src/CBCNodeListViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,21 @@
#import "CBCCatalogExample.h"
#import "private/CBCRuntime.h"

void CBCAddNodeFromBreadCrumbs(CBCNode *tree, NSArray<NSString *> *breadCrumbs, Class aClass);
@interface CBCNode()
@property(nonatomic, strong, nullable) NSMutableDictionary *map;
@property(nonatomic, strong, nullable) Class exampleClass;
@end

@implementation CBCNode {
NSMutableDictionary *_map;
NSMutableArray *_children;
Class _exampleClass;
BOOL _isPresentable;
}

- (instancetype)initWithTitle:(NSString *)title {
self = [super init];
if (self) {
_title = [title copy];
_map = [NSMutableDictionary dictionary];
self.map = [NSMutableDictionary dictionary];
_children = [NSMutableArray array];
_isPresentable = NO;
CBCFixViewDebuggingIfNeeded();
}
return self;
Expand All @@ -45,72 +44,75 @@ - (NSComparisonResult)compare:(CBCNode *)otherObject {
}

- (void)addChild:(CBCNode *)child {
_map[child.title] = child;
self.map[child.title] = child;
[_children addObject:child];
}

- (NSDictionary *)map {
return _map;
}

- (void)setExampleClass:(Class)exampleClass {
_exampleClass = exampleClass;
}

- (void)setIsPresentable:(Class)exampleClass {
_isPresentable = CBCCatalogIsPresentableFromClass(exampleClass);
}

- (void)finalizeNode {
_children = [[_children sortedArrayUsingSelector:@selector(compare:)] mutableCopy];
}

#pragma mark Public

- (BOOL)isExample {
return _exampleClass != nil;
return self.exampleClass != nil;
}

- (NSString *)exampleViewControllerName {
NSAssert(self.exampleClass != nil, @"This node has no associated example.");
return NSStringFromClass(_exampleClass);
}

- (UIViewController *)createExampleViewController {
NSAssert(_exampleClass != nil, @"This node has no associated example.");
return CBCViewControllerFromClass(_exampleClass);
NSAssert(self.exampleClass != nil, @"This node has no associated example.");
return CBCViewControllerFromClass(self.exampleClass, self.metadata);
}

- (NSString *)exampleDescription {
NSAssert(_exampleClass != nil, @"This node has no associated example.");
return CBCDescriptionFromClass(_exampleClass);
NSString *description = [self.metadata objectForKey:CBCDescription];
if (description != nil && [description isKindOfClass:[NSString class]]) {
return description;
}
return nil;
}

- (NSURL *)exampleRelatedInfo {
NSAssert(_exampleClass != nil, @"This node has no associated example.");
return CBCRelatedInfoFromClass(_exampleClass);
NSURL *relatedInfo = [self.metadata objectForKey:CBCRelatedInfo];
if (relatedInfo != nil && [relatedInfo isKindOfClass:[NSURL class]]) {
return relatedInfo;
}
return nil;
}

- (BOOL)isPrimaryDemo {
return CBCCatalogIsPrimaryDemoFromClass(_exampleClass);
id isPrimaryDemo;
if ((isPrimaryDemo = [self.metadata objectForKey:CBCIsPrimaryDemo]) != nil) {
return [isPrimaryDemo boolValue];
}
return NO;
}

- (BOOL)isPresentable {
return _isPresentable;
id isPresentable;
if ((isPresentable = [self.metadata objectForKey:CBCIsPresentable]) != nil) {
return [isPresentable boolValue];
}
return NO;
}

@end

@implementation CBCNodeListViewController

- (instancetype)initWithNode:(CBCNode *)node {
NSAssert(!_node.isExample, @"%@ cannot represent example nodes.",
NSAssert(!self.node.isExample, @"%@ cannot represent example nodes.",
NSStringFromClass([self class]));

self = [super initWithNibName:nil bundle:nil];
if (self) {
_node = node;

self.title = _node.title;
self.title = self.node.title;
}
return self;
}
Expand Down Expand Up @@ -154,7 +156,7 @@ - (void)viewDidAppear:(BOOL)animated {
#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return (NSInteger)[_node.children count];
return (NSInteger)[self.node.children count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView
Expand All @@ -164,15 +166,15 @@ - (UITableViewCell *)tableView:(UITableView *)tableView
cell =
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
cell.textLabel.text = [_node.children[(NSUInteger)indexPath.row] title];
cell.textLabel.text = [self.node.children[(NSUInteger)indexPath.row] title];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CBCNode *node = _node.children[(NSUInteger)indexPath.row];
CBCNode *node = self.node.children[(NSUInteger)indexPath.row];
UIViewController *viewController = nil;
if ([node isExample]) {
viewController = [node createExampleViewController];
Expand All @@ -184,30 +186,61 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath

@end

static CBCNode *CBCCreateTreeWithOnlyPresentable(BOOL onlyPresentable) {
NSArray *allClasses = CBCGetAllClasses();
NSArray *breadcrumbClasses = CBCClassesRespondingToSelector(allClasses,
@selector(catalogBreadcrumbs));
NSArray *classes;
if (onlyPresentable) {
classes = [breadcrumbClasses filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
return CBCCatalogIsPresentableFromClass(object);
}]];
} else {
classes = breadcrumbClasses;
static void CBCAddNodeFromBreadCrumbs(CBCNode *tree,
NSArray<NSString *> *breadCrumbs,
Class aClass,
NSDictionary *metadata) {
// Walk down the navigation tree one breadcrumb at a time, creating nodes along the way.

CBCNode *node = tree;
for (NSUInteger ix = 0; ix < [breadCrumbs count]; ++ix) {
NSString *title = breadCrumbs[ix];
BOOL isLastCrumb = ix == [breadCrumbs count] - 1;

// Don't walk the last crumb
if (node.map[title] && !isLastCrumb) {
node = node.map[title];
continue;
}

CBCNode *child = [[CBCNode alloc] initWithTitle:title];
[node addChild:child];
child.metadata = metadata;
if ([[child.metadata objectForKey:CBCIsPrimaryDemo] boolValue] == YES) {
node.metadata = child.metadata;
}
if ([[child.metadata objectForKey:CBCIsDebug] boolValue] == YES) {
tree.debugLeaf = child;
}
node = child;
}
CBCNode *tree = [[CBCNode alloc] initWithTitle:@"Root"];
for (Class aClass in classes) {
// Each example view controller defines its own "breadcrumbs".

NSArray *breadCrumbs = CBCCatalogBreadcrumbsFromClass(aClass);
node.exampleClass = aClass;
}

static CBCNode *CBCCreateTreeWithOnlyPresentable(BOOL onlyPresentable) {
NSArray *allClasses = CBCGetAllCompatibleClasses();
NSArray *filteredClasses = [allClasses filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
NSDictionary *metadata = CBCCatalogMetadataFromClass(object);
id breadcrumbs = [metadata objectForKey:CBCBreadcrumbs];
BOOL validObject = breadcrumbs != nil && [breadcrumbs isKindOfClass:[NSArray class]];
if (onlyPresentable) {
validObject &= ([[metadata objectForKey:CBCIsPresentable] boolValue] == YES);
}
return validObject;
}]];

CBCNode *tree = [[CBCNode alloc] initWithTitle:@"Root"];
for (Class aClass in filteredClasses) {
// Each example view controller defines its own breadcrumbs (metadata[CBCBreadcrumbs]).
NSDictionary *metadata = CBCCatalogMetadataFromClass(aClass);
NSArray *breadCrumbs = [metadata objectForKey:CBCBreadcrumbs];
if ([[breadCrumbs firstObject] isKindOfClass:[NSString class]]) {
CBCAddNodeFromBreadCrumbs(tree, breadCrumbs, aClass);
CBCAddNodeFromBreadCrumbs(tree, breadCrumbs, aClass, metadata);
} else if ([[breadCrumbs firstObject] isKindOfClass:[NSArray class]]) {
for (NSArray<NSString *> *parallelBreadCrumb in breadCrumbs) {
CBCAddNodeFromBreadCrumbs(tree, parallelBreadCrumb, aClass);
CBCAddNodeFromBreadCrumbs(tree, parallelBreadCrumb, aClass, metadata);
}
}
}
Expand All @@ -232,30 +265,3 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
CBCNode *CBCCreatePresentableNavigationTree(void) {
return CBCCreateTreeWithOnlyPresentable(YES);
}

void CBCAddNodeFromBreadCrumbs(CBCNode *tree, NSArray<NSString *> *breadCrumbs, Class aClass) {
// Walk down the navigation tree one breadcrumb at a time, creating nodes along the way.

CBCNode *node = tree;
for (NSUInteger ix = 0; ix < [breadCrumbs count]; ++ix) {
NSString *title = breadCrumbs[ix];
BOOL isLastCrumb = ix == [breadCrumbs count] - 1;

// Don't walk the last crumb

if (node.map[title] && !isLastCrumb) {
node = node.map[title];
continue;
}

CBCNode *child = [[CBCNode alloc] initWithTitle:title];
[node addChild:child];
[node setIsPresentable:aClass];
if (CBCCatalogIsDebugLeaf(aClass)) {
tree.debugLeaf = child;
}
node = child;
}

node.exampleClass = aClass;
}
Loading

0 comments on commit 6c44e44

Please sign in to comment.